Site Search :
Standard Enterprise XML Methodology Pattern Setting Tunning Other
Article Contributors
GuestBook
Javapattern Maven
XSourceGen Dev
JetSpeed Test
JLook Image
jLook Family Site


Web Application Framework개발방법(3)-View page Composing
 
이번 편에서는 FrameworkEngine에서 forward되어진 layout에서 어떤 방식으로 화면을 composing할 수 있는 지에 대한 방안을 논의하도록 한다. ( 2003/04/07 ) 606
Written by ienvyou - 최지웅
1 of 1
 

자, 저번 글까지는 엔진의 구성에 대하여 알아보았다. 어떻게 라이프사이클의 흐름을 타며 어떠한 
방법으로 애플리케이션이 reflection에 의하여 처리되어지는 지를 볼수 있었다.

아직 읽어보지 않았는가? 그럼 아래의 글들을 먼저 읽고 오기 바란다.

Web Application Framework개발방법(1)-Framework의 개요 
http://www.javapattern.info/viewArticle.do?articleId=1048168663754

Web Application Framework개발방법(2)-LifeCycle,Controller 
http://www.javapattern.info/viewArticle.do?articleId=1048479655248

이전글에서는 framework이 무엇이며, 그러한 프레임웍들은 어떠한 라이프 사이클을 타게 되는것인지
Control은 어떻게 처리를 해야하는지에 대하여 설명을 한 것이다.
Reflection중심의 사상으로서 규칙이 정해지게 되면 일반의 개발방식은 이미 프레임웍상에서
정의해놓은 인터페이스를 통하여 재정의한 perform이란 메소드로 control을 할 수 있다라고 이야기했다.

자, 그러면 request를 받아서 business logic을 처리한 후 우리는 어떻게 화면조립을 이루어야 할 것인가?
보통 초기의 서블릿에서는 PrintWriter의 내용을 이용하여 출력을 하도록 했었는데 pw.println()으로는 
화면이 복잡해질 경우 상당한 유지보수의 문제가 발생을 하게 되었고 이로 인해 jsp파일로의 전이가
가능하도록 메소드가 고안되어져야 했는데 그것이 바로 RequestDispatcher클래스의 include(), forward()가
그것이다.

이러한 메소드의 제공으로 쉬운 jsp로의 변환이 가능하게 되었으며, request라는 객체에 business logic에서
반환된 object들을 setting시킬 수 있는 기능을 제공함으로서 servlet과 jsp간의 쉬운 객체의 이동을 이루어내게
하였다.

servlet측에서는 
    request.setAttribute("name", valueObject);
    RequestDispatcher rd = request.getRequestDispatcher("target");
    rd.forward(request,response);
의 형태로 target을 호출하게 되며

jsp측에서는

<jsp:useBean id="name" class="value object type class" scope="request"/>
or
value object type class xx = request.getAttribute("name");

등의 형태로 받을 수 있다는 걸 알아야 한다.


자, 이제 business control부분을 engine에서 처리한 후 어떻게 view page를 할 것인가에 대한 전략을 세워보아야 할 것인데 이전 시간에 어떻게 설정을 했었는지를 다시 한번 적어보자 web.xml에서 확장자 view가 들어오게 되면 자동으로 com.javapattern.framework.composer.FrameworkComposer클래스가 동작이 되도록 반영시켜 놓았다. Composer에서는 들어오는 request를 읽어들여 자신이 보여줘야 할 페이지에 대한 정보를 이용하여 각각의 jsp파일들을 하나의 템플릿을 이용하여 브라우져에 보여질 수 있도록 만들어내게 하면 될것인데 참조xml은 webpage_config.xml이란 파일명으로 아래와 같다. 만약 이글을 읽는 당신이 struts tiles에 익숙해진 개발자라면 쉽게 접할 수 있는 내용이다. 참조 : webpage_config.xml


<?xml version="1.0" encoding="euc-kr"?>

<webpage-definitions>
  
<!-- Sample -->
  <webpage>
  	<name>test</name>
  	<description>설명</description>
  	<title>화면 제목</title>
	<layout>/template.jsp</layout>
  	<page key="header" 	url="/sample/header.jsp"/>
  	<page key="menu" 	url="/sample/menu.jsp"/>
  	<page key="body" 	url="/sample/body.jsp"/>
	<page key="sidebar" 	url="/sample/sidebar.jsp"/>
  	<page key="footer" 	url="/sample/footer.jsp"/>
  </webpage>
<!-- Sample end. -->
<webpage>
  	<name>poll.admin</name>
	<layout>/template.jsp</layout>
  	<page key="menu" 	url="/menu.jsp"/>
  	<page key="body" 	url="/component/poll/admin/pollAdmin.jsp"/>
  </webpage>

</webpage-definitions>
   
위의 환경설정 정보는 화면의 레이아웃을 구성할 경우 FrameworkComposer 클래스를 이용하여 presentation을 구성하는 중요정보이다. webpage tag의 경우 중요한 element를 가지고 있다. - name : request시 들어오는 URI의 name으로서 페이지를 식별하는 가장 중요한 정보이다 - description : 이 웹 페이지에 대한 개요를 사용할 수 있다. 주석의 의미로 생각하자. - title : 웹브라우져에 나타나는 html title정보를 설정 - layout : 보여지는 웹 페이지는 여러 가지 template형태로 구성되어질 수 있으므로 서로 다른 레이아웃을 가진 페이지가 필요하다면 이 부분에 설정하도록 만든다. - page : 보통 jsp파일에 대한 url을 가지고 있으며, 중복되어 선언되어 질 수 있도록 한다. 기본적으로 5개의 레이아웃으로 웹페이지를 구성하게 되면 기본적으로 가질 수 있는 header, menu, body, sidebar, footer로 구성되는 페이지를 가질 수 있도록 설정한다. 왜 위와 같이 설정을 했는가? 보통의 웹페이지를 만들게 되면 대부분 5개의 영역을 주로 이루게 되는게 가장 빈번하게 사용이 되는 페이지는 상단의 header부분과 하단의 footer부분으로 나누어 지게 된다. 그러한 부분을 기존에는 개발시에 보통 <@ include file="xx.jsp" > 또는 <jsp:include page="xx.jsp"/> 등으로 처리했을 것이다. 하지만 잘 생각해보자. 매 페이지를 조립할 때마다 copy-paste를 또 할것인가? 보통 귀찮은 일이 아니다. 그렇다면 어찌 만들어 볼까나? controller처럼 페이지도 중앙집중적인 형태를 이용한 control비슷한 jsp를 만들어보면 어떨까? 흠.. 괜찮은 방법일 것 같다. 즉 template jsp를 이용하여 큰 골격을 잡은 후 각 영역에 배치되어질 페이지들을 무슨 짓을 해서든지 포함시키면 될까? 자. 화면을 아래처럼 한번 만들어보는 뼈대를 잡아보도록 하자. 전체의 그림은 총 5개의 영역으로 나뉘어짐을 볼 수 있는데 header, menu, body, sidebar, footer의 구성이다. 일반적인 웹을 만들게 되면 가장 dynamic하게 바뀌는 부분이 body부분이다. 즉 개발자에게 이렇게 주문하자. 당신은 body를 보이게 할 수 있도록 중점적으로 코딩을 하시오! 라고 말이다. 위의 xml파일에서 조금 유심히 보아야 할 부분이 있다. 만약 이런 경우가 생겼다고 가정을 하자. 어떤 페이지를 body부분을 조립하여 만들되 sidebar영역이 불필요한 경우는 어떻게 할것인가? 원래 뼈대는 5부분으로 나뉘고 PL이란 사람왈 "다른 짓 하면 다 죽여버릴꺼야, 무조건 5개영역으로 해"라고 하면 대체 어떤 놈을 없애버려야 할까? ^^ 고민된다. 내 생각엔 PL이란 놈을 없애야 할것 같다. 유연하지 못한 구조.. 위의 5개라고 한정지어놓은 것 자체부터가 발상이 안좋다. 놀새같으면 이렇게 물어보겠다. "아저씨~! 스킨몰라요? 스킨?" 바르는 스킨이 아니라 환경스킨말이다. 말그대로 겉모양 꾸며주는 껍데기를 지칭하는 것이다. <layout>/template.jsp</layout> 위의 layout정보가 바로 skin의 형태이다. Struts tiles에서 아주 잘 표현하고 있다. 거기에는 여기서 쓰는 기본적인 것 말고도 extends개념까지 확장시켜서 사용하므로 만든 사람이 머리가 좀 아팠으리라~ 각설하고 이렇게 layout을 구성하여 화면을 보여준다고 가정을 했을때 확장자 .view로 들어오는 request를 Composer는 어떻게 만들어낼까? 한번 코드를 보도록 하자.

package com.javapattern.framework.composer;

import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class FrameworkComposer extends HttpServlet
{
    protected ServletContext context;

    public void init()
    {
        this.context = getServletContext();

        try {
            URL webpageXml = context.getResource("/WEB-INF/webpage_config.xml");
            PageConfigController.init(webpageXml);
        } catch(Exception e) { e.printStackTrace(); }	
    }

    public void execute(HttpServletRequest req, HttpServletResponse res)
    throws IOException, ServletException
    {
        String reqName = getPageName(req);
        trace("com.javapattern.framework Composer by "+reqName);
        
        PageConfig page = PageConfigController.getPageConfig(reqName);
        trace(page.toString());
        
        req.setAttribute("webpage",page);
        if( page.getLayout() == null ) page.setLayout("/template.jsp");
        RequestDispatcher rd = req.getRequestDispatcher(page.getLayout());
        rd.forward(req,res);
    }

    private static String getPageName(HttpServletRequest request)
    {
        String uri = request.getRequestURI();
        String contextPath = request.getContextPath();

        int idx = uri.lastIndexOf(".view");
        
        return uri.substring(contextPath.length()+1,idx);
    }

    public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws IOException, ServletException
    {
        execute(req,res);
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws IOException, ServletException
    {
        execute(req,res);	
    }

    private void trace(String msg)
    {
        System.out.println("com.javapattern.framework Compser  ] "+msg);
    }
}
   
여기서 잠깐. 위코드의 System.out.println으로 딴지걸지 말기!!!! 위의 코드에서 눈여겨볼 부분은 당근 execute메소드인데 들어오는 request URI를 이용하여 engine처럼 확장자 .view를 없애고 web.xml에 설정된 환경로딩당시 메모리에 저장시켜놓은 위의 webpage_config.xml정보를 찾아내어 해당 요청이 가야할 jsp페이지를 webpage라는 이름으로 저장시킨후 해당 jsp로 forwarding시키는 일이 전부이다. 흠.. 간단하지 않은가? 위의 그림에서 본 template.jsp의 코드를 한번 보도록 하자.


<%@ taglib uri="/WEB-INF/tld/framework_composer.tld" prefix="composer" %>
<%@ page contentType="text/html;charset=euc-kr" %>
<%
	/************************************
	 *	※ responseponse the Presponseentation
	 *----------------------------------*/
	response.setHeader("Cache-Control","no-cache");
	response.setHeader("Pragma","no-cache");
%>
<HTML>
<HEAD>
<TITLE><composer:insert parameter="title"/></TITLE>
<LINK REL="stylesheet" HREF="/css/common.css" TYPE="text/css">
</HEAD>
<BODY leftmargin="0" topmargin="0">
<TABLE BORDER='0' CELLSPACING='0' CELLPADDING='0' WIDTH='100%' HEIGHT='768'>
<TR>
	<TD valign=top colspan=3><composer:insert parameter="header"/></TD>
</TR>
<TR>
	<TD valign=top width="20%"><composer:insert parameter="menu"/></TD>
	<TD valign=top><composer:insert parameter="body"/></TD>
	<TD valign=top width="10%"><composer:insert parameter="sidebar"/></TD>
</TR>
<TR>
	<TD valign=top colspan=3><composer:insert parameter="footer"/></TD>
</TR>
</TABLE>
</BODY>
</HTML>


  
위의 코드에서는 composer라는 태그를 만날 경우 parameter값을 입력하게 된다. 눈치빠른 당신이라면 paramter의 value값이 무엇인가를 지칭하고 있음을 알 수 있을 것이다. webpage_config.xml의 tag가 맞는가? 거기에 설정된 값을 확인해보자. 결국 위의 코드의 핵심은 모두 Tag library가 처리하고 있음을 주지하자. 1. FrameworkComposer에서 요청이 들어온 request URI(xx.view라는 확장자를 가진)를 해석하여 request.setAttribute()로 해당 layout jsp를 요청한다 2. layout jsp는 화면에 대한 기본골격을 가지며 composer:insert라는 tag library를 통하여 parameter를 처리한다. 3. tag library객체는 FrameworkComposer에서 세팅한 webpages의 정보를 request객체로부터 읽어들인후 설정값이 이용하여 pageContext에 출력하도록 한다. 4. 성공적으로 화면이 뿌려진다. 위의 내용이 view composer의 전체적인 흐름을 글로서 표현한 것이다. tag library의 핵심 코드를 보도록 하자.



    public int doStartTag()
    throws JspTagException 
    {
        try {
            JspWriter out = pageContext.getOut();
            out.flush();
        } catch(Exception e) {e.printStackTrace();}
        
        ServletRequest request = pageContext.getRequest();
        webpage = (PageConfig)request.getAttribute("webpage");
        if( (webpage!=null) && (parameter!=null) ) {
            try {
                if( (parameter!=null) && !parameter.equals("title")) {
                    page = webpage.getPage(parameter);
                }
            } catch(Exception e) {
                stop=true;
            }
        }
        return SKIP_BODY;
    }

    public int doEndTag()
    throws JspTagException
    {
        if(stop) return EVAL_PAGE;
        try {
            if( (parameter!=null) && parameter.equals("title") ) {
                JspWriter out = pageContext.getOut();
                out.print(webpage.getTitle());
            } else if(webpage!=null) {
                ServletRequest request = pageContext.getRequest();
                ServletResponse response = pageContext.getResponse();
                RequestDispatcher rd = request.getRequestDispatcher(page.getUrl());
                try {
                    rd.include(request,response);
                } catch(Exception th) { 
                    th.printStackTrace();
                    JspWriter out = pageContext.getOut();
                    out.println("<font size=+4 color=red>Error:</font><br>");
                    out.println("Definition for webpage \"");
                    out.println(" not found.<br>");
                    out.println("Check the webpages.xml file.[InsertPageTag]");
                    out.flush();
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
            throw new JspTagException(e.getClass().getName()+
                " ] "+e.getMessage());	
        }
        return EVAL_PAGE;
    }
  
doStartTag()에서는 이미 FrameworkComposer에서 request에 세팅해놓은 webpage를 얻어온다 webpage = (PageConfig)request.getAttribute("webpage"); PageConfig객체가 얻어지면 그 안에는 각각의 영역의 정보가 들어있는 value값들을 뽑아낼수 있게 되는데 <compser:insert parameter="footer"/>를 만나게 되면 PageConfig객체에서 footer에 해당하는 webpage정보를 찾아내려 한다. 만약 존재하게 된다면 해당하는 URL을 RequestDispatcher rd = request.getRequestDispatcher(page.getUrl()); 의 형태로 dispatch시킬 준비를 하여 include를 실행하도록 한다. 여기서 주의할 점은 이미 layout의 형태로 forward되었고 해당 parameter는 한 페이지의 한영역이기 때문에 다시 forward시키면 안된다는 것이다. 따라서 rd.include(request, response)를 통하여 해당 위치에 삽입되도록 하면 그만인 것이다. 조금 이해가 되는가? (이해가 안되면 TagLib, RequestDispatcher를 작동방식을 더 공부해야 할 것이다) 자, 이렇게 웹페이지를 조립할 수 있는 방법에 대하여 알아 보았고 다음 시간에는 실제 프레임웍소스를 전체 공개한 후 weblogic6.1(7.0은 권고하지 않는다)을 기준으로 설치 및 테스트와 개발 및 실행방법을 설명하도록 하겠다. 건투를 빈다.
 
1
References
 
Copyright ⓒ 2003 www.javapattern.info & www.jlook.com, an jLOOK co.,LTD