프로그래밍 이야기

[Spring Framework] RESTful Web Service와 JSP,URL Mapping 문제해결

박디 2013. 9. 15. 23:03

보통 우리가 인터넷을 사용하면서 주소창에 흔히 볼수있는 주소 표현으로 다음과 같이..

 

http://xens.tistory.com/content/list.jsp?user=park&page=1 과 같은형태를 많이 보게 됩니다. 이를 RESTful 하게 표현하면

 

http://xens.tistory.com/content/list/user/park/page/1 과 같이 표현할 수 있게됩니다. 개념상으로는 단순히 이렇게 주소

 

표기를 위해서 뜬금없이 나타난 것은 아니고 'URI로 자원대상을 명시하고 Method로 자원에대한 행위를 정의한다'

 

를 베이스로 가지고 있는 아키텍쳐 이며 기본적으로 CRUD(Create, Read, Update, Delete)와 매치될수 있는 형태의 서비스

 

에서 쓰이고 있습니다. 저도 여기저기서 글만 봤지 최근에서야 공부겸 예제를 작성해 봤는데 글 제목에서 썼듯 URL Mapping

 

문제로 굉장한 삽질을 했습니다. 분명 시간이 지나면 잊어버릴게 뻔하니 간단히 정리를 해봅니다.

 

예제를 만들때는 Spring Framework 3.0.5와 Tomcat 7, jQuery를 사용했습니다. 이클립스에서 프로젝트 구조는 다음과

 

같다고 보시면 됩니다.

 

 

 

 

※JSP 호출 오류 해결방법

  REST형식으로 호출하면서 처음부터 진입을 가로막는것이 JSP 입니다. 처음 시작페이지가 content.jsp라고 하겠습니다.

 

 요즘에는 보통 서비스(URL)호출시 AJAX를 사용하여 콜백으로 온 데이터를 통해 결과를 출력하거나 하는 개발 방식을 많이

 

 사용하고 있는데 예를 들어서 content.jsp 에서 아래와 같이 호출한다고 가정합니다(처음 실행페이지가 content.jsp).

 

 $.ajax({

   ...

   url : "/person/parkdi"

   ...

 });

 

 그리고 스프링 컨트롤러는 다음과 같이 작성 했다고 가정합니다.

 

 @RequestMapping(value="/person/{id}", method=RequestMethod.GET)
 public ModelAndView getDataRestfulUrlPattern(@PathVariable("id") String id){...}

 

 이렇게 작성하고 바로 호출을 해보면 호출하는 content.jsp 자체가 에러가 납니다. 일단 이상현상을 보게된 후

 

 제일 먼저 봐야할것은 WAS의 web.xml 파일입니다. 우리는 Spring Framework를 사용했기 때문에 DispatcherServlet를

 

 다음과 비슷하게 매핑시켜줬을 겁니다.

 

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>ContextConfigLocation</param-name>
      <param-value>/WEB-INF/webmvc_config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

 

실행할때의 톰캣의 Console로그를 보시면 content.jsp파일도 DispatcherServlet에서 처리하려고 하는것을 볼수 있습니다.

 

그러나 위의 web.xml 설정과 같이 *.do에 대한 패턴을 매핑하도록 했기 때문에 content.jsp에 대한 매핑 URL을 찾을수가

 

없어서 에러가 발생하게 됩니다. 우선 문제에 대한 해결책은.. web.xml을 수정해야 합니다.

 

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>ContextConfigLocation</param-name>
      <param-value>/WEB-INF/webmvc_config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <servlet-mapping> 
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.jpg</url-pattern>
    <url-pattern>*.gif</url-pattern>
    <url-pattern>*.png</url-pattern>
    <url-pattern>*.ico</url-pattern>
    <url-pattern>*.swf</url-pattern>
 </servlet-mapping>       

 

우선 문제에 대한 해결책은 위의 붉은 글씨처럼 Dispatcher서블릿의 url-pattern을 '/' 로 변경하고 deault servlet의 url-pattern

 

을 추가해줬습니다. 호출하는 페이지인 content.jsp에서는..

 

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

 $.ajax({

   ...

   url : "<c:url value='/person/parkdi' />"    //컨텍스트Path(spring_webmvc_restful_ex1/person/parkdi)

   ...

 });

 

컨트롤러에서는 위에 작성된 예와 같이 그대로 두고 호출하면 됩니다. 일단 문제는 해결됐지만 이유는 알아야 되겠죠.

 

※오류 원인

 

JSP는 WAS(톰캣 등)의 서블릿 컨테이너에서 처리를 하게 됩니다. 톰캣을 예로 보면 이클립스 프로젝트에서의 web.xml이 아닌

 

%CATALINA_HOME%(톰캣 설치 디렉토리)의 conf/web.xml을 열어보면

 

 <sevlet-mapping>

  <servlet-name>jsp</servlet-name>

  <url-pattern>*.jsp</url-pattern>

 </sevlet-mapping>

 <servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
       <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
     </init-param>
    <load-on-startup>3</load-on-startup>
  </servlet>

 

위와같이 org.apache.jasper.servlet.JspServlet 에서 jsp를 처리한다는 것을 볼 수 있습니다. JSP 인식오류 해결을 위해

 

위에서 추가했던 <servlet-name>default</servlet> 의 경우 서블릿 컨테이너의 DefaultServlet을 의미하는데 

 

url-pattern을 '/'로 정의한 경우에 디폴트 서블릿을 의미하며 이 디폴트 서블릿은 서블릿 매핑 URL에 걸리지 않는 요청을

 

처리한다고(톰캣 설정파일의 주석) 되어있습니다. 그래서 이제 DispatcherServlet의 url-pattern을 '/'로 설정함으로서

 

JSPServlet의 JSP처리와 DispatcherServlet의 처리를 분리 시켰습니다. 이렇게 분리 할경우 JSP와 스프링 컨트롤러 매핑은

 

잘 처리되지만 css, js등의 정적 컨텐츠들이 나오지 않는것을 볼수 있습니다. 이는 DispatcherServlet의 url-pattern을 '/'로

 

설정하면서 톰캣의 server.xml에 정의되있는 DefaultServlet의 url-pattern을 무시하게 되기 때문에 DefaultServlet이 작동하지

 

않게 되기 때문입니다. 따라서 호출시 정적 컨텐츠들도 DispatcherServlet이 처리하게 되니 표현되지 않게 됩니다.

 

<servlet-mapping> 
  <servlet-name>default</servlet-name>
  <url-pattern>*.js</url-pattern>
  <url-pattern>*.css</url-pattern>
  <url-pattern>*.jpg</url-pattern>
  <url-pattern>*.gif</url-pattern>
  <url-pattern>*.png</url-pattern>
  <url-pattern>*.ico</url-pattern>
  <url-pattern>*.swf</url-pattern>

</servlet-mapping> 

그래서 DefaultServlet이 처리할 url-pattern을 재정의 해준것이 윗 부분입니다. 사실 위와 같이 정의해줄경우 정적 컨텐츠에

 

대한 url-pattern을 전부다 정의해줘야 줘야하므로 지저분하기도 합니다. 그래서 스프링 3.0에서는 이 부분을

 

<mvc:default-servlet-handler /> 를 선언해줘서 해결을 한다고 합니다. 여기에는 주요 WAS의 DefaultServlet 명에

 

대한 내용을 처리하도록 되있다고 하네요. 저도 위의 DefaultServlet의 매핑을 재정의 해주기 전에 발견해서 적용해봤는데

 

xmlns:mvc="http://www.springframework.org/schema/mvc ,

xsi:schemaLocation=http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd

 

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" p:alwaysUseFullPath="true" />

 

위 내용이 분명히 선언되있는데도 아래와 같이

 

org.xml.sax.SAXParseException: cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'mvc:default-servlet-handler'.

 

찾을수 없다고 계속 떠서 포기했습니다. 찾아봐도 위 내용의 XML확인이나 <mvc:annotation-driven /> 추가 여부만

 

확인하라는 답변밖에 없더군요.. 혹시 비슷한 경험 있으신 분은 답변 주시면 감사하겠습니다. 어쨋든 그냥 REST의 개념만

 

정리하고 CRUD에 해당하는 스프링의 RequestMethod(POST,GET,PUT,DELETE)를 좀 더 알아보려고 했던 것인데

 

하루종일 삽질만 한 것 같습니다;;