고급 CGI 프로그래밍

이택경
서울시 강남구 청담동 81-8 우원빌딩 5층
(주)다음커뮤니케이션 개발팀
E-mail : kyung@daum.co.kr
URL : http://cezanne.daum.co.kr/~kyung/
Abstract:
WWW는 하이퍼링크기능과 편리한 사용자 인터페이스로 인하여 사용자들이 급증하고 있는 추세이다. 일반적인 WWW 시스템은 정적인 문서만으로 구성되어, 클라이언트의 다양한 요청을 처리하기 위해서는 CGI를 통한 문서의 동적인 생성이 필요하다. 하지만 WWW 시스템은 상태를 유지하지 않는 특성을 지니고 있어서 CGI 프로그램의 제작에 어려움이 있다.
본고에서는 CGI의 작동원리에 대해 전반적으로 알아보고, 새로운 방식인 dynamic document, 화일 업로드를 설명한다. 또한 상태를 유지하지 않는 문제점을 해결하기 위한 모델을 제시하고, 새로운 방식인 쿠키에 대해 설명한다.
Keywords:
WWW, CGI, Cookie, Client/Server, Form

1. 서론

WWW(World Wide Web) 시스템이 도입되면서부터, WWW의 편리한 사용자 인터페이스로 인하여 일반사용자들의 인터넷 사용이 급증하고 있는 추세이다. 하지만 기존의 정적인 문서만으로는 늘어나는 사용자들의 다양한 요청을 효과적으로 처리하지 못하며, 이를 위해서 CGI를 통한 문서의 동적인 생성이 필요하다.

WWW 시스템은 클라이언트/서버 모델에 기반을 두고 있으며, 상태를 유지하지 않는(stateless) 특성을 지니고 있다. 이는 서버의 부하를 줄여준다는 장점을 가지고 있으나, 상태를 유지하기 위해 별도의 시스템이 필요하다는 단점을 안고 있다. 따라서 본고에서는 CGI의 일반적인 작동원리에 대해 살펴보고, Netscape의 새로운 방식인 동적문서(dynamic document), 화일 업로드를 설명하고, 상태를 유지하기 위한 모델을 제시하며, 새로운 방식인 쿠키에 대해 설명한다.

2. CGI

CGI(Common Gateway Interface)는 서버와 외부 프로그램과의 인터페이스를 위한 표준이다. 일반적으로 서버가 넘겨주는 문서는 정적인 문서이다. 이에 비해 CGI를 통해 외부 프로그램에 정보 요청을 하고 외부 프로그램이 적절한 문서를 동적으로 생성시킬 수 있다. 다음 그림은 동적으로 생성된 문서 예이다.


그림 1 : BBS에서 동적으로 생성된 사용자 리스트

2.1 작동원리

다음 그림과 같이 클라이언트가 문서를 요청하면 서버는 CGI 프로그램에 필요한 인수들을 전달하며 실행시킨다. 실행된 결과로 문서가 동적으로 생성되며, 이 문서는 서버를 거쳐서 클라이언트에게로 넘겨진다.


그림 2 : CGI의 작동원리

CGI 프로그램을 지정하는 방법은 1) URL에서 CGI 프로그램과 전달할 인수들을 지정하거나, 2) 폼문에서 CGI 프로그램과 전달할 인수(이후 사용자가 입력)들을 지정하는 방법이 있다. 또한 인수들을 CGI 프로그램으로 전달하는 방식은 "GET" 방식과 "POST" 방식의 두가지가 있다. "GET" 방식은 모든 인수들을 환경변수들을 통해서 전달하게 되며, "POST" 방식은 환경변수와 "stdin"을 통해서 인수들을 전달하게 된다. URL에서 지정하는 방법은 "GET" 방식을 이용하며, 폼문에서 지정하는 방법은 "GET"과 "POST&quuot; 방식 두가지 모두를 이용한다.

2.2 폼문 관련 태그(Fill-Out Form Tag)

폼문은 다음 그림과 같이 사용자로부터 필요한 필드들을 입력받는 방식이다.


그림 3 : BBS에서 전자우편을 쓰고자 할 때 나타나는 폼문

폼문의 문법은 다음과 같다.

2.3 CGI의 입력

인수들은 다음과 같은 형태로 넘어온다.

NAME1=VALUE1&NAME2=VALUE2...

"GET" 방식에서는 위의 인수들이 "QUERY_STRING"이라는 환경변수를 통해 넘어오며, "POST" 방식에서는 위의 인수들이 "stdin"을 통해 넘어오게 된다. 그외 필요한 정보들은 다른 환경변수들을 통해 넘어오게 된다. 넘어오는 인수들은 표준 URL 형식으로 인코드되어 넘어오므로 적절히 디코드(decode)시킬 필요가 있다. 인코드 형식은 공백은 '+'로 변환되고, 특수문자는 %XX(16진수)로 변환된다.

2.4 CGI의 출력

CGI는 세가지의 헤더 - Location, Content-type, Status - 를 정의하고 있다. 서버는 CGI의 출력인 이 헤더정보를 HTTP 응답헤더(HTTP response header)에 적절히 삽입하여 클라이언트로 넘겨준다.

2.5 화일 업로드

화일 업로드는 Netscape 2.0 이상에서 지원되며, 업로드하고자 하는 화일을 대화상자에서 선택하여 서버쪽으로 전송시킨다. 다음 그림은 화일 업로드를 이용하여 예이다.


그림 4 : BBS에서 공개자료실에 업로드하고자 할 때 나타나는 대화상자

화일업로드를 위해서는 "type"이 "file"인 "<input>" 태그를 이용하면 되며, 또한 "enctype"을 "multipart/form-data"로 지정해야 된다. 이를 이용하면 "browse"버튼이 우측에 나타나게 된다. 화일 업로드 폼문의 예는 다음과 같다.

<form enctype="multipart/form-data" method="post" action="URL">
Upload File : <br>
<input type="file" name="filename" size="50"><br>
Keyword : <br>
<input type="text" name="keyword" size="50"><br>
File Description :<br>
<textarea name="description" cols="70" rows="5"></textarea><br>
<input type="submit" value="Upload">
</form>

위의 폼문을 적절히 입력한 뒤, "Upload"버튼을 누르면, CGI는 환경변수와 "stdin"으로 다음과 같은 데이타를 전송받게 된다.

환경변수

getenv("CONTENT_TYPE") => multipart/form-data; boundary=---------------------------19241282464929 getenv("CONTENT_LENGTH") => 7209

stdin

-----------------------------19241282464929
Content-Disposition: form-data; name="filename"; filename="C:\Program Files\Netscape\Navigator\Readme.txt"
Content-Type: text/plain


Netscape Navigator 2.0 is subject to the terms detailed in the...
...
...


-----------------------------19241282464929
Content-Disposition: form-data; name="keyword"

text
-----------------------------19241282464929
Content-Disposition: form-data; name="description"

This is "readme.txt" file.
-----------------------------19241282464929--

위의 예에서 "keyword" 인수는 "text"를 값으로 가지며, "description" 인수는 "This is .."를 값으로 가진다. "filename" 인수는 대화상자에서 선택한 화일명을 가지며, 업로드된 화일은 "text/plain" 형식의 화일이고, "Netscape Navigator 2.0 is ..." 부분이 업로드된 화일의 내용이다.

3. 동적문서(Dynamic Document)

기존의 WWW 시스템에서 클라이언트는 항상 사용자의 입력만을 따라 새로운 자료를 가져오며, 서버는 클라이언트의 요청만을 따라 결과 자료를 넘겨주는 피동적인 입장에 있다. 이에 비해, 동적문서는 사용자의 입력이 없어도 일정주기가 지나면 새로운 자료를 가져올 수 있으며, 서버가 클라이언트의 요청 없이도 새로운 자료를 넘겨주는 능동적인 입장에 설 수 있게 한다. 이 방식은 현재 Netscape 1.1 이상에서만 지원된다.

3.1 클라이언트 당기기(Client Pull)

3.1.1 작동원리

사용자의 입력이 없어도 일정주기가 지나면 새로운 자료를 가져올 수 있는 방법으로서, HTML 문서내에 현재 문서의 지속시간과 다음 문서를 명시하고 지속시간이 끝나면 다음 문서를 다시 불러오게한다.

이 과정을 그림으로 나타내면 다음과 같다.


그림 5 : 클라이언트 당기기의 작동원리

3.1.2 응용분야

다음과 같은 분야에 응용이 가능하다.

3.1.3 문법

다음과 같이 메타태그(meta tag)안에 지속시간과 다음 문서를 명시하며, 이 메타태그는 HTML 문서의 HEAD 부분에 와야 한다.

<META HTTP-EQUIV="Refresh" CONTENT="12; URL=http://host.domain/next.html">

유의할 점은 다음과 같다.

3.1.4 평가

3.2 서버 밀기(Server Push)

3.2.1 작동원리

클라이언트의 요청 없이도 서버쪽에서 계속하여 새로운 자료를 보내는 방법으로서, MIME 형식중 "multipart" 형식을 이용하여 복합문서형태로 새로운 자료를 계속 보낸다. 따라서 서버쪽에서 보다 능동적인 자료의 전송이 가능하다.

이 과정을 그림으로 나타내면 다음과 같다.


그림 6 : 서버 밀기의 작동원리

3.2.2 응용분야

응용분야는 클라이언트 당기기와 유사하며, HTML 문서내의 일부분(이미지, 오디오 등)만을 동적으로 보이게 하는 것이 가능하다.

3.2.3 문법

서버쪽에서 생성된 HTTP 응답 메시지는 다음과 같아야 한다.

HTTP/1.0 200 OK
Content-type: multipart/x-mixed-replace;boundary=TestBoundary

--TestBoundary
Content-type: text/plain

first document

--TestBoundary
Content-type: text/html

<html>
<body>
second document
</body>
</html>

--TestBoundary--

위의 예는 각각 "first document", "second document" 를 출력하는 두 개의 연속적인 문서로 구성된 복합문서이며, 문서가 변하는 시간은 서버쪽에서 두번째 문서를 전송하는 시기를 조정함으로써 가능하다.

동적문서를 중단시킬려면 서버쪽에서 전체 복합문서를 모두 보내었거나, 클라이언트쪽에서 사용자가 중단 버튼을 누르면 된다.
다음은 1부터 10까지의 숫자를 연속적으로 출력하는 C 예제 프로그램이다.

#include <stdio.h>

main()
{

}

3.2.4 평가

4. CGI 프로그램의 모델링 및 디버깅

4.1 상태정보 유지 모델(State-Info Management Model)

WWW의 클라이언트/서버 모델을 구성하는 HTTP 프로토콜은 연결이 없고(connectionless), 상태를 유지하지 않는(stateless) 프로토콜이다. 연결이 없다는 것은 한번의 연결에 오직 하나의 요청만을 할 수 있다는 것을 의미하며, 상태를 유지하지 않는다는 것은 연속적인 처리에 필요한 메모리를 가지고 있지 않다는 것을 의미한다. 상태를 유지하지 않기 때문에 얻는 이점은 서버가 부가적인 정보들을 유지할 필요가 없기 때문에 더욱 효율적으로 작동될 수 있다는 점이다. 하지만 반면에 이전 처리로부터 필요한 정보들을 매 연결마다 다시 전송해야 한다는 단점을 안고 있다. 예를 들어 BBS에서 이전메뉴로 돌아가고자 할 경우, 이전 처리로부터 현 상태에 관련된 정보들을 모두 넘겨받아야 한다.

WWW 시스템은 클라이언트와 서버간의 연속적인 통신에 의해 작업을 수행하게 되며, 이 과정에서 필요한 CGI들이 전체 작업흐름에 따라 순차적으로 실행되게 된다. 따라서 각 CGI의 실행은 전체 작업흐름 중의 한 프로세스로 간주될 수 있으며, 전체 작업은 다음 그림과 같이 각 CGI들의 상태도표(state diagram)로 나타낼 수 있다.


그림 7 : CGI들간의 상태도표

각 CGI들은 다음 CGI의 실행에 필요한 모든 정보들을 생성시켜, 이를 URL의 인수나 폼문의 "hidden" 형식의 "<input>" 태그를 이용하여 클라이언트에 반환되는 HTML 문서에 포함시킨다. 그러면 클라이언트는 사용자의 선택에 따라 다음 CGI를 수행하게 되며, HTML 문서내에 포함된 정보들을 다음 CGI로 넘겨주게 된다. 이 진행과정을 그림으로 나타내면 다음과 같다.


그림 8 : CGI들의 진행과정

위의 그림에서 CGI A는 다음 CGI B에 필요한 정보 B와 그 다음 CGI C에 필요한 정보 A를 생성시킨 뒤, 이 정보들을 HTML 문서내에 포함시켜 클라이언트쪽에 넘겨준다. 클라이언트는 다시 URL에 이 정보들을 포함시켜 CGI B를 호출한다. CGI B는 넘겨받은 정보들 중 필요한 정보인 정보 B를 이용하여 작업을 수행한 뒤, 다음번 CGI인 CGI C에 필요한 정보 A를 HTML 문서내에 포함시켜 넘겨준다. 이 과정에서 CGI A는 비록 CGI B에는 필요하지 않지만 CGI C에 필요한 정보 A를 생성시켜 CGI B로 넘겨주고, CGI B는 이 정보 A를 단순히 CGI C쪽으로 넘겨만 주는 것을 알 수 있다. 이후 클라이언트는 폼문의 정보와 함께 필요한 정보 A를 CGI C쪽으로 넘겨주게 된다.

여러 CGI들을 이용하여 전체적인 하나의 시스템을 구축할 경우, 다음과 같은 두가지 방식 중 하나를 이용하여 작업 진행과정에서 필요한 정보를 유지하여야 한다.

1) 각 CGI들이 필요로 하는 정보들을 모두 취합하여 클라이언트와 서버사이의 통신에 항상 포함시킨다.

2) 전체 작업진행과정의 상태도표를 작성한 뒤, 모든 가능한 경로에 필요한 최소한의 정보들을 각 CGI들이 생성시키게 한다.

4.2 통합인터페이스 모델

많은 CGI들로 구성된 WWW 시스템일 경우, 통합된 인퍼테이스를 통하여 하나의 CGI를 두면 보다 일관성을 가지고 시스템을 구성할 수 있다.

예) BBS에서 사용자를 삭제하고자 할 때의 URL

/cgi-bin/DeleteUser?ID=guest : CGI 이름이 수행작업을 지정한다.
/cgi-bin/BBS?Menu=User&Cmd=Delete&ID=guest : CGI는 BBS 하나만 두고 인수들을 통해 수행작업을 지정한다.

여러 CGI를 통합된 인터페이스로 다음과 같이 모델링할 수 있다.


그림 9 : 통합인터페이스 모델

각 모듈들은 다음과 같다.

4.3 디버깅

CGI 프로그램은 일반적인 방법으로 디버깅을 하기가 곤란하며 다음과 같은 방법들을 이용할 수 있다.

4.3.1 입력

CGI의 입력을 시뮬레이션하기 위해서는 먼저 환경변수들을 적절하게 설정한 뒤, "GET"인 경우는 인수들을 "QUERY_STRING"에 지정한 뒤 해당 CGI를 실행시키면 되며, "POST"인 경우는 인수들의 크기를 "CONTENT_LENGTH"에 지정하고 해당 CGI를 실행시킨 다음 입력될 인수들을 지정된 크기까지 타이핑하면 된다. 통합인터페이스 모델의 경우, 외부프로그램들은 명령어에 인수(*argv[])를 포함하여 실행시키면 된다.

4.3.2 출력

일반적으로 프로그램의 상태를 추적하기 위해 "stderr"로 중간결과값들을 출력시킨다. 하지만 WWW 시스템에서 서버는 CGI의 "stderr"와 "stdout"로 가는 출력들을 모두 가로챈다. 따라서 중간결과값들을 출력하기 위해서는 HTML 문서를 생성시킬때 주석문안에 넣으면 된다. 그리고 진행과정을 검사하기 위해서는 서버에서 생성하는 log 화일들을 분석하면 된다. 또한 HTTP 응답헤더부분을 검사하기 위해서는 80번 포트로 telnet을 열어 HTTP를 시뮬레이션하면 된다. HTTP를 시뮬레이션한 예는 다음과 같다.

[cezanne-kyung] telnet cezanne.daum.co.kr 80
Trying 203.248.135.66...
Connected to cezanne.daum.co.kr.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.0 200 Document follows
Date: Tue, 12 Mar 1996 23:07:41 GMT
Server: NCSA/1.5
Content-type: text/html
Last-modified: Wed, 21 Feb 1996 15:30:08 GMT
Content-length: 1881

<HTML>
<HEAD>
<TITLE>DAUM ON-LINE</TITLE>
....

5. 쿠키(Cookie)

쿠키는 CGI와 같은 서버쪽 인터페이스에서 클라이언트에 상태정보를 저장하고 추출할 수 있는 시스템이다. 쿠키를 이용하면 상태정보를 효율적으로 관리할 수 있어, WWW에 기반을 둔 클라이언트/서버 시스템을 더욱 확장시킬 수 있다.

5.1 작동원리

서버쪽에서 결과문서를 클라이언트에 반환할 때, 클라이언트가 저장할 쿠키(상태정보)를 같이 보낼 수 있다. 이후에 클라이언트가 서버쪽에 문서를 요청할 때, 현재 클라이언트에 저장된 쿠키 중 조건이 일치하는 쿠키들을 다시 서버쪽으로 넘겨 준다.
이 과정을 그림으로 나타내면 다음과 같다.


그림 10 : 쿠키의 작동원리

5.2 응용분야

다음과 같은 분야에 응용이 가능하다.

5.3 문법

클라이언트에 쿠키를 저장하기 위해서는 HTTP 응답헤더에 다음과 같이 들어가게 된다.

Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure

일단 쿠키가 저장되면, 이후에 클라이언트가 서버쪽에 문서를 요청할 때 도메인과 경로가 유효한 쿠키들을 HTTP 요청헤더(HTTP request header)에 포함시켜 서버쪽에 넘겨주게 된다. HTTP 요청헤더에 다음과 같이 들어가게 된다.

Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ...

클라이언트는 쿠키를 무한정으로 저장하는 것이 아니라, 정해진 갯수만을 저장할 수 있다. 최소한으로 저장되는 최대 갯수는 다음과 같다.

따라서 지정된 갯수를 초과하면 이전에 저장된 쿠키는 새로운 쿠키가 덮어쓰게 되며, 이때 최근에 가장 적게 사용된 순서에 따라 지워지게 된다 - LRU(Least Recently Used) 방식. 유의할 점들은 다음과 같다. 예제를 보면 다음과 같다.

서버가 문서 반환시
Set-Cookie: USER=guest; path=/
클라이언트가 "/" 경로의 문서 요청시
Cookie: USER=guest;
서버가 문서 반환시
Set-Cookie: USER=guest2; path=/CUG
클라이언트가 "/CUG" 경로의 문서 요청시
Cookie: USER=guest2; USER=guest

C 예제 프로그램은 다음과 같이 작성된다.

5.4 평가

4.1에서 상태정보유지를 위한 CGI를 쿠키로 바꾸어 구현해 보면 다음 그림과 같이 된다.


그림 11 : 쿠키의 진행과정

위의 그림에서 CGI A는 CGI B, CGI C에 필요한 쿠키 B, 쿠기 A를 클라이언트에 넘겨준다. 그다음 클라이언트가 CGI B를 호출할 때 CGI B의 경로에 유효한 쿠키 B를 넘겨준다. 다시 클라이언트가 CGI C를 호출할 때 CGI C의 경로에 유효한 쿠키 A를 넘겨준다. 여기서 기존의 시스템과 가장 큰 차이점은, 클라이언트가 쿠키를 저장하고 있기에 CGI B가 CGI C에 필요한 쿠키 A를 유지하기위한 버퍼역할을 할 필요가 없다는 점이다.

쿠키는 이와같이 상태정보유지를 더욱 매끄럽게 구현할 수 있으나, 쿠키를 한번에 하나씩밖에 지정할 수 없으며, 쿠키의 저장갯수에 제한을 갖는 단점을 안고 있다.

6. 결론

앞에서 CGI의 작동원리와 새로운 방식들, 상태 유지를 위한 방법들을 설명하였다. WWW 시스템은 편리함과 확장성으로 인하여 앞으로도 꾸준히 보급이 증가될 것으로 추측된다. 그러나 WWW 시스템은 상태 유지가 어렵다는 단점을 안고 있다. 쿠키 방식의 도입으로 인하여 상태 유지가 용이해졌으나, 아직 쿠키의 기능은 제약점이 많다. 따라서 향후 이의 보완이 요구된다.

참고문헌(References)

[최 95]
최윤수, "서버 인터페이스", 가자! 웹의 세계로 - 강의자료집 (제2회 WWW-Workshop), WWW-KR, 1995.
[NCSA 95]
"The Common Gateway Interface", NCSA, 1995.
URL: http://hoohoo.ncsa.uiuc.edu/cgi/
[Net.Genesis 95]
Net.Genesis, Hall, D., Build a Web Site, PRIMA, 1995.
[Netscape 95a]
"An Exploration of Dynamic Documents", Netscape, 1995.
URL: http://home.mcom.com/assist/net_sites/pushpull.html
[Netscape 95b]
"Persistent Client State HTTP Cookies", Netscape, 1995.
URL: http://home.netscape.com/newsref/std/cookie_spec.html