이명헌 경영 스쿨
[펄] CGI의 의미 및 펄을 이용한 간단한 CGI 개발
CGI의 개념과 환경변수, 그리고 CGI
이명헌 [ 2002-6-20 ]
참고: 웹 기반 복리계산기는 복리계산기를 클릭하세요.

CGI 란?

cgi는 "common gateway interface"의 약자입니다. 게이트웨이란 써버 컴퓨터에는 존재하지만 웹써버 프로그램을 통해서는 접근할 수 없는 다른 프로그램으로의 입구를 뜻합니다. 예를 들어, 일반적인 웹브라우져로 웹 써버 프로그램에 접속한 경우, 써버 컴퓨터에 있는 mysql db에는 절대로 직접 접근할 수 없습니다. 하지만 mysql 데이타를 다룰 수 있는 cgi 프로그램을 이용하면 웹브라우져를 통해 mysql 데이타에 접근할 수 있습니다. 즉, cgi 프로그램이 mysql db로의 'gateway'를 제공하는 것입니다. "common gateway interface"는 써버 컴퓨터에 존재하는 여러 데이타와 프로그램으로의 공통된(common) 게이트웨이를 만들어주는 인터페이스인 것입니다.

그러면 게이트웨이를 통해서 무엇이 들어가고 무엇이 나올까요? 회원등록 폼(form)에 개인 정보를 입력하는 경우를 생각해 봅시다. 회원등록 페이지는 이름, 주소, id, 패쓰워드 등의 정보를 입력할 수 있는 html 폼으로 이뤄져 있습니다. 사용자는 이 폼에 자신의 이름, 주소 등의 정보를 입력하고 "보내기"(submit) 버튼을 클릭하게 됩니다. 이 때 입력된 사용자 데이타는 어떤 방식을 거쳐서 웹 써버 프로그램에 도착하고, 웹 써버에서는 이들 데이타를 처리합니다. '처리'는 데이타를 단순히 써버 컴퓨터에 저장하는 것일 수도 있고, 써버 컴퓨터에 존재하는 다른 프로그램으로 프로세싱해서 다른 출력물을 만들어 내는 것일 수도 있습니다. 어쨌든 사용자가 입력한 데이타가 들어가고, 이들 데이타는 cgi라는 인터페이스를 통해서 웹 써버 또는 웹 써버와 연결된 다른 컴퓨터에 있는 프로그램을 만나고, 이들 프로그램에 의해 처리된 결과는 다시 cgi라는 인터페이스를 통해서 사용자 쪽(웹브라우져)으로 보내지는 것입니다.

CGI
(위 그림은 O'Reily 책에서 발췌했습니다.)
cgi 프로그램이 단순히 사용자와 써버사이의 데이타 전달 역할에 그치지 않을 수도 있습니다. cgi 프로그램 자신이 사용자 데이타나 써버 정보를 복잡하게 가공할 수도 있습니다. 여러 데이타가 유통될 수 있는 인터페이스를 통칭해서 cgi라 한다고 이해하면 됩니다.

웹 써버로 들어 가는 정보에는 어떤 것이 있을까요? 크게 세 가지입니다.

  1. 클라이언트 정보, 써버 정보, 사용자 정보
  2. 사용자가 폼에 입력한 정보
  3. 부가적인 패쓰(path) 정보

마지막 부가적 패쓰정보는 별로 중요하지 않고 위의 두 가지가 중요합니다. 첫 번째에 나오는, 클라이언트, 써버, 사용자 정보는 현재 사용자가 어떤 브라우져를 사용하는지, 어떤 싸이트를 거쳐서 이 웹써버에 접속하고 있는지, 사용자의 ip 주소는 무엇인지, 또 써버 쪽에서 어떤 웹 써버 프로그램을 쓰고 있는지, DocumentRoot의 path는 어떻게 되는지 등을 의미합니다. 이런 정보는 사용자가 폼에 직접 입력한 데이타와 함께 http header에 담겨서 써버에 전달됩니다. (http로 나누는 대화를 읽어 보세요.) 이를테면 게시판에 글을 올리는 경우를 생각해 봅시다. 게시판 글쓰기 폼에 사용자가 입력한 글과 함께, 현재 사용자가 어떤 웹브라우져를 쓰고 있고, ip 주소가 무엇인지 등의 정보가 함께 웹 써버에 전달되는 것입니다. 그러면 이 전달된 정보는 어디에 저장될까요?

환경 변수 (Environment Variables)

웹써버의 환경변수(Environment Variable)에 전달된 정보가 저장됩니다. CGI는 이 환경변수에 접근해서 클라이언트 컴퓨터 정보나 사용자가 입력한 데이타를 가져오는 것입니다. 환경변수에는 다음과 같은 것이 있습니다. 이런 것이 있다는 정도로만 우선 파악하면 됩니다.

SERVER_NAME 써버의 호스트네임과 ip 주소
SERVER_SOFTWARE 써버 소프퉤어의 버전과 이름
SERVER_PROTOCOL 사용자 요청을 처리하는 프로토콜의 버전과 이름
SERVER_PORT 써버가 동작하고 있는 포트 넘버 (포트에 관한 설명은 TCP/IP 및 네트웍 프로토콜 설명 클릭)
REQUEST_METHOD 요청 방식
PATH_INFO cgi 프로그램에 전달된 부가적 패쓰 정보
DOCUMENT_ROOT 웹문서의 루트 디렉토리
QUERY_STRING 사용자가 폼에 입력한 정보
REMOTE_HOST 사용자의 호스트네임
REMOTE_ADDR 사용자의 IP 주소
CONTENT_TYPE 마임 (MIME) 타입
CONTENT_LENGTH 표준입력을 통해 CGI 프로그램에 전달되는 데이타의 길이
HTTP_REFERER 사용자가 CGI 프로그램에 접근하기 전에 머물던 문서의 주소
HTTP_USER_AGENT 사용자가 사용하는 브라우져의 종류

웹 브라우져로 cgi 프로그램에 접근하는 순간 위 정보가 웹 써버의 환경변수에 담기게 되는 것입니다. 펄을 이용해서 이들 정보에 접근하는 경우 %ENV 해쉬를 통해서 접근할 수 있습니다. 각각의 환경변수는 $ENV{'환경변수'}의 형태로 다루면 됩니다. 이야기를 더 진행시키기 전에 우선 HTTP 헤더(header)에 관해서 알고 있어야 합니다. 아직까지 http로 나누는 대화를 읽지 않았으면 읽고 오세요.

링크에서 설명한 것처럼, 웹 써버가 사용자 웹 브라우져로 요청된 문서를 보낼 때 http 리스판스 헤더와 함께 전달합니다. 그리고 그 헤더에는 다음과 같은 정보가 담겨 있습니다.

HTTP/1.0 200 Found
Date: Mon, 10 Feb 1997 23:48:22 GMT
Server: Apache/1.2
Content-type: text/html
Last-Modified: Tues, 11 Feb 2000 22:45:55 GMT

이것이 http 리스판스 헤더입니다. 써버에서 요청된 데이타를 보내 주는 http 프로토콜 버전, status code (200 Found; 요청한 문서가 있더라.), 보내 주는 날짜, 웹 써버의 버전, 보내 주는 파일의 마임타입, 마지막 변형된 날짜 등이 웹 써버로부터 클라이언트 쪽으로 전달되는 것을 알 수 있습니다.

그렇다면 헤더와 클라이언트의 요청에 따라 보내 주는 데이타 사이의 구분은 어떻게 할까요? 써버에서 클라이언트에 http라는 프로토콜을 통해 보내주는 데이타 중 과연 어디까지가 http 헤더이고 어디서부터 요청된 데이타인지를 구분하는 방법이 있어야 하지 않겠습니까?

http 프로토콜의 경우 '빈 줄 하나가 띄워진 다음부터가 요청된 데이타'로 약속되어 있습니다. 즉, 위와 같은 헤더 정보가 나온 다음 빈 줄 한 줄이 나오면 헤더는 끝나고 데이타가 시작된다고 웹 브라우져에서 인식하는 것입니다. http 리스판스 헤더 중 대부분은 웹써버에서 알아서 설정해주므로 (물론 직접 설정할 수도 있습니다.) 써버 버전이나 http의 버전, status 코드 등은 굳이 우리가 작성하지 않아도 됩니다. 우리는 전달되는 데이타의 마임타입을 가르키는 Content-type만 지정해 주면 됩니다. 다음의 코드를 보세요.

#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "<html><head></head><body>";
print "서버이름: $ENV{'SERVER_NAME'}<br />";
print "서버 포트: $ENV{'SERVER_PORT'}<br />";
print "프로토콜: $ENV{'SERVER_PROTOCOL'}<br />";
print "사용자 웹브라우져: $ENV{'HTTP_USER_AGENT'}<br />";
print "사용자 IP 주소: $ENV{'REMOTE_ADDR'}";
print "</body></html>";

제일 중요한 부분은 첫 줄입니다. 보면, Content-type: text/html 다음에 빈 줄 한 줄을 만들고 있는 것을 알 수 있습니다. (\n\n)
Content-type은 보내줄 문서의 마임타입을 지정한 것입니다. 어쨌든 빈 줄이 나왔으므로 이제 http 헤더는 끝났다고 보고, 그 다음 나오는 내용부터 웹 브라우져에 띄워 주게 되는 것입니다. (또는 마임타입에 따라서 연결된 프로그램을 실행하게 됩니다. 예를 들어 mp3 파일이라면 윈앰프가 뜨면서 mp3 파일이 실행되는 식입니다.) 위의 코드를 보면, $ENV{'SERVER_NAME'}, $ENV{'REMOTE_ADDR'}처럼 %ENV 해쉬를 이용해서 환경변수에 접근하고 있는 것을 알 수 있습니다.

이 코드를 적당한 이름으로 저장하고 (예: env.pl) 퍼미션을 755로 준다음 (chmod 755 env.pl) 웹 브라우져에서 띄워 보면 (http://써버이름/env.pl) 다음과 비슷한 결과가 출력될 것입니다.

서버이름: abc.com
서버 포트: 80
프로토콜: HTTP/1.1
사용자 웹브라우져: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
사용자 IP 주소: 61.77.173.220

퍼미션이 755인 문서는 그 문서를 웹 브라우져로 보내 주는 것이 아니라 웹 써버에서 실행(execution)합니다. 즉, cgi 프로그램이 웹 브라우져에 의해 요청되는 경우 일반 html 문서처럼 웹 브라우져로 보내서 그대로 보여 주는 것이 아니라 웹 써버에서 바로 실행을 해야 합니다. 그래서 퍼미션을 755로 셋팅합니다. (Permission 755는 -rwxr-xr-x죠? r 은 read, w 는 write, x 는 execute)

모든 환경변수를 다 출력해 보고 싶다면 다음과 같이 할 수 있습니다.

#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "<html><head></head><body>";
foreach $name (sort keys %ENV) {
print "<b>$name : </b> $ENV{$name} <br />"
}
print "</body></html>";

적당한 이름으로 저장한 다음 퍼미션을 755로 하고 웹 브라우져에서 실행해 보면 모든 환경변수를 다 볼 수 있습니다.

같은 기능을 하는, 조금 더 '펄 스타일'에 가까운 코드는 이렇습니다.

#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "<html><head></head><body>";
print qq($_ : $ENV{$_}<br />) foreach sort keys %ENV;
print "</body></html>";

펄의 default variable인 $_을 활용해서 foreach 구문을 한 줄로 줄여 놓은 것을 볼 수 있습니다. qq( )는 큰 따옴표로 괄호안을 묶어 준다는 의미이구요. 큰 따옴표 안에서 또 큰 따옴표를 사용할때도 탈출 \"을 할 필요가 없기 때문에 자주 쓰이는 것입니다. 예를 들어,

print "이것은 \"펄\" 프로그램 입니다";
print qq(이것은 "펄" 프로그램 입니다);

둘은 똑같은 것입니다. qq( )가 아주 편리하죠?

위의 환경 변수 출력 프로그램은 터미널에서 한 줄로 입력할 수도 있습니다.

perl -e 'print qq($_ : $ENV{$_}\n) foreach sort keys %ENV'

perl -e ' '는 터미널에서 한 줄 짜리 펄 코드를 바로 실행할 때 사용하는 것입니다. 이렇게 여러 줄이 될 수 있는 것을 축약해서 한 줄로 만드는 데 펄의 묘미가 있습니다. 유닉스 긱스는 이런 '한 줄'에 약간 집착하기도 합니다. 영어로는 "one-liner"라고 합니다. 파이프 문자를 이용해서 복잡한 기능을 한 줄로 멋지게 만들어 내는 것을 단순하고 아름답다라고 생각하는 유닉스 긱스가 상당수 있습니다. 펄이 유닉스 사용자에게 어필한 여러 이유 중 하나가 펄의 "simple and elegant"한 코딩 스타일이 유닉스 스타일과 잘 어울린다는 것입니다.

cgi 프로그램은 퍼미션만 755로 설정했다면 굳이 웹 브라우져를 실행하지 않아도 확인해 볼 수 있습니다. 위의 env.pl의 경우, 프로그램이 있는 디렉토리로 cd해서 이동한 다음, ./env.pl라고 입력하면 터미널 창에서 실행할 수 있습니다. cgi를 디버깅할 때 웹 브라우져 상에서 하기는 조금 곤란합니다. 에러를 브라우져로 띄워줄 수 있게 코딩할 수도 있지만 번거롭습니다. cgi 프로그램에 큰 버그가 없는지 간단하게 확인하는 방법이 터미널 창에서 바로 실행해 보는 것입니다.

펄을 이용해서 어떻게 환경변수에 접근하는지는 이해가 되죠? 사용자가 폼에 입력한 정보에 접근하는 방법은 훨씬 더 복잡하므로 다음 글, "펄을 이용한 복리계산기 만들기"에서 자세하게 설명합니다.

이번에는 웹 써버에서 cgi를 거쳐 밖으로 나갈 수 있는 것은 뭐가 있는지 알아봅시다. 다음과 같은 것이 나가게 됩니다.

  1. 바이너리 데이타들 (그래픽 파일, ..)
  2. 보내 주는 문서를 캐쉬에 저장할 것인가를 지정
  3. 특별한 http status code

바이너리 파일은 단순한 그래픽 파일일 수도 있고 웹 써버상의 다른 프로그램(데이타베이스 프로그램,...)에서 처리한 결과물일 수도 있습니다. 그런 바이너리 파일이 cgi를 거쳐서 사용자 컴퓨터 쪽으로 갈 수 있습니다. 글 서두에 얘기한 것처럼 써버(또는 써버와 연결된 다른 컴퓨터)에 있는 DB 프로그램으로 처리한 데이타가 사용자 쪽으로 건너갈 수 있는 것입니다.

두번째, 세번째에 관해서는 조금 있다가 살펴 보고 우선 http 헤더에 대해서 조금 더 얘기를 해 봅시다.

사용자가 링크를 클릭하는 순간 만들어지는 http 리퀘스트 헤더에는 Accept가 있습니다. '나는 이런 마임타입의 파일을 처리할 수 있다'는 것을 웹 써버에 알려 주는 것입니다. 보통은 */*, 즉 임의의 모든 마임타입을 다 처리할 수 있게 되어 있습니다. 웹 브라우져에 의해 보내진 Accept 정보는 웹 써버 환경변수 중 $ENV{'HTTP_ACCEPT'}에 저장됩니다.

한편 웹 써버에서 어떤 문서(또는 그래픽, 싸운드..)를 보내 줄 때도 '다음의 데이타는 이런 마임타입이다.'라고 알려주는 부분이 있습니다. 그것이 http 리스판스 헤더 중 Content-type입니다. 위에서 본 예의 경우, Content-typetext/html로 지정해서 "빈 줄 한 줄이 나온 다음의 내용은 html 텍스트이므로 그 걸로 처리해라"고 얘기하고 있는 것입니다. 따라서 이 헤더를 전달 받은 웹 브라우져는 운영체계에서 text/html을 처리하도록 지정된 프로그램(이 경우에는 웹브라우져 자신)을 실행해서 전달받은 데이타를 처리하게 됩니다. (마임타입(Mime Type)을 읽어 보세요.)

이와 비슷한 것으로 Content-length도 있습니다. 이것은 나가는 데이타가 어느 정도의 길이라는 것을 알려주는 부분입니다. 이처럼 웹 써버에서 클라이언트로 보내는 리스판스 헤더를 cgi 프로그램을 이용해서 각각 지정할 수 있습니다. 위에서 Content-type을 지정한 것처럼요.

헤더 정보 중에는 캐쉬를 할 것인가를 지정하는 부분이 있습니다. 웹 브라우져는 웹 써버로부터 받은 문서나 그래픽 등을 하드디스크 내에 캐쉬라는 형태로 저장합니다. 그래서 브라우징 속도를 빠르게 하는 것입니다. 그 캐쉬에 지금 보내는 내용을 저장할 것인가 안 할 것인가를 http 리스판스 헤더의 Expires, Pragma를 통해 지정할 수 있습니다. 예를 들어 text/html 문서를 보내면서 사용자 컴퓨터에 캐쉬로 저장되기를 원치 않는 경우에는,

#!/usr/bin/perl
print "Content-type: text/html\n";
print "Pragma: no-cache\n\n";
print "<html> .. .";
.. .. .

Content-type : text/html 다음에 빈 줄을 만들지 않고 그냥 줄 바꿈만 하고 있는 것을 볼 수 있습니다.(\n 한 개) 따라서 아직은 헤더가 끝나지 않은 것이고, 그 다음 줄 Pragma 에서 캐쉬에 저장하지 않는 것으로 지정한 다음 빈 줄을 하나 만들고 (\n 두 개), 이제 헤더가 끝났으므로, 전달될 데이타가 나오고 있습니다.

마찬가지로 날짜를 지정할 수도 있습니다.

#!/usr/bin/perl
print "Content-type: text/html\n";
print "Expires: Wednesday, 27-Dec-99 05:13:10 GMT\n\n";
.. .. .

지금까지 공부해 본 내용을 바탕으로 웹 기반 복리계산기를 만들어 봅시다. 그 과정을 통해서 사용자가 입력한 데이타를 어떻게 처리하는지 자세히 설명합니다. 다음 글은 펄을 이용한 복리계산기 만들기를 클릭하세요.

지금 계신 곳은: TECH > [펄] CGI의 의미 및 펄을 이용한 간단한 CGI 개발