주요글: 도커 시작하기
반응형
Struts의 템플릿에 대해서 살펴보고, JSP만을 이용하여 모델 2 구조를 구현해본다.

Struts의 템플릿 기능

Struts는 Jakarta 프로젝트 중의 하나로서 모델-뷰-컨틀롤러(MVC) 패턴에 기반하여 웹 어플리케이션을 개발할 수 있는 프레임워크를 제공해주는 API 이다. 현재 1.0.2 버전이 안정화된 버전이며, http://jakarta.apache.org/struts/index.html 에서 Struts에 대한 다양한 구할 수 있다. 앞서 말했듯이 Struts는 MVC 패턴, 즉 모델 2 구조를 구현하기에 알맞은 프레임워크를 제공하고 있다. 예를 들어, Struts는 기능과 모델 사이의 연결이나 모델과 뷰 사이의 연결 등을 하나의 컨트롤러를(그리고 하나의 설정파일을) 통해서 중앙집중식으로 처리할 수 있다는 장점을 제공하고 있다. 게다가 메시지 자원(resource)의 지역화(localization)을 지원한다는 장점도 있다. 하지만, Structs API를 사용하여 모델2 구조를 구현하려면 구현해야 할 클래스들이 적잖이 많다. 대형 프로젝트에서는 각 기능에 따라 모델 클래스를 분류할 경우 웹 어플리케이션을 관리하는 데 큰 도움이 되는 것이 사실이지만, 1-3개월 정도의 기간이 소비되는 소형 프로젝트에서는 불필요하다 생각될 정도로 처리해야 할 것들이 많아지는 것도 사실이다. 그래서 필자가 생각해낸 것이 Struts의 템플릿 기능을 사용하여 모델 2 구조를 구현해보는 것이었다.

Struts의 템플릿 기능은 말 그대로 JSP 페이지를 템플릿으로 처리할 수 있도록 해 주는 기능이다. 예를 들어 대부분의 웹 사이트는 다음 그림과 같이 하나의 일관된 웹 페이지 디자인을 전체 페이지에 걸쳐서 공통으로 사용하며, 보통 이러한 일정한 형태의 디자인이 적게는 2-3개 많으면 10개 정도 사용되는 것이 일반적이다.


위 그림은 IBM의 한 페이지를 살펴본 것인데, 그림을 보면 사이트가 헤더-서브메뉴-내용-풋터로 구성되어 있으며, 이 중 서브메뉴와 내용 부분은 페이지에 따라 변경되고 헤더와 풋터는 변경되지 않는다는 것을 알 수 있다. Struts의 템플릿 기능은 이와 같이 동일한 구조를 갖는 페이지들이 수십 또는 수백개 이상 존재할 때 유용하게 사용할 수 있다. 실제로 얼마나 Struts의 템플릿 기능이 유용하게 사용될 수 있는 지 살펴보기 위해 간단하게 Struts의 템플릿 기능을 사용하여 헤더-내용-푸터의 구조를 갖는 페이지를 작성해보도록 하자.

Struts를 사용하려면?

Struts를 사용하려면 다음과 같이 하면 된다.
  1. http://jakarta.apache.org/struts/index.html에서 Struts 1.0.2 버전을 다운로드 받는다.
  2. 다운로드 받은 파일의 압축을 푼 다음의 파일들을 알맞게 복사한다.
    • /lib/struts.jar 파일을 WEB-INF/lib/ 디렉토리에 복사하거나 [톰캣]/lib와 같이 JSP 콘테이너가 클래스패스로 인식하는 디렉토리에 복사한다.
    • /lib/struts-template.tld 파일을 WEB-INF/tlds/ 디렉토리에 복사한다.

  3. web.xml 파일에 다음의 내용을 추가한다.
    <taglib>
        <taglib-uri>/WEB-INF/tlds/struts-template.tld</taglib-uri>
        <taglib-location>/WEB-INF/tlds/struts-template.tld</taglib-location>
    </taglib>


Strut의 템플릿을 사용할 때는 [템플릿을 사용하는 JSP 페이지], [템플릿 JSP 페이지], [각 부분을 담당하는 JSP 페이지] 이렇게 3가지 구성요소가 필요하다. 예제에서 사용할 [템플릿 JSP 페이지]인 template.jsp는 다음과 같다.

    <%--
        filename: template.jsp
        템플릿 파일.
    --%>
    <%@ page contentType="text/html; charset=euc-kr" %>
    <%@ taglib uri="/WEB-INF/tlds/struts-template.tld" prefix="template" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    
    <body>
    
    <table border="0" cellspacing="0" cellpadding="0" 
           width="100%" bgcolor="#3333AA">
        <tr>
            <td width="100%" rowspan="2">
            <font color="#FFFFFF">
            <template:get name='header'/>
            </font>
            </td>
        </tr>
    </table>
    
    <table border="0" cellspacing="0" width="100%">
        <tr>
            <td>
            <template:get name='content'/>
            </td>
        </tr>
    </table>
    
    <table border="0" cellpadding="0" cellspacing="0"
           width="100%" bgcolor="#000000">
        <tr>
            <td width="100%" align="center" class="copyright">
            <font color="#FFFFFF" size="-1">
            <template:get name='footer'/>
            </font>
            </td>
        </tr>
    </table>
    
    </body>
    
    </html>

template.jsp는 일반적인 JSP 페이지와 큰 차이점이 없다. 차이점이 있다면 커스텀 태그인 <template:get>을 사용한다는 점이다. 위 코드를 보면 세 개의 <template:get> 커스텀 태그가 사용된 것을 알 수 있는데, 바로 이 <template:get> 커스텀 태그가 템플릿 기능의 핵심적인 기능을 제공한다. 각각의 <template:get> 태그를 보면 name 속성의 값을 'header', 'content', 'footer'로 지정한 것을 알 수 있는데, <template:get> 태그는 바로 이 name 속성의 값에 해당하는 페이지를 그 위치에 삽입한다. 지금의 설명만으로는 뭔가 부족할 것이다. 실제로 위 template.jsp를 템플릿으로 사용하는 JSP 페이지를 살펴보자. 다음은 template.jsp를 템플릿으로 사용하는 index.jsp의 소스 코드이다.

    <%--
        page: index.jsp
        template.jsp를 템플릿으로 사용하는 JSP 페이지
    --%>
    <%@ taglib uri="/WEB-INF/tlds/struts-template.tld" prefix="template" %>
    <template:insert template="/template.jsp">
        <template:put name="header" content="/header.jsp"/>
        <template:put name="content" content="/mainContent.jsp"/>
        <template:put name="footer" content="/footer.jsp"/>
    </template:insert>

위 코드에서 눈여겨 볼 부분은 <template:insert> 태그와 <template:put> 태그이다. 먼저 <template:insert> 태그는 template 속성을 통해서 어떤 JSP 파일을 템플릿으로 사용할 지 지정한다. 위 코드에서는 앞에서 작성한 template.jsp를 템플릿으로 사용한다고 지정하였다. 그리고 <template:insert> 태그에 중첩된 <template:put> 태그는 사용할 템플릿 파일에 어던 파일들을 삽입할 지를 지정한다. <template:put> 태그는 name 속성과 content 속성을 갖고 있는데, name 속성의 값은 템플릿의 파일의 <template:get>의 name 속성에서 사용되는 값과 동일하며, content 속성은 name 속성의 값이 일치하는 <template:get< 태그의 위치에 삽입될 페이지를 지정한다. index.jsp와 template.jsp의 관계를 정리하면 다음과 같다.

  • index.jsp는 template.jsp를 템플릿으로 사용한다. (index.jsp의 <template:insert> 태그)
  • template.jsp의 <template:get name='header'/>에는 header.jsp가 삽입된다. (index.jsp의 <template:put name="header" content="/header.jsp"/>)
  • 마찬가지로 'content'와 'footer'에 해당하는 <template:get> 태그에는 각각 mainContent.jsp와 footer.jsp가 삽입된다.
실제로 웹브라우저에서 index.jsp를 실행해보면 다음과 같은 결과가 출력된다.


만약, index.jsp가 아닌 다른 페이지에서 같은 모양의 JSP 페이지를 만들어 내고 싶다면 다음과 같이 하면 될 것이다. 다음 코드의 경우는 index.jsp와 똑같은 디자인을 사용하고 똑같은 헤더와 풋터를 사용하고 내용 부분만 index.jsp와 다른 JSP 페이지를 사용하게 된다.

    <%@ taglib uri="/WEB-INF/tlds/struts-template.tld" prefix="template" %>
    <template:insert template="/template.jsp">
        <template:put name="header" content="/header.jsp"/>
        <template:put name="content" content="/contentList.jsp"/>
        <template:put name="footer" content="/footer.jsp"/>
    </template:insert>

템플릿에 대한 보다 자세한 사용방법은 http://jakarta.apache.org/struts/doc-1.0.2/api/org/apache/struts/taglib/template/package-summary.html에서 얻을 수 있으니 참고하기 바란다.

JSP만을 이용한 모델 2 구조와 Struts의 템플릿 기능 적용

사실 앞에서 살펴본 내용만으로도 Struts의 템플릿의 기본적인 기능을 사용하는 데는 큰 어려움이 없다. 사실, Struts의 템플릿 기능은 이 글의 핵심 내용이 아니다. 이 글의 핵심 내용은 Struts의 템플릿 기능을 사용하여 JSP 만으로 MVC 패턴을 구현하는 것이다. 물론, 이 글을 읽고 있는 다수의 개발자들은 '서블릿+JSP+모델객체'로 구성된 모델 2 구조를 사용하면 될거라고 생각할 것이다. 뭐, 틀린 말은 아니고 일반적으로 '서블릿+JSP+모델객체'의 모델 2 구조가 정석으로 받아들여지고 있다. 하지만, MVC 패턴에서 컨트롤러의 역할을 하는 서블릿을 수정해야 되는 경우 컴파일을 다시 해야 하고, 운영 환경이었을 경우 클래스를 교체해야 하는 시기 및 서블릿 엔진의 재가동 여부 등을 모두 고려해야 한다. 특히, 소규모 프로젝트에서 서블릿을 만들고 컴파일하고 때때로 서블릿 엔진을 재가동하는 등의 작업은 개발자에게 있어서 매우 귀찮은 작업이 된다.

이러한 귀찮은 작업을 없앨 수 있는 방법이 바로 템플릿 기능을 사용하여 MVC 패턴을 구현해보는 것이다. 생각해보면 JSP 만으로 MVC 패턴을 구현하지 못하리란 법도 없다. 왜냐면, JSP가 곧 서블릿이기 때문이다. (JSP에 대한 서적을 제대로 읽은 독자라면, JSP 페이지가 서블릿으로 컴파일된 후 실행된다는 것을 알고 있을 것이다.) 따라서, 컨트롤러 서블릿을 만들듯, JSP를 컨트롤러 JSP로 만들면 매우 간단하게 MVC 패턴을 구현할 수 있게 된다. 만약 if-else 구문에 기반한 컨트롤러를 만든다면, JSP 페이지는 다음과 같은 형태를 취하게 될 것이다.

  <%
      String cmd = request.getParameter("cmd");
      String viewPage = null;
      if (cmd.equals(Command.ADD_PROCESS)) {
          someProcessingAdd(request, response);
          viewPage = ADD_PROCESS_PAGE;
      } else if (cmd.equals(Command.ADD_FORM)) {
          someProcessingAddForm(request, response);
          viewPage = ADD_FORM_PAGE;
      } else if (cmd.equals(Command.LIST)) {
          someProcessingList(request, response);
          viewPage = LIST_PAGE;
      }
  %>
  <jsp:forward page = "<%= viewPage %> />
  ...
  

위 코드는 MVC 패턴을 구현하는 방법 중 가장 단순한 형태이다. 물론, someProcessingXX() 종류의 메소드를 커맨드 패턴을 적용하여 객체단위로 분리시킬 수 있을 것이고, 팩토리 패턴을 적용하면 위 JSP 코드는 보다 간결해질 것이다. 하지만, 어떤 패턴을 적용하든지 간에 위와 같은 형태의 코드가 MVC 패턴을 구현하는 가장 기본적인 형태가 된다. (이에 대한 내용은 자바캔 필자가 올린 'JSP Model 2 Architecture 2부, 커맨드 패턴의 적용'을 참고하기 바란다.)

JSP 만으로 MVC 패턴을 구현하는 것이 서블릿을 사용하여 MVC 패턴을 구현하는 것과 별다른 차이점이 없기 때문에 JSP 만으로 MVC 패턴을 구현하다 해서 별다른 어려움이 있는 것은 아니다. 소규모 프로젝트의 경우는 컴파일 과정을 줄이면서 동시에 객체지향적 방법을 적용할 수 있기 때문에 서블릿-JSP로 구성된 모델 2 구조보다 JSP-JSP로 구성된 MVC 패턴 구조가 현실적으로 더 좋은 구조라고도 볼 수 있다.

지금쯤이면 글을 읽는 여러분중에 몇몇은 JSP-JSP로 모델2 구조를 구현하는 것과 Struts의 템플릿 기능 사이에 어떤 관계가 있을까 하고 의심의 눈초리를 보내고 있을지도 모르겠다. 물론 둘 사이에 특별한 관계가 있는 것은 아니다. 하지만, JSP-JSP로 구성된 MVC 패턴 구조에 Struts의 템플릿 기능을 더하면 개발자는 많은 편의를 얻게 된다. 다음의 코드를 살펴보자.

    <%@ taglib uri="/WEB-INF/tlds/struts-template.tld" prefix="template" %>
    <%
        String cmd = request.getParameter("cmd");
        String viewPage = null;
        if (cmd.equals(Command.ADD_PROCESS)) {
            someProcessingAdd(request, response);
            viewPage = ADD_PROCESS_PAGE;
        } else if (cmd.equals(Command.ADD_FORM)) {
            someProcessingAddForm(request, response);
            viewPage = ADD_FORM_PAGE;
        } else if (cmd.equals(Command.LIST)) {
            someProcessingList(request, response);
            viewPage = LIST_PAGE;
        }
    %>
    <%-- jsp:forward 대신에 template:insert를 사용한다 --%>
    <template:insert template="/template.jsp">
        <template:put name="header" content="/header.jsp"/>
        <template:put name="content" content="<%= viewPage %>"/>
        <template:put name="footer" content="/footer.jsp"/>
    </template:insert>
    ...
    

어떤가? 간단하게 컨트롤러 개발의 귀찮음과 웹페이지 레이아웃의 유지가 한꺼번에 해결되는 것을 알 수 있다. 만약 컨트롤러를 서블릿으로 한다면 다음과 같은 불편함이 발생할 수 있다.

  • 템플릿을 사용하는 JSP 페이지로 포워딩을 해야 한다.
  • 템플릿을 사용하지 않는 경우, 별도의 레이아웃 관리 기법을 개발해야 한다.
  • 컨트롤의 코드가 바뀌면 다시 소스 코드를 컴파일 해야하고, 때에 따라서 어플리케이션 서버를 재가동해야 하는 경우도 발생한다.
이 글에서 제시한 방법을 사용한다면, 위와 같은 불편함이 줄어들 뿐만 아니라 하나의 템플릿을 사용하면서도, 적은 노력을 기울여서 일정한 레이아웃을 유지하면서 동시에 다양한 화면 구성을 만들어낼 수 있다는 장점이 있다. 또한, 템플릿 파일 자체가 JSP 페이지이기 때문에 해당 템플릿을 사용하여 출력되는 모든 웹페이지에 대해서 동일한 처리를 할 수 있다. 예를 들어, 로그인 여부를 파악한다던가, 각 페이지에 대한 사용자 추적 로그를 작성한다던가, 통합적으로 에러 로그를 처리하는 등의 작업을 하나의 템플릿 파일(또는 경우에 따라서 4-5개 정도의 템플릿 파일에서)을 통해서 일괄적으로 처리할 수 있다.

결론

필자가 이 글을 통해서 말하고자 하는 건, 이 글에서 제시한 방법이 실제 개발과정에서 생각하는 것 이상으로 편리하다는 점이다. 그렇다고 해서 관리가 전혀 안 되는 것도 아니다. 웹 화면을 구성하는 각 요소들이 각각의 JSP로 분리되기 때문에 관리하기가 오히려 더 수월해지며, 컨트롤 역할을 하는 JSP 역시 하나의 객체로서 처리되기 때문에(즉, 서블릿으로 변환되기 때문에) 객체 단위의 관리가 가능하다.

실제로 필자는 이 글에서 소개한 방법을 사용하여 물류 관련 경매 사이트를 개발해보았다. DB 테이블과 1대1로 매핑되는 자바빈 객체와 DB 테이블에 대해서 CRUD(Create-Read-Update-Delete) 작업을 처리해주는 래퍼 객체를 기반으로 하였으며, JSP-JSP로 구성된 MVC 구조와 Struts의 템플릿 기능을 혼합해서 사용했었는데 서블릿 기반의 모델 2 구조를 사용하는 것보다 편리하고 빠르게 개발을 할 수 있었다. JSP를 컨트롤러 객체로 작성했을 경우 한가지 문제점이 발생하는 데 그건 바로 Javadoc을 사용하여 컨트롤러 JSP에 대한 문서화를 할 수 없다는 것이다. 서블릿으로 작성할 경우 다른 자바 클래스처럼 Javadoc을 사용하여 컨트롤러에 대한 문서를 간단하게 생성할 수 있지만, JSP의 경우는 J2SDK에서 제공하는 Javadoc으로는 처리할 수 없는 것이 사실이다. 하지만, JSP 페이지에 대해서 별도의 문서화 방법을 강구할 수 있기 때문에, 이것도 문제가 되지 않는다.

여러분에게 필자의 방법을 강요할 순 없지만, 대규모 프로젝트가 아니라면(설사 대규모 프로젝트라 할지라도) 이 글에서 소개한 JSP를 기반으로 MVC 구조를 구현해볼 것을 권한다. 아마 기대했던 것 이상의 개발 효과를 얻을 수 있을 것이다.

관련링크:

+ Recent posts