@gypark - 개인 자료라고 믿기 어려울 정도의 방대한 Perl 자료를 제공하고 있는 gypark.pe.kr의 주인장, Raymundo라는 닉을 사용하기도 한다.
예전에 리눅스 서버에서 만든 CGI를 윈도우에서 써 보고 싶은데, 짧은 CGI 스크립트를 하나 쓰자고 Apache 웹 서버를 설치하는 건 과하다 싶었습니다. 그렇다고 요즘 많이 쓴다는 Dancer나 Mojolicious 같은 웹 프레임워크를 따로 배워서 새로 스크립트를 작성하자니 이것도 배보다 배꼽이 더 큰 느낌이었죠. 그런데 Plack을 사용하여 기존 CGI 스크립트를 그대로 실행할 수 있었습니다. 이 글에서는 간단한 예를 통해 그 과정을 설명합니다.
사용하는 모듈은 아래와 같습니다.
사실 저는 여전히 잘 모르겠습니다. :-) 다만 문서에서 읽은 설명에 의하면 아래와 같습니다.
윈도우7에 Strawberry Perl 5.14를 쓰고 있는데, Plack 모듈의 경우 설치할 때 테스트에서 무수히 많은 에러가 나서 설치가 안 되었습니다. 정확한 이유는 모르겠고, 일단은 notest force install Plack
명령을 써서 강제로 설치해 주었는데, 큰 문제는 없었습니다.
간단한 CGI 스크립트 예제입니다. 이 코드 자체가 중요한 건 아니지만, 읽는 분들이 곧바로 실험해 볼 수 있게 코드 전체를 적었습니다. 이 CGI 스크립트는 간단한 텍스트 파일 관리자입니다. 다음과 같은 일을 합니다.
*.txt
파일의 내용을 보여줍니다.원래는 실험 결과 이미지를 포함한 HTML 파일 여러 개를 한 눈에 보면서 불 필요한 걸 지우고 중요한 건 다른 폴더로 옮기는 용도로 만들었던 스크립트였습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | #!/usr/bin/env perl # index.pl # 현재 폴더 안의 모든 텍스트 파일의 내용을 보여주거나 # 검색한 단어가 들어 있는 파일만 보여주거나 # 선택한 파일들을 삭제하는 스크립트 use strict; use warnings; # no warnings 'redefine'; use CGI; my $HEADING_STYLE = 'background-color: #aaaacc; margin-top: 10pt; padding: 2pt' ; my $q = new CGI; main: { print $q ->header(-type => "text/html; charset=UTF-8" ); # html 헤더 print << EOF ; <head> <title>Manage text files</title> <meta http-equiv= "Content-Type" content= "text/html; charset=UTF-8" > </head> <body> <p> EOF # 검색 form print $q ->start_form(-action=> "./index.pl" ). "\n" . "Text Search : " . $q ->textfield( 'content' , '' , 20, 20) . "\n" . "<br /><br />\n" . $q ->submit( 'search' , '검 색' ) . "\n" ; # 선택한 파일 삭제 if ( $q ->param( 'delete' ) ) { print "<hr />\n" ; print "<ul>\n" ; foreach my $file ( $q ->param( 'delete_files' ) ) { unlink $file ; print "<li>delete $file</li>\n" ; } print "</ul>\n" ; } # 모든 파일 또는 검색 텍스트를 포함한 파일들 출력 my @matches = sort glob ( "*.txt" ); my $content = $q ->param( 'content' ) || "" ; if ( $q ->param( 'search' ) and $content ) { # 파일내용 검색 @matches = grep { text_match( $_ , $content ) } @matches ; } print "<hr />\n" ; foreach my $file ( @matches ) { print $q ->h1({-style=> $HEADING_STYLE }, $file ); print $q ->checkbox(-name => 'delete_files' , label => 'Delete?' , -value => $file ), "<p>\n" ; print "<pre>\n" , file_content( $file ), "\n</pre>\n" ; } print "<p>" ; print $q ->submit( 'delete' , '선택한 파일 삭제' ); print $q ->end_form(); # html 나머지 부분 출력 print << EOF ; </body> </html> EOF } # $file 을 읽어서 $search 가 매치되는지 여부 반환 sub text_match { my ( $file , $search ) = @_ ; open my $in , "<" , $file ; my $text ; { local $/ = undef ; $text = < $in >; } close $in ; if ( $text =~ / $search /i ) { return 1; } return 0; } # $file 파일의 내용 반환 sub file_content { my $file = shift ; open my $in , "<" , $file ; my $text ; { local $/ = undef ; $text = < $in >; } close $in ; return $text ; } |
다음 스크린샷은 각각의 기능을 테스트하는 모습을 보여줍니다.
그림 1. 테스트하는 모습 (원본)
이 스크립트를 윈도우에서 실행해 보겠습니다. 먼저, CGI 환경을 에뮬레이트해주는 CGI::Emulate::PSGI 모듈을 사용하여 기존 스크립트를 구동하는 PSGI 스크립트를 만듭니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/usr/bin/env perl # app.psgi use strict; use warnings; use CGI::Emulate::PSGI; use Plack::Builder; my $index = CGI::Emulate::PSGI->handler( sub { do "index.pl" ; CGI::initialize_globals() if defined &CGI::initialize_globals; } ); |
이 스크립트를 plackup
프로그램을 써서 구동시킵니다.
1 2 | D:\psgi_example> plackup app.psgi HTTP::Server::PSGI: Accepting connections at http://0:5000/ |
디폴트로 5000번 포트를 사용합니다. 웹브라우저에서 http://127.0.0.1:5000 주소에 접속하면 짜잔~ 똑같은 화면이 나옵니다. 검색이나 파일 삭제 기능도 그대로 동작합니다.
위의 예제에서는 주소에 접속했을 때 무조건 index.pl
스크립트를 띄우게 됩니다.
두 개 이상의 스크립트를 사용할 경우는 Plack::Builder 모듈을 사용하여,
각 스크립트에 대응하는 URL을 장착(mount)해 줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #!/usr/bin/env perl # multi.psgi use strict; use warnings; use CGI::Emulate::PSGI; use Plack::Builder; my $cgi1 = CGI::Emulate::PSGI->handler( sub { do "cgi1.pl" ; CGI::initialize_globals() if defined &CGI::initialize_globals; } ); my $cgi2 = CGI::Emulate::PSGI->handler( sub { do "cgi2.pl" ; CGI::initialize_globals() if defined &CGI::initialize_globals; } ); builder { mount "/cgi1.pl" => $cgi1 ; # URL => 핸들러 mount "/cgi2.pl" => $cgi2 ; } |
또는, 아예 다수 스크립트가 들어있는 디렉토리를 통채로 지정하여 사용할 수도 있습니다. Plack::App::CGIBin 모듈을 사용합니다.
1 2 3 4 5 6 7 8 9 10 11 12 | #!perl # bin.psgi use strict; use warnings; use Plack::App::CGIBin; use Plack::Builder; my $app = Plack::App::CGIBin->new(root => "./" )->to_app; # "./"은 실제 스크립트가 있는 디렉토리 builder { mount "/" => $app ; # 여기에 "/"는 URL에 해당하는 경로 }; |
http://127.0.0.1:5000/cgi1.pl와 같이 접속할 수 있습니다. 이렇게 해서, 과거에 만들어 사용하던 CGI 스크립트를, 무거운 웹 서버 설치없이 손쉽게 재사용할 수 있었습니다.
1.
plackup
으로 웹 서버를 띄운 상태에서도 CGI 스크립트를 수정하면 변경 사항이 곧바로 반영이 됩니다.
그러나 PSGI 스크립트 쪽을 수정할 경우는 plackup
을 재시작해야 합니다. 이것이 불편하면 --reload | -r
옵션을 쓸 수 있습니다.
1 | plackup -r app.psgi |
2.
CGI 스크립트에 서브루틴이 정의되어 있으면 웹 브라우저로 접속할 때마다 서브루틴이 재정의되었다는 경고문이 콘솔 창에
뜹니다. 이것은 CGI 스크립트 쪽에 no warnings 'redefine'
프라그마를 넣어서 없앨 수 있습니다.
3.
제가 실제로 사용했던 스크립트의 경우, 내부에 system()
함수를 써서 외부 프로그램을 실행하는 루틴이 있었는데,
system()
의 인자로 썼던 "path/to/binary"
경로명의 슬래시가 윈도우에서는 문제가 되어서, 백슬래시를 써서
"path\\to\\binary"
형태로 고쳐주어야 했습니다.
Articles by
Seoul Perl Mongers,
Artwork by
Yura Lee
Designed by
Yura Lee
& Hojung Youn,
Edited by
Hojung Youn,
JellyPooo
& Keedi Kim.