이글은 webwork,sitemesh를 기반으로 한 프레임워크에 적합한 내용입니다. 그리고, 이 글은 모든 내용이 다 맞지 않습니다.

한글 자료가 거의 전무하여 적어 놓게 되었습니다. 회사이름은 밝히기 않기 위해. 대충 가명썼습니다..

 

Concept

Tiles Layout은 만들어진 템플릿 jsp를 기준으로 extends 태그를 통해 확장하는 구조를 가지고 있는 반면, Sitemesh Layout은 action과 pattern을 이용하여 쉽게 장식을 할 수 있다. Lucy의 기본 프레임인 Webwork와 Sitemesh는 같은 Opensympony 프로젝트에 같이 포함되어 있어서 궁합(?)이 아무래도 Tiles보다는 높다.

Tiles는 특정 템플릿에 적용되는 여러 jsp(보통 하나의 Action에 해당되기도 한다.)에 일일히 지정해야 하는 불편함이 있어서, 환경설정 파일이 길어지는 단점이 있다. 하지만, Sitemesh는 action별로 분류하고, 서비스에 맞게 pattern(예, '*')을 지정하여 임의의 jsp 혹은 action을 그룹단위로 묶어 처리한다.

Tiles를 Sitemesh로 변환하기에 앞서 먼저 xwork.xml의 서술된 action과 result를 보고 활용해야 한다. Sitemesh는 요청받은 URL을 기준으로 처리하기 하기 때문에 action의 이름(예, index.gl)을 가지고 처리하는 것이 Sitemesh에서 쉽게 처리할 수 있다.
첫번째, 환경 설정을 먼저 해야 한다. Sitemesh를 쓰기 위해서는 당연히 web.xml에서 기술하고, 관련된 WEB-INF/web/sitemesh.xml 파일과 WEB-INF/web/decorators.xml 파일을 생성해야 한다.
두번째, Tiles 환경 설정파일(tiles.xml 라고 하자.)에서 Layout 템플릿을 Sitemesh 법칙에 맞게 변경한다. Tiles에 종속적인 태그라이브러리는 당연히 Sitemesh가 제공하는 태그 라이브러리로 바꾸면 된다.
그리고, Action과 jsp파일의 패턴을 템플릿에 적용시킨다.

간단하지 아니한가?! 이 글을 쓰는 시점에 game1을 Tiles에서 Sitemesh로 바꿨더니 줄 수가 1/3정도로 줄어들었다. 만약 처음부터 Sitemesh로 개발하면, 스크롤할 필요도 없이 잘 끝낼 수도 있다.

이 설명이 다인가 하고 놀랬을 터이지만, 놀라지 마라. 상세하고 친절한 설명이 계속 진행된다.

변환 가이드

1. web.xml에서 sitemesh를 적용한다.

  • 가장 먼저 tiles 관련 설정을 web.xml에서 제거한다.
  • [web-app home directory]/WEB-INF/web.xml의 <web-app> 태그안에 다음의 내용이 추가되어야 한다. webwork-cleanup - sitemesh - webwork 의 순서대로 등록해야 한다.
    web.xml
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    	version="2.4">
    
    .....fileter의 webwork-cleanup 정의 .....
             <filter>
    		<filter-name>sitemesh</filter-name>
    		<filter-class>
    			com.opensymphony.module.sitemesh.filter.PageFilter
    		</filter-class>
    		<init-param>
    			<param-name>pageEncoding</param-name>
    			<param-value>MS949</param-value>
    		</init-param>
    	</filter>
    
    .....fileter의 webwork 정의 .....
    
    
    .....filter-mapping의 webwork-cleanup 정의 .....
    	<filter-mapping>
    		<filter-name>sitemesh</filter-name>
    		<url-pattern>/*</url-pattern>
    		<dispatcher>REQUEST</dispatcher>
    	</filter-mapping>
    .....filter-mapping의 webwork 정의 .....
    
    </web-app>

2. sitemesh.xml파일을 생성한다.

[web-app home directory]/WEB-INF/sitemesh.xml 파일을 생성한다.

디폴트로 여러 decoration 환경 설정을 포함할 수 없기 때문에, com.google.common.sitemesh.mapper.MultipleConfigDecoratorMapper 클래스를 사용해야 한다. 이 클래스는 <param> 태그의 이름을 오름차순 정렬하여 우선순위를 정하기 때문에, config.1, config.2 와 같이 우선순위가 높은 decorator를 지정할 수 있도록 해야 한다.

sitemesh.xml
<sitemesh>
	<excludes file="/WEB-INF/decorators.xml"/>

	<page-parsers>
		<parser default="true" class="com.opensymphony.module.sitemesh.parser.DefaultPageParser" />
		<parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
	</page-parsers>

	<decorator-mappers>
		<mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
			<param name="property.1" value="meta.decorator" />
			<param name="property.2" value="decorator" />
		</mapper>

		<mapper class="com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper">
			<param name="decorator.parameter" value="decorator" />
			<param name="parameter.name" value="confirm" />
			<param name="parameter.value" value="true" />
		</mapper>

                  <mapper class="com.google.common.sitemesh.mapper.MultipleConfigDecoratorMapper">
                            <param name="config.1" value="/common/decorators/decorators.xml" />
                            <param name="config.2" value="/WEB-INF/decorators.xml" />
                  </mapper>
	</decorator-mappers>
</sitemesh>

google 소스 트리에서 web/common/decorators/ 디렉토리에 공통으로 쓰일 decorators.xml 파일이 있고, 하위 폴더에 common-decorators 디렉토리가 있어 공통으로 쓰일 Layout 템플릿을 지정할 수 있다. 현재는 'underLayout'라는 이름으로 지정한 decorator page를 설정하고 있다.

web/common/decorators/decorators.xml
<decorators defaultdir="/common/decorators/common-decorators">
    <decorator name="underLayout" page="underLayout.jsp"/>
</decorators>

3. decorators.xml파일을 생성한다.

[web-app home directory]/WEB-INF/decorators.xml 파일을 생성한다. 정의된 defaultdir를 통해 파일안에서 상대 경로를 써서 Layout 페이지를 지정할 수 있다.

decorators.xml
<decorators defaultdir="/WEB-INF/decorators">
</decorators>

4. Tiles의 Layout 템플릿 정의를 Sitemesh의 decorator로 수정한다.

Tiles와 약간 다른 부분이다. Tiles는 임의의 파일을 메인 Layout 템플릿으로 지정하고, 이안에 저장될 스크립트, 상위메뉴, 우메뉴등등을 지정한다. 그리고, content 또는 body 부분을 "/"로 지정하여 extends한 Tiles 태그에서 적용될 jsp페이지를 적는다.

Sitemesh는 먼저 decorator 태그를 이용하여 태그를 지정하여 부분 Layout 템플릿을 정의한다. 그리고 나서, 메인 Layout 템플릿을 정의한다. 마지막으로 Tiles의 흔적이 남는 tiles디렉토리도 tiles에서 decorators로 변경해야 한다.

tiles.xml
<definition name=".layout.master.main" path="/WEB-INF/tiles/renewal_2006/layout/game1Layout.jsp">
    <put name="script" value="/share/tiles/game_script.jsp"/>
    <put name="common_activex" value="/common/common_activex.jsp"/>
    <put name="topmenu" value="/share/tiles/header_casual.jsp"/>
    <put name="rightmenu" value=".layout.master.rightmenu"/>
    <put name="content" />
    <put name="footer" value="/share/tiles/game1MainFooter.jsp"/>
</definition>
decorators.xml
<decorator name="script" page="/share/decorators/game_script.jsp"/>
<decorator name="common_activex" page="/common/common_activex.jsp"/>
<decorator name="topmenu" page="/share/decorators/header_casual.jsp"/>
<decorator name="rightmenu" page="renewal_2006/layout/rightMenuLayout.jsp"/>
<decorator name="footer" page="/share/decorators/game1MainFooter.jsp"/>
<decorator name="submenu" page="/share/decorators/right_casual.jsp"/>

<decorator name=".layout.master.main" page="renewal_2006/layout/game1Layout.jsp">
</decorators>

5. 메인 Layout 템플릿 소스를 Sitemesh의 정의에 맞게 수정한다.

game1Layout.jsp에 page 태그와 decorator 태그를 이용하여 수정한다. 쉽게 설명하면, page 태그는 이미 정의된 부분 Layout템플릿을 사용하는 것이고, decorators 태그는 실제 적용될 jsp 파일의 title, body 태그에서 정의된 내용을 참조하기 위함이다. (자세한 내용은 매뉴얼을 꼭 참조하라)

<tiles:insert> 일리먼트는 <page:applyDecorator> 일리먼트로 수정한다. 그리고, 실제 데이터가 들어갈 부분 content attribute을 <decorator:body/>로 바뀐다.

xwork.xml에서 action에서 result로 지정되어 dispatch된 jsp파일의 내용이 들어가게 된다. 이렇게 함으로서, tiles에 의존적이 않던 jsp 코드는 전혀 수정할 필요가 없게 된다. 만약, tiles.xml에서 정의된 content attribute에 정의된 show.jsp가 있다면, 이 내용 자체가 <decorator:body/>에 그대로 적용될 것이다. 유의할 점은 body 태그를 쓴다는 것이다.

Before,game1Layout.jsp,
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
<tiles:importAttribute scope="request" ignore="true"/>
<tiles:insert attribute="script"/>
<tiles:insert attribute="common_activex"/>
<tiles:insert attribute="topmenu"/>
<tiles:insert attribute="content"/>
<tiles:insert attribute="rightmenu"/>
<tiles:insert attribute="footer"/>
game/show.jsp
<script> 불라 불라 </script>
<table> 불라 불라 </table>
After,game1Layout.jsp
<%@ taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator" %>
<%@ taglib uri="http://www.opensymphony.com/sitemesh/page" prefix="page" %>
<page:applyDecorator name="script"/>
<page:applyDecorator name="common_activex"/>
<page:applyDecorator name="topmenu"/>
<decorator:body/>
<page:applyDecorator name="rightmenu"/>
<page:applyDecorator name="footer"/>

6. 개별 페이지를 Layout템플릿에 적용시킨다.

  • Tiles의 definition이 Sitemesh의 decorator을 이용하여 메인 Layout 템플릿이 변경되었으니, 적용될 각 jsp 또는 action을 지정하면 된다. '*' 같은 pattern을 이용하여 Sitemesh를 잘 활용하여 한줄이라도 아껴쓰자.
    tiles.xml
    <definition name=".layout.master.main.goldwing" extends=".layout.master.main.goldwing.default">
    	<put name="gamegen" value="/WEB-INF/tiles/goldwinglayout/inpage/goldwinggen.jsp"/>
    </definition>
    decorators.xml
    <decorator name=".layout.master.main.goldwing.default" page="/WEB-INF/tiles/goldwinglayout/masterMainLayout.jsp">
             <pattern>goldwinglayout/inpage/goldwinggen.jsp</pattern>
             <pattern>/goldwing/*.gl</pattern>
    </decorator>
  • xwork.xml에 정의된 action을 이용하여 패턴화한다. xwork.xml에서 정의된 same Action을 참조하여 tiles.xml에서 사용되고 있음을 확인하고, decorators.xml에 decorator를 적용시킨다.
    xwork.xml
    <action name="same" class="com.google.game1.action.SameGameAction" >
    	<result name="main" type="tiles">.same.main</result>
    	<result name="iframe" type="tiles">.same.iframe</result>
    	<result name="rank" type="tiles">.same.rank</result>
    	<result name="includedrank" type="tiles">.layout.master.includedrank.casual</result>
    	<result name="search" type="tiles">.layout.master.searchrank.casual</result>
    </action>
    tiles.xml
    <definition name=".same.main" extends=".layout.master.main">
    	<put name="content" value="/same/layout/content_same.jsp"/>
    </definition>
    
    <definition name=".same.rank" extends=".layout.master.main">
    	<put name="content" value="/same/rank/rank_same.jsp"/>
    </definition>
    
    <definition name=".same.iframe" extends=".layout.master.main">
    	<put name="content" value=".layout.master.content.iframe"/>
    </definition>
    decorators.xml
    <decorator name=".layout.master.main" page="/WEB-INF/tiles/renewal_2006/layout/game1Layout.jsp">
    	<pattern>/same.gl</pattern>
    </decorator>
  • xwork.xml의 action을 수정한다
    xwork.xml에는 여전히 Tiles의 흔적이 남아 있다.바로 action의 result 태그에 type으로 지정되었다. 이를 dispatcher로 바뀌거나 아예 그 부분을 삭제한다. type을 지정하지 않으면, dispatcher 타입이다.
    Before,xwork.xml
    <action name="same" class="com.goole.game1.action.GoogleAction" >
    	<result name="main" type="tiles">.same.main</result>
    	<result name="iframe" type="tiles">.same.iframe</result>
    	<result name="rank" type="tiles">.same.rank</result>
    	<result name="includedrank" type="tiles">.layout.master.includedrank.casual</result>
    	<result name="search" type="tiles">.layout.master.searchrank.casual</result>
    </action>
    After,xwork.xml
    <action name="same" class="com.google.game1.action.SameGameAction" >
    	<result name="main">/same/layout/content_same.jsp</result>
    	<result name="iframe">/share/decorators/iframe_casual.jsp</result>
    	<result name="rank">/same/rank/rank_same.jsp</result>
    	<result name="includedrank">/share/rank/includedrank_casual.jsp</result>
    	<result name="search">/share/rank/searchrank_casual.jsp</result>
    </action>
  • 모든 jsp 파일에서 tiles에서 decorators로 변경된 path를 적용한다.
    이클립스를 검색을 이용하여 파일의 내용에 tiles가 들어간 xml, jsp파일을 검색해서 모두 변경해야 한다.
    Before, content_same.jsp
    <jsp:include page="/share/tiles/channel_casual.jsp"/>
    After, content_same.jsp
    <jsp:include page="/share/decorators/channel_casual.jsp"/>
  • Tiles의 insert 페이지를 변환한다.
    Tiles가 적용환 jsp파일에 <tiles:insert> 태그가 적용된 페이지를 <page:applyDecorator> 태그로 수정하고, decorators.xml에 decorator패턴으로 지정한다.
    jsp
    <tiles:insert page="/cross/bbs/pagelist.jsp"/>
    decorators.xml
    <decorator name="pageList" page="/cross/bbs/pagelist.jsp"/>
    jsp
    <page:applyDecorator name="footer"/>
  • 큰 Layout 템플릿에 속해 있는 일부 템플릿을 다른 템플릿으로 교체하는 경우
    Tiles는 이런 경우 extends를 이용하여 큰 Layout 템플릿을 extends하고, 해당 attribute만 put을 함으로서, overwrite가 가능하다. 그러나 Sitemesh는 새롭게 Layout을 설정해야 하고 서로 다르게 적용되어야 한다.
    예를 들어보자. 메인 Layout은 header, footer, rightmenu, leftmenu Layout을 사용하고 있다. 그 중 headerLayout만 다르고, 나머지 Layout은 같이 쓴다. a.jsp는 headerLayout.jsp라는 헤더 Layout을 사용하고, 그 외 모든 jsp 파일에서는 newHeaderLayout.jsp를 사용한다.
    메인 Layout에 <page:applyDecorator>를 이용하여 특정 템플릿 jsp를 정의한 decorator page을 정의만 할 수 있기 때문에(Tiles처럼 extends를 할 수 없음) a.jsp와 기타 jsp 파일은 서로 다른 메인 Layout을 사용해야 한다.
    <decorator name=".layout.master.main.goldwing.default" page="masterMainLayout.jsp">
    	<pattern>/a.jsp</pattern>
    </decorator>
    
    <decorator name=".layout.master.main.goldwing.default.norightmenu" page="masterMainLayout1.jsp">
    	<pattern>/*.jsp</pattern>
    </decorator>
  • Layout이 필요없는 페이지
    총 3가지 방법이 있다.
    • decorators.xml 파일에서 exclude 태그를 사용
    • jsp파일에서 메타 태그를 이용하여 decorator의 page가 null인 것을 지정
    • EmptyAction을 사용하여 page가 null인 decorator를 패턴으로 지정
      이 중, 첫번째 방법으로 Layout을 적용하는 것을 따로 빼어낼 수 있다.
under.jsp(common_google)
<%@ page language="java" pageEncoding="MS949" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Untitled</title>
<meta http-equiv="Content-Type" content="text/html; charset=euc-kr">
<META name="decorator" content="underLayout"/>
<style>
...
...
decorators.xml
<decorators>
..
    <exclude>
        <pattern>/teasing.gl</pattern>
    </exclude>
...
 </decorators>
xwork.xml
<package name="empty" extends="com.google.news">
  <action name="teasing" class="com.google.news.action.auth.EmptyAction">
    <result name="success">/empty/m.jsp</result>
  </action>
</package>
  • ControllerURL 처리 방법
    • Inceptor 처리
    • 해당 controllerURL의 내용을 클래스로 전환.
  • Action에 Request 파라미터가 추다되어도 Sitemesh 적용
    search.gl action에 지정한 decorator 패턴에 그대로 적용된다.
    search.gl?m=list 의 경우도 <pattern>/search</pattern>와 똑같은 decorator를 사용한다. 따로 설정을 해 줄 필요 없다.

 

Posted by '김용환'
,