캠핑과 개발


tiles2는 Spring MVC와 연동시 그 뷰 네임을 definition 네임으로 하기 때문에 각각의 뷰마다 각각의 definition이 있어야 하므로 번거롭기 짝이없다. tiles2기반으로 프로젝트를 하는 사람들이 이를 간과할리 없다. 찾아보니 Dynamic-tiles2라는 간단한 라이브러리가 그것을 도와준다.

Dynamic-tiles2는 David Winterfeldt라는 사람이 http://www.springbyexample.org/ 운영하면서 만든 라이브러리이다. Spring의 2개의 클래스를 확장하고 1개의 클래스로 처리하는 비교적 초간단 라이브러리이다.(소스코드를 보면 어렵지 않게 이해할수 있다.) 사이트는 이외에도 간단한 예제들이 많이 있어 Spring을 처음 접하는 사람에게 유용하리라 생각된다.

기본에 이어서 설명한다ViewResolver를 아래와 같이 변경한다.

[기존]

 <bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView" />
 </bean>

[신규]

<bean id="tilesViewResolver" class="org.springbyexample.web.servlet.view.tiles2.TilesUrlBasedViewResolver">
        <property name="viewClass" value="org.springbyexample.web.servlet.view.tiles2.DynamicTilesView" />
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
        <property name="tilesDefinitionName" value="default" />
     <property name="tilesBodyAttributeName" value="body" />
     <property name="tilesDefinitionDelimiter" value="." />
    </bean>

tilesDefinitionName 은 기본 tiles의 Definition 이름이고, tilesBodyAttributeName 은 해당 바뀔 속성의 이름이다. tilesDefinitionDelimiter 상속시 구분자이다. 본 라이브러리를 보면 알겠지만 각각의 Spring UrlBasedViewResolver 와 TilesView를 확장하여 간단히 확장한 소스이다.(이게 spring의 장점중에 하나이다.)

어쨌든 이와 같이 ViewResolver를 설정하고 tiles2 설정을 아래와 같이 변경한다..

[기존]

 <definition name=".default" template="/WEB-INF/templates/main.jsp">
  <put-attribute name="title" value="Simple Tiles 2 Example" type="string" />
  <put-attribute name="header" value="/WEB-INF/templates/header.jsp" />
  <put-attribute name="footer" value="/WEB-INF/templates/footer.jsp" />
  <put-attribute name="menu" value="/WEB-INF/templates/menu.jsp" />
  <put-attribute name="body" value="/WEB-INF/templates/blank.jsp" />
 </definition>
 <definition name="index" extends="default">
  <put-attribute name="body" value="/WEB-INF/jsp/index.jsp" />
 </definition>
 <definition name="member/list" extends="default">
  <put-attribute name="body" value="/WEB-INF/jsp/member/list.jsp" />
 </definition>

 [신규]

 <definition name=".default" template="/WEB-INF/templates/main.jsp">
  <put-attribute name="title" value="Simple Tiles 2 Example" type="string" />
  <put-attribute name="header" value="/WEB-INF/templates/header.jsp" />
  <put-attribute name="footer" value="/WEB-INF/templates/footer.jsp" />
  <put-attribute name="menu" value="/WEB-INF/templates/menu.jsp" />
  <put-attribute name="body" value="/WEB-INF/templates/blank.jsp" />
 </definition>

IndexController는 변함이 없다.

 @Autowired
 private SampleService sampleService;
 
 @Autowired
 private TilesUrlBasedViewResolver tilesViewResolver;
 
 
 /**
  * Tiles를 이용한 일반적인 요청입니다.
  */
 @RequestMapping("/sample/index.do")
 public ModelAndView index1(HttpServletRequest request, HttpServletResponse response){
  
  //ModelMap modelMap = new ModelMap();
  //modelMap.addAttribute("documentList", sampleService.index());
  //service call
  sampleService.index();
  request.setAttribute("name", "name");
  return new ModelAndView("sample/index");
 }
 
 /**
  * Tiles의 Default layout가 아닌 body layout를 요청한 예입니다.
  */
 @RequestMapping("/index.do")
 public void index2(){
  System.out.println("/index.do");
  //다른 페이지로 이동하려고 하면 이렇게 합니다.
  tilesViewResolver.setTilesDefinitionName("body");
 }
 
 /**
  * Tiles를 거치지 않고 redirect로 페이지를 이동합니다.
  * @param request
  * @param response
  * @return
  */
 @RequestMapping("/sample/redirect.do")
 public String redirect(HttpServletRequest request, HttpServletResponse response){
  tilesViewResolver.setRedirectContextRelative(true);
  return "redirect:/jsp/sample/redirect.jsp";
 }
 /**
  * Tiles를 거치지 않고 forward로 페이지를 이동합니다.
  * @param request
  * @param response
  * @return
  */
 @RequestMapping("/sample/forward.do")
 public String forward(HttpServletRequest request, HttpServletResponse response){  
  tilesViewResolver.setRedirectContextRelative(true);
  request.setAttribute("name", "name!!!");
  return "sample/forward";
 } 
 
 /**
  * Tiles를 거치지 않고 void 처리합니다.
  * @param request
  * @param response
  * @return
  */
 @RequestMapping("/sample/voidcall.do")
 public void voidcall(HttpServletRequest request, HttpServletResponse response) throws Exception{  
  tilesViewResolver.setRedirectContextRelative(true);
  response.getWriter().print("voidcall!!!");
 }

요약하면, 기존의 tiles2 설정에서 viewresolver를 Dynamic-tiles2 라이브러리 함수로 바꾸고 tiles 설정파일을 기존의 각각의 definition 네임를 제거하면 된다.


출처 : http://yunsunghan.tistory.com/259


Tiles는 기존에 UI Layout에서 인기있었던 라이브러리였다. 비록 각각의 뷰 파일을 만들어야 하는 번거러움이 있음에도 불구하고, include를 제거할수 있는 좋은 대안으로 인기가 있었다. 그후 Sitemesh 라는게 유행했는데 뷰마다 각각의 파일을 만들필요가 없어서 인기가 있었다. 최근까지도 나는 이걸 이용했었다. 다시또 유행하는게 Tiles 2 인데 이놈은 Spring Reference에 나올 정도로 인기가 급상승 중이다. 이런 Layout 라이브러리(또는 프레임웍)는 왼만해서는 새로 변경하지 않는데, Sitemesh의 불편한점(누구나 처음 쓸때 겪은 Sitemesh의 곤란함)을 털어보고자 Tiles2에 도전해 본다.

일반적인 Controller 를 이용한 매핑 예제를 알아본다.(Tiles는 Annotaion과 무관함으로 annotation 기반이라고 해서 특별히 설정할것은 없다.)

Spring MVC 설정에서 ViewResolver는 보통 아래와 같다.

 <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />  
  <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
 </bean>

이것은 Controller를 Annotation기반으로 할때도 변함없다. 그러나 Tiles2는 UrlBasedViewResolver를 사용하고 뷰클래스도 TilesView를 사용한다.

 <bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView" />
 </bean>

물론 Sitemesh처럼 설정파일도 있다.
Sitemesh와 설정의 차이점은 설정이 Spring Bean으로 Integration되었다는것이다. TilesConfigurer를 아래와 같이 설정한다.

 <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
  <property name="definitions">
   <list>
    <value>/WEB-INF/tiles-defs/templates.xml</value>
   </list>
  </property>
 </bean>

이것이 Spring과 연동하는 끝이다. 두개의 Bean 설정만으로 연동이 끝이다.

나머지 Tiles2 사용법은 Sitemesh를 사용해봤다면 크게 다를만한 것이 없다. 간단히 개념만 정리한다면 Controller View Name만 Tiles2 definition과 일치하면 해당 layout으로 랜더링된다. 이해하기 쉽게 순서적으로 나열해 보면 먼저 Controller를 다음과 같이 만든다.

 @RequestMapping("/index.do")
 public void index(ModelMap model){
  model.addAttribute("msg","Donkey!!");
 }

이것은 index.do를 요청하면 index() 메서드를 호출하고 msg변수를 model에 담아 index viewName과 매핑한다. 만약 Tiles2 설정파일(templates.xml)이 아래와 같다면,

 <definition name="default" template="/WEB-INF/templates/layout1.jsp">
  <put-attribute name="header" value="/WEB-INF/templates/header.jsp" />
  <put-attribute name="footer" value="/WEB-INF/templates/footer.jsp" />
  <put-attribute name="body" value="/WEB-INF/templates/body.jsp" />
 </definition>
 <definition name="index" extends="default">
  <put-attribute name="body" value="/WEB-INF/jsp/index.jsp" />
 </definition>


definition name이 index인것과 매핑되여 해당 /WEB-INF/jsp/index.jsp 파일과 랜더링 된다. 물론 default definition을 상속했으니 해당 layout의 특성을 가져오고 body속성(attribute)만 index.jsp로 바뀐다.

이렇게 일일히 모든 view를 definition 테그로 만든다는것은 별로 좋은생각이 아닌것 같다. 이런 설정을 줄여줄수 있는 방법이 있다. 거기에 대해서 다음글에 포스트하기로 한다.

출처 : http://yunsunghan.tistory.com/258


Spring 2.5에서는 annotation이 강화 되었다. 특히 SpringMVC는 상당히 많은 변화가 있다.
(POJO 기반의 Controller작성이 가능한)  아래 예제처럼 말이다.

Controller

@Controller
public class ChargeTableController  {

 @Autowired
 public ChargeFacade chargeFacade;

 @RequestMapping("/charge/chargeTableList.do")
 public String chargeTableList(ModelMap model, HttpServletRequest request){
  int currentPage = ServletRequestUtils.getIntParameter(request, "currentPage", 0);
  int countPerPage = ServletRequestUtils.getIntParameter(request, "countPerPage", 10);
 
  ChargeTableResult result = chargeFacade.chargeTableList(currentPage, countPerPage);
  model.addAttribute("result", result );
return "/charge/chargeTableList";
 .......

그래서 본격적으로 프로젝트 기본 템플릿을 만들기로 했다. 그러나 템플릿 예제를 만들던중 문제가 생겼다.  문제는 이렇다.

보통 어떤 행위가 없는(텅빈) Controller를 만들어 SimpleUrlHandlerMapping에 매핑해야 할때가 있다. 요청하는 행위가 단순히  Spring DispatcherServlet통해서 html이나 jsp를 매핑만 시켜주는 행위만 필요하다면 편리하게 처리하는 방법이 있다. 바로 UrlFilenameViewController 이다. 보통 아래와 같이 사용한다.

    <bean id="urlFilenameViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />

 <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="mappings">
       <props>
       <prop key="*.html">urlFilenameViewController</prop>     
       </props>
   </property>
 </bean>


이렇게 설정만 하면 수많은 html,jsp파일을 Controller없이 매핑 시킬수 있다.

그런데 Spring2.5의 Annotation기반으로 SpringMVC를 사용하면 위와 같은 설정이 안먹힌다.
포럼에도 이것 때문에 삽질한(나를포함) 사람들이 좀 있는것 같다.
왜그럴까? 하고 고민하고 학습 해야하는데 못했다.-_-;
그러나 해결방법은 간단하다. DefaultAnnotationHandlerMapping을 추가해 주면된다.

    <bean id="urlFilenameViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
 <bean id="annotationMapper" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
   <property name="order" value="2" />
</bean>
 <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="mappings">
       <props>
       <prop key="*.html">urlFilenameViewController</prop>     
       </props>
   </property>
  <property name="order" value="1" />
 </bean>

만약 Mapping 순서를 정하고 싶을때 order 속성으로 우선순위를 정할 수 있다.

출처 : http://yunsunghan.tistory.com/135