'Java에서 RESTful 웹 서비스 구현하기'에 해당되는 글 1건

  1. 2010.09.24 Java에서 RESTful 웹 서비스 구현하기
01.JAVA/Java2010. 9. 24. 17:05
반응형

이번 테크팁에서는 Java에서 JAX-RS: Java API for RESTful Web Services (JSR-311) 사양을 따르는 RESTful 웹 서비스를 작성하는 방법과 그 참조 구현인 Jersey를 소개한다. REST(Representational State Transfer)의 기본 원리 몇 가지와 JAX-RS 및 Jersey에 대해 학습하게 된다.

이 팁에서는 JAX-RS 개념과 기술을 보여 주기 위해 샘플 애플리케이션을 사용한다. Jersey 다운로드 페이지에서 최신 버전의 Jersey 스냅샷을 다운로드하여 샘플을 구할 수 있다. 이 팁의 코드 예제는 다운로드 패키지에 포함된 샘플의 소스 코드에서 따온 것이다.

RESTful 웹 서비스 소개

REST는 WWW와 같은 분산형 시스템을 위한 소프트웨어 아키텍처 스타일 중 하나이다. 이 용어는 2000년에 Roy Fielding의 박사 논문에서 소개되었으며, 그 이후 네트워킹 커뮤니티에서 널리 사용되어 왔다. REST에서 중요한 개념 중 하나는 리소스의 존재인데, 각 리소스는 전역 식별자인 URI를 사용하여 참조할 수 있다. 이 리소스를 조작하기 위해 네트워크, 클라이언트 및 서버 구성 요소가 HTTP와 같은 표준화된 인터페이스를 사용하여 통신하고 이 리소스의 표현을 교환한다.

RESTful 웹 서비스는 RESTful 아키텍처 스타일을 사용하여 작성한 서비스이다. RESTful 방식의 웹 서비스 작성은 SOAP 기반 기술을 사용하여 인터넷에서 서비스를 배포하는 방법의 대안으로 새롭게 각광받고 있는데, 가벼울 뿐 아니라 직접 HTTP를 통해 데이터를 보낼 수 있기 때문이다.

RESTful 웹 서비스의 원리

RESTful 웹 서비스의 기본 원리는 다음과 같다.

  • 리소스(Resource)와 표현(Representation). 특정 웹 서비스에 단 하나의 종단점을 제공하고 그 종단점이 여러 작업을 수행하게 하지 않고 리소스에 대한 액세스를 제공한다. 리소스는 여러분이 클라이언트로 하여금 액세스할 수 있게 해주는 웹 애플리케이션의 한 부분이다. 이 리소스는 네트워크를 통해 전송할 수 없으므로 "이 리소스를 제공한다"는 것은 그 상태의 표현을 전달하는 것을 의미한다.

  • 주소 지정 가능성(Addressability)과 연결성(Connectedness). 리소스는 표현을 갖지만, 그 주소를 지정할 수 없다면 리소스의 표현을 전달하더라도 쓸모 없다. REST에서는 모든 리소스가 하나 이상의 주소, 즉 URI를 가져야 한다. 리소스 주소를 지정하려면 URI를 지정하면 된다. 이러한 개념을 "주소 지정 가능성"이라고 한다. 웹 애플리케이션을 게시하면 서로 연결된 많은 URI를 제시하게 된다. 그 연결성 덕분에 "부트스트랩 URI"라고 부르는 URI 하나만 클라이언트에게 전달하면 된다.

  • 동일한 인터페이스(Uniform interface) URI와 표현을 통해 클라이언트와 서버 간에 리소스를 주고 받을 수 있게 하더라도 아직 통신을 설정할 수는 없다. 사용할 통신 프로토콜/인터페이스가 필요하다. REST 아키텍처에서는 그러한 인터페이스가 동일해야 한다. 즉 어떤 URI에 액세스하더라도 인터페이스가 같아야 한다. 예를 들어, WWW에서는 어떤 URI(리소스 주소)에 들어가더라도 웹 브라우저에서는 HTTP GET 메소드를 사용하여 해당 웹 페이지(리소스 표현)를 읽어들여 표시한다.

  • 상태 비보존성(Statelessness). 상태 비보존성이란 웹 애플리케이션이 클라이언트의 상태에 대한 정보를 보관하지 않는다는 것이다. REST에서는 HTTP 세션의 개념을 수용하지 않는다. 클라이언트는 필요하다면 자신의 작업을 추적할 책임이 있다. 서비스는 리소스를 유지하면서 클라이언트에게 동일한 인터페이스를 제공한다.

JAX-RS와 Jersey

JAX-RS는 Java에서 RESTful 웹 서비스를 작성할 수 있는 표준화된 API를 제공한다. 이 API는 기본적으로 주석 그리고 관련된 클래스 및 인터페이스의 모음을 제공한다. 주석을 POJO(Plain Old Java Object)에 적용하면 웹 리소스를 공개할 수 있다. API는 아직 완성되지 않았다. 최종 버전은 Java EE 6에 포함될 것이다. JAX-RS에 대한 자세한 내용은 jsr311 프로젝트에서 확인할 수 있다.

Jersey는 JAX-RS의 참조 구현 중 하나이다. Jersey 프로젝트 다운로드 페이지에서 다운로드 가능한 Jersey 배포판을 구할 수 있다. 최신 Jersey 스냅샷을 선택하고 압축을 풀면 Jersey 구현이 그 사용법을 알려주는 몇 가지 예제와 함께 번들되어 있음을 알 수 있다. 그 예제 중 하나를 살펴보도록 하자.

JAX-RS 사용 예: 북마크 애플리케이션

Jersey와 함께 배포되는 예제 중 하나가 북마크 애플리케이션이다. examples/Bookmark 하위 디렉토리에 있는 이 애플리케이션에서는 JAX-RS API를 사용하여 사용자가 저장한 북마크에 대한 정보를 유지 관리한다. 이 애플리케이션을 실행하고 특정 사용자를 지정하면 그 애플리케이션은 다음과 비슷한 데이터를 반환한다.

   {sdesc":"test desc","userid":"testuserid","uri":
   "http://java.sun.com","ldesc":"long test description"}

이 애플리케이션은 JSON(JavaScript Object Notation) 형식으로 데이터를 반환한다.

examples/Bookmark 하위 디렉토리 아래의 resources 하위 디렉토리까지 이동하면 이 애플리케이션의 다음 리소스를 볼 수 있다.

  • UsersResource: 사용자 목록을 나타낸다.
  • UserResource: 특정 사용자를 나타낸다.
  • BookmarksResource: 특정 사용자의 북마크 목록을 나타낸다.
  • BookmarkResource: 특정 북마크를 나타낸다.

다시 말하자면 REST에서 어떤 리소스를 주소 지정하려면 URI를 지정한다. 그러나 리소스와 통신하기 위해서는 HTTP와 같은 통신 프로토콜도 지정해야 한다. 다음은 북마크 애플리케이션의 리소스에 해당하는 URI와 HTTP 메소드이다.

리소스
URI 경로
HTTP 메소드
UsersResource
/users
GET
UserResource
/users/{userid}
GET, PUT, DELETE
BookmarksResource
/users/{userid}/bookmarks
GET, POST
BookmarkResource
/users/{userid}/bookmarks/{bmid}
GET, PUT, DELETE
 

JAX-RS의 기본 원리를 이해하기 위해 이 리소스 중 두 가지를 살펴보자. UsersResourceUserResource이다.

UsersResource

다음은 UsersResource 클래스 소스 코드 중 일부이다.

   @UriTemplate("/users/")

   public class UsersResource {
     
       @HttpContext UriInfo uriInfo;    

       @PersistenceUnit(unitName = "BookmarkPU")
       EntityManagerFactory emf;

       /** Creates a new instance of Users */
       public UsersResource() {
       }
   
       public List<UserEntity> getUsers() {
           return emf.createEntityManager().createQuery(
                  "SELECT u from UserEntity u").getResultList();
       }
   
       @UriTemplate("{userid}/")
       public UserResource getUser(@UriParam("userid")
              String userid) {
           return new UserResource(
                  uriInfo, emf.createEntityManager(), userid);
       }

       @HttpMethod("GET")
       @ProduceMime("application/json")
       public JSONArray getUsersAsJsonArray() {
           JSONArray uriArray = new JSONArray();
           UriBuilder ub = null;
           for (UserEntity userEntity : getUsers()) {
               ub = (ub == null) ?
                      uriInfo.getBuilder() : ub.clone();
               URI userUri = ub.
                       path(userEntity.getUserid()).
                       build();
               uriArray.put(userUri.toString());
           }
           return uriArray;
       }
   }

UsersResource 클래스에는 @UriTemplate("/users/") 주석이 있다. @UriTemplate는 리소스의 URI 경로를 식별하는 JAX-RS 주석이다. 여기서 주석은 URI 경로를 /users/로 식별한다.

   @UriTemplate("/users/")

클래스에 @UriTemplate 주석을 추가하면 클래스는 "루트 리소스 클래스"가 된다. 또한 /users/ URI 경로에 액세스하는 클라이언트 요청의 경우 이 리소스가 적합한 응답을 제공해야 함을 의미한다. 또한 /users/ URI 경로가 북마크 웹 애플리케이션 전체에서 부트스트랩 URI 경로이다.

UsersResource 클래스에 포함된 또 다른 JSR-311 주석이 @HttpContext이다.

   @HttpContext UriInfo uriInfo;

이 주석은 클래스 필드 또는 메소드 매개 변수에 정보를 주입한다. UsersResource 클래스에서 @HttpContext는 URI에 대한 정보를 uriInfo 변수에 주입하고, 이는 URI 관련 정보를 제공할 때 사용할 수 있다.

UsersResource 메소드

UsersResource 클래스는 getUsergetUsersAsJsonArray라는 두 메소드를 갖는다. 일단 getUser를 건너뛰고 getUsersAsJsonArray를 집중적으로 살펴보도록 한다. getUsersAsJsonArray 메소드는 모든 사용자 리소스에 대한 URI를 반환한다. 이 메소드에는 두 개의 JSR 311 주석, 즉 @HttpMethod@ProduceMime이 추가되었다. @HttpMethod 주석은 주석이 추가된 메소드가 HTTP 요청 처리에 사용되어야 함을 의미한다. 또한 이 주석은 메소드가 응답할 HTTP 요청의 유형도 지정한다. 이 예제에서 이 주석은 getUsersAsJsonArray 메소드가 HTTP GET 요청을 서비스하도록 지정한다.

   @HttpMethod("GET")

이와 같이 REST 요청을 서비스하는 메소드를 "리소스 메소드"라고 부른다.

@ProduceMime 주석은 메소드가 생성할 수 있는 MIME 형식을 지정한다. 여기서 이 주석은 getUsersAsJsonArray 메소드가 기존의 모든 사용자 리소스의 URI 어레이를 포함하는 JSONArray 개체를 반환하도록 지정한다.

   @ProduceMime("application/json")


사용자 리소스 가져오기
사용자 리소스 가져오기

이 메소드는 다음과 같은 JSON 어레이 개체를 클라이언트에 반환한다.

   ["http://localhost:8080/Bookmark/resources/users/joe",
   "http://localhost:8080/Bookmark/resources/users/mary"]

JSON 어레이는 URI, 즉 두 사용자 리소스인 joemary의 링크를 포함한다.

getUser 메소드는 특정 사용자에 대한 정보를 가져온다. 예를 들어, 클라이언트가 사용자 joe에 대한 정보를 얻으려면 그 URI에서 리소스에 액세스한다. 여기서는 http://localhost:8080/Bookmark/resources/users/joe이다. UsersResource 클래스는 joe의 URI 경로(/users/joe)를 비롯해 /users/로 시작하는 경로에 대한 모든 요청을 서비스한다.

여기서 getUser 메소드에 @UriTemplate("{userid}/") 주석이 붙는 것이 중요하다. 그러면 메소드가 "하위 리소스 로케이터"가 된다. 또한 getUser 메소드에는 @UriParam 주석이 붙는다. 따라서 getUser 메소드가 호출되면 현재 요청 URI 경로의 사용자 ID가 userid 매개변수에 주입되는 것이다.

getUser 메소드에는 @HttpMethod 주석이 연결되지 않는다. 따라서 이 메소드의 출력은 리소스 클래스 개체로 간주한다. 즉 요청 처리가 리소스 클래스에 위임되며 거기서 알맞은 @HttpMethod-annotated 메소드를 찾을 것이다. getUser 메소드는 UserResource 개체를 반환하므로

   public UserResource getUser(@UriParam("userid")
                 String userid) {
           return new UserResource(...)

UserResource 클래스에서 알맞은 메소드가 호출된다.

사용자 삽입 이미지
사용자 리소스 가져오기


UserResource

언급한 대로 UsersResource 클래스의 getUser 메소드의 요청 처리는 새로 인스턴스화된 UserResource 개체의 메소드로 위임된다. 다음은 UserResource 클래스 코드 중 일부로 그 메소드 중 하나인 getUser를 보여 준다.

   @HttpMethod("GET")
   @ProduceMime("application/json")
   public JSONObject getUser() throws JSONException {
       if (null == userEntity) {
           throw new NotFoundException(
                  "userid " + userid + "does not exist!");
       }
       return new JSONObject()
           .put("userid", userEntity.getUserid())
           .put("username", userEntity.getUsername())
           .put("email", userEntity.getEmail())
           .put("password", userEntity.getPassword())
           .put("bookmarks",
                uriInfo.getBuilder().path("bookmarks").build());
    }

이 메소드 역시 @HttpMethod("GET")@ProduceMime("application/json") 주석이 붙는다. 여기서 getUsers 메소드는 HTTP GET 요청을 서비스하고 JSONObject 개체를 반환한다. JSONObject 개체는 특정 사용자의 표현(예: useridjoe인 사용자의 표현)을 포함한다.

UserResource에서 소스 코드의 나머지를 살펴볼 수 있다. @ConsumeMime와 같이 메소드가 수용할 수 있는 MIME 형식을 식별하는 JSR 311 주석을 더 볼 수 있다.

샘플 코드 빌드 및 배포

이 팁의 샘플 코드는 NetBeans 프로젝트로 사용 가능하다. NetBeans IDE 또는 명령줄에서 샘플을 빌드하고 배포할 수 있다. 어떤 경우에서든

  1. GlassFish V2를 다운로드하지 않았다면 다운로드하여 설치한다.

  2. Jersey 다운로드 페이지에서 최신 Jersey 스냅샷을 다운로드하고 그 압축을 푼다. 새로 추출된 디렉토리가 <sample_install_dir>/jersey로 나타나는데, 여기서 <sample_install_dir>은 샘플 애플리케이션을 설치한 디렉토리이다. 예를 들어, Windows 시스템에서 C:\에 압축을 풀었다면 새로 생성된 디렉토리는 C:\jersey이다.

NetBeans에서의 샘플 코드 빌드 및 배포

  1. NetBeans 5.5.1 IDE가 없으면 다운로드하여 설치한다.

  2. NetBeans IDE를 시작한다. 아직 하지 않았다면 다음과 같이 NetBeans에서 GlassFish V2를 등록한다.

    • Runtime 창에서 Servers 노드를 오른쪽 클릭한다.
    • Add Server를 선택한다.
    • 서버를 Sun Java System Application Server로 둔다.
    • Next 버튼을 클릭한다.
    • Browse 버튼을 클릭하고 GlassFish V2를 설치한 위치로 이동한다.
    • Choose 버튼을 클릭한다.
    • Next 버튼을 클릭한다.
    • GlassFish에 다른 암호를 선택하지 않았다면 Admin Password를 기본값인 adminadmin으로 설정한다.
    • Finish 버튼을 클릭한다.

  3. 다음과 같이 Bookmark 프로젝트를 연다.

    • File 메뉴에서 Open Project를 선택한다.
    • Bookmark 하위 디렉토리로 이동한다.
    • Open Project Folder 버튼을 클릭한다.

  4. 다음과 같이 Bookmark 프로젝트를 빌드하고 배포한다.

    • Projects 창에서 Bookmark 프로젝트 노드를 오른쪽 클릭한다.
    • Deploy Project를 선택하거나 F6(기본 프로젝트 실행)을 누른다.

명령줄에서의 샘플 코드 빌드 및 배포

  1. AS_HOME 환경 변수를 GlassFish v2 설치 디렉토리로 설정한다. 예를 들면 (여기서는 bash 구문임)

          export AS_HOME= <GF_install_dir>

    이다. 여기서 <GF_install_dir>은 GlassFish v2를 설치한 디렉토리이다.

  2. <sample_install_dir>/jersey 디렉토리 아래 /examples/Bookmark 디렉토리로 이동한다. 명령줄에 다음 명령을 입력하여 Bookmark 애플리케이션을 빌드한다(bash 구문).

          AS_HOME/lib/ant/bin/ant run-on-glassfish

샘플 코드 실행하기

다음과 같이 배포된 Bookmark 애플리케이션을 명령줄 HTTP 도구인 Curl을 사용하여 실행할 수 있다.

  1. Curl을 다운로드하지 않았다면 다운로드한다.

  2. 명령줄에 다음 명령을 입력하여 새 사용자를 추가한다(포맷팅 때문에 지금과 이후 단계에서 명령은 여러 행으로 표시한다).

          curl -i --data "{\"userid\":\"techtip\",\"username\":
          \"TechTip User\",\"email\":\"techtip@example.com\",
          \"password\":\"TEST\"}" -H Content-type:application/json
          -X PUT
          http://localhost:8080/Bookmark/resources/users/techtip/

    그러면 HTTP GET 요청이 UsersResource 클래스의 getUser 메소드로 디스패치되고, 이는 새 UserResource 개체를 인스턴스화한다. 이 요청은 다시 putUser 메소드에 디스패치된다.

    다음과 같은 출력이 나타나야 한다.

          HTTP/1.1 204 No Content
          X-Powered-By: Servlet/2.5
          Server: Sun Java System Application Server 9.1_01
          Date: Thu, 01 Nov 2007 14:31:53 GMT

  3. 명령줄에 다음 명령을 입력하여 사용자 목록을 가져온다.

          curl -i -X GET
          http://localhost:8080/Bookmark/resources/users/

    그러면 UsersResource 클래스의 getUsersListAsJson 메소드를 호출한다.

    다음과 비슷한 출력이 나타난다.

          HTTP/1.1 200 OK
          X-Powered-By: Servlet/2.5
          Server: Sun Java System Application Server 9.1_01
          Content-Type: application/json
          Transfer-Encoding: chunked
          Date: Thu, 01 Nov 2007 14:34:07 GMT

          ["http:\/\/localhost:8080\/Bookmark\/resources\/users\
          /techtip"]

  4. 명령줄에 다음 명령을 입력하여 사용자의 표현을 가져온다.

          curl -i -X GET
          http://localhost:8080/Bookmark/resources/users/techtip/

    그 결과 작업은 2단계와 비슷하다.

    다음과 같은 출력이 나타나야 한다.

          HTTP/1.1 200 OK
          X-Powered-By: Servlet/2.5
          Server: Sun Java System Application Server 9.1_01
          Content-Type: application/json
          Transfer-Encoding: chunked
          Date: Thu, 01 Nov 2007 14:35:38 GMT

          {"userid":"techtip","username":"TechTip User",
          "email":"techtip@example.com","password":"TEST",
          "bookmarks":"http:\/\/localhost:8080\/Bookmark\/resources
          \/users\/techtip\/bookmarks"}

요약

이 팁에서는 Java에서 JSR-311(Java API for RESTful Web Services) 사양에 부합하는 RESTful 웹 서비스를 작성하는 방법을 소개했다. JAX-RS에 대한 자세한 내용은 jsr311 프로젝트에서 확인할 수 있다. JAX-RS의 참조 구현인 Jersey에 대해서는 Jersey 프로젝트에서 확인할 수 있다.

저자 정보

Jakub Podlesak는 Jersey 프로젝트 팀원이다. 그는 GlassFish v2 웹 서비스 스택인 Metro 개발에 참여한 바 있다.

Paul Sandoz는 JSR 311: Java API for RESTful Web Services의 공동 사양 책임자 겸 구현 책임자이다. 그는 W3C, ISO 및 ITU-T 표준화 기구에서 일한 바 있으며, GlassFish 웹 서비스 스택, 특히 Fast Infoset의 표준화, 구현, 통합 및 상호 운용성 분야에서 성능과 관련된 여러 기술 개발 및 향상에 기여했다.


이 아티클의 영문 원본은
http://java.sun.com/mailers/techtips/enterprise/2007/TechTips_Nov07.html
에서 볼 수 있습니다.


출처: http://blog.sdnkorea.com/blog/471
Posted by 1010