티스토리 뷰



Spring 2.5.x 에서 멀티 리졸버 (Multi Resolver) 를 사용하여 Tiles 2.0.x 을 적용시에 여러가지 애로사항을 확인하여야 합니다.
여러가지 애로사항중에 아래 내용은 제가 처했던 상황에서의 애로사항을 기준으로 작성하였습니다.
말이 너무 길어지는 관계로 혼자 독백 형식으로 말을 좀 짧게 하겠으니 미리 양해부탁드립니다. (_ _)

-- 기존 개발환경은 이렇다.
사용언어 : Java
프레임워크 및 버전 : Spring 2.5.x
MVC 동작 환경 : SimpleUrlHandlerMapping 와 PropertiesMethodNameResolver 를 사용하여 MultiActionController 를 상속받아 Controller 처리하며 ​ViewResolver 는 사용하지 않고 있다.


-- 추가
Tiles 2.0.x 를 적용하고자 한다.


-- 상황 및 애로사항은 이러했다.
1. A,B,C 3개의 서비스가 1개의 프로젝트 안에 구성되어 있다.
2. 그로므로 하나의 설정 파일이 3개의 서비스 모두에 영향을 끼칠 수가 있다.
3. 3개의 프로젝트 모두 ViewResolver 를 사용하지 않고 view 처리 시 return new ModelAndView("/path/page.jsp"); 로 사용한다.
4. 현재 C 서비스의 일부분에만 Tiles 2.0.x 를 적용하고자 한다.
5. Tiles 의 사용을 위해서는 ViewResolver 를 어쩔 수 없이 사용해야 하는 상황이다.
6. UrlBasedViewResolver, InternalResourceViewResolver 2가지 뷰리졸버를 사용하여야 했다.
- UrlBasedViewResolver 는 Tiles 2.0.x 를 사용하기 위함이고 InternalResourceViewResolver 는 Tiles 2.0.x 를 적용하지 않을 나머지 서비스들을 위해서였다.

위의 6가지 상황으로 정리가 되었고 UrlBasedViewResolver, InternalResourceViewResolver 이렇게 2개의 뷰리졸버를 적용을 시켰다.

그리고는 아래와 같은 문제들이 터졌다.

1. 뷰객체들이 원하지 않는 뷰리졸버를 타는 것이었다.
-> Tiles 가 적용되어야 하는 객체가 InternalResourceViewResolver 를 다거나 아니면 반대의 상황으로 말이다.
-> 이것은 단순히 뷰리졸버 설정시 우선순위 (order) 를 주지 않은 것이라고 생각하여 order 를 친절히 주었다.

2. InternalResourceViewResolver 의 order 는 1, UrlBasedViewResolver 의 order 는 2
-> Tiles 가 적용되지 않는 서비스를 수행하니 잘 작동되었다.
-> Tiles 를 적용한 서비스를 수행하니 1 순위인 InternalResourceViewResolver 에서 걸리지 않더니 UrlBasedViewResolver 에도 걸리지 않고 404 를 뱉는다.

3. order 순위를 바꾸어 보았다. UrlBasedViewResolver 를 order 1로, InternalResourceViewResolver 를 order 2 로.
-> 반대의 상황이 발생되었다.
-> Tiles 가 적용되는 서비스는 정상동작한다.
-> Tiles 가 적용되지 않은 서비스는 위와 마찬가지로 후순위 뷰리졸버를 타지않고 바로 404 를 뱉는다.

처음에는 뭐가 문제인지 이유도 모르고 계속 Spring 2.5 에서 Tiles 2.0.x 버전을 적용하는 문서들만 계속 검색했다. 덕분에 Spring 2.5 에서 Tiles 2.0.7 이상, 그러니까 Tiles 2.1 버전대 이상의 적용은 동작은 하나 정상적으로 동작하지 않는 다는 정보는 얻었지만 내가 위에 직면한 문제를 해결 할 수는 없었다. 그러다가 뷰리졸버에 대해서 검색 한 결과 다음과 같은 내용을 발견하였다.

​UrlBasedViewResolver 는 null 을 리턴하지 않고 404 Exception 처리를 하므로 후순위 뷰리졸버를 타지 않는다.

이거였다. 내가 찾던 내용이 이거였다. 근데 이상했다. 아니 그럼 ​InternalResourceViewResolver 를 order 1 로 하고 UrlBasedViewResolver 를 order 2 로 하였다면
Tiles 가 적용되어야 하는 경우라면 당연 후순위인 UrlBasedViewResolver 를 타야 하는 것이 아닌가?


이건 모르겠다. 찾아보니

​InternalResourceViewResolver 는 뷰이름에 매핑되는 뷰객체를 리턴. null 을 리턴하지 않음.

이라고 되어있는데 뷰이름에 매핑되는 뷰객체를 리턴해야 하는데 없다면 null 을 리턴하는게 맞지 않나? 싶지만 정확한 이유는 잘 모르겠다.

다만 InternalResourceViewResolver 가 UrlBasedViewResolver 를 상속받은 걸로봐서 내부적으로 비슷한 처리가 되지 않나 싶은 추측만 들뿐이다.

실제로 null 을 리턴하는 다른 ViewResolver 들을 살펴보면 return null; 처리가 되어있지만 위의 2개 UrlBasedViewResolver 는 그렇지 않았다.

​- XmlViewResolver 의 loadView 메소드
protected View loadView(String viewName, Locale locale) throws BeansException {
BeanFactory factory = initFactory();
try {
return (View)factory.getBean(viewName, View.class);
}
catch (NoSuchBeanDefinitionException ex) {}

return null;
}

​- UrlBasedViewResolver 의 loadView 메소드
View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
return (View)getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
}

하지만 역시 궁금한건 결국 DispatcherServlet 의 render 메소드 부분을 보면 view 가 null 이면 ServletException 을 던지는데... 이 이상은 잘 모르겠다.

아무튼 사용해야 하는 ​2 가지 뷰리졸버 모두 null 을 리턴하지 않기에 어떻게 해서든 처리를 해야만 했다.

그렇게 또 검색을 하여 결국 ​멀티 뷰리졸버 사용 시 임의로 뷰리졸버 클래스를 생성하여 null 로 리턴을 해주는 코드를 찾았다.

사실 이 생각도 하긴 했었는데 대체 뷰를 어떻게 캣치해서 리턴해줘야 하는 지 감도 오지 않아서 포기했었는데 역시 대단한 사람들은 참 많다.

그래서 결국 위의 모든 상황의 정리 및 해결은 아래와 같이 하였다.


1. Tiles jar 다운로드
http://archive.apache.org/dist/tiles/v2.0.5/ 에서 tiles-2.0.5-bin.zip 다운로드

2. jar lib 에 추가
tiles-api-2.0.5.jar
tiles-core-2.0.5.jar
tiles-jsp-2.0.5.jar

3개 파일 프로젝트의 lib 폴더로 옮기기

--> 압축해제 시 해당 폴더 lib 아래에 들어있는 3개 jar 파일도 필요하므로 없다면 아래 jar 들도 추가해준다.
--> Spring Frameworkd 를 사용하고 있다면 어떻게 해서든 있을 것이다.
commons-beanutils-1.7.0.jar
commons-digester-1.8.jar
commons-logging-api-1.1.jar

3. CustomViewResolver 생성. 이 클래스가 임의로 null 을 리턴해주는 클래스.
package my.package;

import java.util.Locale;

import org.springframework.core.Ordered;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;

public class CustomViewResolver extends UrlBasedViewResolver implements Ordered {
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
View viewObj = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
// JSP 를 뷰객체로 사용 시 InternalResourceViewResolver 구현체를 상속받는 JSTLView ViewClass 를 사용하여 처리한다.
if (viewObj instanceof JstlView) {
JstlView jv = (JstlView) viewObj;
/*
* viewName 이 .jsp 로 끝나지 않을 시 다음 순위로 지정된 ViewResolver 가 처리할 수 있도록 null 리턴.
*/
if (jv.getBeanName().indexOf(".jsp") == -1) {
return null;
}
}
return viewObj;
}
}

4. servlet 설정파일 하단에 2개의 ViewResolver 설정
- schema tag 사용이 가능하도록 xmlns 설정
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- ViewResolver 설정
<!-- 1 순위로 임의로 생성한 ViewResolver 클래스가 선처리 후 null 리턴 시 후 순위인 tilesViewResolver 가 처리하도록 -->
<bean class="my.package.CustomViewResolver" p:order="1" p:viewClass="org.springframework.web.servlet.view.JstlView"/>
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/config/tiles/tiles-defs.xml</value>
</list>
</property>
</bean>

<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver" p:viewClass="org.springframework.web.servlet.view.tiles2.TilesView" p:order="2" />

5. 4번의 tiles definitions 에서 설정한 xml 파일 위치에 이제 각각 사용할 tiles definition 파일을 생성한다.

6. 임의로 생성한 호텔쪽 tiles-defs.xml 은 이러함.

- tiles-defs.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">

<tiles-definitions>
<definition name="base.layout" template="/view/jsp/tiles/layout.jsp">
<put-attribute name="title">메인 레이아웃</put-attribute>
<put-attribute name="header" value="/view/jsp/tiles/header.jsp" />
<put-attribute name="header_script" value="/view/jsp/tiles/script.jsp"/>
<put-attribute name="body">CONTENTS</put-attribute>
<put-attribute name="footer" value="/view/jsp/tiles/footer.jsp" />
</definition>
<definition name="product.list" extends="base.layout">
<put-attribute name="body" value="/view/jsp/product/list.jsp" />
</definition>
<definition name="product.detail" extends="base.layout">
<put-attribute name="body" value="/view/jsp/product/detail.jsp" />
</definition>
</tiles-definitions>

7. layout 에는 attribute 로 지정된 각 tiles 파일들을 insertAttribute 해준다.
...
<head>
<tiles:insertAttribute name="header" />
</head>
<body>
<tiles:insertAttribute name="body" />
<tiles:insertAttribute name="footer" />
</body>

...

8. Controller 에서는 Tiles 를 사용하여 처리 할 viewName 은 definition name 으로 지정된 이름을 사용한다.
- 기존 : return new ModelAndView("/view/jsp/product/list.jsp");
- 변경 : return new ModelAndView("product.list");


위 처럼 해결하였다. 그리고 덧붙여 뷰리졸버 관련 찾으면 많이 나오는 내용들에 대해서도 잠시 붙여본다. 상세한 적용 코드까지 기술하려면 너무 방대하므로 뷰리졸버 관련 정의 내용만 덧붙여본다.


​- BeannameViewResolver
논리적 뷰 이름과 동일한 ID를 갖는 <bean> 으로 등록된 View 의 구현체를 찾는다

- ContentnegotiationViewResolver
요청되는 콘텐츠 형식에 기반을 두어 선택한 하나 이상의 다른 뷰 리졸버에 위임한다.
- FreeMarkerViewResolver
FreeMarker 기반의 템플릿을 찾는다. 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성한다.
- InternalResourceViewResolver
* 일반적으로 많이 사용되는 뷰리졸버
* 웹 어플리케이션의 WAR 파일 내에 포함된 뷰 템플릿을 찾는다
뷰 템플릿의 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성한다.
예)
controller
return new ModelAndView("/goods/product");
xml
<property name="prefix" value="/WEB-INF/package" /> - 접두어
<property name="suffix" value=".jsp" /> - 접미어
--> /WEB-INF/package/goods/product.jsp 를 찾게된다.
--> 접두어(prefix) + 뷰객체명 + 접미어(suffix)

​- JasperReportsViewResolver
Jasper Reports 리포트 파일로 정의된 뷰를 찾는다. 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성한다.

- TilesViewResolver
Tiles 템플릿으로 정의된 뷰를 찾는다. 템플릿 이름은 논리적 뷰 이름에 접두어와 접미어를 붙여 구성한다.

- UrlBasedViewResolver
ViewResolver 의 구현체로 특별한 매핑 정보 없이 view 이름을 URL 로 사용
View 이름과 실제 view 자원과의 이름이 같을 때 사용할 수 있다.
예)
controller
return new ModelAndView("product");
tiles-definition.xml
<definition name="product" extends="base">
....

​- ResourceBundleViewResolver
ViewResolver 의 구현체로 리소스 파일을 사용한다.
view.properties 를 기본 리소스 파일로 사용한다.

- VelocityLayoutViewResolver
VelocityViewResolver 의 서브클래스로 스프링의 VelocityLayoutView 를 통해 페이지 구성을 지원한다.

- VelocityViewResolver
Velocity 기반의 뷰를 찾는다. 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성한다.

- XmlViewResolver
ViewResolver 의 구현체로 XML 파일을 사용한다.
/WEB-INF/views.xml 을 기본 설정파일로 사용한다.

- XsltViewResolver
XSLT 기반의 뷰를 찾는다.
XSLT 스타일 시트의 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함