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

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의
개인적으로 SiteMesh의 동장 방식이 Tiles보다 좋기 때문에 SiteMesh를 선호하는데, SiteMesh를 사용하면서 언제나 아쉬운 점은 <servlet-mapping>의 <url-pattern>의 값을 /catalog/*와 같은 경로 기반으로 설정한 경우 SiteMesh의 데코레이터 설정 파일에서 경로 패턴에 기반한 매칭을 사용할 수 없다는 점이다. (관련글: SiteMesh를 이용한 웹 페이지 데코레이션, http://javacan.tistory.com/entry/131) 특히 요즘처럼 (REST 방식의 유행으로) 확장자 없는 URL을 제공하는 게 멋처럼 느껴질 때에는 더더욱 SiteMesh의 지원이 아쉬웠다.

이런 아쉬움을 해소하기 위해 <servlet-mapping>의 <url-pattern>의 값에 상관없이 URL 경로를 이용해서 패턴 매칭을 하는 DecoratorMapper 클래스를 작성해보았다.

WemadeConfigDecoratorMapper 클래스 구현 코드

아래 코드는 실제로 구현해서 사용하고 있는 코드이다.

package com.madvirus.sitemesh;

import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import com.opensymphony.module.sitemesh.Config;
import com.opensymphony.module.sitemesh.Decorator;
import com.opensymphony.module.sitemesh.DecoratorMapper;
import com.opensymphony.module.sitemesh.Page;
import com.opensymphony.module.sitemesh.mapper.AbstractDecoratorMapper;
import com.opensymphony.module.sitemesh.mapper.ConfigLoader;

/**
 * 서블릿 경로가 아닌 컨텍스트 경로를 제외한 요청 URI를 이용해서 패턴 매칭을 수행한다.
 * 
 * Sitemesh 2.4.1 버전의 ConfigDecoratorMapper로부터 코드를 가져와서 작성하였음.
 * 
 * @author 최범균
 * @version 2010. 3. 5.
 */
public class WemadeConfigDecoratorMapper extends AbstractDecoratorMapper {

    private ConfigLoader configLoader = null;

    /** Create new ConfigLoader using '/WEB-INF/decorators.xml' file. */
    public void init(Config config, Properties properties,
            DecoratorMapper parent) throws InstantiationException {
        super.init(config, properties, parent);
        try {
            String fileName = properties.getProperty("config",
                    "/WEB-INF/decorators.xml");
            configLoader = new ConfigLoader(fileName, config);
        } catch (Exception e) {
            throw new InstantiationException(e.toString());
        }
    }

    /**
     * Retrieve {@link com.opensymphony.module.sitemesh.Decorator} based on
     * 'pattern' tag.
     */
    public Decorator getDecorator(HttpServletRequest request, Page page) {
        String thisPath = request.getRequestURI();
        String contextPath = request.getContextPath();
        if (thisPath.startsWith(contextPath)) {
            thisPath = thisPath.substring(contextPath.length());
        }
        String name = null;
        try {
            name = configLoader.getMappedName(thisPath);
        } catch (ServletException e) {
            e.printStackTrace();
        }
        Decorator result = getNamedDecorator(request, name);
        return result == null ? super.getDecorator(request, page) : result;
    }

    /**
     * Retrieve Decorator named in 'name' attribute. Checks the role if
     * specified.
     */
    public Decorator getNamedDecorator(HttpServletRequest request, String name) {
        Decorator result = null;
        try {
            result = configLoader.getDecoratorByName(name);
        } catch (ServletException e) {
            e.printStackTrace();
        }

        if (result == null
                || (result.getRole() != null && !request.isUserInRole(result
                        .getRole()))) {
            // if the result is null or the user is not in the role
            return super.getNamedDecorator(request, name);
        } else {
            return result;
        }
    }

}

sitemesh.xml 파일 설정에서 커스텀 DecoratorMapper 사용

sitemesh.xml 파일에서는 아래 코드와 같이 앞서 구현한 DecoratorMapper를 사용하도록 설정한다.

<sitemesh>
    <property name="decorators-file" value="/WEB-INF/decorators.xml"/>
    <excludes file="${decorators-file}"/>

    <page-parsers>
        <parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser" />
    </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.FrameSetDecoratorMapper"/>
        <mapper class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
            <param name="decorator" value="printable" />
            <param name="parameter.name" value="printable" />
            <param name="parameter.value" value="true" />
        </mapper>
        <mapper class="com.opensymphony.module.sitemesh.mapper.FileDecoratorMapper"/>
        <mapper class="com.madvirus.sitemesh.WemadeConfigDecoratorMapper">
            <param name="config" value="${decorators-file}" />
        </mapper>
    </decorator-mappers>
</sitemesh>

web.xml 파일 및 decorator.xml 파일 설정

web.xml 파일에서 다음과 같이 경로 기반의 패턴을 사용하고 있다고 해 보자.

<web-app ...>
    ...
    <servlet-mapping>
        <servlet-name>ControllerServlet</servlet-name>
        <url-pattern>/my/*</url-pattern>
        <url-pattern>/data/*</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

위 코드에서 ControllerServlet은 경로 기반의 <url-pattern>을 사용하고 있는데, 앞서 구현한 커스텀 DecoratorMapper를 사용함으로써 다음과 같이 데코레이터 파일에서 URL 패턴을 이용해서 매칭을 할 수 있게 된다.

<decorators defaultdir="/decorators">

    <decorator name="my" page="/WEB-INF/view/common/decorator/my.jsp">
        <url-pattern>/my/*</url-pattern>
    </decorator>

    <decorator name="data" page="/WEB-INF/view/common/decorator/data.jsp">
        <url-pattern>/data/*</url-pattern>
    </decorator>

</decorators>

그 동안 경로 기반의 서블릿 매핑을 사용할 때 SiteMesh 때문에 섭섭함이 있었던 개발자에게 본 글이 조금이나마 도움이 되길 바란다.


Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 삽지리 2010.07.13 10:45 신고  댓글주소  수정/삭제  댓글쓰기

    도움이 되었습니다. 감사합니다.