음, 웹 프로그래밍에 대해서는 영 쥐약이라 솔직히 무슨 말인지 영 어렵다만, 대충 다음과 같이 이해가 된다.
- CGI가 웹서버와 CGI스크립트 사이의 인터페이스를 제공하는 것처럼, PSGI는 펄 기반 웹서버와 펄 기반 웹애플리케이션 사이의 인터페이스를 제공한다.
- PSGI에는 스펙만 명시되어 있고, Plack은 PSGI를 구현한 일종의 reference implementation
- Catalyst나 Dancer 등 Perl 웹 프레임워크들이 PSGI를 지원한다.
1. 사용 사례
주인장의 연구실 컴퓨터에는 실험 결과가 담긴 폴더가 매우 많이 있었다. 이게 어떤 구조로 진화했냐 하면:
- 한 번 실험하면 폴더 하나에 결과가 담긴다. 그래프는 이미지 뷰어로 쉽게 볼 수 있도록 jpg로도 저장하고, 파라메터나 기타 코멘트 등은 txt로 저장했다.
- 생각해보니 txt 따로 jpg 따로 볼 게 아니라, html로 만들면 html만 더블클릭하면 텍스트와 그림을 한 번에 볼 수 있겠더라. html로도 저장했다.
- 이렇게 폴더 하나에 jpg, txt, html 각각 수십개가 생겼고, 다시 이런 폴더가 시간이 흐르면서 계속 늘어났다.
- 폴더 하나를 들여다 볼 때마다 수십개의 html파일을 일일이 열어보자니 귀찮아졌다. 그래서 그 폴더 내의 모든 html의 body 부분을 모아서 하나의 all.html 파일로 다시 만드는 스크립트 collect.pl을 만들었다.
- 그런데 이런 폴더가 다시 수십, 수백개가 되어 가니까 일일이 들어가서 열면서 보는 것도 힘들고, 검색을 했으면 좋겠더라. 그래서 index.pl 파일을 만들어서 최상위 폴더에 두었다. 이 index.pl에는 텍스트 입력창이 있어서 거기에 입력을 하면 하위 폴더들을 뒤지면서 html의 텍스트에서 일치하는 부분이 있는 것들만 모아서 출력해준다. 이 시점에서 CGI를 쓰게 되어서, public_html 아래쪽에 마운트를 하였다.
- 어떤 폴더의 데이타는 영 쓸데가 없어서 며칠 안에 삭제하고, 어떤 폴더는 별도의 폴더에 복사해서 따로 보관하고 싶어졌다. 그래서 collect.pl을 수정하여, all.html 파일 내에는 각 그림-html 쌍마다 삭제 또는 보관 여부를 묻는 체크박스를 달고, 이 체크박스에 체크한 후 submit 버튼을 누르면 move.pl이 불려서 체크된 파일에 대해 삭제 또는 복사 동작을 한다.
즉 다음과 같은 구조로 파일이 보관되어 있었다:
/
`- index.pl --> data/ 폴더 내에 있는 텍스트를 검색해서 결과를 출력해주는 CGI
`- collect.pl --> data/ 내 특정 폴더에 저장된 파일들을 모아 all.html을 생성해주는 스크립트
`- move.pl --> all.html에서 submit버튼을 누르면 호출하여 특정 번호만 삭제
`- data/
`- 0001/ --> 이런 데이타 폴더가 수천개
`- 0001_1.jpg --> 데이타 그림과, 데이타 설명이 붙은 텍스트와 HTML이 수십개씩
`- 0001_1.txt
`- 0001_1.html
`- 0001_2.jpg, 0001_2.txt, ...
`- all.html --> 폴더 안의 모든 html, jpg를 하나의 html안에 합쳐둔 것
`- 0002/
`- 0003/
...
문제는 이 결과 폴더를 나중에 윈도우 쪽으로 다 옮기고 시간이 흐르고 나니 정말 필요한 부분만 남기고 이젠 지워버리고 싶은데, html을 보는 거야 상관이 없는데 삭제 또는 복사를 하려니 CGI를 동작시킬 방법이 필요했다.
- 윈도우에서 이런 CGI 몇 개를 사용하자고 아파치 웹서버를 설치하자니 그게 또 싫고
- 네이버 펄 까페에 [질문글]을 올렸더니 aero님께서 Plack 관련 글 하나를 링크해주셨는데 기존 CGI 스크립트를 그대로 가져와 쓸 수 있다는 것에 눈이 번쩍.
그래서 저 advent calendar 의 24개 기사를 앞에서부터 훑어 보면서 참고하여 해 보았다.
- 일단 1일자 기사[1]를 보면서 PSGI와 Plack 모듈 설치
- 2일자 기사[2] : 간단히 실행 테스트를 해 봄
- 서브루틴 레퍼런스 형태로 제작을 한 후,
plackup
프로그램을 사용하여 구동
- 5일자 기사[3] : 평범한 웹서버처럼 정적인 파일 또는 디렉토리 리스팅을 해 줄 수 있다. 내 경우 data 폴더 아래의 all.html 등 정적 파일을 볼 수 있어야 해서 참고해 둠
- 9일자 기사[4] : 핵심 기사에 있는 형태 그대로 다음과 같이 app.psgi 파일을 작성한 후 plackup으로 구동. 웹브라우저에서 localhost:5000으로 접속하니 그대로 실행되었다.
use CGI::Emulate::PSGI;
CGI::Emulate::PSGI->handler(sub {
do "./index.pl";
CGI::initialize_globals() if &CGI::initialize_globals;
});
- 문제는, 기존에 작성한 스크립트가 index.pl 하나만 있는 게 아니라 move.pl도 있고, 또 data 디렉토리 아래의 정적인 파일들도 볼 수 있어야 한다는 것.
- 12일자 기사[5] : 여기에서 서로 다른 URL에 대하여 서로 다른 애플리케이션을 구동할 수 있는 방법이 나온다. 이제 index.pl과 move.pl을 각각 띄울 수 있게 됨
- "mount in DSL" 섹션의 코드를 참고
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;
});
my $move = CGI::Emulate::PSGI->handler(sub {
do "move.pl";
CGI::initialize_globals() if defined &CGI::initialize_globals;
});
builder {
mount "/" => $index;
mount "/move.pl" => $move;
}
- 이제는
localhost:5000
으로 접근하면 index.pl
이 실행되고, localhost:5000/move.pl
로 접근하면 move.pl
이 실행된다.
- 17일자 기사[6] : 5일자[3]와 12일자[5]의 결합! 이제 data 디렉토리 아래에 있는 정적인 html파일과 jpg, txt 파일들을 접근할 수 있다
use CGI::Emulate::PSGI;
use Plack::Builder;
use Plack::App::Directory;
my $index = CGI::Emulate::PSGI->handler(sub {
do "index.pl";
CGI::initialize_globals() if defined &CGI::initialize_globals;
});
my $move = CGI::Emulate::PSGI->handler(sub {
do "move.pl";
CGI::initialize_globals() if defined &CGI::initialize_globals;
});
builder {
mount "/" => $index;
mount "/data" => Plack::App::Directory->new( root => './data' );
mount "/move.pl" => $move;
}
-
/
요청에는 index.pl이 구동, /data/*
요청에는 해당 디렉토리 리스트 또는 파일이 정적으로 불리고, /move.pl
요청에는 move.pl이 구동된다. 완성 :-)
- 4일자 기사[7] : 내가 작성한 코드가 바뀔 때마다 plackup을 재실행할 필요가 없게 실시간 재로딩을 가능하게 함
-
plackup -r app.psgi
: 이렇게 할 경우는 브라우저에서 cgi스크립트가 있는 경로에 접근할 때마다 스크립트가 다시 불리면서 서브루틴이 재정의된다고 경고가 떠서, cgi스크립트 쪽에 no warnings 'redefine'
을 추가해주어야 했다
-
plackup -L Shotgun app.psgi
: 이렇게 할 경우는 그런 경고도 뜨지 않아서 좋음
- plackup을 실행하면 클라이언트의 요청이 들어올 때마다 로그가 STDERR로 출력되어서 명령 프롬프트창에 계속 뜬다. 이걸 없애는 건 Plack Wiki에 FAQ 항목[8]을 참조.
--access-log
옵션을 주어서 별도의 파일에 기록하게 하거나, -E deployment
옵션을 주어서 아예 출력되지 않게 함
d:\Temp\placktest>plackup -L Shotgun --access-log access.log app.psgi
HTTP::Server::PSGI: Accepting connections at http://0:5000/
실제로 실행해보니, 워낙 간단한 스크립트라서 그랬는지는 몰라도, 기존 스크립트를 거의 건드리지 않고 실행할 수 있었다. 손을 대야 했던 곳은 딱 두 곳:
- 위에 언급한대로 같은 스크립트가 여러번 불리면서 서브루틴이 재정의된다고 경고가 뜨길래
no warnings 'redefine'
추가해준 것
- move.pl이 데이타를 삭제 또는 이동시킨 후에 all.html을 갱신하기 위해 실행하는
system( "../../collect.pl" )
이 코드가 디렉토리 구분자가 "/"라 실행이 안 되어서, 백슬래쉬로 고쳐준 것.
- 이것은
system( "perl ../../collect.pl" )
이렇게 앞에 perl 인터프리터를 제대로 적어주면, 스크립트 경로에는 그냥 슬래쉬로 적혀도 인식한다.
아주 만족스럽군.
2. 기타 & Comments
기타분류
https://metacpan.org/module/Plack::App::CGIBin 사용하면 cgi들어있는 디렉토리만 지정해주면 되는거 같습니다.