|
실용성 있는 CGI 로 게시판을 만들어 보기로 하자. 리눅스 래드햇에서 구현하는 SSI와 DB(Postgre) 연동을 통한 CGI로 게시판 만들기
1. 개요 CGI로 맘에 꼭 드는 게시판을 만들어 보기로 하자. 이번 호에서는 만들기 위해서 필요한 배경지식과 간단한 라이브러리 사용법에 대해서 알고 넘어가기로 하자. 또 시간이 남으면 만들고자 하는 게시판의 구성에 대해서도 잠깐 언급하고 넘어가도록 하겠다. 그러면 머리가 복잡할 때 이렇게 간단한 CGI를 짜면서 기쁨을 얻기 바라겠다.
2. 기능성보다는 실용성 있는 CGI에 대한 이야기. CGI(Common Gateway Interface) - 불과 1년 전만 해도 무척이나 생소했던 단어였는데. 어느덧 CGI 사장설까지 나왔다. 무척이나 기술변화가 빠르다는 것을 실감하게 된다. 하지만 아직도 쓸 만한 기술이 CGI인 듯 싶다. 사용자에게 크게 부담감 주지 않고 기다리는 시간에서 자유로울 수 있다는 장점이 있다. 자바가 많이 발전해서 충분한 기대와 또 가능성이 현실로 많이 다가 왔지만 아직도 DataBase와 관련된 부분에서 CGI 기술이 많이 활용되고 있다. 물론 DataBase를 다룰 때 상태유지하기 위한 노력이 필요하기는 하지만 약간 안정성 있는 DataBase 엔진을 사용한다면 쓸 만한 Application을 만드는데 그리 많은 노력이 필요하지 않다. 여러모로 CGI는 많이 알려져 있고 또 부담없이 개발할 수 있는 프로그램 인터페이스인 듯 싶다. 왜 CGI가 쉬운가? 이유는 간단하다. 출력의 경우 MIME헤더만 출력해주면 나머지 다른 부분은 일반 프로그램을 짜는 것과 동일하고 다만 CGI 입력에 대한 Decoding 만이 문제인데 그것도 라이브러리들을 이용해서 간단하게 해결할 수 있기 때문이다. 여기서는 유명한 프로그램 기법이기 때문에 원론 적인 얘기는 많이 생략하고 바로 활용할 수 있는 것들을 중심으로 이야기를 풀어 나가고자 한다. 만일 원론적인 내용이 알고 싶다면 http://hoohoo.ncsa.uuic.edu/cgi/ 를 확인해 보라. CGI 에 대해서 부정적인 견해를 많이 가지고 있는 사람이라면 이 글의 이쯤에서 글을 그만 읽는다고 해서 손해 날 것은 없다고 본다. 무엇보다도 필자가 이 지면을 통해서 잠깐 말하고 싶은 것이 있는데 그것은 바로 content에 관한 것이다. 신 기술만을 가지고 인터넷상(아니 어쩌면 프로그램세계에서)에서 무언가를 꿈꾼다는 것은 무리가 있기 때문이다. 우리나라 여건상 기술만을 개발하기만 한다고 해서 이득 볼 일은 많이 없다고 느껴진다. 어쩌면 가장 중요한 일은 기존에 개발되어있는 좋은 기술이 있다면 그것을 활용해서 인터넷상의 유저들을 끌어 모으는 일일 것이다. 좀더 깜찍하고 상큼한 아이디어로 사용자들을 매료시키는 일이야 말로 웹상에서 Application을 개발하거나 Homepage를 제작하거나 할 때 생각해 두어야 할 일이 아닐까? 필자가 만드는 게시판이 그렇게 상큼하고 매력적이라고 말할 수는 없지만 대부분의 웹보드(웹게시판)프로그램에서 간과하고 넘어가는 부분에 기능을 추가하여 보았다.
3. CGI와 관련해서 알아두면 가끔은 쓸 만한 기술 SSI(Server Side Include) 푸쉬기술 이전에 주로 사용되던 것이 바로 이 기법이다. 이것을 Html에서 웹서버에 있는 명령을 실행하거나 파일등을 include 할 때 사용하는데 명령어나 CGI 프로그램을 실행시키는 것은 약간 무모한 짓이기 때문에 파일만을 include시키는 것만을 사용하기로 한다. SSI 파일은 보통 확장자가 .shtml를 끝나는 파일을 많이 사용한다. .html을 바로 사용하지 않는 이유는 SSI를 이용하게 되면 문서를 일일이 파싱해야 하기 때문에 서버에 부담을 주기 때문인데 가급적이면 확장자를 구분해서 사용하도록 하고 자제해서 사용하는 것이 좋고 만일 사용하지 않을 수 있는 다른 아이디어가 있다면 그것을 이용하는 것이 좋다. 어쨌든 필자가 이것을 이용해서 하고 싶은 일은 게시판에서 만들어낸 동적인 출력물을 html에 포함 시키기 위한 것이다. SSI가 작동하기 위해서는 httpd 의 srm.conf 와 access.conf 파일을 수정해서 SSI 가 되도록 한다. access.conf 파일의 일부분을 보면 httpd 도큐멘트 root가 있는데, 그 곳의 부분이 아래와 같이 되어 있을 것이다. Option 부분에 include를 추가 시켜주면 된다. 아래는 이미 추가가 되어 있는 상태이다. < Directory /var/www/docs > AllowOverride None srm.conf 파일에서는 중간쯤에 다음과 같은 항목이 주석표시(#)가 되어 있다면 없애주도록 하고 중간부분에 AddHandier server-parsed 항목을 찾아서 아래와 같이 수정해 주면 된다. : 이제 SSI가 작동될 준비는 끝이 난 것이다. httpd을 다시 구동시키고(ps -ax | grep httpd를 실행시킨 다음 가장 작은 번호의 httpd 의 PID를 가지고 kill -HUP httpd 의 PID를 하면 된다.) 간단하게 SSI 관련 명령어에 대해서 살펴보기로 하자. SSI 명령어는 html 의 주석인 <!---->사이에
들어간다. 자, 그럼 우리에게 필요한 것은 파일을 include 하는
것이므로 한번 확인해보도록 하자.
4. 게시판을 만들기 전에 필요한 DB연동에 대한 이야기. 게시판을 만드는데 DataBase를 연결해야 하는가? 그게 의문점으로 남을지 모르겠다. 왜냐하면, File I/O를 이용해서 직접 구현할 수도 있는데 말이다. 장단점이 있다. 일단 File I/O를 사용하게 되면 빠르게 구현할 수 있지만 복잡한 정렬기능이라든가 데이터의 일부분을 가져오는데 많은 코드가 필요하게 되는 단점이 있다. 간단한 방명록을 만드는 경우라면 File I/O를 사용하는 것이 오히려 효과적일 수도 있다. DataBase를 연동하게 되면 덩치는 커지는 대신 다양한 자료관리 기능을 제공해 준다. 필자는 DataBase를 직접 연동한다. 이유라면, 편리하게 프로그램이라든가 데이터를 관리할 수 있기 때문이다. 또한 Application을 개발하는데 들이는 시간을 절약할 수 있기 때문이다. 또한 어떤 DataBase 엔진이건 간에 API를 사용하는 방법이 거의 비슷하게 구성되어 있기 때문에 하나를 제대로 쓸 줄 아는 사람이라면 다른 DataBase 엔진에서 제공해주는 API 라든가 JDBC를 사용할 때도 쉽게 전이가 가능하다. CGI 로 데이타를 처리할 때 한가지 주요하게 기억해 두어야 할 것은 WEB 의 특성인 상태없음 (stateless) 이다. 말하자면 프로그램이 한번 접속해서 데이터를 받아오면 다음에 접속할 때 프로그램을 처음부터 다시 시작한다는 것을 의미한다. 이것은 무엇을 의미하는가? 결국 중간에 어떤 결과 값을 가져오고 난 다음 상태를 기억했다가 일부분을 연속적으로!! 본다는 것은 불가능하다는 소리가 된다. 결국 이것을 구현하기 위해서는 여러 가지 변수에 상태 값을 CGI에서 CGI로 넘겨줌으로써 계속 상태를 유지하는 것처럼 동작하게 하는 일종의 트릭을 사용해야 한다. 이 성질(stateless) 때문에 CGI와 DataBase 연동의 문제는 매번 프로그램의 재시작을 함으로써 서버족에 오버헤드가 발생한다는 것이다. 어쩌면 이것은 CGI 의 한계일지도 모른다. 물론 JAVA를 쓴다면 이 문제는 해결할 수도 있다. 하지만 서버가 충분히 데이터를 다룰 수 있을 만큼의 퍼포먼스를 내고 최소한 UNIX 환경이라면 그렇게 걱정할 것이 없다. 펜티엄 100에 32M의 메모리를 가졌다면 우리가 일상에서 사용하고 있는 왠만한 중소급 data를 처리하는 데에는 그렇게 큰 걱정될 것은 없다고 본다.
5. CGI Library - cgihtml - 1.66 왜 Library를 사용해야 하는 건지? 여기서 다시한번 말하지만 프로그램을 개발할 때 어디에 중점을 맞추고 있는가를 잊으면 안된다. 만일 CGI 자체 구현이 목적이라면 그것에만 신경을 써야 할 것이고 Application 이 목적이라면 Application 에만 신경을 쓸 수 있도록 자신의 환경을 만들어야 한다. 가끔 가다가 이상한 자존심(?)을 발휘하는 사람들이 있어서 모든 것을 자신의 손으로 개발해야 한다는 식의 사고방식으로 시작하려고 하는 사람들이 있다는 것이다. 아무튼 여기서 우리의 목적은 Application을 개발하는 데 있다. 자신이 Application을 개발하는데 있어서 중요한 Content를 구현하는데 전력을 다할 수 있어야 하는 것이다. 필자가 이 라이브러리를 이용하는 이유는 여러 가지가 있다. Text 상태에서 직접 데이터를 입력하고 디버깅할 수 있기 때문에 웹에서의 골치 아픈 Debugging 작업에서 벗어나게 해 주기 때문이다. 이 라이브러리에 대해서 설명하기 전에 웹에서의 작업을 잠시 얘기해보자. 왜 골치 아픈 Debugging 인가? 그냥 Application 에서와 달리(특히 UNIX 에서) CGI에서는 여러 가지 부분에서 Error 가 발생할 수 있기 때문이다. 그리고 에러가 나면 httpd 는 아주 무성의한 error message 만을 내고 만다. error debug를 하기 위해서 할 수 있는 가장 원시적인 방법인 print를 이용하는 것을 허용하지 않는다. 결국 중간에 어디에선가 error 가 나면, 기존에 나오는 모든 출력이 무시되고 httpd error message 만 나오기 때문이다. 그렇기 때문에 이게 httpd에서 난 것인지, Application에서 난 것인지 잘 알 수가 없다. UNIX에서 Debugging Tool을 따로 쓰지 않는 사람이라면 이렇게 error debugging 하는 것은 정말 머리털 빠지는 일일 수밖에 없다. 더군다나 CGI 만 개발하고 httpd 에 대해서 잘 모른다면 정말 속수무책 이다. 보통 error 가 발생하는 경우는 httpd의 특성상 주어지는 디렉토리의 권한 문제에서 날 수가 있고 아니면 UNIX 의 디렉토리 권한문제, 또는 CGI 코드에서 아니면 우리가 연동해서 쓰고자 하는 DB 엔진의 setting 문제 일 수도 있다. 이렇게 여러 가지 경우의 수를 다 검사해보아야 하는데 이런 상황을 잘 모르는 상태에서 프로그래밍을 하다가 보면 짧은 몇 줄 짜고 Debugging 하는데 날 밤 꼴딱 새는 것은 정말 우스운 일이 되는 것이다. 이런 경우에 CGI를 개발하는데 의욕을 상실하게 되고 우리가 원하는 목적을 제대로 달성할 수 없게 되는 것이다. 만일 여기서 Web Broswer 만 어떻게 벗어난다면 그나마 Debugging 하기는 편리할 것이다. cgi-html도 Text 모드에서 직접 CGI 입력변수를 넣을 수 있도록 해준다. 그렇게 함으로 얻을 수 있는 장점은 바로 httpd의 무시무시한 server error message를 피할 수 있다. 이렇게 debugging 하는 것이 100% 완벽하지는 않았지만 필자의 경험으로는 90% 정도는 효과가 있는 듯 싶다. 그러면 이제 cgi-html 에 대해서 잠깐 설명을 하고 넘어가기로 하자. 기본적인 구성 프로그램 흐름은 다음과 같다. 자세한 library 설명은 다음 글에서 소개하도록 하고 여기서는 어떻게 사용하는가만 확인해 보도록 하겠다. 1 /* template using cgihtml.a library
*/ cgi-html 에서는 3개의 header 파일을 제공해주는데 cgi-lib.h는 cgi 입력변수를 파싱하는데 필요한 함수들이 정의가 되어있다. 입력변수들을 처리하기 위해서 llist 라는 구조체를 이용하여 linked list를 사용한다. html-lib.h가 있는데 이것은 html tag와 관련이 있는 함수들을 정의 해 두었다. 다음 string-lib는 문자열 함수를 처리하는 데 필요한 함수들을 제공해주고 있다. 특히 많이 사용할 함수가 있는데 그것은 문자열 메모리 할당 함수이다. 만일 CGI 입력변수를 처리하는 경우라면 반드시 llist 구조체를 반드시 선언해준다. 11번째 줄에서 entries 라는 구조체를 선언해주었다. 그 다음 CGI로 들어오는 입력을 처리해주기 위해서 읽어주는 작업이 필요하다. 15번째 줄을 보면 read_cgi_input 함수를 사용하였다. 이것을 통해서 linked list 에 CGI 입력 값을 저장한다. 그 다음엔 CGI 변수의 값을 필요할 때 불러서 쓰기만 하면 된다. CGI 변수를 부르는 함수로 cgi_val() 함수를 쓴다. CGI 입력 값 중에서 변수명을 넣으면 리스트에서 찾아서 그 값을 리턴해준다. 문자열 값으로 리턴이 되는데, 여기서 주의해야 할 사항이 있다. 16, 17 번째 줄을 살펴보자. 둘 다 제대로 값이 들어간다. 그런데 16 번째 줄처럼 포인터를 이용해서 값을 받을 때 제대로 값을 전달되지 않는 경우가 있으므로 17 번째 줄처럼 cgi_html에서 제공하는 newstr 함수를 써서 메로리 할당한 다음 자동으로 스트링을 복사하던지 아니면 문자배열을 선언한 다음 strcpy 명령으로 복사를 해서 쓰는 것이 낫다. 어쨌든 17 번째 줄처럼 사용할 것을 권장한다. html_header() 는 html MIME type을 출력하는 것인데 puts("Content-type : text/html\n\n") ; 과 똑같은 결과가 된다. 한가지 기억해 두어야 할 것은 HTML 형식으로 출력하려고 한다면 반드시 넣어주어야 한다. 그렇지 않으면 httpd 가 어떤 종류의 문서를 출력해야 할지 몰라서 황당한 server error를 내고 말 것이다. 이렇게 한 다음 나머지 처리하고 싶은 것을 처리한 다음 list 구조체의 메모리를 해제해주어야 한다. 21번째 줄처럼 list_clear() 함수를 이용하면 된다. 주의할 것은 &entries을 파라메터로 넘겼다는 것이다. 일반 프로그램에서 추가되는 것이 얼마 되지 않는다는 것을 알 수 있다. 컴파일하는 방법은 다음과 같다. cc -o program.cgi program.cgi.o cgihtml.a 컴파일러는 cc 또는, gcc 를 사용하면 된다. 여기서 -o 옵션을 쓰면 생성하고 싶은 파일이름을 지정하는 것이고 cgihtml.a 라이브러리 archive를 포함시켜 주면 된다. cgi-html 라이브러리는 아래의 사이트에 가면 받을 수 있다. eekim.com FTP Site ftp://ftp.eekim.com/users/eekim/cgihtml/ Harvard Computer Society FTP Site ftp://hcs.harvard.edu/pub/web/tools/cgihtml/ cgi-html 홈페이지가 있는데 이곳에 가면 취근의 정보를 얻을 수 있으니까 한번 가보도록 하라. http://www.eekim.com/software/cgihtml/. cgi-html 함수 레퍼런스를 실어보았다. 참고가 되었으면 좋겠다.
cgi_html 함수 레퍼런스-cgi-lib.h에 포함되어 있는 것들 Library 변수 cgi-lib.h CGI SERVER_SOFTWARE Library 함수들 short accept_image() void unescape_url() int real_cgi_input(llist *entries) ; char* cgi_val(llist 1, char *name) ; char **cgi_val_multi(llist 1, char *name)
; char* cgi_name(llist 1, char *value) ; char **cgi_name_multi(llist 1, char *value)
; int parse_cookies(llist *entries) ; void print_cgi_env() ; void print_entries(llist 1) ; char* escape_input(char *str) ; short is_form_empty(llist 1) ; short is_field_exists(llist 1, char *str)
; short is_field_empty(llist 1, char *str)
;
-html-lib.h : html 과 관련된 함수들 Library functions void html_header() ; void mime_header(char *mime) ; void nph_header(char *status) ; void show_html_page(char *loc) void status(char *status) ; void pragma(char *msg) ; void set_cookie(char *name, char *value,
char *expires, char *path, void html_begin(char *title) ; void html_end() ; void h1(char *header) ; ... void h6(char
*header) ; void hidden(char *name, char *value) ;
- string-lib.h : 문자열 처리와 관련된 함수이다. Library functions char* newstr(char *str) ; char* substr(char *str, int offset, int
len) ; char* replace_ltgt(char *str) ; char *lower_case(char *buffer) ;
query 의 결과를 텍스트상태에서 확인하는 방법 command line 상에서 프로그램을 실행시키면 아래와 같은 결과가 나온다. --- cgihtml Interactive Mode --- Enter CGI input sting. Remember to encode
appropriate characters. 입력문자열이 다음과 같다고 가정한다면 name=eugene&age=21 query의 결과로 다음과 같은 것이 출력될 것이다. Input string : name=eugene&age=21 <h1>Quey results</h1> segmentation fault가 일어난다면 이렇게 파일을 실행시켜 봄으로써 프로그램의 error를 잡을 수 있을 것이다.
6. Postgres C Library - libpq 리눅스 레드햇을 설치했을 경우 포스트그래스의 디렉토리와 실행환경에 대해서 설명을 해주고 인스톨본을 설치했을 경우의 환경을 설명해 준다. 포스트그래스에 대한 내용은 너무나 유명한 내용이기 때문엔 필요한 API 만을 설명한고 나머지는 reference를 참고 하도록 한다. 참고로 LIBPQ에 대한 아래의 설명은 www.linuxlab.co.kr에 있는 강좌파일을 참고 했음을 밝혀 둔다. -LIBPQ는 Postgres에 대한 API로 사용자 프로그램은 헤더화일로 libpq-fe.h를 include 하고 library로 libpq.a를 link 하여 작성한다. /usr/local/pgsql/include/libpq-fe.h - 환경변수와 초기화(setenv 함수를 이용설정?) PGHOST : Postgres 가 탑재된 서버이름 설정 PGOPTIONS : 부가적인 runtime option PGPORT :포트번호 설정 PGTTY :서버로부터의 디버깅 메시지를 출력할 파일이나 화면(tty) 설정 PGDATABASE : POSTGRES 데이터베이스 이름 설정 PGREALM : POSTGRES 와 사용할 Kerberos realm 설정 - API 1) 데이터 베이스 연결/종료 : PQsetdb, PQfinish, PQreset PGconn *PQsetdb void PQfinish(PQconn *conn) void PQreset(PGconn *conn) 2) 연결검사 : PQstatus(PQdb, PQhost, PQoptions, PQport, PQtty) ConnStatusType *PQstatus(PGconn *conn) char *PQdb(PQconn *conn) char *PQhost(PQconn *conn) char *PQport(PQconn *conn) char *PQoptions(PQconn *conn) char *PQtty(PQconn *conn) 3) 에러메세지 관련 함수 char *PQerror Message(PQconn *conn) ; void PQtrace(PQconn *conn, FILE*debug_port)
; void PQuntrace(PQconn *conn) ; 4) 쿼리(Query) PGresult *PQexec(PQconn *conn, char *query)
; ExecStatusType PQresult structure(PGresult
*res) ; ◆query 한 tuple(instance, record)결과를
추출하는 함수 int PQntuples(PGresult *res) ; int PQnfields(PGresult *res) ; char *PQfname(PGresult *res, int field_index)
; int PQfnumber(PGresult *res, char* field_name)
; Oid PQftype(PGresult *res, int field_num)
; int2 PQfsize(PGresult *res, int field_index)
; char *PQgetvalue(PGresult *res, int tup_num,
int field_num) ; int PQgetlength(PGresult *res, int tup_num,
int field_num) ; char *PQcmdStatus(PGresult *res) ; char *PQoidStatus(PGresult *res) ; void PQprintTuple(PGresult *res) ; void PQclear(PQresult *res) ; 5) Fast Path : db내의 internal function호출 PGresule* PQfn(PGconn,conn) 6) Asynchronous Notification PGnotify* PQNotifies(PGconn *conn) ; 7) COPY 명령과 관계된 함수들 * copy : libpq에 의해 사용된 커넥션에
/으로 데이터를 쓰거나, 읽어오는 프로토콜 이하 함수들은 이
커넥션에 direct로 붙어서(일반적인 PQexec으로는 실행되는
query는 하나의 논리작업 단위인데, 이하 함수 호출들은 여러
번의 함수호출이 하나의 논리작업단위를 형성한다) 이 명령어를
지원한다. 즉, PQexec으로 copy 명령을 실행시키고, 바로 이어서
direct read/write 수행 int PQgetline(PGconn *conn, char *string,
int length) ; void PQputline(PGconn *conn, char *string)
; int PQendcopy(PGconn *conn) ; [ 예제 ] 7) 사용자 인증과 관련된 함수 char *fe_getauthname(char* errorMessage)
; void fe_setauthsvc(char *name, char* errorMessage)
; 이번 호에서는 앞으로 사용할 툴들에 대해서 잠깐 둘러 보았다. 내용이 그렇게 썩 잘 정리가 된 것은 아니지만 필자가 생각하여 정리한 어설픈 얘기들이 CGI로 프로그래밍 해보려고 하는 여러분에게 어느 정도는 도움이 될 것이라고 생각한다. 다시한번 말하지만 프로그래밍을 공부할 때는 실용적인 것과 미래지향적이 있다는 것을 잊지 말기 바란다. |