Spring framework이 유행하고 REST Style URL이 유행하면서 직면하는 문제가 하나 있다.
다음과 같은 url로 서비스를 한다고 가정해보자.
/article
/article/1
/article/add
음 갈끔한 URL이다. ㅋ
그러면 web.xml에 다음과 같이 url-pattern을 설정하게 된다.
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
REST Style의 url을 가져야 함으로 url-pattern은 /* 처럼 걸어야 한다.
.do와 같은 확장자를 가져서는 안된다. <-- 촌스러... ㅋㅋ
아 물론 viewResolver는 간단히 아래처럼 걸었다. 물론 default다. 흐
<!-- viewResolver -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
이렇게 하고 /article을 호출하면, 다음과 같은 에러를 만나게 된다.
경고: No mapping found for HTTP request with URI [/WEB-INF/jsp/article.jsp] in DispatcherServlet with name 'dispatcher'
흐... RequestMapping를 타고나서, jsp를 호출하려고 하였더니, /* url-pattern에 걸려들어 에러가 나는 것이다.
이를 어쩌란 말인가??? 흐
여기서 등장하는 것이 우리의 servlet spec이다.
SRV.11.2 Specification of Mappings 를 보면 아래와 같이 나와 있다.
In theWeb application deployment descriptor, the following syntax is used to define mappings:
1. A string beginning with a ‘/’ character and ending with a ‘/*’ suffix is used for path mapping.
2. A string beginning with a ‘*.’ prefix is used as an extension mapping.
3. A string containing only the ’/’ character indicates the "default" servlet of the application. In this case the servlet path is the request URI minus the context path and the path info is null.
4. All other strings are used for exact matches only.
1, 2, 4는 알겠는데, 3번은 좀 그렇다. ㅋ 우리의 주인공은 바로 이 3번이다. 3번을 보면 '/'만 딸랑 있을 경우에는 default servlet를 탄다고 한다. 아 그런데 그래서 뭐? default servlet이 먼데? 뭘 어쩌라는 거야?
ㅋ, 아! 톰켓을 참조하도록 하자. 톰켓의 conf/web.xml를 열어서 default servlet을 찾아보자. 그러면 주석에 이런 말이 나온다.
<!-- The default servlet for all web applications, that serves static -->
<!-- resources. It processes all requests that are not mapped to other -->
<!-- servlets with servlet mappings (defined either here or in your own -->
<!-- web.xml file. This servlet supports the following initialization -->
<!-- parameters (default values are in square brackets): -->
It processes all requests that are not mapped to other servlets with servlet mappings
이 런 말이 있다. 이 default servlet은 servlet mapping에 하나도 걸리지 않는 녀석들을 처리한다고 한다. 온갖 mapping을 통과해 버린 녀석들을 말하는 것이다. 그러면 뭐가 남을까? 바로 jpg, html 등 정적인 리소스만 남게 된다. 톰켓만으로도 이미지를 보여 줄 수 있는 것은 이 default servlet이 있기 때문이다.
흐하하하하하하~ 여기서 살짝 잔머리를 굴려 보자.. ㅋㅋ
일 반적으로 web과 was를 분리해서 쓰니까, jpg나 html 등 정적인 리소스는 web 서버가 맡아서 해야한다. 그리고 나머지들은 was로 넘어 와야 한다. 위의 상황의 문제는 *.jsp가 DispatcherServlet를 타면 안되는 상황이다. ㅋ, jsp는 was에서 처리를 해야 하는 것이지만 /* url-pattern에 의해서 DispatcherServlet를 타면 안되는 상황이다.
ㅋㅋ 원래 default servlet의 목적을 살짝쿵 바꿔 버리는 것이다.
그런데, SRV.11.2.1 Implicit Mappings에 나와 있는 것처럼, *.jsp는 이미 mapping되어 있다. 톰켓안에 벌써 되어 버려 있다. 물론 이 conf/web.xml 파일 안에 말이다. ㅋㅋ
그러므로 *.jsp는 톰켓에 걸려 있는 servlet을 타면 되고, 이를 통과한 /article은 default servlet을 타면 된다는 것이다. 이야... 쥑인다!!!!!
앗 그런데, conf/web.xml에 이미 default servlet이 설정되어 있다. 앗.. 어쩌지? ㅋㅋ SRV.11.2.1 Implicit Mappings(If a *.jsp mapping is defined by the Web application, its mapping takes precedence over the implicit mapping.) 에 나와 있는 것처럼 해당 web application에 재정의를 하게 되면, implicit Mapping 보다 우선적으로 적용된다고 한다. 흐흐.
이렇게 해서 *.jsp를 제외하고 Spring의 DispatcherServlet을 태울 수 있는 것이다. 아래처럼 url-pattern만 /* 에서 /로 바꿔 버리면 된다. 아래처럼.. 흐
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>