Spring을 이용하면 RESTful 서비스를 쉽게 구현할수가 있습니다. 예전에 Apache CXF를 Spring과 연동하여 서비스를 구현한적이 있는데 RESTful 형식이 아무래도 SOAP보다는 쉽게 적용할 수 있습니다. 그렇다고 CXF를 이용한 XML 웹 서비스가 많이 복잡하지는 않습니다만...
확장자로 매핑되는게 아니기때문에.. web.xml에 다음과 같이 설정을 합니다.
web.xml
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Spring 설정파일은 다음과 같이 정의합니다.
spring-servlet.xml
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
JSON으로 데이터를 주고 받을 생각임으로 원래는 AnnotationMethodHandlerAdapter 빈을 등록하고 messageConverters 속성에 MappingJacksonHttpMessageConverter를 추가하여야 하나 <mvc:annotation-driven/>이 아래에 있는 bean설정을 자동으로 설정해줍니다. messageConverter를 설정해야만 @RequestBody, @ResponseBody 같은 annotation을 통해 HTTP 요청, 응답 메세지 본문을 통채로 다룰 수 있습니다. @RequestBody는 "Content-Type" header값에 따라 messageConverter가 선택되고, @ResponseBody "Accept" header값에 따라 선택됩니다.
당연히 MappingJacksonHttpMessageConverter를 사용하기 위해서는 jackson 라이브러리를 추가해줘야 합니다. 전 jackson-all-1.9.4.jar을 사용했습니다.
토비의 스프링3, p.1274를 보면 <mvc:annotation-driven/>은 빈의 설정을 변경할 수 없으니, 설정 변경이 필요할 때는 AnnotationMethodHandlerAdapter, DefaultAnnotationHandlerMapping 직접 빈으로 등록하는 내용이 있습니다. <mvc:annotation-driven/>에 의해 자동으로 등록되는 빈을 다시 <bean>태그로 등록하지 않도록 하라는 내용도 있습니다. 그래서 위의 설정은 아래로 대체될수 있습니다.
<mvc:default-servlet-handler/>는 RESTful 을 구현했을때(특정 확장자로 매핑되는게 아닌 경우) 스태틱 리소스(html, js, css 등)에 대한 요청 처리를 서블릿 컨테이너의 디폴트 서블릿으로 처리하게 만들어 줍니다. 이설정은 외국 WAS에만 자등으로 등록됨으로 국내 JEUS같은 WAS를 사용하신다면 <mvc:default-servlet-handler default-servlet-name="WorkerServlet"/>와 같이 JEUS의 디폴트 서블릿 이름을 설정해주어야 합니다. JSON외의 다양한 포맷을 사용하실려면 콘테트 교섭(?)이 가능한 ContentNegotiatingViewResolver를 사용하시면 됩니다.
spring-servlet.xml
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"
p:order="0" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
p:messageConverters-ref="messageConverters" />
<util:list id="messageConverters">
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"
p:supportedMediaTypes="application/json" />
</util:list>
이제 설정부분에 대한 모든 정의는 끝났고, 실제 Controller, Service, Dao클래스를 직접구현하시면 됩니다. 아래는 Controller 소스입니다.
MemoController.java
package com.tistory.aircook.api.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tistory.aircook.api.service.MemoService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* MemoController
* @author francis Lee
* @since 2012. 07. 10.
*/
@Controller
public class MemoController {
private final Log logger = LogFactory.getLog(getClass());
@Autowired
private MemoService memoService;
@RequestMapping(value = "/memos", method = RequestMethod.GET)
@ResponseBody
public Map list() {
if (logger.isDebugEnabled()) {
logger.debug("action LIST, method " + RequestMethod.GET);
}
List list = memoService.getListMemo();
if (logger.isDebugEnabled()) {
logger.debug("list : " + list);
}
Map result = new HashMap();
result.put("result", Boolean.TRUE);
result.put("data", list);
return result;
}
@RequestMapping(value = "/memos/{seq}", method = RequestMethod.GET)
@ResponseBody
public Map view(@PathVariable String seq) {
if (logger.isDebugEnabled()) {
logger.debug("action VIEW, method " + RequestMethod.GET);
}
Map map = memoService.getMemo(seq);
if (logger.isDebugEnabled()) {
logger.debug("map : " + map);
}
Map result = new HashMap();
result.put("result", Boolean.TRUE);
result.put("data", map);
return result;
}
@RequestMapping(value = "/memos", method = RequestMethod.POST)
@ResponseBody
public Map add(@RequestBody Map body) {
if (logger.isDebugEnabled()) {
logger.debug("action ADD, method " + RequestMethod.POST);
logger.debug("body : " + body);
}
int key = (Integer) memoService.setInsertMemo(body);
if (logger.isDebugEnabled()) {
logger.debug("key : " + key);
}
Map result = new HashMap();
result.put("result", Boolean.TRUE);
return result;
}
@RequestMapping(value = "/memos/{seq}", method = RequestMethod.PUT)
@ResponseBody
public Map modify(@PathVariable String seq, @RequestBody Map body) {
if (logger.isDebugEnabled()) {
logger.debug("action MODIFY, method " + RequestMethod.PUT);
logger.debug("seq : " + seq);
logger.debug("body : " + body);
}
body.put("seq", seq);
int affected = memoService.setUpdateMemo(body);
if (logger.isDebugEnabled()) {
logger.debug("affected : " + affected);
}
Map result = new HashMap();
result.put("result", Boolean.TRUE);
return result;
}
@RequestMapping(value = "/memos/{seq}", method = RequestMethod.DELETE)
@ResponseBody
public Map remove(@PathVariable String seq) {
if (logger.isDebugEnabled()) {
logger.debug("action REMOVE, method " + RequestMethod.DELETE);
logger.debug("seq : " + seq);
}
int affected = memoService.setDeleteMemo(seq);
if (logger.isDebugEnabled()) {
logger.debug("affected : " + affected);
}
Map result = new HashMap();
result.put("result", Boolean.TRUE);
return result;
}
@ExceptionHandler(Exception.class)
// @ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Map handleException(Exception e) {
Map result = new HashMap();
result.put("result", Boolean.FALSE);
return result;
}
}
파이어폭스의 플러그인중 RESTClient를 설치해 서비스를 테스트 할 수 있습니다. 아래는 실제 화면 캡처입니다.
'spring' 카테고리의 다른 글
Spring을 이용한 RESTful 서비스 3 (0) | 2012.07.12 |
---|---|
Spring을 이용한 RESTful 서비스 2 (1) | 2012.07.11 |
Spring + iBatis 프레임워크 구성시 오라클 LOB 타입 사용하기 (5) | 2012.04.20 |
AJAX 호출시 Exception 처리방법 (2) | 2012.01.02 |
스프링 2.5 스키마와 네임스페이스 (1) | 2009.09.30 |