Home Map Index Search News Archives Links About LF
[Top Bar]
[Bottom Bar]
[Photo of the Author]
Luis Colorado
글쓴이 소개:

Luis Colorado님은 스페인에 있는 Telefonica Sistemas S.A.에서 유닉스시스템과 인터넷접근을 가능하게 해주는 관리자로 일하고 계십니다. 글쓴이는 Universidad Complutense of Madrid에서 물리학을 전공하였으며 몇몇 유닉스 유틸리티를 오픈소스의 형태로 개발해오고 계십니다.

글쓴이에게 편지쓰기

순 서:
들어가는 글
개발동기
M4
작동원리
사용자가 CGI로 정보전달하기
다운로드

PG2CGI를 이용한 웹에서 데이터베이스 접근

[Ilustration]

요약:

이 글에서는 HTML페이지에서 데이터베이스에 접속할 수 있게 해주는 새로운 프로그램에 대해 이야기하고자 합니다. 이 프로그램이 설계된 목적은 데이터접근을 통제하고, 환경설정에서 유연성을 획득하며, 레이아웃 형식에 독립적으로 만들어주는 것잆니다. 이 프로그램은 최근 1년동안 Linux JournalLinuxFocus (www.linuxfocus.org) 에서 많이 등장하는 M4에 대한 몇몇 기사를 읽다가 떠오른 생각들을 모아서 개발한 것입니다.


들어가는 글

이 글을 통해 나는 여러분께 웹을 통하여 데이터베이스를 이용할 수 있게하는 패키지를 개발하게 된 아이디어와 근본원리에 대해 이야기하고자 한다. 즉 PG2CGI패키지에 대한 사용설명서를 쓰기 위하여 적는 것이 아니라 간단한 소개와 소프트웨어를 사용해 보고 독자들이 평가를 해본 다음 여러분의 피드백을 보내주기를 기대하면서 이 글을 적는다. (이미 참고할만한 가이드가 프로그램의 배포판에 들어 있다. 이 배포판이 있는 URL은 이 글 뒤에 있다.)

개발동기

나는 리눅스저널과 리눅스포커스에서 HTML소스를 관리하기 위하여 M4를 사용하는 것에 대한 연재기사의 결과물로 이 프로그램을 개발하였다. 연재된 글들은 웹페이지를 관리하는 독립실행형 도구로서, 동적으로 내용을 생성하는 도구로서의 M4의 잠재성과 유용성을 보여주었다.

반면 매우 다양한 웹 서버나 데이터베이스를 이용하는 것은 양쪽 환경사이를 연결해주는 유틸리티의 부족을 가져왔다.(대부분의 이런 다리 역할을 하는 응용프로그램들은 상업용이거나 다룰 수 있는 특정 형식에 따라야 하는 심각한 의존성을 가지고 있다.)

이러한 소프트웨어 패키지들은 데이터베이스와 웹 환경을 하나로 통합해준다. 그러기 위해서 다음과 같은 요구조건을 만족하여야 한다.:

이 때 유틸리티는 요구조건들을 만족시키기 위하여 효율성면에서 약간의 조그마한 불이익을 감수한다.(유틸리티는 실행되는 동안 M4를 여러분 호출하게 된다.) 그러나 그 결과는 대부분의 경우 만족스럽다.(대부분의 경우를 고려해보면 HTML문서를 동적으로 생성하기 위하여 데이터베이스의 질의는 이미 여러분 수행되어 있다.)

M4

M4는 오래전에 개발된 매크로 처리 도구이다. 우리가 만든 소프트웨어는 이 매크로처리기를 매우 유용하게 사용한다.:

이러한 매크로처리기의 반복사용은 GNU의 M4유틸리티로 검사하는 것을 잘 만족시켜주지만 결국 효율성면에서 손실을 가져온다.

올바른 표현식(정규 표현식):

이 유틸리티는 주어진 설정규칙이 옳은지 그른지를 확인하기 위하여 정규표현식을 엄격히 사용한다. 정규표현식은 우리에게 매우 편리한 기능을 제공하기 때문에 간단한 비교보다 더 선호되고 있다. 정규표현식은 다음과 같은 장점을 가지고 있다.:

PG2CGI는 사용된 설정규칙을 검증할 때 정규표현식을 이용한다.

하나의 정규표현식에서 복수의 하위체인들로 그룹화할 수 있기때문에 표현식의 문법과 정규표현식에서 드라이버로 전달하기 위한 데이터의 파싱은 쉽게 이루어진다.

한 예를 살펴보자: 클라이언트가 잘의문에 어떤 정보를 전달하려 한다면 그 데이터들은 다음 문법에 알맞게 바뀌어져야 한다.:

항목=값
    

질의문은 이 형식을 만족해야 하기 때문에 문장에 부가적 정보가 있을 수 없다.

선택규칙에서 다음 조건을 쓸 때 이 문법을 반드시 사용하는 것은 쉬운 일이다.:

  QUERY_STRING: "^FIELD=[^&]*$";
	  

윗 줄은 질의문이 적절한 문법을 만족할 경우에만 규칙들이 호출되는 것을 보장한다. 위의 예에서 항목의 값을 괄호로 묶을 경우 프로그램은 일치하는 값을 파싱할 수 있게 해준다.:

  QUERY_STRING: "^FIELD=([^&]*)$";
    

이 과정에서 프로그램은 네비케이터에 의해 만들어지는 %xx형식으로 전달되는 값을 적절한 값으로 변환시켜준다.

작동원리

이제 어떻게 작동하는지 이야기 해보자. 처음으로 실행되면 프로그램은 웹서버로부터 환경변수들의 값을 받아서 그 값들을 이용하여 스스로 적절한 환경설정을 해준다. 환경변수는 프로그램이 확인해보는 모든 것들이다.:클라이언트가 무엇인가? 어떤 요청이 이루어졌는가? 요청의 내용은 무엇인가? 클라이언트가 지원하는 정보의 형식(MIME형식)은 무엇인가?등등.... PG2CGI는 그 다음 규칙의 왼쪽에 사용되는 규칙을 선택한다. 현재는 세가지형식의 규칙이 제공된다.:

이러한 방법으로 규칙을 만들수 있다. 다음에는 우리는 규칙에서 유효한 왼쪽항목을 정하기 위하여 많은 조건들을 묶는다. 조건들은 중괄호 "{}"사이에 위치한다.

규칙의 오른쪽과 왼쪽은 중괄호"{}"로 구분을 함으로써 경계를 나타낸다. 그리고 왼쪽과 오른쪽은 '->'기호를 이용하여 구분된다.

오른쪽 항목들은 동일한 구문을 가진 조건들이 들어있다.: 변수의 이름,':'문자, 문자열과 종결을 의미하는 ';'문자. 오른쪽에 있는 모든 조건은 M4에 의해 수행되어져 주어진 값을 변수에 할당하는 것이다.:

다른 변수는 템플릿파일이나 적절한 드라이버에 의해 사용되어질 수도 있다.

정보가 어떻게 사용자로부터 CGI로 전달되나?

매우 간단하다. 각각의 규칙에서 왼쪽에 있는 정규표현식으로부터 그룹들이 만들어 지는데 이 그룹들은 각각 변수로 변환되어진다.(이 새로운 변수들의 이름은 `term_<i>_match_<j>'과 같은 형식이다. 이때 <i>는 규칙에 사용된 순서번호이다.(결국 첫 조건은 1을, 두번째 조건은 2를 가지는 형태가 된다.) 만약 클라이언트로 부터 다음과 같은 질의문이 전달되었다고 하자.:

`NAME=JOSE&FAMNAME1=DE+LA+FUENTE&FAMNAME2=LOPEZ'

웹싸이트에 선언된 규칙은 다음과 같다.:

     QUERY_STRING: "NAME=([^&]*)";
     QUERY_STRING: "FAMNAME1=([^&]*)";
     QUERY_STRING: "FAMNAME2=([^&]*)";
결과는 다음과 같이 될 것이다.::
     term_0_match_0  <- "NAME=JOSE";
     term_0_match_1  <- "JOSE";
     term_1_match_0  <- "FAMNAME1=DE LA FUENTE";  
           (공백은 +문자로 바뀌어짐을 알 수 있다.)
     term_1_match_1  <- "DE LA FUENTE";
     term_2_match_0  <- "FAMNAME2=LOPEZ";
     term_2_match_1  <- "LOPEZ";
드라이버:

이 글에서 구체적으로 드라이버에 관한 이야기는 하지 않았다. 그러나 이미 PG2CGI배포판에 들어 있는 문서자료를 통해 드라이버의 사용방법을 설명했다. 관심이 있는 독자라면 꼭 참고설명서에 포함되어 있는 글을 읽어보기 바란다.

POSTGRESQL로 운영되는 데이터베이스에 연결하는 경우에는 단 하나의 드라이버가 사용된다 그러나 이미 개발자는 LDAP형식의 데이터베이스를 위한 다른 드라이버를 개발할 계획을 가지고 있다.

보기:

전체적인 예제 소스를 살펴보자

slug.ctv.es에 있는 공지사항과 관련된 데이터베이스에서 공지사항 테이블을 만들어보자. 이는 테이블에서 각각의 레코드를 찾아보거나 전체 목록을 만들기 위하여 두개의 템플릿을 사용하는 가장 단순한 예이다.

/etc/html2sql.cfg
{
  PATH_INFO: "^/avisos/?$"; # PATH_INFO변수에 의해 선택된다.
  [SERVER_ADMIN: ".*"];     # SERVER_ADMIN으로부터 정보를 얻는다. 선택사항
} -> {
  DRIVER:   "POSTGRESQL";
  PGTTY:    "/dev/console";  # 콘솔로 로그하도록 한다.
  PGDATABASE:     "postgres";
# 우리는 항상 질의를 한다.(늘 내부적으로 템플릿파일에서 개별적인 레코드들을
# 연결하기 위하여 질의문을 사용한다.)
  PGQUERY:  "select oid,ct,titulo,texto,mt"
      " from avisos"
      " where (dt is NULL or dt > 'now')"
      " order by mt desc";
# 파일은 템플릿을 가지고 있다.
  M4FILE:    "/usr/local/etc/httpd/plantillas_m4/avisos.m4";
  WEBMASTER: "term_1_match_0";
  #TESTMODE: "TRUE";
}

# 다음 선택규칙은 알려진 우선키값인 OID를 가지는 공지사항(aviso)를 선택할 수
# 있도록 해준다. 이 정보는CGI의 PATH_INFO변수에 포함되어 있다.

{
  PATH_INFO: "^/avisos/([0-9]+)/?$";
  SERVER_ADMIN: ".*";
} -> {
  DRIVER:   "POSTGRESQL";
  PGTTY:    "/dev/console";  # 앞에서 처럼 콘솔로 로그한다.
  PGDATABASE:     "postgres";
  OID:      "term_0_match_1"; # OID를 부여한다.

# 한번더 선택이 중요하다. 우리는 필드의 시작부분에 OID를 포함시키며
# 이경우 우리는 이 레코드를 지우기 위한 하이퍼링크를 적게 하고 싶다.

  PGQUERY:  "select oid,ct,titulo,texto,mt,dt,autor"
            " from avisos"
            " where (dt is NULL or dt > 'now') and oid=OID";
# 이제 템플릿이 서로 다르다.
  M4FILE:    "/usr/local/etc/httpd/plantillas_m4/avisos_oid.m4";
  WEBMASTER: "term_1_match_0";
  #TESTMODE: "TRUE";
}

/usr/local/etc/httpd/plantillas_m4/avisos.m4
define(<<<for>>>, <<<dnl
ifelse(eval((<<<$2>>>) <= (<<<$3>>>)), 1,
<<<define(<<<$1>>>,<<<$2>>>)$4<<<>>>dnl
for(<<<$1>>>,eval(<<<$2>>>+1),<<<$3>>>, <<<$4>>>)dnl
>>>)dnl
>>>)dnl
divert(0)dnl
Mime-Version: 1.0
Content-type: text/html

<HTML>
  <BODY BGCOLOR="#ffffff">
    <CENTER>
      <H1>AVISOS A LOS NAVEGANTES QUE PASAN POR SLUG</h1>
    </center>
    <B>Nota:</b> Esta secci&oacute;n ha sido creada para dar a conocer
    cualquier noticia de inter&eacute;s relacionada con
    <A HREF="http://slug.ctv.es/">SLUG</a>,
    <A HREF="http://LuCAS.ctv.es/">LuCAS</a>,
    <A HREF="http://www.HispaLinux.ctv.es/">HispaLinux</a>
    y en general, cualquier servicio prestado por <B>slug.ctv.es</b>.<p>
    <CENTER><HR WIDTH=100></center>
ifelse(PGRES_RESULTSTATUS, <<<PGRES_TUPLES_OK>>>,<<<dnl
ifelse(PGRES_NTUPLES, 0,<<<dnl

      <!-- la tabla est\xe1 vac\xeda -->
      No hay avisos.<p>

>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
      <CENTER>
        <!-- contenido de la tabla -->
        <TABLE>
          <TR>
            <TH></th>
            <TH ALIGN="LEFT">Fecha-Hora&nbsp;</th>
            <TH ALIGN="LEFT">Asunto&nbsp;</th>
          </tr>

for(<<<i>>>,0,eval(PGRES_NTUPLES-1),<<<dnl
          <TR>
            <TD>
              <A HREF="/cgi-bin/pg2cgi/avisos/cell(i,0)">
                <IMG SRC="/icons/burst.gif">
              </a>
            </td>
            <TD><B>cell(i,1)&nbsp;</b></td>
            <TD>cell(i,2)&nbsp;</td>
          </tr>
>>>)dnl /* for */

        </table>
      </center>

>>>)dnl /* PGRES_NTUPLES */

>>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/

      Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
      Mensaje del servidor: PGRES_ERRORMSG<P>

>>>)dnl

      <CENTER><HR WIDTH=100></center>
      <FONT SIZE=-2>
        <A HREF="mailto:WEBMASTER?subject=TABLON DE ANUNCIOS"><CODE>WEBMASTER</code></a>
      </font>
  </body>
</html>


/usr/local/etc/httpd/plantillas_m4/avisos_oid.m4
divert(-1)
$Id: generic_list.m4,v 1.1 1998/07/06 17:13:33 luis Exp $
define(<<<cell>>>, <<<PGRES_CELL_$1_$2>>>)
define(<<<field>>>, <<<PGRES_FNAME_$1>>>)
define(<<<for>>>, <<<dnl
ifelse(eval((<<<$2>>>) <= (<<<$3>>>)), 1,
<<<define(<<<$1>>>,<<<$2>>>)$4<<<>>>dnl
for(<<<$1>>>,eval(<<<$2>>>+1),<<<$3>>>, <<<$4>>>)dnl
>>>)dnl
>>>)dnl
divert(0)dnl
Mime-Version: 1.0
Content-type: text/html

<HTML>
  <BODY BGCOLOR="#ffffff">
    <CENTER>
      <H1>AVISO OID</h1>
    </center>
    <CENTER><HR WIDTH=100></center>

ifelse(PGRES_RESULTSTATUS, <<<PGRES_TUPLES_OK>>>,<<<dnl
ifelse(PGRES_NTUPLES, 0,<<<dnl

      <!-- la tabla est\xe1 vac\xeda -->
      No existe el aviso OID, o ha caducado.<p>

>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
      <CENTER>
        <!-- contenido de la tabla -->
        <TABLE>
          <TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha de entrada:</b></font> </td>
            <TD width="80%">cell(0,1)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha &uacute;ltima modif:</b></font> </td>
            <TD>cell(0,4)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha eliminaci&oacute;n:</b></font> </td>
            <TD WIDTH=*>cell(0,5)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Autor:</b></font> </td>
            <TD><font size=+1><a href="mailto:cell(0,6)?subject=[TABLON-SLUG] cell(0,2)">cell(0,6)</a></font></td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Asunto:</b></font> </td>
            <TD><font size=+1><B>cell(0,2)<B></font></td>
          </tr><TR>
            <TD COLSPAN=2 BGCOLOR="#c0ffff"><font color="#404040">cell(0,3)</font></td>
          </tr>
        </table>
      </center>

>>>)dnl /* PGRES_NTUPLES */

>>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/

      Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
      Mensaje del servidor: PGRES_ERRORMSG<P>

>>>)dnl

      <CENTER><HR WIDTH=100></center>
      <FONT SIZE=-2>
        <A HREF="mailto:WEBMASTER?subject=TABLON DE ANUNCIOS"><CODE>WEBMASTER</code></a>
      </font>
  </body>
</html>

결과는 이 URL에서 볼 수 있다.:
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/20384

다운로드

PG2CGI프로그램은 다음 URL들에서 다운로드 받을 수 있다.:

http://slug.ctv.es/~luis/utils/pg2cgi.tar.gz
http://slug.ctv.es/~luis/utils/pg2cgi.README
ftp://slug.ctv.es/pub/slug/luis/pg2cgi.tar.gz
ftp://slug.ctv.es/pub/slug/luis/pg2cgi.README

번역 : 이주호


본 웹싸이트는 Miguel 햚gel Sep?veda님에 의해 관리되고 있습니다.
© Luis Colorado 1998
LinuxFocus 1999