이번 포스팅에서는 준비된 개발 환경을 테스트 하기 위해
Maven + Spring MVC를 이용한 REST 서비스 프로젝트를 만들어 보도록 하겠습니다.
일단 Maven을 이용해서 Web Application을 만들어 보겠습니다.
Maven을 이용해서 Project를 만드는 일은 Console 창에서도 가능하지만
우리는 이미 만들어 놓은 개발환경(m2eclipse)을 이용해서 만들도록 하겠습니다.
아래와 같이 m2e를 활용하여 maven webapp을 만듭니다.
먼저 File - New - Others.. 메뉴를 선택하여 아래와 같이 Maven Project를 생성합니다.

Maven에서 제공하는 Archetype을 이용해서 만들 예정이므로 별다른 설정 없이 Next를 클릭합니다.

Maven에서는 Archetype이라는 Project Templating Toolkit을 제공하여 손쉽게 Maven Project를 구성 할 수 있게 해줍니다.
Web Application을 만들기 위해 아래와 같이 Maven에서 제공하는 maven-archetype-webapp 을 선택해 줍니다.

그러면 아래와 같이 Group Id, Artifact Id 등을 설정할 수 있는 화면이 나옵니다. Group Id, Artifact Id, Version은 Maven Repository 내에서 각 Package를 구분하는 구분자로 사용됩니다.
여기서는 테스트 프로젝트로 사용할 것이기 아래와 같이 마음에 드는대로 생성해 줍니다.
(Real world 프로젝트에서는 주로 Group Id에는 com.company 등과 같이 해당 솔루션사 구분자를 넣어주고,
Artifact Id 에는 Eclipse Project 명을 넣어주며, Version은 개발 중 이므로 0.0.1-SNAPSHOT 형태로 사용합니다.
Package 명은 자동으로 {groupId}.{artifaceId}로 생성 됩니다.)

위의 조건대로 넣어주면 아래와 같이 Eclipse Maven Web Project가 생성되어 있음을 확인할 수 있습니다.

하지만 위의 구조에서는 src/main/java 등의 몇몇 Maven 기본 디렉토리는 아직 만들어 지지 않음을 알 수 있으며,
프로젝트 또한 Dynamic Web Project로 인식되지 않아 WTP를 이용할 수 없습니다.
2. Dynamic Web Project로 변경
Maven Eclipse Project를 Dynamic Web Project로 인식 시키기 위해 아래와 같이 프로젝트를 선택하고 마우스 오른쪽 버튼 메뉴 Configure - Convert to Faceted Form.. 을 통해 Dynamic Web Project로 변경합니다.

Dynamic Web Project로 변경하기 위해 Dynamic Web Module을 선택하여 줍니다.
또한 Apache Tomcat v7.0에서 테스트 할 예정이기 때문에 Java Version은 1.6 이상으로
Dynamic Web Module Version은 3.0으로 선택하고 Runtimes에 Tomcat v7.0을 선택해 줍니다.

위와 같이 설정하고 나면 아래와 같이 Project가 Dynamic Web Project로 변경되어 디렉토리 구조가 변경되어 있는 것을 볼 수 있습니다.

Maven에서 Web Application Source 디렉토리는 src/main/webapp 이므로 위의 프로젝트에서 자동 생성 된 WebContent 디렉토리는 삭제해 줍니다.
Maven Webapp Project와 Dynamic Web Project의 각기 다른 설정들을 조정해 주기 위해 아래와 같이 프로젝트의 Properties 중 몇 가지를 조정해 줍니다. (프로젝트를 선택하고 마우스 오른쪽 버튼 메뉴에서 Properties 선택)
먼저 Deployment Assembly를 선택하여 Project에서 Tomcat에 Deployment 될 항목을 정리해 줍니다.
Tomcat에서 구동 될 필요가 없는 src/test/java 항목과 WebContent 항목을 선택하여 remove 해 주고,
src/main/webapp 디렉토리와 Maven Dependency를 아래와 같이 Add해 줍니다.


위와 같이 src/main/webapp를 추가해 주고


Maven Dependencies를 추가해 주면

위와 같이 Web Deployment Assembly가 정리되게 됩니다.
(Spring 관련 Library 등 Maven Dependency를 이용하여 추가한 Library들을 위의 Web Deployment Assembly에 Maven Dependencies를 통해 등록해 주지 않으면 Tomcat을 통해 테스트 할 때 관련 Library들이 함께 배포되지 않아
java.lang.ClassNotFoundException 이 발생하게 됩니다.)
마찬가지로 라이브러리 배포와 관련하여 (Java Export 기능을 사용할 수도 있으므로) Java Build Path도 아래와 같이 Order and Export에 Maven Dependencies를 추가 설정해 줍니다.

(추가적으로 Spring Tool Suite가 설치되어 있다면 Project를 선택하고 마우스 오른쪽 버튼 메뉴 Spring Tools - Add Spring Project Natures 를 설정하여 Spring Framework 관련 기능을 활성화 시킵니다.)

이제 Maven Test Project를 작업할 기본 환경이 아래와 같은 구조로 완성되었습니다.

3. Spring MVC REST 서비스 만들기
이제 본격적으로 Spring MVC 기반의 REST 서비스 프로그램을 작성해 보도록 하겠습니다.
Maven에서 Library 관리는 pom.xml 파일에서 관리됩니다. m2eclipse 플러그인을 설치하였기 때문에 현재 개발 환경에서 pom.xml 파일은 Maven Pom Editor를 통해 Dependency를 관리 할 수 있습니다.
아래와 같이 Maven Pom Editor를 통해 필요한 Dependency Library를 추가해 줍니다.
(Maven Pom Editor의 Dependencies 탭에서 Add.. 버튼을 이용하면 필요 Library를 검색하여 추가할 수 있습니다.)

위와 같이 spring-webmvc, spring-web, jackson-core-asl, jackson-mapper-asl 라이브러리를 추가해 줍니다.
더불어 Log4J 대신 logback을 사용하기 위해 logback-classic, jcl-over-slf4j를 추가해 주었습니다.
(과거 Log4J 개발자가 새롭게 만들고 있는 logback의 장점은 여러가지 이지만 개인적으로 Running 중인 Application을 중단하지 않고 Log Level을 조정할 수 있다는 게 가장 마음에 들었습니다.)

위와 같이 라이브러리들을 추가해 주면 pom.xml 파일은 아래와 같이 Dependency 들이 추가되어 있음을 알 수 있습니다.
추가적으로 Java compile version을 1.6으로 설정해 주기 위해 pom.xml 파일에 아래와 같이 maven-compiler-plugin을 작성해 줍니다.
**pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.odysseymoon</groupId>
<artifactId>RestService</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>RestService Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.2</version>
</dependency>
</dependencies>
<build>
<finalName>RestService</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
최종적으로 생성된 pom.xml 파일은 위와 같이 이루어 져 있습니다.
WEB과 Spring-MVC를 위한 spring-web, spring-webmvc 라이브러리,
REST 서비스를 위해 JSON 또는 XML과 Java Object의 Mapping을 위한 Jacson 라이브러리,
Logging 기능을 위한 Logback 관련 라이브러리를 위와 같이 추가해 주면
Maven에 의해 연관된 라이브러리들이 아래와 같이 추가되어 있음을 확인 할 수 있습니다.

다음은 src/main/webapp/WEB-INF/web.xml 파일을 아래와 같이 수정해 줍니다.
** web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>REST 샘플 어플리케이션</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
Maven에 의해 생성된 기본 web.xml은 Servlet 2.3 을 지원하는 DTD로 설정되어 있습니다.
우리는 Java SE6, JEE6, Tomcat 7, Spring 3.2의 기준에 맞춰 Servlet 3.0 스키마로 변경하고,
REST 서비스를 위해 URI가 /rest/ 아래인 모든 서비스를 spring DispatcherServlet으로 맵핑 했습니다.
(참고 : Java Servlet http://en.wikipedia.org/wiki/Java_Servlet )
web.xml에서 Servlet Name을 dispatcher로 설정해 주었기 때문에
src/main/webapp/WEB-INF/dispatcher-servlet.xml 파일을 아래와 같이 생성해 줍니다.
** dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:component-scan base-package="my.odysseymoon" />
<mvc:annotation-driven />
</beans>
Annotation을 이용한 Spring MVC를 작성할 예정이므로 위와 같이 설정하여 my.odysseymoon 아래 패키지들을 자동으로 Scanning하여 Spring Bean을 설정하게 하였습니다.
다음으로 간단한 REST 서비스를 위해 DB를 사용하지 않은 controller, service, dto의 3가지만 이용하여 아래와 같은 구조로 각 파일을 생성해 줍니다.

위와 같은 위치에 아래의 파일들을 생성해 줍니다.
** RestServiceController.java
package my.odysseymoon.controller;
import my.odysseymoon.dto.RestDTO;
import my.odysseymoon.service.RestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/restTest")
public class RestServiceController {
final static Logger logger = LoggerFactory.getLogger(RestServiceController.class);
@Autowired
private RestService restService;
@RequestMapping(value="/{id}", method=RequestMethod.GET)
@ResponseBody
public RestDTO getRest(@PathVariable("id") long id) {
logger.debug("###get!!");
return restService.findRest(id);
}
@RequestMapping(value="/{id}/{name}/{job}", method=RequestMethod.POST)
@ResponseBody
public RestDTO putRest(@PathVariable("id") long id, @PathVariable("name") String name,
@PathVariable("job") String job) {
return restService.createRest(id, name, job);
}
@RequestMapping(value="/{id}/{name}/{job}", method=RequestMethod.PUT)
@ResponseBody
public RestDTO updateRest(@PathVariable("id") long id, @PathVariable("name") String name,
@PathVariable("job") String job) {
return restService.updateRest(id, name, job);
}
@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
@ResponseBody
public RestDTO deleteRest(@PathVariable("id") long id) {
return restService.deleteRest(id);
}
}
/restTest 아래로 들어오는 요청을 처리하는 Controller로 @RequestMapping을 통해 REST서비스를 받아 @ResponseBody를 통해 JSON 또는 XML로 객체를 응답해주는 서비스 입니다.
@Controller : Spring MVC에서 Controller Bean으로 설정해 줍니다.
@RequestMapping : URI를 맵핑해 주며 HTTP Header / Method를 처리해 줍니다.
@PathVariable : REST URI를 파라미터로 맵핑해 줍니다.
@ResponseBody : return value를 HTTP response body로 변환해 줍니다.
위의 소스에서 만들어진 REST 서비스의 Request Mapping은 아래와 같습니다.

** RestService.java
package my.odysseymoon.service;
import java.util.concurrent.ConcurrentHashMap;
import my.odysseymoon.dto.RestDTO;
import org.springframework.stereotype.Service;
@Service("RestService")
public class RestService {
private final ConcurrentHashMap<Long, RestDTO> dtoMap = new ConcurrentHashMap<Long, RestDTO>();
public RestDTO findRest(long id) {
return dtoMap.get(id);
}
public RestDTO createRest(long id, String name, String job) {
return dtoMap.put(id, new RestDTO(id, name, job));
}
public RestDTO updateRest(long id, String name, String job) {
return dtoMap.replace(id, new RestDTO(id, name, job));
}
public RestDTO deleteRest(long id) {
return dtoMap.remove(id);
}
}
RestService에서는 테스트용으로 DB를 사용하지 않기 때문에 ConcurrentHashMap을 이용해 DTO 객체를 임시로 저장합니다. ConcurrentHashMap을 이용하면 Tread-safe하게 객체를 관리할 수 있습니다.
** RestDTO.java
package my.odysseymoon.dto;
public class RestDTO {
private long id;
private String name;
private String job;
public RestDTO() {
this(0, "", "");
}
public RestDTO(long id, String name, String job) {
this.id = id;
this.name = name;
this.job = job;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
DTO 객체는 위와 같이 간단하게 id, name, job 3개의 filed로 구성되어 있습니다.
** ResponseBody를 XML로 전송하길 원한다면
현재 Test Project에서는 Jackson Mapper를 이용하여 ResponseBody를 JSON으로 마샬링하게 되었있습니다.
만약 XML로 마샬링 하기를 원한다면 아래와 같이 DTO 객체에 @XmlRootElement 어노테이션을 추가해 줍니다.
package my.odysseymoon.dto;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class RestDTO {
......
}
** Logback을 위한 logback.xml
logback을 이용한 logging을 위해 src/main/resource 아래에 logback.xml 파일을 아래와 같이 생성합니다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.springframework">
<level value="DEBUG"/>
</logger>
<logger name="my.odysseymoon">
<level value="DEBUG"/>
</logger>
</configuration>
이제 만들어진 REST 서비스를 테스트 해보겠습니다.
Unit 테스트를 먼저 해야 겠지만 TestCase는 생략하고 바로 Tomcat에 올려 REST Client Tool을 이용해 간단히 테스트 해보겠습니다.
* Test Tool
Test Tool을 간단히 소개하겠습니다.
개인적으로 Chrome을 사용하는데 Chrome Web Store에 아주 심플하고 강력한 HTTP Client 가 있습니다.
아래와 같이 Chrome Web Store에서 Dev HTTP Client를 찾아서 설치해 줍니다.

* Tomcat Deployment & 구동
아래와 같이 Eclipse에서 Tomcat Server에 Project를 추가하고 실행해 줍니다.


이제 Tomcat 서버를 실행시키고 에러 없이 정상적으로 실행되는 지 확인합니다.
(에러가 생긴다면 Project - Properties 창을 열어서 Deployment Assembly 등이 정상적으로 설정되어 있는지 확인해 봅니다.)
서버가 아래와 같이 문제 없이 실행되었다면 Dev HTTP Client를 통해 REST를 테스트 해봅니다.

* Dev HTTP Client를 통한 테스트
서비스 테스트 URL http://localhost:8080/RestService/rest/restTest/ 이 기본이 됩니다.

위에서 정의된 Request Method와 Resource URL을 이용해서 REST를 테스트 해 봅니다.

위와 같이 POST로 등록을 한 후 GET을 통해 JSON 객체가 조회되는 REST 서비스가 정상적으로 동작함을 볼 수 있습니다.