티스토리 뷰

2002-6-20

CGI 란?

CGI는 "common gateway interface"의 약자입니다. CGI를 이해하기 위해 예를 하나 들어 볼께요. 웹 브라우져로 웹 서버 프로그램에 접속한 경우에 그 서버 컴퓨터에 있는 mysql DB에 그냥은 직접 접근할 수 없습니다. mysql 데이타를 다룰 수 있는 CGI 프로그램을 이용하면 웹 브라우져를 통해 mysql 데이타에 접근할 수 있습니다. CGI 프로그램이 mysql DB로의 gateway를 제공하는 것입니다. 서버 컴퓨터에 존재하는 여러 데이타나 프로그램으로의 게이트웨이를 만들어 주는 인터페이스인 것입니다.

 

그러면 게이트웨이를 통해서 무엇이 들어가고 무엇이 나올까요? 쉬운 예로 회원 등록 폼(form)에 개인 정보를 입력하는 경우를 생각해 봅시다. 회원 등록 페이지는 이름, 주소, ID, 패스워드 등의 정보를 입력할 수 있는 HTML 폼으로 이뤄져 있습니다. 사용자는 이 폼에 자신의 이름, 주소 등의 정보를 입력하고 "보내기"(submit) 버튼을 클릭하게 됩니다. 이 때 입력된 사용자 데이타는 웹 서버에서는 이 데이타를 처리합니다. 처리는 데이타를 단순히 서버 컴퓨터에 저장하는 것일 수도 있고, 서버 컴퓨터에 존재하는 다른 프로그램으로 프로세싱해서 출력물을 만들어 내는 것일 수도 있습니다. 어쨌든 사용자가 입력한 데이타가 들어가고, 이들 데이타는 CGI라는 인터페이스를 통해서 웹 서버나 웹 서버와 연결된 다른 컴퓨터에 있는 프로그램에 의해 처리되고 다시 CGI라는 인터페이스를 통해서 클라이언트 쪽 웹브라우져로 보내지는 것입니다. CGI 프로그램은 단순히 클라이언트와 서버 사이의 데이타 전달 역할에 그치지 않고 그 자체가 데이타를 복잡하게 가공할 수도 있습니다.

 
CGI의 개념

 

클라이언트에서 웹 서버로 보내는 정보에는 크게 세 가지가 있습니다.

 

 (1) 클라이언트 정보, 서버 정보, 사용자 정보

 (2) 사용자가 폼에 입력한 정보

 (3) 부가적인 패쓰(path) 정보

 

클라이언트 쪽 사용자가 어떤 브라우져를 사용하는지, 어떤 사이트를 거쳐서 이 웹 서버에 접속하고 있는지, 클라이언트의 IP 주소는 무엇인지, 또 서버 쪽에서 어떤 웹 서버 프로그램을 쓰고 있는지, DocumentRoot의 path는 어떻게 되는지가 서버로 보내지는데 사용자가 폼에 입력한 데이타와 함께 HTTP 헤더(header)에 담겨서 서버에 전달됩니다. 이렇게 전달된 것은 웹 서버의 환경변수(Environment Variable)에 저장됩니다.

환경 변수 (Environment Variables)

CGI는 이 환경변수에 접근해서 클라이언트 컴퓨터 정보나 사용자가 입력한 데이타를 가져 올 수 있습니다. 환경변수에는 다음과 같은 것이 있습니다. 

 

웹 서버 환경변수

펄을 이용해서 환경변수에 접근하는 것은 %ENV 해시(hash)를 이용합니다. 각 환경변수는 $ENV{'환경변수'}의 형태로 다루게 됩니다. 아래 내용을 이해하려면 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 프로토콜 버전, 상태 코드(status code), 위 예는 200 Found; 요청한 문서가 있다, 그리고  데이타 전송 날짜, 웹 서버의 버전, 보내는 파일의 미디어 타입, 마지막 변경 날짜 등이 웹 서버로부터 클라이언트 쪽으로 전달됩니다.

 

그런데 클라이언트에 보내는 데이타 중 어디까지가 HTTP 응답 헤더이고 어디서부터 클라이언트가 요청한 데이타인지를 구분하는 기준이 있어야 할 것입니다. HTTP 트랜잭션과 헤더 글에서 설명한 것처럼, 빈 줄 하나가 띄워진 다음부터를 요청된 데이타로 하자는 약속이 되어 있습니다. 즉, 위와 같은 헤더 정보가 나온 다음 빈 줄이 한 줄이 나오면 응답 헤더가 끝나고 데이타가 시작된다고 웹 브라우져에서 인식합니다. 응답 헤더 중 따로 지정할 부분은 클라이언트로 전달해 주는 데이타의 미디어 타입을 지정하는 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) 줄 바꿈을 하는 개행문자를 연달아 두 개 써준 것입니다. 그러므로 웹 브라우져에서는 그 다음 줄부터 헤더가 아닌 서버에서 클라이언트로 보내는 요청된 문서나 데이타라고 인식을 하고, 그 내용을 웹 브라우져에 띄우거나 바로 처리할 수 없는 미디어 타입이라면 연결된 프로그램을 실행하게 됩니다. 

 

위의 코드는 $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이므로 이 CGI 프로그램은 클라이언트 웹 브라우져로 보내는 것이 아니라 웹 서버 상에서 실행(execution)합니다. 위 프로그램이 간단한 CGI 프로그램입니다. CGI 프로그램은 클라이언트에서 요청하는 경우 일반 HTML 문서처럼 웹 브라우져로 보내서 보여 주는 것이 아니라 웹 서버에서 바로 실행해야 하므로 퍼미션을 755로 하는 것입니다. (Permission 755는 -rwxr-xr-x)

 

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

#!/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"라고 합니다. 

 

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


펄을 이용해서 어떻게 웹 서버 프로그램의 환경변수에 접근하는지 이제 쉽게 이해할 수 있을 것입니다. 이번에는 웹 서버에서 CGI를 거쳐 클라이언트 쪽으로 가는 것은 어떤 것이 있는지 알아 봅시다.

 

 (1) 바이너리 데이타(binary data)

 (2) 보내는 데이타를 캐시에 저장할 것인가를 지정

 (3) 특별한 HTTP 상태 코드(status code)

 

바이너리 파일은 단순한 그래픽 파일일 수도 있고 웹 서버에 있는 다른 프로그램(데이타베이스 프로그램 같은)에서 처리한 결과물일 수도 있습니다. 바이너리 파일이 전송될 수 있기 때문에 DB 프로그램으로 처리한 데이타를 웹 서버를 통해 클라이언트 쪽으로 보낼 수 있는 것입니다.

 

클라이언트 쪽에서 링크를 클릭하거나 주소창에 주소를 입력할 때 만들어지는 요청 헤더(request header)에는 Accept 필드가 있습니다. 이런 종류의 미디어 타입의 파일을 처리할 수 있다는 것을 웹 서버에 알려 주는 것입니다. 보통은 */*, 임의의 모든 미디어 타입을 다 처리할 수 있게 되어 있습니다. 웹 브라우져에 의해 보내진 Accept 정보는 웹 서버 환경변수 중 $ENV{'HTTP_ACCEPT'}에 저장됩니다.

 

한편 웹 써버에서 텍스트나 그래픽 등을 보낼 때도 이 데이타는 이런 미디어 타입이라고 알려주는 부분이 있습니다. 그것이 응답 헤더 중 Content-type입니다. 위에서 본 예의 경우, Content-typetext/html로 지정해서 빈 줄 한 줄이 나온 다음의 내용은 HTML 텍스트라고 얘기하고 있는 것입니다. 이와 비슷한 것으로 Content-length도 있습니다. 이것은 클라이언트 쪽으로 보내는 데이타가 어느 정도의 길이라는 것을 알려 줍니다. 위에서 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";
......
반응형
댓글