또 다른 면도 있다. 프로그램은 책이나 잡지 같은 문자라기보다는 말과 같아서 시간이 지나면서 변해간다. 프로그램은 제대로 돌아가기만 해서는 부족하다. 다른 사람이 바로 이해할 수 있어야 한다. 또, 쉽게 고칠 수 있도록 코드를 조직화해야 한다. 어떤 작업을 처리할 수 있는 방법이 열 가지 있다고 하자. 아마도 이 중에서 일곱 가지 정도는 어색하고, 기능이 떨어지고, 이상할 것이다. 나머지 세 가지 중에서 과연 어떤 것이 일 년 후에 같은 작업을 처리할 때도 쓸 수 있을까?- Guy L. Steele Jr, Effective Java 서문 中
SiteMesh는 웹 페이지 레이아웃 및 데코레이션 프레임워크이다. 일반적으로 사이트는 통일되고 일관된 룩앤필과 네비게이션, 레이아웃을 유지해야 하며 SiteMesh는 이러한 사항들을 조직화할 수 있도록 도와주고 또한 변화와 수정에 탁월한 대처 능력을 가지고 있다.
SiteMesh는 웹 서버에 요구된 정적/동적 요청(request)을 가로채고 요청된 페이지 자원을 파싱하며 설정된 프로퍼티와 렌더링할 데이터를 컨텐츠로부터 읽어 들인 후 원래의 페이지에 장식과 수정을 가하여 최종 페이지를 생성한다. 이것은 GoF의 장식자 패턴(decorator pattern)에 기반하고 있으며, 작동하는 시퀀스는 서블릿 스펙 2.3부터 새롭게 추가된 필터(filter)를 기반으로 하고 있다. SiteMesh는 모든 정적/비정적 페이지를 그래픽 객체와 컨테이너로 간주하며 이러한 개념은 각각의 페이지들을 객체로 바라보고 컴포넌트화 할 수 있는 방법을 제시한다. 그 결과 모든 페이지들은 결합, 분리되고 재사용될 수 있다. 이것은 GoF의 복합 패턴(composite pattern)에 기초하고 있다.
SiteMesh는 견고하다. 적절하게 정의된 디자인 템플릿과 레이아웃은 원하는 모든 자원(HTML, JSP/서블릿)들에 적용될 것이며 이것을 구현하기 위해 SIteMesh는 HTML 작성자에게 통일되고 간결한 형태의 HTML만을 요구할 것이다. 이로 인하여 사이트는 전체적으로 쉽게 유지관리 및 확장될 수 있을 것이다. 또한 SiteMesh는 유연하다. 장식자와 프로퍼티의 조합은 새로운 확장을 가능하게 하며 SiteMesh의 틀을 이루고 있는 파서(parser)와 맵퍼(mapper)를 구현하여 새로운 서비스를 만들어낼 수도 있다. 여기서 독자와 함께 진행할 내용과 순서는 다음과 같다.
이 글의 모든 예제는 J2SE 5.01, 톰캣 5.5.72, 이클립스 3.0.13에서 개발되고 테스트되었다(톰캣 5.5.7은 J2SE 5.0을 요구한다). 물론 톰캣 4.x와 J2SE2로 테스트 환경을 구성해도 좋을 것이다. 편의상 이클립스 3.0.1에는 톰캣 플러그인 4, Lomboz, EMF(Eclipse Modeling Framework) 2.0.16을 설치했다(참고자료 8~13).
자동화된 개발 환경은 개발자로부터 복잡한 설정과 반복적인 배포의 수고를 덜어주고 잦은 리팩토링을 가능하게 할 것이며, 이는 개발자에게 창조적인 사고에 필요한 시간을 더 많이 할애해줄 것이다. 개발자들은 앞으로 더 게을러질지도 모르겠다.
필터는 http-request, http-response의 헤더와 바디를 수정할 수 있는 객체이다. 필터는 스스로 응답(response)을 생성하지 않는다는 점에서 웹 컨텐츠와는 다르다. 필터는 모든 종류의 웹 컨텐츠(정확히는 그것에 대한 http-requests와 http-responses)를 필터링할 수 있다( http://java.sun.com/j2ee/1.3/docs/tutorial/doc/Servlets8.html). 필터는 http-request의 전처리 단계이며 http-response의 후처리 단계이다. 필터가 수행할 수 있는 역할은 다음과 같다.
다음은 자바월드에 포스팅 된 필터의 예제(http://www.javaworld.com/javaworld/jw-06-2001/jw-0622-filters.html)로써 정적 혹은 비정적 페이지에 요청이 도달하여 응답이 완료되기까지의 수행 시간을 기록한 것이다. 테스트 컨테이너인 톰캣 5.5.7로의 적재는 앞에서 다운받은 timer.war 파일을 TOMCAT_HOME/webapps에 복사한 후 톰캣을 재 기동하는 것만으로 충분하다. 우선 예제를 수행해보고 로그를 살펴보자 <화면 1>을 보면 톰캣 기동 후 http://localhost:/timer에 접근하면 콘솔 표준 출력을 통해 접근한 url의 소요 시간이 기록되는 것을 확인할 수 있다. index.html, index.jsp, 또 index.jsp, servlet-2_3-fcs-spec.pdf 등 총 4번의 요청을 시도했다. index.jsp의 첫 번째 호출에 대한 응답 시간은 두 번째 호출의 그것에 비해 컴파일 시간이 추가적으로 소요됐음을 확인할 수 있다.
<화면 1> TimerFilter가 남긴 로그
2005. 2. 12 오후 7:19:10 org.apache.catalina.startup.Catalina start 정보: Server startup in 4236 ms 2005. 2. 12 오후 7:19:41 org.apache.catalina.core.ApplicationContext log 정보: /timer/: 0ms 2005. 2. 12 오후 7:19:46 org.apache.catalina.core.ApplicationContext log 정보: /timer/index.jsp: 1242ms 2005. 2. 12 오후 7:19:53 org.apache.catalina.core.ApplicationContext log 정보: /timer/index.jsp: 0ms 2005. 2. 12 오후 7:24:50 org.apache.catalina.core.ApplicationContext log 정보: /timer/servlet-2_3-fcs-spec.pdf: 170ms
예제 애플리케이션의 TimerFilter.java의 구현을 살펴보자. 필터 클래스는 javax.servlet.Filter 인터페이스를 구현해야 하며 다음의 세 콜백 메쏘드를 정의해야 한다.
<리스트 1>은 예제에 첨부된 TimerFilter.java의 doFilter() 구현으로 수행 시간을 기록하는 지극히 간단한 소스로 구성되어 있다. <리스트 2>는 예제에 첨부된 web.xml의 일부를 발췌한 것으로 작성된 필터를 웹 애플리케이션에 설정하는 방법을 나타내고 있다.
<리스트 1> TimerFilter.java의 doFilter() 구현
01 public void doFilter(ServletRequest request, ServletResponse response, |
<리스트 2> web.xml에 TimerFilter 등록 및 맵핑
01 <web-app> |
지금까지 필터의 개념을 살펴보고 예제를 수행해 보았다. http://java.sun.com/j2ee/1.3/docs/tutorial/doc/Servlets8.html에서 더 많은 예제를 찾아볼 수 있으며 실제로 톰캣 4.x 버전의 한글 처리 문제로 널리 사용되고 있다. 서블릿 스펙 2.3의 제 6장은 모두 필터에 관한 내용이므로 확인해 보는 것도 도움이 될 것이다.
드디어 SiteMesh다. 언제나처럼 SiteMesh의 예제를 다운받아 서블릿 컨테이너에 적재할 것이며 작동되는 모습을 살펴보고 예제 소스를 분석해 봄으로써 SiteMesh의 전체적인 구조와 행위를 파악해 볼 것이다. www.opensymphony.com/sitemesh/download.html에서 배포되는 SiteMesh 2.2.1의 sitemesh-example.war 파일을 다운받는다. 혹시 파일이 sitemesh-example.zip이면 sitemesh-example.war로 변경하자. 다운받은 웹 아카이브를 적재하는 방법은 다음과 같다.
동작을 살피고 테스트를 수행하기 위해서는 첫 번째 방법으로도 충분하다. 하지만 실제 개발 환경을 구축하고, 맵퍼의 확장까지 테스트하기 위해서는 두 번째 방법을 권하고 싶다. 첫 번째 방법에 비해 상대적으로 조금 복잡할 뿐 어렵지는 않다. 실제 개발 환경을 직접 구축해보는 것은 여러 면에서 큰 도움이 될 수 있을 것이다.
그럼 두 번째의 조금 복잡한 방법의 설정을 함께 진행해 보자. 우선 앞서 말한 대로 이클립스와 EMF, Lomboz, 톰캣 플러그인의 설치 및 설정이 완료됐다는 가정 아래 진행한다. Lomboz와 톰캣 플러그인은 설치 후 몇 가지 설정이 필요하다. 마음 같아서야 모든 것을 담고 싶지만 한정된 지면인지라 그저 죄송스러울 따름이다. 실제로 이클립스+Lomboz+톰캣 플러그인 관련한 훌륭한 문서가 많은 것으로 알고 있다. 그런 것들을 참조한다면 진행에 무리가 없을 것으로 생각된다.
이클립스의 비어 있는 워크벤치가 주는 설레임과 함께 시작해 보자. 전체 과정을 갈무리한 이미지를 보며 설명하는 것이 가장 좋겠지만 지면의 제약으로 자세한 설명으로 대체한다(마음 같아서야 톰캣을 인스톨하고 이클립스에 여타 다른 플러그인까지 모두 애드온한 상태에서 예제 파일 복사하여 프로젝트 설정까지 모두 맞춘 후 CD로 제작하여 배포하고 싶다).
이클립스의 [File | New Project]를 선택하여 [Project Select Wizard]를 시작한다. 프로젝트 트리에서 자바 노드를 확장하여 [Lomboz J2EE Project]를 선택한다. [J2EE Project Creation Wizard]가 실행될 것이다. [Project name] 란에 SiteMeshExample이라고 입력하고 다음 단계로 진행한다. [Java settings]라는 이름의 [build path]를 정의하는 창이 나타난다. 우리의 프로젝트에 맞게 수정해야 하지만 디폴트 설정대로 진행하고 모든 설정이 완료된 후 수정하자. 역시 다음으로 진행. [Create J2EE Module] 창에서 하나의 웹 모듈만을 추가할 것이다. 웹 모듈 탭에서 Add? 버튼을 클릭한 후 [SiteMeshExampleWeb]이라고 입력한다. 추가되면 [You must add a server]라는 메시지를 확인할 수 있다.
이곳에서 추가한 Lomboz 서버를 사용하진 않지만 편의상 톰캣을 지정해 주도록 하자. [Targeted Servers] 탭으로 이동한 후 type의 콤보 박스에서 [Apache Tomcat v5.0.x]를 선택한 후 추가하고 [Finish] 버튼을 클릭한다. SiteMeshExample 프로젝트가 멋지게 생성되어 있을 것이다. 이제 빌드 패스를 수정해야 한다. SiteMeshExample 프로젝트를 오른쪽 클릭하고 프로퍼티를 클릭하여 프로퍼티 창을 오픈하자. 프로퍼티 창에서 [Java Build Path]를 선택한 후 [source] 탭으로 이동하여 [Add Folder]를 클릭한 후 j2src 폴더의 체크박스를 선택한 후 [OK] 버튼을 클릭한다. 확인 창이 나타나면 [Yes]를 선택하자.
소스 경로를 설정한 후에는 [source] 탭 아래 부분에 [Default output folder]를 브라우즈하여 /SiteMeshExample/SiteMeshExampleWeb/WEB-INF/classes 경로로 수정한다. 새롭게 추가되는 자바 파일의 디폴트 패키지는 j2src에서 시작되고 컴파일된 클래스 파일은 /SiteMeshExample/SiteMeshExampleWeb/WEB-INF/classes에 위치되어 설정할 SiteMeshExampleWeb 웹 애플리케이션이 참조된다. 빌드 경로의 설정이 끝났으면 왼쪽 트리 메뉴에서 [Tomcat] 항목으로 이동하여 톰캣 플러그인 설정을 추가하자. [General] 탭의 [Is a Tomcat Project] 체크박스를 체크하고 [Context name] 항목에 [/SiteMeshExampleWeb]을 입력한다. 루트 컨텍스트(root context)를 나타내는 /가 누락되지 않도록 주의하자. [subdirectory to set as web application root] 항목에도 /SiteMeshExampleWeb이라고 입력한다. 이 과정은 SIteMeshExmpleWeb의 컨텍스트와 루트 폴더를 설정하는 과정이다. 설정한 내용을 톰캣의 server.xml에 기록하기 위해 다음과 같은 절차를 수행해야 한다.
[SiteMeshWeb 프로젝트를 오른쪽 클릭 | Tomcat Project | Update Context Definition] 업데이트가 완료되면 톰캣 설치 폴더 하위 conf 폴더 내의 server.xml 설정 파일에 SiteMeshExampleWeb 컨텍스트가 추가된 것을 확인할 수 있다. 이제 개발 환경의 설정이 끝났다. 이클립스의 [Tomcat start] 버튼을 클릭하여 톰캣을 기동하고 http://localhost:/SiteMeshExampleWeb에 접근하면 환영 페이지를 확인할 수 있다. 마지막으로 sitemesh-example.war 예제 파일을 개발 환경으로 임포트해야 한다. SiteMeshWeb 프로젝트를 오른쪽 클릭한 후 [Import] 메뉴를 선택한다. 선택 창에서 [Zip file]을 선택한 후 [next] 버튼을 클릭한다. [From zip file] 항목에 다운받은 sitemesh-example.zip 파일을 선택하고 [Into folder] 항목은 웹 루트인 SiteMeshExample/SiteMeshExampleWeb을 선택한다. 중복된 파일은 모두 덮어 쓰도록 한다. 톰캣을 재 기동하고 http://localhost:/SiteMeshExampleWeb에 접근해 보자 SiteMesh 예제 사이트가 열릴 것이다.
지금까지 SiteMesh 예제 사이트를 이클립스 워크벤치에 구축해 보았다. 비단 SiteMesh 뿐만 아니라 J2EE 프로젝트를 위한 이클립스 설정은 이와 같은 방법과 크게 다르지 않으리라고 본다. 추가로 권하고 싶은 것은 SIteMesh를 좀 더 깊숙이 살펴보기 위해 SiteMesh 개발 환경을 설정하는 것이다 www.opensymphony.com/sitemesh/download.html에서 sitemesh-2.2.1.zip을 다운받아 압축을 풀고 sitemesh-2.2.1.zip/src/java의 모든 패키지를 구축한 개발 환경의 j2src 폴더 밑에 복사한다. sitemesh-2.2.1.zip/lib 밑의 모든 jar 파일을 구축한 개발 환경에 lib 폴더를 추가하여 복사한 후 프로젝트의 빌드 패스에 복사한 jar 파일들을 라이브러리 패스에 추가하자. 이렇게 되면 SiteMesh 라이브러리 개발 환경까지 완료 된다.
SiteMesh의 소스를 살펴보면서 전체적인 흐름을 파악한다면 도움이 될 것이다. 또 글의 마지막 단계에서 제작할 맵퍼를 컴파일 하기 위해서도 필요한 단계이다.
이제부터 SiteMesh의 예제 사이트를 살펴보고 장식자(decorator), 장식자 페이지(decorator page), 장식자 연결자(decorator mapper)에 대해 알아보자.
<화면 2> SiteMesh 예제 사이트
![]() |
처음 우리를 맞는 것은 index.html이다. <화면 1>은 크게 네 영역으로 나누어 볼 수 있다. A라고 표기된 패널 영역과 B 헤더 영역, C 풋터 그리고 D 바디 영역이다.
<리스트 3> index.html
01 <html> |
뭔가 복잡한 웹 화면과 대조적으로 <리스트 3>의 내용은 상당히 간략하다. 크게 두 부분으로 나누어 볼 수 있는데 SiteMesh Sample Site 문구가 담긴 title 엘리먼트와 컨텐츠를 담고 있는 body 엘리먼트이다. SiteMesh가 <리스트 1>이 나타내야 하는 화면에 패널을 추가하고, 타이틀 내용을 가져다 헤더를 만들고, 풋터를 추가했다는 것을 알 수 있다. 적용된 장식자는 main이고 장식자 페이지는 /decorators/main.jsp이다.
<리스트 4> 장식자 페이지, /decorators/main.jsp
01 <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %> |
<리스트 4>의 main이라 명명된 장식자는 SiteMeshExampleWeb의 전반적인 페이지에 적용되기 위해 제작된 메인 레이아웃이다. 장식자 페이지 /decorators/main.jsp의 소스를 보며 SIteMesh의 개략적인 기능을 살펴보자. 먼저 5라인의 <decorator:title/>은 장식될 페이지(여기서는 index.html)의 title 엘리먼트의 텍스트를 가져와 삽입한다. default 어트리뷰트는 title 엘리먼트의 텍스트를 얻는데 실패할 경우 보여줄 텍스트이다. 14~16라인의
<화면 2>의 A 영역을 표현하고 있는 부분이 14~16라인이라는 사실을 어렵지 않게 알 수 있다. 23라인에서는 <decorator:title/> 태그를 이용하여 페이지 상단의 헤더를 장식했고 28번 라인에서 <decorator:body/> 태그를 이용하여 페이지의 본문을 장식하였다. 32라인부터 페이지에 하단을 장식할 풋터가 자리하고 있다. 43라인은 <decorator:usePage/> 태그를 이용하여 요청 객체에 접근한 후 현재 페이지의 url에 ?printable=true 쿼리스트링을 삽입하여 프린트 페이지의 링크를 생성하였다.
이 링크는 index.html?printable=true로 연결되고 SiteMesh는 printable=true라는 쿼리스트링을 파싱하게 되면 printable 장식자를 적용하여 왼쪽 패널과 헤더, 풋터를 제외한 인쇄하기 좋은 페이지로 장식할 것이다.
<표 1> SiteMesh 태그 라이브러리
장식자 태그, 페이지 태그 | 장식자 페이지를 생성할 때 사용되는 태그 |
---|---|
<decorator:head /> | 장식될 페이지의 <head>태그의 내용을 삽입 |
<decorator:body /> | 장식될 페이지의 <body>태그의 내용을 삽입 |
<decorator:title /> | 장식될 페이지의 <title>태그의 내용을 삽입 |
<decorator:getProperty /> | 장식이 완료된 HTML 페이지의 <body> 태그 내에 이벤트 핸들러를 생성하기 위해 사용 |
<decorator:usePage /> | 장식자 페이지에서 장식될 페이지의 페이지 객체를 얻을 수 있게 함 |
페이지 태그 | 장식자 페이지 내에서 다른 장식자를 포함할 때 사용 |
<page:applyDecorator /> | 현재 장식자 페이지 내에 장식될 페이지와 장식자를 지정하여 삽입한다. |
<page:param> | <page:applyDecorator /> 사용시 해당 장식자에게 파라미터를 전달하기 위해 사용 |
그렇다면 브라우저가 톰캣에게 보낸 GET /SiteMeshExampleWeb/index.html http-request는 어떻게 됐기에 원래의 index.html에 대한 요청이 무시되었을까? <리스트 5>의 내용을 보면 다음과 같이 필터를 정의하고 있다.
<리스트 5> /WEB-INF/web.xml 중 필터가 정의된 부분
1 <filter> |
SiteMeshExampleWeb에게 요청되는 모든 것은 PageFilter가 가로채어 처리하도록 필터가 설정되어 있다. 이 부분에 대해서는 나중에 다시 살펴보자.
SiteMesh의 기본적인 얼개를 살펴보았다. 이제부터 (여전히 궁금한 채 남아 있는) 어떻게 main이라는 장식자가 /decorators/main.jsp를 장식자 페이지로 갖는지, 또 왜 index.html에 장식자 main을 적용한 것인지, panel 장식자는 무엇이고 쿼리스트링에 따른 printable 장식자를 적용하게 된 것은 어떻게 가능한지 설정을 살펴보자. SiteMesh의 설정을 담당하고 있는 것은 /WEB-INF/sitemesh.xml과 /WEB-INF/decorator.xml 두 파일이다. 우선 decorator.xml의 설정 내용, 여러 장식자의 정의와 특정 장식자가 적용될 페이지의 리스트가 설정되어 있다.
<리스트 6> /WEB-INF/decorator.xml
01 <?xml version="1.0" encoding="ISO-8859-1"?> |
main 장식자는 모든 패턴의 리소스에 적용되며 main.jsp를 장식자 페이지로 사용함을 알 수 있다. 그 외의 panel, printable, black, nonpanelsource, badpanelsource는 모두 패턴을 지정하지 않았고, 프리메이커(freemaker)와 벨로시티는 특정 페이지를 적용했다. 적절한 패턴이 설정되지 않은 장식자는 main 장식자 소스에서 보았듯이 다른 장식자 내에서
지금까지 독자 여러분은 장식자와 장식자 페이지에 대해 알아보았다. 이제 장식자 연결자를 알아볼 차례다. 장식자 연결자는 SIteMesh에게 장식에 사용될 적합한 장식자를 선정해주는 역할을 하며 기본적으로 지원되는 장식자 연결자는 다음과 같은 조건에 따라 장식자 선정을 수행하도록 고안되었다.
필요에 따라 독자 여러분은 특정 조건에 반응하도록 장식자 연결자를 제작할 수도 있을 것이다. 장식자 연결자는 /WEB-INF/sitemesh.xml에 정의되어 있다. 해당 경로에서 설정 파일을 찾는데 실패하면 sitemesh-2.2.1.jar에 패키징되어 있는 sitemesh-default.xml 파일로 대체한다.
<리스트 7> /WEB-INF/sitemesh.xml
01 <sitemesh> |
PageDecoratorMapper
메타 태그를 통하여 장식자를 선택할 때 사용된다. decorator_test라는 이름의 장식자를 특정 페이지에 적용하고 싶다면 와 같이 페이지에 메타 태그를 추가하거나 또는 <HTML decorator="decorator_test">와 같이 <HTML> 태그를 수정하여 사용한다.
AgentDecoratorMapper
브라우저에 따른 장식자를 선택할 수 있도록 한다. 브라우저 타입은 일반적인 경우와 같이 request.getHeader("User-Agent")를 통해 구해온다. 다른 연결자들이 특정 장식자를 지칭하여 반환하는 것과 비교적으로 AgentDecoratorMapper는 적용될 장식자의 장식자 페이지명을 브라우저에 따라 수정한다. 예를 들어 장식자 연결자 체인에 의해 main 장식자가 선정됐다면 장식자 페이지는 /decorators/main.jsp가 적용될 것이다. 하지만 http 클라이언트가 익스플로러라면 main 장식자에 /decorators/main-ie.jsp 장식자 페이지를 적용하게 된다. 모질라 기반 브라우저라면 /decorators/main-ns.jsp가 장식자 페이지로 결정될 것이다. 예상한대로 선정된 장식자 페이지(브라우저에 따른 서브픽스가 추가된)를 찾는데 실패한다면 기본 장식자 페이지인 main.jsp가 적용될 것이다.
PrintableDecoratorMapper
PrintableDecoratorMapper는 url 쿼리스트링의 printable=true라는 문자열에 반응한다. /WEB-INF/decorator.xml에 정의된 장식자 중 printable 장식자를 리턴할 것이다.
ParameterDecoratorMapper
ParameterDecoratorMapper는 설정에 정의된 파라미터와 동일한 스트링이 url 쿼리스트링에 존재할 때 정의해 놓은 장식자를 선정하도록 되어 있다. 앞의 예에서는 confirm=true 문자열과 일치할 경우 somedecorator가 장식자로 선정될 것이다. 몇 가지 장식자 연결자의 쓰임새를 살펴보았다. 지금까지 장식자, 장식자 페이지, 장식자 연결자에 대해 모두 알아보았다. 이제부터 SiteMesh가 어떤 흐름을 가지고 필터를 포함한 4개의 오브젝트를 장식에 참여시키는지 알아보자. 현재 파싱이 지원되는 content-type은 text/html이므로 text/html request를 기준으로 한다.
request 단계
모든 http-request는 PageFilter에서 가로채어진다. PageFilter는 컨텍스트 초기화 시점에 decorators.xml에 정의된 parsing 단계
PageFilter는 요청된 자원에 대한 웹 애플리케이션의 수행 결과를 doChain(request, response) 메쏘드를 호출하여 받아낸다. 응답 인자에 ServletResponse가 아닌 HttpServletResponseWrapper를 상속받아 확장한 PageResponseWrapper 객체를 사용한다. 따라서 페이지 아웃풋은 writer로 보내지지 않는다. PageResponseWrapper에서 캡처된 페이지 아웃풋 데이터는 content-type에 따라 선택된 파서에 의해 파싱되어 진다. 현재는 반환 페이지의 content-type이 text/html일 경우만 FastPageParser가 작동한다(보다시피 웹 애플리케이션과 관계없이 수행 결과를 파싱하므로 자바 기반이 아닌 사이트에도 문제 없이 서비스될 수 있을 것이다). 개인적으로 PageResponseWrapper가 HttpServletResponseWrapper를 상속받는 대신 ServletResponse 인터페이스를 구현하고 HttpServletResponseWrapper 클래스를 private 필드로 갖도록 컴포지션을 사용하는 것이 좋지 않았을까 생각한다.
장식자 선정 단계
적합한 장식자를 결정한다. 이 과정은 연결자 클래스의 getDecorator() 메쏘드에서 이뤄진다. 처음 장식자 선정을 시도한 연결자 클래스가 적합한 장식자를 얻어낸다면 리턴할 것이고 아니면 장식자 체인상의 상위 링크의 getDecorator() 메쏘드를 호출하게 된다. 정확한 장식자가 결정되기까지 이 작업은 반복된다. 따라서 장식자 연결자 체인 상에 잘못 구현된 장식자 연결자가 있다면 장식자 선정 단계는 실패할 것이다.
장식 단계
장식자가 결정되고 장식자로부터 장식자 페이지를 구해 올 수 있다면 PageFilter는 applyDecorator() 메쏘드를 호출하여 장식을 수행한다. applyDecorator() 내에서 장식자 페이지에 대한 요청을 생성하고 장식자 페이지는 FastPageParser에 의해 파싱된 결과 페이지의 적절한 부분들을 커스텀 태그를 통해 가져와 출력 페이지를 작성할 것이다. 복잡할 것 같던 SiteMesh의 시퀀스는 생각보다 간략하다.
장식자 연결자를 직접 만들어 보자. 제작할 연결자는 HttpSession에 저장된 특정 키 값에 저장된 장식자의 이름에 따라 장식자를 리턴해 주는 것이 목적이다. <리스트 8>을 코딩 및 컴파일하고 /WEB-INF/sitemesh.xml에 SessionDecoratorMapper의 설정을 알려주도록 한다. 가급적이면 ParameterDecoratorMapper의 정의 바로 아래 추가하자. 등록된 다른 장식자들의 행동 패턴을 파악할 필요가 있는 부분이다.
<리스트 8> SessionDecoratorMapper.java
<리스트 9> /WEB-INF/sitemesh.xml에 등록된 모습
아이디어의 빈곤과 마감의 압박으로 급조된 예제인지라 조금 부실하더라도 이해하기 바란다. 독자들은 직접 멋진 연결자와 나아가 파서까지 생성해 보기 바란다. 대형 포탈 사이트에서 제공하는 사용자 별 스킨 기능을 구성하는 것도 어렵지 않을 듯하다. 장식자와 파서의 확장은 무궁무진해 보인다. 특정 폴더의 gif/jpeg 자원에 대한 뷰어를 위해 특별한 파서와 장식자를 작성해도 좋을 것이다. jpeg 이미지의 촬영 정보를 레이아웃에 맞춰 보여줄 수도 있을 것이고 이미지 위에 워터마킹을 위한 처리도 가능할 것으로 보인다. 생각만 해도 즐겁지 않은가?
마지막으로 주의할 사항 두 가지를 짚고 넘어가자.
톰캣 5.5.7에서 앞의 설정이 예상대로 작동하지 않은 부분도 있었지만 forward()와 include()에 의한 필터의 호출은 문제없이 수행됐다. SiteMesh 프레임워크는 도입 계획에 따라 여러 가지 모습을 보여 줄 수 있다. 객체지향과는 다분히 거리가 먼 웹 페이지 구조를 조직화하고, 컴포넌트화 할 수 있는 방법을 제시하여 사이트를 한층 견고하게 하는데 일조를 할 수도 있으며, SiteMesh가 제공하는 유연함을 바탕으로 이전에는 불가능할 것 같았던 새로운 서비스를 구성할 수도 있을 것이다. 이제 남은 것은 개발하는 즐거움뿐이다.
정리 : 강경수 elegy@imaso.co..kr
[출처] [본문스크랩] SiteMesh에 대한 깊은 내용.들.|작성자 메멘토
1 <mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
2 <param name="property.1" value="meta.decorator" />
3 <param name="property.2" value="decorator" />
4 </mapper>
1 <mapper class="com.opensymphony.module.sitemesh.mapper.AgentDecoratorMapper">
2 <param name="match.MSIE" value="ie" />
3 <param name="match.Mozilla [" value="ns" />
4 <param name="match.Opera" value="opera" />
5 <param name="match.Lynx" value="lynx" />
6 </mapper>
1 <mapper class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
2 <param name="decorator" value="printable" />
3 <param name="parameter.name" value="printable" />
4 <param name="parameter.value" value="true" />
5 </mapper>
1 <mapper class="com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper">
2 <param name="decorator.parameter" value="somedecorator" />
3 <param name="parameter.name" value="confirm" />
4 <param name="parameter.value" value="true" />
5 </mapper>
[#5] SiteMesh 구조와 흐름
[#6] 연결자를 만들어 보자
01 public class SessionDecoratorMapper extends AbstractDecoratorMapper {
02 private String sessionKey;
03
04 public void init(Config config, Properties properties, DecoratorMapper parent) throws InstantiationException {
05 super.init(config, properties, parent);
06 sessionKey = properties.getProperty("session.key", null);
07 }
08
09 public Decorator getDecorator(HttpServletRequest request, Page page) {
10 Decorator result = null;
11
12 HttpSession session = request.getSession(false);
13
14 if (session != null && session.getAttribute(sessionKey)!= null) {
15 result = getNamedDecorator(request, (String) session.getAttribute(sessionKey));
16 }
17
18 return result == null ? super.getDecorator(request, page) : result;
19 }
20 }
1 <mapper class="kr.co.imaso.sitemesh.module.mapper.SessionDecoratorMapper">
2 <param name="session.key" value="decorator_selected" />
3 </mapper>
[#7] 남은 것은 개발의 즐거움뿐
1 <filter-mapping>
2 <filter-name>Logging Filter</filter-name>
3 <url-pattern>/products/*</url-pattern>
4 <dispatcher>REQUEST</dispatcher>
5 <dispatcher>FORWARD</dispatcher>
6 <dispatcher>INCLUDE</dispatcher>
7 </filter-mapping>
[#8] 문서정보
의 "개발자 추천 숨어있는 오픈소스" 코너에 기고된 글입니다.
SSISO Community