이 REST(Representational State Transfer) 스타일 아키텍처를 통해 요청과 응답은 자원의 표현에 대한 전송 관련하여 빌드된다. 자원은 일반적으로 URI(Uniform Resource Identifier)를 사용하는 글로벌 ID로 식별된다. 클라이언트 애플리케이션은 HTTP 메소드(예: GET, POST, PUT 또는 DELETE)를 사용하여 자원 또는 자원의 콜렉션을 조작한다. 일반적으로 GET 메소드는 자원 또는 자원의 콜렉션을 받거나 나열하는 데 사용되고, POST는 작성하는 데 사용되며, PUT은 업데이트 또는 바꾸는 데 사용되며, DELETE는 자원을 제거하는 용도이다.
예를 들어, GET http://host/context/employees/12345는 ID 12345인 직원의
표현을 받는다. 응답 표현은 자세한 직원 정보가 들어있는 XML 또는 ATOM이 되거나,
더 나은 UI를 제공하는 JSP/HTML 페이지가 될 수 있다. 어느 표현을 보게 되느냐의 문제는
서버 측 구현과 클라이언트가 요청한 MIME 유형에 따라 다르다.
RESTful 웹 서비스는 HTTP와 REST의 원칙을 사용하여 구현되는 웹 서비스이다. 일반적으로 RESTful 웹 서비스는 기본 자원 URI, 지원하는 표현/응답 MIME 유형 및 지원하는 조작을 정의할 것이다.
이 기사에서는 Spring을 사용하여 Java 서버 측 RESTful 웹 서비스를 빌드하는 방법에 대해 살펴보자. 예제는 브라우저, curl 및 클라이언트가 요청하기 위한 Firefox 플러그인 RESTClient를 사용할 것이다. 이 기사에서 사용한 소스 코드를 다운로드할 수 있다.
이 기사에서는 독자가 REST 기본에 익숙하다고 가정한다. 참고자료를 통해 REST에 대한 자세한 정보를 확인할 수 있다.
Spring 프레임워크가 REST를 지원하기 전에, 사람들은 Restlet, RestEasy 및 Jersey와 같은 몇 가지 다른 구현을 사용하여 Java 세상에서 RESTful 웹 서비스를 빌드하는 데 도움을 받았다. 이 그룹에서 가장 중요한 Jersey는 JAX-RS(JSR 311)의 참조 구현이다. 참고자료를 통해 JSR 311과 Jersey에 대해 더 자세히 확인할 수 있다.
널리 사용되는 Java EE Framework인 Spring은 릴리스 3에서 RESTful 웹 서비스를 빌드하기 위한 지원을 추가했다. 비록 REST 지원이 JAX-RS의 구현이 아니라고 하더라도 스펙에서 정의한 것보다 더 많은 기능이 있다. REST 지원은 Spring의 MVC 계층과 원활하게 통합되고, Spring으로 빌드하는 애플리케이션이 쉽게 채택할 수 있다.
Spring REST 지원의 주요 기능은 다음과 같다.
- 자원 식별 및 URI 맵핑을 지원하기 위한
@RequestMapping및@PathVariable과 같은 어노테이션 - 서로 다른 MIME/내용 유형으로 서로 다른 표현을 지원하기 위한
ContentNegotiatingViewResolver - 유사한 프로그래밍 모델을 통해 원래 MVC 계층으로의 원활한 통합
이 섹션에서의 예제는 Spring 3 환경을 설정하고 Tomcat으로 통합할 수 있는 "Hello world" 애플리케이션을 작성하는 과정을 단계별로 안내한다. 그 다음에 더 복잡한 애플리케이션으로 넘어가서, 여러 MIME 유형 표현 지원 및 JAXB 지원과 같이 Spring 3 REST 지원의 정수를 소개한다. 코드 스니펫은 그 개념을 설명하는 데 유용하다. 이 기사에 사용된 샘플 코드는 모두 다운로드 가능하다.
Hello World: Spring 3 REST 지원 사용하기
예제를 따라 개발 환경을 설정하려면 다음 사항이 필요하다.
- IDE: JEE용 Eclipse IDE(버전 3.4 이상)
- Java SE5 이상
- 웹 컨테이너: Apache Tomcat 6.0(Jetty 및 기타 등등도 작동 예정임)
- Spring 3 프레임워크(이 기사를 쓰는 현재로서는 버전 3.0.3이 가장 최신임)
- 기타 라이브러리: JAXB 2, JSTL, commons-logging
Eclipse에서 동적 웹 애플리케이션을 작성하고 런타임으로 Tomcat 6을 설정한다. 그러면 Spring WebApplicationContext를 사용하기 위해 web.xml 파일을 설정해야 한다. 예제는 Spring bean 구성을 두 개의 파일로 분배한다. rest-servlet.xml이 MVC/REST 관련 구성을 처리할 것이고, rest-context.xml은 서비스 계층 구성을 처리한다(예: 데이터 소스 bean). 목록 1은 web.xml에서 Spring 구성 스니펫을 보여준다.
목록 1. web.xml에서 Spring WebApplicationContext 사용
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/rest-context.xml
</param-value>
</context-param>
<!-- This listener will load other application context file in addition to
rest-servlet.xml -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
|
rest-servlet.xml 파일에서 Spring MVC 관련 구성(Controller, View, View Resolver)을 설정한다. 목록 2에는 가장 중요한 스니펫을 보여준다.
목록 2. rest-servlet.xml에서 Spring MVC 구성 설정
<context:component-scan base-package="dw.spring3.rest.controller" />
<!--To enable @RequestMapping process on type level and method level-->
<bean class="org.springframework.web.servlet.mvc.annotation
.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation
.AnnotationMethodHandlerAdapter" />
<!--Use JAXB OXM marshaller to marshall/unmarshall following class-->
<bean id="jaxbMarshaller"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>dw.spring3.rest.bean.Employee</value>
<value>dw.spring3.rest.bean.EmployeeList</value>
</list>
</property>
</bean>
<bean id="employees" class=
"org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg ref="jaxbMarshaller" />
</bean>
<bean id="viewResolver" class=
"org.springframework.web.servlet.view.BeanNameViewResolver" />
|
상기 코드에서는
-
Component-scan - Spring 어노테이션이 있는 클래스에 대한 자동 스캔을 사용함
실제로는 제어기 클래스에서 정의한@Controller어노테이션을 발견하는 데 사용된다. DefaultAnnotationHanlderMappings및AnnotationMethodHandlerAdapter- Spring으로 처리되는 클래스나 메소드에서
@ReqeustMapping어노테이션을 작성할 Bean
이 어노테이션은 다음 섹션에서 자세히 논의할 것이다. Jaxb2Mashaller- 오브젝트 XML 맵핑(OXM)을 하기 위해 JAXB 2를 사용하는 마샬러/언마샬러(marshaller/unmarshaller)를 정의함
MashallingView- Jaxb2Mashaller를 활용하는 XML 표현
view를 정의함 BeanNameViewResolver- 사용자가 지정한 bean 이름을 사용하는 보기 리졸버를 정의함
예제는 "employees"라는 이름의MarshallingView를 사용할 것이다.
이렇게 Spring 관련 구성을 완료한다. 다음 단계는 사용자 요청을 처리하는 제어기를 쓰는 것이다. 목록 3에는 제어기 클래스를 보여준다.
목록 3. 패키지 dw.spring3.rest.controller에서 EmployeeController
@Controller
public class EmployeeController {
@RequestMapping(method=RequestMethod.GET, value="/employee/{id}")
public ModelAndView getEmployee(@PathVariable String id) {
Employee e = employeeDS.get(Long.parseLong(id));
return new ModelAndView(XML_VIEW_NAME, "object", e);
}
}
|
@RequestMapping 어노테이션은 Spring REST 기능의 키이다. 이는 어노테이션된 메소드로
어느 HTTP 메소드(RequestMethod.GET)와 어느 URI(/employee/{id})를 처리해야 하는지 지정한다. 다음에 유의하자.
{id}플레이스홀더의 경우, {}에서의 값은@PathVariable어노테이션을 사용하는 메소드 매개변수로 삽입될 수 있다.- XML_VIEW_NAME은 rest-servlet.xml에서 정의한 보기 이름인
employees와 동일하다. -
employeeDS는 그 구현이 이 기사의 범위를 벗어나는 간단한 메모리 기반 데이터 소스이다.
웹 애플리케이션을 Tomcat으로 발행하자. 이제 브라우저를 열 수 있고
http://<host>:<port>/<appcontext>/service/employee/1을 입력한다.
브라우저에 1과 동일한 ID의 직원의 XML 보기가 표시되어야 한다.
Spring REST 지원의 더 많은 기능에 대해 읽고 알아보자.
자원은 GET, POST, PUT 및 DELETE와 같은 HTTP 메소드를 사용하여 조작된다.
이미 직원 정보를 검색하는 GET 메소드를 사용하는 방법에 대해
살펴보았다. 이제 POST, PUT 및 DELETE에 대해 알아볼 것이다.
@RequestMapping 어노테이션의 기능을 사용하여 다른 메소드를 처리하기 위한 코드는 매우
유사하다. 목록 4에는 EmployeeController 클래스의 코드 스니펫을 보여준다.
목록 4. dw.spring3.rest.controller에서 EmployeeController
@RequestMapping(method=RequestMethod.POST, value="/employee")
public ModelAndView addEmployee(@RequestBody String body) {
Source source = new StreamSource(new StringReader(body));
Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
employeeDS.add(e);
return new ModelAndView(XML_VIEW_NAME, "object", e);
}
@RequestMapping(method=RequestMethod.PUT, value="/employee/{id}")
public ModelAndView updateEmployee(@RequestBody String body) {
Source source = new StreamSource(new StringReader(body));
Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
employeeDS.update(e);
return new ModelAndView(XML_VIEW_NAME, "object", e);
}
@RequestMapping(method=RequestMethod.DELETE, value="/employee/{id}")
public ModelAndView removeEmployee(@PathVariable String id) {
employeeDS.remove(Long.parseLong(id));
List<Employee> employees = employeeDS.getAll();
EmployeeList list = new EmployeeList(employees);
return new ModelAndView(XML_VIEW_NAME, "employees", list);
}
|
상기 코드에서는
RequestMethod.<Method>값은 어느 HTTP 메소드가 어노테이션된 메소드를 처리해야 하는지 식별한다.@RequestBody를 통해 HTTP 요청의 본문 내용은 매개변수로 삽입될 수 있다.예제에서 본문은 직원을 표현하는 XML 데이터이다. 필자는 JAXB 2를 사용하여 XML을 Java bean으로 언마샬한 다음에 이를 지속한다. 샘플 요청 본문은 다음과 같이 가능하다.
<employee><id>3</id><name>guest</name></employee>
- 메소드 매개변수로 삽입할 수 있는 기타 유용한 어노테이션은
@PathVariable,@RequestParm등등이다. Spring 문서에 어노테이션의 전체 목록이 있다(참고자료 참조).
일반적으로 자원의 콜렉션도 조작해야 할 것이다. 예를 들어, 단지 한 명의 개인에 대한 정보가 아니라 모든 직원의 정보를 얻으려고 할 수 있다. 이는 이전 케이스와 유사하게 구현할 수 있다. 변경해야 하는 것은 /employee에서 /employees로 URI를 변경하는 것뿐이다. employee(직원)의 복수형은 콜렉션 시맨틱에 적절히 맞는다. 목록 5에는 구현을 보여준다.
목록 5. EmployeeController에서 getAllEmployees
@RequestMapping(method=RequestMethod.GET, value="/employees")
public ModelAndView getEmployees() {
List<Employee> employees = employeeDS.getAll();
EmployeeList list = new EmployeeList(employees);
return new ModelAndView(XML_VIEW_NAME, "employees", list);
}
|
Employee 콜렉션에 대한 랩퍼 클래스를 명시해야 한다. java.util.List 클래스를 적절히 마샬할 수 없기 때문에
랩퍼 클래스가 JAXB 2에 필요하다. 목록 6에는 EmployeeList 클래스를 보여 준다.
목록 6. dw.spring3.rest.bean에서 EmployeeList
@XmlRootElement(name="employees")
public class EmployeeList {
private int count;
private List<Employee> employees;
public EmployeeList() {}
public EmployeeList(List<Employee> employees) {
this.employees = employees;
this.count = employees.size();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@XmlElement(name="employee")
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
|
REST 서비스의 또다른 일반 기능은 요청에 따라 다른 표현을 제작할 수 있는 것이다. 예를 들어, 클라이언트가 모든 직원의 HTML/text 표현을 요청하면 서버는 사용자에 잘 형성된 HTML 페이지를 만들어야 한다. 클라이언트가 직원의 application/XML 표현을 요청하면 서버는 그 대신에 XML 결과를 만들어야 한다. 또다른 대중적인 표현 유형은 ATOM과 PDF이다.
Spring 3은 ContentNegotiatingViewResolver라는 새 보기 리졸버를 소개한다. 이는
요청 내용 유형(요청 헤더에서 Accept 특성) 또는 URI 접미부에 따라 보기 리졸버를 전환할 수 있다. 다음 예제는
ContentNegotiatingViewResolver를 사용하여 여러 표현 지원을 구현한다.
rest-servlet.xml 파일에서는 정의된 원래의
viewResolver를 주석 처리(comment out)한다. 목록 7에 표시된 대로 그 대신에 ContentNegotiatingViewResolver를
사용한다.
목록 7. 내용 협상 정의
<bean class="org.springframework.web.servlet.view
.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="html" value="text/html"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view
.BeanNameViewResolver"/>
<bean 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>
</list>
</property>
</bean>
|
정의는 application/xml과 text/html의 두 개의 요청 내용 유형을 처리하기 위한 지원을 보여준다. 코드는
두 가지 보기 리졸버도 정의한다. 하나는 application/xml을 처리하는 BeanNameViewResolver이고 나머지 하나는 text/html을
처리하는 UrlBasedViewResolver이다.
실제로는 브라우저에 http://<host>:<port>/<appcontext>/service/employees를 입력하면
이는 직원에 대한 text/html을 요청한다. 그러면
UrlBasedViewResolver이 효력을 발휘하고, Spring에는 그 보기로 /WEB-INF/jsp/employees.jsp를 취할 것이다. 요청 헤더인
Accept:application/xml을 추가하고 요청을 호출할 때에
BeanNameViewResolver이 효력을 발휘할 것이다. 목록 5에서 코드에 따라 이는 employees라는
이름의 보기를 사용하여 표현할 것이다. 이는 정의된 JAXB 2 마샬러 보기이다.
getAllEmployees()에 대한 제어기 코드는 변경되지 않을 것이다. employees.jsp
페이지에서 자체를 렌더링하기 위해 employees라는 이름의 모델 오브젝트를 사용할 것이다. 목록 8에는 employees.jsp에 대한 코드 스니펫을 보여준다.
목록 8. /WEB-INF/jsp에서 employees.jsp
<table border=1>
<thead><tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr></thead>
<c:forEach var="employee" items="${employees.employees}">
<tr>
<td>${employee.id}</td>
<td>${employee.name}</td>
<td>${employee.email}</td>
</tr>
</c:forEach>
</table>
|
이제까지 직원에 대한 CRUD(작성, 읽기, 업데이트 및 삭제) 조작을 지원하는 간단한 RESTful 웹 서비스를 개발하였다. 이 섹션에는 서비스와 통신하는 방법에 대해 살펴본다. curl을 사용하여 REST 서비스를 테스트할 것이다.
REST 서비스를 테스트하기 위해 RESTClient라는 Firefox 플러그인도 사용할 수 있다. 이는 사용하기에 간편하며 UI도 훌륭하다. 다운로드 정보는 참고자료를 참조한다.
curl은 HTTP와 HTTPS 프로토콜을 사용하는 서버로 요청을 전송할 수 있는 대중적인 명령행 도구이다. 이는 아무 HTTP 메소드로도 내용을 전송할 수 있기 때문에 RESTful 웹 서비스와 통신하는 데 유용한 도구이다. curl은 Linux®와 Mac®에서 내장 유틸리티이다. Windows® 플랫폼의 경우 이 도구를 다운로드할 수 있다(참고자료 참조).
모든 직원을 얻는 첫 번째 curl 명령을 초기화하려면 다음을 입력한다.
curl –HAccept:application/xml http://localhost:8080/rest/service/employees |
그림 1에 표시된 대로 응답은 XML 형식이고, 모든 직원이 들어있을 것이다.
그림 1. 모든 직원의 XML 표현
브라우저에서 동일한 URL도 시도할 수 있다. 이 경우에 Accept 헤더는 text/html을 지정하여 employees.jsp로 정의한 테이블이 표시될 것이다. 그림 2에서 예제를 보여준다.
그림 2. 모든 직원의 HTML 표현
새 직원을 서버로 POST하기 위해 아래 코드를 사용한다. 목록 4에서
addEmployee() 코드는 요청 본문을 사용하고 Employee 오브젝트로 언마샬할 것이다.
curl -X POST -HContent-type:application/xml --data "<employee><id>3</id><name>guest</name><email>guest@ibm.com</employee>" http://localhost:8080/rest/service/employee |
새 직원이 추가되었다. 첫 번째 예제를 사용하여 직원 목록을 확인할 수 있다.
PUT 메소드는 POST와 유사하다.
curl -X PUT -HContent-type:application/xml --data "<employee><id>3</id><name>guest3</name><email>guest3@ibm.com</employee>" http://localhost:8080/rest/service/employee/3 |
위의 코드는 ID가 3인 직원의 데이터를 업데이트한다.
이제 Spring 3은 MVC 계층에서 REST를 지원하여, Spring API와 어노테이션을 사용하여 RESTful 웹 서비스를 빌드할 수 있다. 이 기사에서 예제로 Java 서버측 RESTful 웹 서비스를 쉽게 빌드하는 데 유용한 몇 가지 새로운 Spring 3 기능을 사용하는 방법에 대해 살펴보았다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| Article source code | src.zip | 9KB | HTTP |
교육
- 위키피디아에서
REST에 대한 소개와 기타 관련 링크를 찾아보자.
- Spring 3의 모든 것에 대해 살펴보자.
- Jersey에 대한 다운로드, 샘플 코드 아카이브,
사용자 안내서 및 JAX-RS API 문서를 찾아보자. Jersey는 RESTful 웹 서비스를 빌드하기 위한 오픈 소스인(이중
CDDL+GPL 라이센스 사용) 프로덕션 품질 JAX-RS(JSR 311) 참조 구현이다.
- 동일한 저자가 지은 "Build a RESTful web service using Jersey and Apache Tomcat"
(developerWorks, 2009년 9월)은 Jersey를 Apache Tomcat으로 통합하여 서블릿 스타일 서비스에서 RESTful 서비스로 어떻게 원활하게 전송할 수 있는지에 대해 설명한다.
- JAXB Reference Implementation Project에 대해 더 자세히 읽어보자.
제품 및 기술 얻기
- Spring 3 다운로드, 문서 및 튜토리얼을 찾아보자.
- Windows용 curl을 찾아보자.
- RESTful/WebDav 서비스를 방문하고 테스트하는 데 사용되는 Firefox
RESTClient
확장을 찾아보자.
-
IBM 제품 평가판을 다운로드하거나
IBM SOA Sandbox의 온라인 시험판을 살펴보고 DB2®, Lotus®, Rational®, Tivoli® 및 WebSphere®의
애플리케이션 개발 도구와 미들웨어 제품을 사용해 볼 수 있다.
토론
- developerWorks 블로그를 통해 developerWorks 커뮤니티에 참여할 수 있다.
- My developerWorks 프로파일을 지금 작성하고 REST, 웹 서비스 또는 Spring에 대한 관심 목록을 설정하자.
My developerWorks에서 최신 정보를 자주 확인하자.
- 웹
개발에 관심이 있는 다른 developerWorks 구성원을 찾아보자.
- My developerWorks:
그룹에서 웹 개발자들과 서로의 웹 개발 경험과 지식을 공유할 수 있다.
- 지식 공유: 웹 주제를
중점적으로 다루는 developerWorks 그룹 중 하나에 참여하자.
- Roland Barcia는 자신의 블로그에서
Web 2.0 및 미들웨어에 대해 설명했다.
- developerWorks 멤버의 shared
bookmarks on web topics를 따라가 보자.
- 빠른 해답: Web 2.0 Apps forum을 방문한다.