저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의
코쿤2를 이용한 웹출판의 핵심 요소인 사이트맵과 각 컴포넌트에 대해서 살펴보며, 하나의 XML 문서를 HTML과 PDF로 변환하는 에제를 작성해본다.

사이트맵과 컴포넌트

코쿤 2는 다양한 형태의 출판 방식을 지원하기 위해서 사이트맵이라는 새로운 개념을 도입하였다. 이 사이트맵의 정보는 sitemap.xmap 파일에 저장되는데, 이 파일은 웹어플리케이션 콘텍스트 디렉토리(cocoon.war를 배포하면 webapps/cocoon 디렉토리)에 위치하게 된다. sitemap.xmap 파일에는 클라이언트의 요청이 들어왔을 때 어떻게 처리할지에 대한 정보부터 사용하고자 하는 Generator, Transformer 등 코쿤을 사용하여 출판을 할 때 필요한 모든 정보가 명시되어 있다. 사이트맵 파일을 작성하는 것이 웹 출판의 모든 것이라고 생각해도 될 정도로 사이트맵 파일을 알맞게 작성하는 것이 중요하다.

sitemap.xmap 파일의 기본적인 구조는 다음과 같다. (이 글을 읽는 동안 코쿤 배포판에 기본적으로 포함되어 있는 sitemap.xmap 파일을 수시로 비교해가면서 읽어보면 내용을 이해하는 데 도움이 될 것이다.)

    <?xml version="1.0" encoding="UTF-8"?>
    <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
        <map:components/>
        <map:views/>
        <map:resources/>
        <map:action-sets/>
        <map:pipelines/>
    </map:sitemap>    

sitemap.xmap 파일은 첫줄에서 알 수 있듯이 XML 문서이며, 루트 요소는 map:sitemap 이다. map:sitemap은 5개의 하위 요소를 포함하고 있는데, 이 글에서는 Matcher, Generator 등 코쿤을 이용한 웹 출판의 핵심 요소들을 설정하는 데 사용되는 map:components 요소와 이 핵심 정보를 조합하여 실제로 출판 방식을 지정하는 map:pipelines 요소에 대해서 살펴볼 것이다.

map:components 요소는 코쿤에서 사용될 컴포넌트를 명시할 때 사용되며, 기본적인 구성은 다음과 같다.

    <?xml version="1.0" encoding="UTF-8"?>
    <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
        <map:components>
            <map:generators/>
            <map:transformers/>
            <map:serializers/>
            <map:readers/>
            <map:selectors/>
            <map:matchers/>
            <map:actions/>
        </map:components>
        <map:views/>
        <map:resources/>
        <map:action-sets/>
        <map:pipelines/>
    </map:sitemap>

코드에서 map:generators, map:transformers, map:serializers, map:matchers는 각각 Generator, Transformer, Serializer, Matcher를 명시하는 데 사용된다. 나머지 컴포넌트인 Reader, Selector, Action 등도 중요하나, 이번 글에서는 가장 핵심 요소에 해당하는 Generator, Transformer, Serializer, Matcher에 대해서만 살펴보기로 하고 나머지 컴포넌트에 대해서는 다음에 설명하도록 하겠다.

모든 컴포넌트의 공통 속성 및 요청 파라미터 사용유무

map:components 요소에 포함되는 모든 컴포넌트는 기본적으로 다음과 같은 두 개의 속성을 갖고 있다.

  • name - 컴포넌트를 나타내는 이름으로서 파이프라인에서 컴포넌트를 참조할 때 사용된다.
  • src - 컴포넌트를 구현한 클래스의 완전한 이름을 명시한다.
예를 들어, XSL/T를 사용하여 XML 문서를 변환할 때 사용되는 Transformer는 다음과 같이 지정한다.

    <map:transformers default="xslt">
        <map:transformer
                name="xslt"
                src="org.apache.cocoon.transformation.TraxTransformer"
                pool-grow="2" pool-max="32" pool-min="8"
                logger="sitemap.transformer.xslt">
            <use-request-parameters>false</use-request-parameters>
            <use-browser-capabilities-db>false</use-browser-capabilities-db>
            <use-deli>false</use-deli>
        </map:transformer>
    </map:transformers>

name과 src 이외에 나머지 속성들은 각 컴포넌트마다 알맞게 지정할 수 있다. 또한, 각 컴포넌트는 <use-request-parameters> 요소의 값을 true로 지정함으로써 클라이언트가 전송한 요청 파라미터를 사용할 수도 있다.

Matcher와 Generator

클라이언트의 요청을 처리하는 시발점이 되는 Matcher

Matcher는 클라이언트 요청을 사이트맵에 명시된 명령과 연관시킬 때 사용되는 코쿤의 핵심 컴포넌트이다. 먼저 Matcher를 사용하려면 사용할 Matcher를 map:components의 map:matchers 요소에 명시해야 한다. 예를 들어, 클라이언트가 요청한 URI를 사용하여 매칭 작업을 처리해주는 Matcher를 사용하려면 다음과 같이 해주면 된다.

    <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
        <map:components>
            ...
            <map:matchers default="wildcard">
                <map:matcher
                        name="wildcard"
                        src="org.apache.cocoon.matching.WildcardURIMatcher"
                />
                <map:matcher
                        name="next-page"
                        src="org.apache.cocoon.matching.WildcardRequestParameterMatcher">
                    <map:parameter name="parameter-name" value="next-state"/>
                </map:matcher>
            </map:matchers>            ...
        </map:components>
        ...
    </map:sitemap>

위 코드는 name이 wildcard인 Matcher 컴포넌트를 지정하고 있으며, wildcard Matcher를 구현한 클래스는 WildcardURIMatcher 이다. 이 Matcher는 클라이언트의 요청 URI를 사용하여 매칭 작업을 처리해준다. 예를 들어, 다음 코드를 살펴보다.

    <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
        <map:components>
            ...
            <map:matchers default="wildcard">
                <map:matcher
                        name="wildcard"
                        src="org.apache.cocoon.matching.WildcardURIMatcher"
                />
            </map:matchers>
            ...
        </map:components>
        ...
        <map:pipelines>
            <map:pipeline>
                <map:match pattern="body-todo.xml">
                    <map:generate type="file" src="xdocs/todo.xml"/>
                    <map:transform src="stylesheets/todo2document.xsl" label="content"/>
                    <map:transform src="stylesheets/document2html.xsl"/>
                    <map:serialize/>
                </map:match>
            </map:pipeline>
        </map:pipelines>
    </map:sitemap>

위 코드에서 map:pipeline에 속해 있는 map:match 요소를 살펴보자. map:match 요소는 map:matcher에서 명시한 Matcher 컴포넌트를 사용하여 매칭작업을 처리해주는데, 위 코드에 있는 map:match 요소는 wildcard Matcher 컴포넌트를 사용하여 매칭작업을 처리하며, 클라이언트 요청한 URI가 body-todo.xml인 경우 해당하는 작업을 한다. 즉, 클라이언트가 요청한 URI가 body-todo.xml일 경우 map:generate를 사용하여 XML 문서를 생성하고, map:transform을 통해서 두번의 변환 과정을 거치며, 마지막으로 map:serialize를 통해서 실제로 출력되는 문서를 생성한다.

위의 map:match 태그는 실제로 다음과 같이 type 속성의 값을 wildcard로 준것과 동일하다.

    <map:match type="wildcar" pattern="body-todo.xml">

map:match 요소의 type 속성을 사용해서 Matcher 컴포넌트를 지정해주는데, 만약 type 속성을 지정하지 않으면, map:matchers 요소의 default 속성에 명시한 Matcher 컴포넌트가 기본적으로 사용된다. 즉, 앞의 코드에서 <map:matchers default="wildcard">와 같이 기본으로 사용될 Matcher 컴포넌트를 wildcard로 지정했기 때문에, map:match 요소가 type 속성을 갖고 있지 않을 경우 wildcard Matcher 컴포넌트가 사용되는 것이다. 기본 컴포넌트를 사용하는 것은 map:matchers를 포함한 모든 컴포넌트에 대해서 같은 방식이 적용된다.

Matcher의 매핑 방법: 와일드카드와 정규표현식

앞에서 살펴본 map:match 요소는 정확하게 클라이언트가 요청한 URI가 정확하게 "body-todo.xml"인 경우에만 map:match 요소 내부에 있는 map:generate, map:transform 등을 실행한다. 하지만, 이처럼 정확하게 일치하는 경우가 아니라 특정 패턴을 지닌 URI를 같은 방법으로 처리하고 싶은 경우가 있을 것이다. 예를 들어, 클라이언트가 요청한 URI가 /publishing/doc1.pdf 이거나 /publishing/doc2.pdf 이거나 상관없이 /publishing/로 시작하고 확장자가 .pdf로 끝나는 URI일 때 같은 처리를 해주고 싶을 때가 있다.

코쿤이 기본적으로 제공하는 (앞에서 예제에 표시했었던 WildcardURIMatcher를 포함한 대부분의) Matcher들은 클라이언트의 요청 URI를 패턴과 비교해서 매칭시키는 기능을 제공하고 있다. 이때 패턴은 와일드카드(Wildcard)와 정규표현식(Regular Expression)의 조합으로 표시하며 pattern 속성을 사용하여 지정한다.

와일드카드와 관련해서 적용되는 규칙은 다음과 같다.

  • 별표('*')는 0개 또는 그 이상의 글자수와 일치하며 경로 구분 글자인 '/'가 나올때까지 적용된다. 예를 들어 클라이언트가 요청한 URI가 '/cocoon/docs/index.html'이고 패턴을 '/*/*/index.html'로 지정했다면, 첫번째 '*'는 'cocoon'에 해당하고 두번째 '*'는 'docs'에 해당된다. 만약 패턴이 '/*/*.index.html'일 경우 '/cocoon/docs/index.html'은 매칭되지 않는다.

  • 연속된 별포('**')는 0개 또는 그 이상의 글자수와 일치하며, 이때 경로 구분 글자까지 포함한다. 따라서 패턴이 '/**/*.html'일 경우, 요청 URI '/cocoon/docs/index.html'에서 처음의 연속된 별표인 '**'에는 'cocoon/docs'가 매칭되며 두번째 '*'는 'index'와 매칭된다.

  • 역슬래시('\')를 사용하여 별표 자체와 역슬래시 자체를 표시할 수 있다. 별포는 '\*' 문장을 사용하여 표시할 수 있으며, 역슬래시는 '\\' 문장을 사용하여 표시한다. 예를 들어, 패턴이 '**/a-\*-is-born.html'일 경우 'some/a-*-is-born.html'이나 '/a-*-is-born.html'은 매칭되지만, 'some/a-any-is-born.html'은 매칭되지 않는다.
정규표현식에 대한 설명은 http://jakarta.apache.org/regexp/apidocs/org/apache/regexp/RE.html를 참고하기 바란다.

XML 문서를 생성해주는 Generator

Matcher가 클라이언트의 요청에 따라 알맞은 처리 흐름을 선택하는 것이라면, Generator는 클라이언트의 요청을 처리하는 첫번째 단계가 된다. 클라이언트로부터 요청이 들어오면 map:match 요소는 Matcher를 사용하여 매칭되는 지 검사하며, 매칭될 경우 map:generate 요소를 사용하여 클라이언트의 요청과 관련된 XML 문서를 생성하게 된다. 이때, map:generate 요소는 XML 문서를 생성하기 위해서 Generator 컴포넌트를 사용하는데, 사용될 Generator 컴포넌트는 map:componenets 요소 안에 다음과 같이 선언하게 된다.

    <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
        <map:components>
            <map:generators default="file">
                <map:generator
                        name="file"
                        src="org.apache.cocoon.generation.FileGenerator" />
                <map:generator
                        name="serverpages"
                        src="org.apache.cocoon.generation.ServerPagesGenerator"
                        pool-grow="2" pool-max="32" pool-min="4" />
            </map:generators>            ...
        </map:components>
        ...
    </map:sitemap>

위 코드에서 FileGenerator는 파일이나 네트워크자원(URL)으로부터 XML 데이터를 읽어와 XML 데이터를 생성해주는 기본 Generator 컴포넌트로서 가장 많이 사용되는 Generator이다.

클라이언트의 요청을 처리하는 파이프라인에서 map:match를 통해서 매칭이 이루어지면 가장 먼저 처리되는 것은 클라이언트 요청과 관련된 XML 데이터(데이터는 SAX 이벤트로 만들어진다)를 만드는 것인데, map:generate 요소가 XML 데이터를 생성한다. map:generate는 map:matcher와 마찬가지로 type 속성에 값을 지정하여 사용할 Generator 컴포넌트를 지정할 수 있으며, type 속성을 지정하지 않을 경우 map:generators의 default 속성에 명시한 Generator 컴포넌트가 사용된다.

    <map:match pattern="body-todo.xml">
        <map:generate type="file" src="xdocs/todo.xml"/>
        <map:transform src="stylesheets/document2html.xsl"/>
        <map:serialize/>
    </map:match>

위 코드와 같이 파이프라인에 매칭을 적용했다고 하자. 이 경우, 클라이언트가 body-todo.xml을 요청하면 map:match를 통해서 매칭이 이루어지며 map:generate가 실행된다. map:generate는 file Generator 컴포넌트를 사용하여 xdocs/todo.xml 파일을 읽어와 XML 데이터를 생성한다. 이렇게 해서 (SAX 이벤트로) 생성된 XML 데이터는 파이프라인의 다음 단계인 map:transform에 전달된다. map:transform은 이름에서 알 수 있듯이 map:generate로부터 전달받은 XML 데이터를 또다른 XML 데이터로 변환해주는 기능을 제공한다.

코쿤이 다양한 Generator를 제공하고 있기는 하지만, 웹 출판에서 비교적 많이 쓰일만한 Generator는 다음과 같다.

Name Class 설명
file FileGenerator 로컬 파일 시스템이나 URL 로부터 XML 문서를 읽어온다. src 속성을 사용하여 읽어올 XML 문서를 지정한다.
예) <map:generate src="document.xml" type="file"/>

html HTMLGenerator 로컬 파일 시스템이나 URL 로부터 HTML 문서를 읽어와 XHTML로 생성한다. src 속성을 사용하여 읽어올 HTML 문서를 지정한다.
예) <map:generate src="index.html" type="html"/>

directory DirectoryGenerator 디렉토리 목록을 XML 문서로 생성한다. 생성된 XML 문서는 다음과 같은 형태를 띈다.
 <dir:directory
    xmlns:dir="http://apache.org/cocoon/directory/2.0"
    name="stylesheets"
    lastModified="1019666489000"
    date="24.04.02 18:41"
    size="461"
    sort="name"
    reverse="false"
    requested="true">
    <dir:directory name="sites"
        lastModified="1019666489000"
        date="24.04.02 18:41" size="118"/>
    <dir:file name="dynamic-page2html.xsl"
        lastModified="1019666489000"
        date="24.04.02 18:41" size="1832"/>
    <dir:file name="simple-xml2html.xsl"
        lastModified="1019666489000"
        date="24.04.02 18:41" size="12676"/>
 </dir:directory>
        

생성된 XML 문서의 루트 노드는 directory이며, directory 노드와 file 노드를 사용하여 디렉토리와 파일을 표시한다. directory 노드와 file 노드는 다음과 같은 속성을 갖는다.
  • name : 디렉토리나 파일의 이름
  • lastModified : 최근 수정일. 1970년 1월 1일 0시 이후로 지난 시간(단위는 1/1000초)으로 표시한다.
  • size : 파일의 크기
  • date (옵션) : 최근 수정일을 숫자가 아닌 인간이 읽을 수 있는 형태로 표시.
다음과 같은 파라미터를 사용하여 원하는 형태로 디렉토리 목록을 생성할 수 있다.
  • depth (옵션) : 디렉토리 구조의 깊이를 지정한다. 기본값은 1이다. 1인 경우 지정한 디렉토리에 포함된 목록만 출력되고, 1 이상일 경우 알맞게 하위 디렉토리까지 목록을 생성한다.
  • dateFormat (옵션) : date 속성을 위한 양식을 지정한다. 이 양식은 java.text.SimpleDateFormat에서 사용되는 패턴을 사용한다.
  • root (옵션) : 루트 패턴. 정규표현식을 값으로 갖는다.
  • include (옵션) : 결과 목록에 포함할 디렉토리 또는 이름을 정규표현식으로 지정한다.
  • exclude (옵션) : 결과 목록에서 제외시킬 디렉토리 또는 이름을 정규표현식으로 지정한다.
  • sort (옵션) : 파일과 디렉토리 목록의 정렬 기준을 지정한다. 기본값은 시스템에 따라 다르다. 사용가능한 값은 "name", "size", "time", and "directory" 이다. "directory"와 "name"은 이름을 사용하여 정렬하는 것은 같지만, "directory"로 값을 지정할 경우 목록에서 디렉토리가 먼저 위치하게 된다.
  • reverse (옵션) : "true"일 경우 역순으로 목록을 생성한다.
imagedirectory ImageDirectoryGenerator DirectoryGenerator와 같은 기능을 제공하며, 추가적으로 파일이 이미지인지의 여부를 확인해준다. 이미지일 경우 생성된 XML 결과의 dir:file 요소에 width 속성과 height 속성이 추가된다. 사용가능한 파라미터는 동일하다.

* 표에 나열한 모든 Generator는 org.apache.cocoon.generation 패키지에 속해 있음

Transformer와 Serializer

Matcher를 사용하여 클라이언트의 요청과 매칭되는 파이프라인을 선택하고 Generator를 사용하여 XML 문서를 생성했다면, 이제 남은 일은 XML 데이터를 클라이언트에 알맞게 보여주어야 한다는 점이다. 클라이언트가 웹브라우저로서 HTML로 결과를 보기를 원한다면 XML 문서를 HTML로 변환해주어야 하며, 클라이언트가 Acrobat Reader라면 PDF로 변환해주어야 할 것이다. 이처럼 map:generate를 통해서 생성된 XML 데이터를 변환한 후 클라이언트에 전송해주는 작업은 Transformer와 Serializer를 통해서 처리된다.

XML 문서를 변환시켜주는 Transformer

Transformer는 Generator가 생성한 XML 데이터를 클라이언트의 요청에 알맞게 또 다른 XML 데이터로 변환해준다. Transformer 컴포넌트를 사용하기 위해서는 사이트맵의 다른 컴포넌트와 마찬가지로 먼저 map:components 안에 사용할 Transformer를 명시해주어야 한다. 다음은 Transformer 컴포넌트를 사이트맵에 명시한 예이다.

    <map:transformers default="xslt">
        <map:transformer
                name="xslt"
                src="org.apache.cocoon.transformation.TraxTransformer">
            <use-request-parameters>false</use-request-parameters>
            <use-browser-capabilities-db>false</use-browser-capabilities-db>
            <use-deli>false</use-deli>
        </map:transformer>        
        <map:transformer
                name="log"
                src="org.apache.cocoon.transformation.LogTransformer"/>
        
        <map:transformer
                name="encodeURL"
                src="org.apache.cocoon.transformation.EncodeURLTransformer"/>
    </map:transformers>

코쿤 2.0은 다양한 Transformer를 제공하고 있는데, 가장 많이 사용되는 Transformer는 XSLT Transformer이다. XSLT Transformer가 많이 사용되는 이유는 XML 데이터를 다양한 형식으로 변환할 수 있기 때문이다. 예를 들어, XML 데이터(주로 컨텐트)와 스타일시트(XSL) 파일을 사용하여 하나의 XML 문서를 HTML, WAP, PDF, PS 등 다양한 형태로 출력할 수 있다. XSLT Transformer를 사용하여 하나의 XML 문서를 다양한 형태로 출판하는 예제는 뒤에서 살펴볼 것이다.

Serializer를 사용한 출판

Matcher를 통해서 사용할 파이프라인이 선택되고, Generator가 생성한 XML 데이터를 Transformer를 통해서 알맞게 변환시키면, 이제 마지막으로 클라이언트가 볼 수 있도록 출판하는 일만 남게 된다. 이 출판 작업은 Serializer를 통해서 이루어진다. Serializer는 Generator와 Transformer를 거쳐 생성된 XML 데이터(SAX 이벤트)를 클라이언트가 사용할 수 있는 바이너리 스트림이나 캐릭터 스트림으로 변환하여 클라이언트에 전송한다.

Serializer도 다른 컴포넌트와 마찬가지로 먼저 사용할 Serializer를 map:serializers에 명시해주어야 한다. 다음 코드는 몇몇 Serializer를 지정해준 예를 보여주고 있다.

    <map:serializers default="html">
        <map:serializer
                name="xml"
                src="org.apache.cocoon.serialization.XMLSerializer"
                mime-type="text/xml" />
    
        <map:serializer
                name="html"
                src="org.apache.cocoon.serialization.HTMLSerializer"
                mime-type="text/html" pool-grow="4" pool-max="32" pool-min="4" >
            <buffer-size>1024</buffer-size>
        </map:serializer>
    
        <map:serializer 
                name="text"
                src="org.apache.cocoon.serialization.TextSerializer"
                mime-type="text/text"/>
    
        <map:serializer 
                name="fo2pdf"
                src="org.apache.cocoon.serialization.FOPSerializer"
                mime-type="application/pdf" >
            <user-config src="F:/fop-fonts/font-config.xml"/>
        </map:serializer>
    </map:serializers>

위와 같이 사용할 Serializer를 선택했다면, 파이프라인에서 다음과 같이 map:serialize를 사용하여 알맞게 XML 데이터를 출판하면 된다.

    <map:match pattern="hello.pdf">
        <map:generate src="content/xml/hello-page.xml"/>
        <map:transform src="style/xsl/simple-page2fo.xsl"/>
        &lt;map:serialize type="fo2pdf"/>
    </map:match>

위 코드는 클라이언트가 요청한 URI가 hello.pdf인 경우, hello-page.xml 파일로부터 XML 데이터를 생성하고, simple-page2fo.xsl을 사용하여 변환한 후, 마지막으로 fo2pdf Serializer를 사용하여 PDF로 출판한다.

Serializer의 종류는 여러가지가 있는데, 그 중에서 많이 사용될 만한 Serializer를 정리하면 다음 표와 같다.

Name Class 설명
html HtmlSerializer SAX 이벤트를 HTML로 출판한다.
xml XMLSerializer SAX 이벤트를 XML로 출판한다.
text TextSerializer SAX 이벤트를 텍스트 데이터로 출판한다.
fo2pdf FOPSerializer FOP 프로젝트를 사용하여 SAX 이벤트를 PDF로 변환한다.
wml XMLSerializer SAX 이벤트를 WML로 출판한다.
* 표에 나열한 모든 Generator는 org.apache.cocoon.serialization 패키지에 속해 있음

[위 표에 나열한 것 이외에 SVG(Scalable Vector Graphic)과 관련된 다양한 Serializer가 존재하니, 관심있는 독자는 코쿤 홈페이지(http://xml.apache.org./cocoon)를 방문해보라.]

파이프라인을 이용한 컴포넌트 조합 및 출판 예제

자, 이제 파이프라인에 대해서 살펴보도록 하자. 앞에서 살펴본 네 가지 기본 컴포넌트를 사이트맵 파일에 명시했다면, 이제 남은 것은 각각의 컴포넌트를 알맞게 조립하여 파이프라인을 만드는 것이다. 파이프라인은 클라이언트의 요청을 어떻게 처리할지 명시해 놓은 것이다. 다음은 파이프라인의 한 예이다.

    <map:pipelines>    
        <map:pipeline>
            <map:match pattern="**.source">
                <map:generate src="cocoon:/{1}" />
                <map:transform src="style/xsl/simple-xml2html.xsl"/>
                <map:serialize/>
            </map:match>
        </map:pipeline>        
        <map:pipeline>
            <map:match pattern="hello.html">
                <map:generate src="content/xml/hello-page.xml"/>
                <map:transform src="style/xsl/simple-page2html.xsl"/>
                <map:serialize type="html"/>
            </map:match>
            
            <map:match pattern="hello.txt">
                <map:generate src="content/xml/hello-page.xml"/>
                <map:serialize type="text"/>
            </map:match>
       
            <map:match pattern="hello.pdf">
                <map:generate src="content/xml/hello-page.xml"/>
                <map:transform src="style/xsl/simple-page2fo.xsl"/>
                <map:serialize type="fo2pdf"/>
            </map:match>
        </map:pipeline>        
    </map:pipelines>

하나의 사이트맵 파일에는 하나의 map:pipelines 요소를 가질 수 있으며, map:piplines는 위 코드에서 볼 수 있듯이 여러개의 map:pipeline 요소를 가질 수 있다. 하나의 map:pipeline는 하나의 파이프라인을 나타낸다. 하나의 파이프라인은 다음과 같은 형태를 취한다.

    <map:pipeline>
        <map:match ... >
            <map:generate type=".." src="..." />
            <map:transform ... />
            <map:serialize ... />
        </map:match>

        <map:match ... >
            <map:generate type=".." src="..." />
            <map:transform ... />
            <map:serialize ... />
        </map:match>
        
        ...
    </map:pipeline>

하나의 파이프라인은 여러 개의 map:match를 가질 수 있다. 따라서 일반적으로 관련된 URI를 처리하는 map:match를 하나의 map:pipeline에 함께 묶어서 관리하는 것이 편리하다. 각각의 map:match 요소는 map:generate로 시작해서 map:serialize로 끝을 맺는다. 즉, 클라이언트의 요청이 map:match와 매칭되면, map:generate로 XML SAX 이벤트를 생성해서 map:serialize로 클라이언트에 출판을 하는 것이 파이프라인의 기본 흐름이다. 이때 map:transform을 사용하여 map:generate가 생성한 XML 데이터를 알맞게 형변환할 수도 있다. (보통 XSLT Transformer를 사용하여 XML 원본 데이터를 map:serialize가 출판하려는 형태로 알맞게 변환한다.

map:match 순서는 중요하다. 예를 들어, 파이프라인에 map:match가 세개 있는데 클라이언트의 요청이 세개와 모두 매칭된다고 해 보자. 이 경우, 첫번째 매칭되는 map:match만 적용되며 나머지 매칭되는 map:match는 적용되지 않는다. 따라서, 파이프라인을 작성할 때는 map:match의 순서도 알맞게 위치시켜야 한다.

HTML, PDF로의 출판 예제

사이트맵은 사실 조합에 불과하다. 사이트 맵의 앞 부분에서 명시한 컴포넌트와 자원, 액션 등을 사용해서 클라이언트의 요청을 알맞게 처리할 수 있도록 조합하는 것에 불과한 것이다. 실제로 간단한 XML 데이터를 HTML과 PDF로 변환하는 예제를 만들어봄으로써 컴포넌트를 조립하는 과정을 살펴보도록 하겠다.

먼저 원본 데이터에 해당하는 XML 문서인 chapter.xml은 다음과 같다.

    <?xml version="1.0" encoding="euc-kr"?>
    
    <book>
        <cover>
            <title>JSP Professioanl Advanced</title>
            <author>최범균</author>
        </cover>
        
        <contents>
            <chapter title="들어가며" number="1">
                <paragraph>몇해전부터 대형 웹 어플리케이션을
                개발하는 데 주로 사용되는 기술이 자바 기반으로
                변화되었다. EJB, 서블릿, JSP 등 기반 기술들이 ... (중략)
                </paragraph>
    
                <paragraph>또한, Struct과 같은 프레임워크가
                개발되면서 JSP의 UI로서의 기능이 강화되고 있다.
                ... (중략)
                </paragraph>
            </chapter>
        </contents>
    </book>
    

이 절에서는 위 XML 문서를 HTML, PDF, TXT의 3가지 형태로 변환하여 출력하는 예제를 작성해볼 것이다. 먼저 테스트 환경은 다음과 같다.

  - Jakarta Tomcat 4.1.10 (JDK1.4용 LE 버전)
  - Cocoon 2.0.4 (JDK 1.4 버전)
  - 톰캣 설치 디렉토리 : F:\jakarta-tomcat-4.1.10-LE-jdk14

관련 자료에서 test.war를 다운로드 받았다면, test.war 파일을 톰캣의 webapps 디렉토리에 복사한 다음 톰캣을 재시작하자. 그러면 test.war가 자동으로 배포되면서 예제를 수행해볼 수 있을 것이다. 참고로 test.war는 cocoon.war 파일의 압축을 풀었을 때 생성되는 코쿤 관련 jar 파일을 그대로 포함하고 있다.

여기서 작성할 에제는 사용자가 http://locahost/test/chapter.html, chapter.pdf, chapter.txt를 요청할 경우 이를 알맞게 변환하여 출력해준다. 이 URL을 요청할 때 코쿤이 처리하도록 하려면 WEB-INF/web.xml 파일을 다음과 같이 작성해주어야 한다.

    <?xml version="1.0" encoding="euc-kr"?>
    
    <!DOCTYPE web-app
        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
        "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
    
    <web-app>
      <servlet>
        <servlet-name>Cocoon2</servlet-name>
        <display-name>Cocoon2</display-name>
        <description>The main Cocoon2 servlet</description>
    
        <servlet-class>
            org.apache.cocoon.servlet.CocoonServlet
        </servlet-class>        
        <!--
            나머지 부분은 에제 소스 코드 참고
        -->
        ...
        
        <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
        <servlet-name>Cocoon2</servlet-name>
        <url-pattern>*.pdf</url-pattern>
      </servlet-mapping>    
      <servlet-mapping>
        <servlet-name>Cocoon2</servlet-name>
        <url-pattern>*.html</url-pattern>
      </servlet-mapping>    
      <servlet-mapping>
        <servlet-name>Cocoon2</servlet-name>
        <url-pattern>*.txt</url-pattern>
      </servlet-mapping>    
      <mime-mapping>
        <extension>css</extension>
        <mime-type>text/css</mime-type>
      </mime-mapping>
    
    </web-app>

위의 web.xml 파일을 보면 클라이언트의 웹출판 요청을 받게 될 CocoonServlet을 지정한 후, serlvet-mapping을 사용하여 클라이언트가 요청한 URI가 *.pdf, *.html, 그리고 *.txt인 경우 CocoonSerlvet이 처리하도록 매핑하였다.

web.xml 파일에 서블릿과 관련된 매핑을 추가했다면, 이제 사이트맵을 작성해나가면 된다. (예제 사이트 맵은 F:\jakarta-tomcat-4.1.10-LE-jdk14\webapps\test 디렉토리에 위치하게 된다.) 예제에서 사용한 사이트맵은 다음과 같다.

    <?xml version="1.0" encoding="euc-kr"?>
    
    <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
        <map:components>
        
            <map:generators default="file">
                <map:generator name="file"
 
                        src="org.apache.cocoon.generation.FileGenerator"/>
            </map:generators>
    
            <map:transformers default="xslt">
                <map:transformer name="xslt"
 
                        src="org.apache.cocoon.transformation.TraxTransformer">
                    <use-request-parameters>false</use-request-parameters>
                    <use-browser-capabilities-db>false</use-browser-capabilities-db>
                    <use-deli>false</use-deli>
                </map:transformer>
            </map:transformers>
            
            <map:serializers default="html">
                <map:serializer name="html" mime-type="text/html"
                        src="org.apache.cocoon.serialization.HTMLSerializer">
                    <encoding>euc-kr</encoding>
                    <buffer-size>1024</buffer-size>
                </map:serializer>
                <map:serializer name="text" mime-type="text/plain"
                        src="org.apache.cocoon.serialization.TextSerializer"/>
                <map:serializer name="fo2pdf" mime-type="application/pdf"
 
                        src="org.apache.cocoon.serialization.FOPSerializer">
                    <user-config
 
        src="F:/jakarta-tomcat-4.1.10-LE-jdk14/webapps/test/WEB-INF/fop-fonts"/>
                </map:serializer>
                <map:serializer name="xml" mime-type="text/xml"
                        src="org.apache.cocoon.serialization.XMLSerializer">
                    <encoding>euc-kr</encoding>
                </map:serializer>
             </map:serializers>
    
            <map:matchers default="wildcard">
                <map:matcher name="wildcard" 
                        src="org.apache.cocoon.matching.WildcardURIMatcher"/>
            </map:matchers>
        
        </map:components>
        
        <map:pipelines>
            <map:pipeline>
                <map:match pattern="chapter.pdf">
                    <map:generate src="chapter.xml" />
                    <map:transform src="xsl/xml2fo.xsl" />
                    <map:serialize type="fo2pdf" />
                </map:match>
                
                <map:match pattern="chapter.html">
                    <map:generate src="chapter.xml" />
                    <map:transform src="xsl/xml2html.xsl" />
                    <map:serialize/>
                </map:match>
                
                <map:match pattern="chapter.txt">
                    <map:generate src="chapter.xml" />
                    <map:serialize type="text"/>
                </map:match>
                
                <map:match pattern="chapter.xml">
                    <map:generate src="chapter.xml" />
                    <map:serialize type="xml"/>
                <map:transform src="xsl/xml2html.xsl" />
                    <map:serialize/>
               

가장 먼저 사용할 컴포넌트를 명시하였다. Generator는 파일로부터 XML 데이터를 생성하는 FileGenerator를 사용하고, Transformer는 XSLT 변환을 해주는 TraxTransformer를 사용한다. Serializer는 XML 문서를 HTML, TXT, PDF로 변환해주므로 html, text, fo2pdf Transformer를 사용한다. html Serializer를 명시한 부분을 보면 <encoding> 태그를 사용하고 있는데, 이 태그를 사용하여 출판한 HTML 문서의 캐릭터셋을 지정할 수 있다. 위 예제에서는 한글을 출판하게 되므로 euc-kr로 지정하였다. fo2pdf Serializer가 PDF로 출판해주는 기능을 제공하는데, fo2pdf를 명시한 부분을 보면 <user-config> 태그를 사용한 것을 알 수 있다. 이때 user-config 태그의 src 속성은 PDF로 변환시 사용되는 폰트에 대한 담고 있는 파일의 경로를 값으로 갖는다. (이 속성을 사용함으로써 한글을 올바르게 출력할 수 있다. 이에 대한 내용은 다음글에서 살펴보도록 하자.)

기본적인 컴포넌트 설정을 마친 후에는 컴포넌트를 알맞게 조합해서 파이프라인을 생성하면 된다. 위 코드의 파이프라인을 보면 클라이언트가 요청한 URL인 chapter.html, chapter.txt, chapter.pdf에 대해서 각각 알맞은 처리를 해주는 것을 알 수 있다. PDF와 HTML로 출판할 때에는 먼저 XML 문서를 알맞게 변환해주는데, 해당하는 파이프라인의 map:transform 태그를 보면 src 속성에서 변환할 때 사용되는 XSL 문서를 명시한 것을 알 수 있다.

위와 같이 sitemap.xmap 파일을 작성했다면 남은 일은 XML을 HTML 그리고 XML을 FO(Formatting object) 형태로 변환할 때 사용되는 XSL 문서를 작성하는 일이 남았다. 이 두 파일은 예제 코드의 test/xsl/ 디렉토리에 존재하니 참고하기 바란다.

이제 남은 것은? 바로 테스트를 해 보는 것이다. 웹브라우저에서 http://localhost/test/chapter.html, chapter.txt, chapter.pdf를 입력해보면 차례대로 다음과 같은 결과 화면이 출력될 것이다.

그림1 XML->HTML로 변환한 결과 화면

그림2 XML->TEXT로 변환한 결과 화면

그림3 XML->PDF로 변환한 결과 화면

예제만 보더라도 코쿤 프레임워크를 사용하여 하나의 XML 문서를 매우 쉽게 다양한 양식으로 출판할 수 있다는 사실을 알 수 있다.

결론

이 글에서는 코쿤을 사용하여 웹 출판을 하는 데 필요한 기본 컴포넌트인 Matcher, Generator, Transformer, Serializer에 대해서 살펴보았으며, 사이트맵의 파이프라인을 사용하여 이들 컴포넌트를 조합하여 알맞게 XML 데이터를 출판하는 방법에 대해서도 살펴보았다. 또한, 예제를 통해서 어떻게 이들 컴포넌트를 사용하고 실제 파이프라인이 어떻게 동작하는 지 알 수 있었을 것이다. 또한, 예제를 통해서 XML 문서를 다양한 양식으로 손쉽게 출판할 수 있다는 것도 알게 되었다.

다음 3번째 글에서는 코쿤을 사용하여 PDF를 생성할 때 문제가 되는 한글처리에 대해서 살펴볼 것이다. 한글이 '#'이나 '?' 등으로 나와서 고생하고 있는 개발자들에게 많은 도움이 될테니 반드시 읽어보기 바란다.

관련링크:
Posted by 최범균 madvirus

댓글을 달아 주세요