Jersey 1.0JAX-RS(Java API for RESTful Web Services, JSR-311)의 오픈 소스 참조 구현으로서 운영 환경에서 사용 가능합니다. Jersey를 이용하면 자바로 RESTful 웹 서비스를 손쉽게 개발할 수 있습니다.

이전의 테크팁, 자바로 RESTful 웹 서비스 구현에서 Paul Sandoz와 필자는 RESTful 웹 서비스, JAX-RS 및 Jersey를 소개하고 JAX-RS 사양에 따라 자바로 RESTful 웹 서비스를 작성하는 방법도 살펴봤습니다. 이번 팁에서는 Jersey 1.0을 사용하여 JSON(JavaScript Object Notation)에서 데이터를 구성하는 방법을 알아보겠습니다. JSON은 JavaScript 언어의 개체 표기를 기반으로 하는 경량급 데이터 교환 형식입니다. 단순한 텍스트 형식 덕분에 JSON은 XML과 같은 다른 데이터 교환 형식의 대안으로 효과적이며, 특히 RESTful 웹 서비스용 데이터 교환 형식으로 각광받고 있습니다.

이번 팁에서는 프린터 상태 정보를 제공하는 Jersey 기반 웹 애플리케이션을 작성해 보겠습니다. 이 애플리케이션은 JSON 형식으로 정보를 반환합니다. 이 애플리케이션을 만들기 위해 Maven 2 소프트웨어 프로젝트 관리 도구를 사용합니다. Maven에 대한 자세한 내용은 Welcome to MavenBuilding Web Applications with Maven 2를 참조하십시오.

Maven 2로 Jersey 기반 웹 애플리케이션 만들기

Maven 2로 Jersey 기반 웹 애플리케이션을 개발하려면 먼저 Maven 2 프로젝트를 만들어야 합니다. 다음 Maven 2 Archetype 플러그인을 실행하면 됩니다.

   mvn archetype:generate -DarchetypeCatalog=http://download.java.net/maven/2


그러면 Maven 2는 Archetype 카탈로그 archetype-catalog.xml(URL: http://download.java.net/maven/2)에 나열된 Archetype 중에서 하나를, 즉 Maven 2 프로젝트 템플릿을 선택하라는 메시지를 표시합니다.

    Choose archetype:
1:
http://download.java.net/maven/2 -> jersey-quickstart-grizzly (Archetype for creating a RESTful web application with Jersey and Grizzly)
2:
http://download.java.net/maven/2 -> jersey-quickstart-webapp (Archetype for creating a Jersey based RESTful web application WAR packaging)
Choose a number: (1/2):


1(jersey-quickstart-grizzly)을 선택합니다. 그러면 웹 애플리케이션의 그룹 ID와 기타 몇 가지 사항을 입력하는 화면이 나타납니다. 다음 값을 사용할 수 있습니다.

   Define value for groupId: : com.example
Define value for artifactId: : printer-status-webapp
Define value for version: 1.0-SNAPSHOT: :
Define value for package: com.example: :
Confirm properties configuration:
groupId: com.example
artifactId: printer-status-webapp
version: 1.0-SNAPSHOT
package: com.example
Y:


입력을 확인하면 Maven 2에서는 printer-status-webapp라는 새 하위 디렉토리를 만듭니다. 여기에는 새 Jersey 기반 웹 애플리케이션용 템플릿이 들어 있습니다. 그림 1에서는 printer-status-webapp 하위 디렉토리의 확장 파일 구조를 보여 줍니다.


그림 1. Maven 2로 작성한 웹 애플리케이션 템플릿 구조

또한 Maven 2에서는 POM(Project Object Model) 파일도 만드는데, 이 파일은 Maven 프로젝트에 대한 XML 표현을 포함하고 있습니다. pom.xml 파일은 printer-status-webapp 하위 디렉토리에 있습니다.

REST 리소스 추가

printer-status-webapp 하위 디렉토리 아래서 src/main/java/com/example까지 탐색하면 MyResource라는 자바 클래스를 발견할 수 있습니다. 이 클래스는 간단한 메시지를 나타내는 리소스의 URI를 식별합니다.

   package com.example;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

// The Java class will be hosted at the URI path "/myresource"
@Path("/myresource")
public class MyResource {

// TODO: update the class to suit your needs

// The Java method will process HTTP GET requests
@GET
// The Java method will produce content identified by the MIME Media
// type "text/plain"
@Produces("text/plain")
public String getIt() {
return "Got it!";
}
}



이 파일의 JAX-RS 주석을 살펴보십시오. @Path 주석은 MyResource 클래스가 어떤 URI 경로에 대한 요청을 서비스하는지 식별합니다. REST에서 중요한 개념 중 하나는 리소스의 존재이며, 각 리소스는 전역 식별자, 즉 URI를 사용하여 참조할 수 있습니다. 이 리소스를 다루기 위해 네트워크, 클라이언트 및 서버의 구성요소들은 HTTP와 같은 표준화된 인터페이스 그리고 이 리소스의 교환 표현을 사용하면서 통신합니다. @GET 주석은 getIt() 메소드가 HTTP GET 요청에 응답함을 나타냅니다. @Produces 주석은 getIt() 메소드가 일반 텍스트를 반환함을 나타냅니다.

Jersey에서 JSON 기능을 시연하기 위해 REST 리소스 몇 개를 추가하겠습니다. 그러려면 애플리케이션에서 JSON 지원을 활성화해야 합니다. pom.xml 파일을 편집하고 다음 부분의 주석 처리를 취소합니다.

         <dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${jersey-version}</version>
</dependency>


또한 pom.xml 파일의 jersey-version 등록 정보를 현재 제공되는 안정된 버전인 1.0으로 업데이트해야 합니다.

    <properties>
<jersey-version>1.0</jersey-version>
</properties>


이제 JSON 리소스를 추가할 준비가 되었습니다. 이를 위해 프린터의 상태 정보를 모델링할 JAXB(Java Architecture for XML Binding) 빈을 만들겠습니다. 그런 다음 이 빈을 REST 리소스로 액세스할 수 있게 합니다.

먼저 src/main/java/com/example 하위 디렉토리에서 다음 코드로 StatusInfoBean.java 파일을 만듭니다.

   package com.example;

import java.util.Collection;
import java.util.HashSet;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class StatusInfoBean {

public String status = "Idle";
public int tonerRemaining = 25;
public final Collection<JobInfoBean> jobs = new HashSet<JobInfoBean>();
}



그런 다음 src/main/java/com/example 하위 디렉토리에서 다음 코드로 JobInfoBean.java 파일을 만듭니다.

   package com.example;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "job")
public class JobInfoBean {
public String name;
public String status;
public int pages;

// just to make JAXB happy
public JobInfoBean(){};

public JobInfoBean(String name, String status, int pages) {
this.name = name;
this.status = status;
this.pages = pages;
}

}



빈을 REST 리소스로 액세스할 수 있게 하려면 src/main/java/com/example 하위 디렉토리의 MyResource.java 파일을 다음과 같이 업데이트해야 합니다.

   package com.example;

import com.sun.jersey.spi.resource.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Singleton
@Path("/status")
public class MyResource {

StatusInfoBean statusInfoBean = new StatusInfoBean();

{{
statusInfoBean.jobs.add(new JobInfoBean("sample.doc", "printing...", 13));
}}

@GET
@Produces("application/json")
public StatusInfoBean getStatus() {
return statusInfoBean;
}

@PUT
@Consumes("application/json")
public synchronized void setStatus(StatusInfoBean status) {
this.statusInfoBean = status;
}
}



getStatus() 메소드가 JSON 형식의 데이터를 클라이언트에 반환하고, setStatus() 메소드는 클라이언트에서 보낸 JSON 형식의 데이터를 처리합니다.

   @Produces("application/json")
public StatusInfoBean getStatus() {...}

@Consumes("application/json")
public synchronized void setStatus(StatusInfoBean status) {...}


애플리케이션 실행

printer-status-webapp 하위 디렉토리에 있는 다음 Maven 2 수명 주기 명령을 사용하여 웹 애플리케이션을 실행할 수 있습니다.

mvn clean compile exec:java


이 명령은 Maven 2에서 생성한 모든 파일을 지우고 자바 소스를 컴파일합니다. 또한 Grizzly 컨테이너를 시작하는데, 이 컨테이너 안의 웹 애플리케이션이 실행됩니다.

브라우저를 열고
http://localhost:9998/status로 이동합니다. 다음과 같이 표시됩니다.

{"status":"Idle","tonerRemaining":"25","jobs":{"name":"sample.doc","status":"printing...","pages":"13"}}


이는 Jersey 기반 웹 애플리케이션이 JSON 형식의 데이터를 반환함을 확인합니다.

애플리케이션 개선

이 애플리케이션이 JSON 형식의 데이터를 반환하지만, 몇 가지 개선이 필요할 수 있습니다. 예를 들어, 다른 프린터 작업을 추가하고 애플리케이션을 다시 실행할 경우 다음과 같은 내용이 반환됩니다.

      {"status":"Idle","tonerRemaining":"25", "jobs":[{"name":"sample.ps","status":"printing...","pages":"13"},{"name":"second.pdf","status":"waiting in queue","pages":"2"}]}



JSON 데이터의 jobs 개체는 어레이이지만, 단일 프린터 작업의 출력에서는 이 점이 분명하게 나타나지 않았습니다. 결과에 하나의 요소만 들어 있더라도 어레이로 표시되도록 애플리케이션을 수정하는 작업이 필요할 수 있습니다. 그렇지 않으면 클라이언트에서 JSON을 처리할 때 문제가 발생할 수 있습니다.

수정이 필요할 사항이 하나 더 있습니다. tonerRemaining 및 pages의 값은 현재 문자열로 나열됩니다. 이 결과를 숫자로 나타내고 싶을 것입니다.

이러한 점들을 개선하기 위해 src/main/java/com/example 하위 디렉토리에 다음 내용으로 MyJAXBContextResolver.java라는 파일을 만듭니다.

   import com.sun.jersey.api.json.JSONJAXBContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;


@Provider
public class MyJAXBContextResolver implements ContextResolver<JAXBContext> {

private JAXBContext context;
private Class[] types = {StatusInfoBean.class, JobInfoBean.class};

public MyJAXBContextResolver() throws Exception {
Map props = new HashMap<String, Object>();
props.put(JSONJAXBContext.JSON_NOTATION, JSONJAXBContext.JSONNotation.MAPPED);
props.put(JSONJAXBContext.JSON_ROOT_UNWRAPPING, Boolean.TRUE);
props.put(JSONJAXBContext.JSON_ARRAYS, new HashSet<String>(1){{add("jobs");}});
props.put(JSONJAXBContext.JSON_NON_STRINGS, new HashSet<String>(1){{add("pages"); add("tonerRemaining");}});
this.context = new JSONJAXBContext(types, props);
}

public JAXBContext getContext(Class<?> objectType) {
return (types[0].equals(objectType)) ? context : null;
}
}


다음과 같이 애플리케이션을 다시 시작합니다.
    mvn clean compile exec:java 


여기서 clean은 옵션입니다.

이제 반환된 JSON은 단일 프린터 작업에서도 어레이 형식으로 결과가 표시되며, tonerRemaining 및 pages 결과는 숫자로 나타납니다.

   {"status":"Idle","tonerRemaining":25,"jobs":[{"name":"sample.doc","status":"printing...","pages":13}]}


JSON 구성 매개 변수

MyJAXBContextResolver 클래스의 내용을 살펴보면 다음 행이 포함되어 있습니다.

   props.put(JSONJAXBContext.JSON_NOTATION, JSONJAXBContext.JSONNotation.MAPPED);


JSONJAXBContext.JSON_NOTATION 옵션은 Jersey 1.0이 웹 애플리케이션에서 JAXB 빈으로부터 생성할 JSON 표기의 형식을 지정합니다. 여기서는 매핑된 형식의 JSON 표기가 사용되도록 지정합니다. 매핑된 표기는 Jersey 1.0의 기본 JSON 표기입니다. 이 표기에서 Jersey 1.0은 다음과 같이 JAXB 빈 사양을 적용합니다.

   @XmlRootElement
public class StatusInfoBean {

public String status = "Idle";
public int tonerRemaining = 25;

}



그리고 다음 JSON 데이터를 생성합니다.
{"status":"Idle", "tonerRemaining":"25"}


MyJAXBContextResolver의 다음 행은 기본 매핑된 표기의 구성 등록 정보를 지정합니다.

   props.put(JSONJAXBContext.JSON_ROOT_UNWRAPPING, Boolean.TRUE);
props.put(JSONJAXBContext.JSON_ARRAYS, new HashSet<String>(1){{add("jobs");}});
props.put(JSONJAXBContext.JSON_NON_STRINGS, new HashSet<String>(1){{add("pages"); add("tonerRemaining");}});


이 세 가지 등록 정보 외에 네 번째 구성 등록 정보인 JSONJAXBContext.JSON_ATTRS_AS_ELEMS를 사용할 수 있습니다. 표 1에서는 매핑된 표기의 네 가지 구성 등록 정보에 대해 설명합니다.

표 1: 매핑된 표기의 구성 등록 정보
등록 정보 형식 설명
JSONJAXBContext.JSON_ROOT_UNWRAPPING Boolean true(기본값)로 설정된 경우 XML 루트 요소에 해당하는 루트 요소가 제거됩니다. Boolean.TRUE
JSONJAXBContext.JSON_ARRAYS Collection<String> 어레이로 처리될 요소로 구성되며, 이러한 요소는 괄호로 구분합니다. new HashSet<String>(1){{add("jobs");}}
JSONJAXBContext.JSON_NON_STRINGS Collection<String> 큰따옴표로 구분할 수 없는 요소로 구성됩니다. new HashSet<String>(1){{add("pages");}}
JSONJAXBContext.JSON_ATTRS_AS_ELEMS Collection<String> JSON에서 요소로 인코딩해야 할 속성으로 구성됩니다(그렇지 않으면 XML 속성 이름이 @로 시작함). new HashSet<String>(1){{add("attr1");}}

Jersey 1.0은 JSONJAXBContext.JSON_NOTATION 외에 다음 두 가지 JSON 옵션을 지원합니다.

  • JSONNotation.MAPPED_JETTISON. Jettison 규칙을 지정합니다. Jettison 규칙을 위해 Jersey 1.0은 StatusInfoBean에 대한 다음 JSON 데이터를 생성합니다.
       {"StatusInfoBean":{"status":"Idle","tonerRemaining":"25"}}
    
    현재 Jersey에서는 Jettison 규칙의 네임스페이스 매핑을 구성하도록 JSONJAXBContext.JSON_XML2JSON_NS 등록 정보를 지원합니다.
  • JSONNotation.BADGERFISH. BadgerFish 규칙을 지정합니다. BadgerFish 규칙을 위해 Jersey 1.0은 StatusInfoBean에 대한 다음 JSON 데이터를 생성합니다.
       {"StatusInfoBean":{"status":{"$":"Idle"},"tonerRemaining":{"$":"25"}}}
    

추가 자료

저자 정보

Jakub Podlesak은 Jersey 프로젝트 팀원으로 일하고 있습니다. 그는 WS-Policy 팀원으로 GlassFish v2 웹 서비스 스택인 Metro 개발에 참여한 바 있습니다.


이 글의 영문 원본은
Configuring JSON for RESTful Web Services in Jersey 1.0
에서 보실 수 있습니다.

"Java EE" 카테고리의 다른 글

2008/12/08 13:13 2008/12/08 13:13

TRACKBACK :: http://blog.sdnkorea.com/blog/trackback/691

댓글을 달아 주세요

[로그인][오픈아이디란?]

◀ Prev 1  ... 168 169 170 171 172 173 174 175 176  ... 806  Next ▶