RESTful 자바 패턴과 실전 응용: 2장 리소스 설계
2장 리소스 설계
Contents Negotiation (콘텐츠 협상)
동일한 URI의 리소스를 여러 가지 표현형으로 제공하여, 클라이언트가 원하는 하나를 선택할 수 있게 하는 것을 말한다. 콘텐츠 협상에는 HTTP 헤더를 이용하는 방법과 URL 패턴을 이용하는 두 가지 방법이 있다. HTTP 헤더를 이용해서 처리하는 것이 더 좋다. 복잡한 비즈니스 관련 문제들을 따로 분리하기 용이하기 때문이다.
HTTP 헤더를 이용한 콘텐츠 협상
- Accept
- @Produces(MediaType.APPLICATION_JSON)
- HTTP 406 Not Acceptable: Accept 요청에 정의된 유형을 서비스할 수 없을 경우
- HTTP 415 Unsupported Media Type: 요청 유형을 지원하지 않을 경우
- Runtime Contents Negotiation in JAX-RS
- Variant
- JAX-RS 명세에서 Variant는 미디어 타입, 콘텐츠 언어, 콘텐츠 인코딩, ETags, 최근 변경 헤더, 기타 다른 필수 조건들이 조합된 객체로, 서버가 지원하는 리소스 표현형을 나타낸다.
URL 패턴을 이용한 콘텐츠 협상
URL의 확장자(.xml, .json 등)에 따라 처리한다.
StreamingOutput과 ChunkingOutput은 위보다 더 가벼운 구현체를 제공한다.
아래와 같이 ObjectMapper를 활용하여 POJO 객체를 JSON으로 매핑시킬 수 있다.
POJO 객체에 @XMLRootExlement annotation을 붙이면, @Produces annotation에서 알아서 JSON 표현형으로 변환해준다.
마지막으로 JSONObject를 이용하여 저수준에서 JSON 파싱, 생성을 할 수 있다.
엔티티 제공자와 여러 가지 표현형
Java 클래스를 entity(representation)으로 변환하거나 역변환하는 방법들에 대해 설명한다. 변환은 Java Serializable을 이용한다. Serialization은 MessageBodyWriter, Deserialization은 MessageBodyReader를 이용한다.
- MessageBodyReader
- Inbound entity -> Java로 deserialization
- Representation-to-Java
- Methods
- isReadable(): 스트림을 자바 타입으로 바꿀 수 있는 체크한다.
- readFrom(): InputStream 클래스에서 타입을 읽어들인다.
- MessageBodyWriter
- Java -> Outbound entity로 serialization
- Java-to-Representation
- Methods
- isWritable(): 자바 타입을 스트림으로 바꿀 수 있는 체크한다.
- getSize(): 사이즈를 알 수 있거나 값이 -1일 때, 바이트 수를 얻는다.
- writeTo(): 타입을 스트림으로 바꾸어 출력한다.
StreamingOutput과 ChunkingOutput은 위보다 더 가벼운 구현체를 제공한다.
javax.ws.rs.core.StreamingOutput
org.glassfish.jersey.server.ChunkedOutput
ChunkedOutput은 nonblocking 모드로 동작한다.Jersey의 JSON 처리
Jersey에서는 JSON 처리를 위한 여러 방법을 제공하고 있다. (https://jersey.github.io/documentation/1.19.1/json.html)아래와 같이 ObjectMapper를 활용하여 POJO 객체를 JSON으로 매핑시킬 수 있다.
POJO 객체에 @XMLRootExlement annotation을 붙이면, @Produces annotation에서 알아서 JSON 표현형으로 변환해준다.
마지막으로 JSONObject를 이용하여 저수준에서 JSON 파싱, 생성을 할 수 있다.
API Versioning
API 폐기URI에 버전 지정
- http://api.foo.com/coffees/1234 (최신 버전의 URI)
- http://api.foo.com/v2/coffees/1234 (버전2의 URI. 버전2가 최신 버전이라면 위와 동일)
- HTTP 301 Moved Permanently
- 요청한 경로의 리소스가 다른 URI로 영구 이동되었음을 의미한다. 구버전 또는 현재 지원하지 않는 버전의 API를 클라이언트가 사용하려고 할 때, 대체되었음을 알린다.
- HTTP 302 Found
- 요청한 URI는 아직 유효하지만, 해당 리소스가 임시로 다른 경로에 옮겨졌음을 뜻한다.
- URI에 버전을 지정하면 가독성이 좋음. URI 버전의 수명 체계를 표준화시켜 구버전을 호출하는 모든 요청을 최신 버전 URI로 리다이렉트할 수 있다.
요청 쿼리 파라미터에 버전 지정
- http://api.foo.com/coffees/1234?version=v2
- 쿼리를 이용하면, 응답이 캐시가 되지 않음. 쿼리 파라미터에 따라 흐름이 분기되어 직관적이지 않고, 유지보수에 좋지 않음
Accept 헤더에 버전 지정
- Accept: application/vnd.foo-v1+json
- github
- Accept: application/vnd.github.v3+json
응답 코드와 REST 패턴
- 성공 2XX
- 200 OK
- PUT, POST, DELETE 메소드로 각각 생성, 갱신, 삭제 작업을 성공적으로 마쳤다. 요청 데이터가 응답에 포함되어 리턴된다.
- 201 Created
- PUT 메소드로 리소스가 생성되었다. 응답에 리소스의 Location 헤더가 포함되어 있어야 한다.
- 202 Accepted
- 서버에서 처리가 아직 완료되지 않아 응답으로 보류한 상태로 비동기 요청 시 쓰인다. 클라이언트 측에서 요청을 모니터링 할 수 있도록 리소스의 Location 헤더를 리턴해야 한다.
- 204 No Content
- DELETE, POST, PUT 메소드를 사용하여 요청을 처리했지만, 클라이언트에게 응답으로 보내줄 정보가 없을 때 사용한다.
- 리다이렉션 3XX
- 301 Permanent
- 모든 요청이 새로운 경로로 리다이렉트되었다.
- 302 Found
- 리소스가 이미 존재하고 유효하다.
- 클라이언트 에러 4XX
- 401 Unauthorized
- Credential에 문제가 있어 요청을 처리할 수 없다.
- 404 Not Found
- 리소스를 찾을 수 없다. 인증되지 않은 요청에 의한 정보 유출을 막기 위해서도 이 응답을 보내는 것이 좋다.
- 406 Not Acceptable
- 클라이언트가 지정한 MIME 타입으로 리소스가 응답할 수 없다. Accept 헤더에 지정된 MIME 타입이 리소스 메소드/클래스의 @Produces 어노테이션으로 지정된 미디어 타입(들)에서 찾을 수 없다.
- 415 Unsupported Media Type
- 클라이언트가 전송한 미디어 타입을 리소스가 사용할 수 없다. Content-Type헤더에 지정된 MIME 타입이 리소스 메소드/클래스의 @Consumes 어노테이션으로 지정된 미디어 타입(들)에서 찾을 수 없다.
- 서버 에러 5XX
- 500 Internal Server Error
- 정확히 원인을 알 수 없지만 서버 측에서 에러가 발생했을 때 보내는 일반적인 응답 코드다.
- 503 Service Unavailable
- 서버가 점검 중이거나 처리 부하가 극심하여 현재 요청을 처리할 수 없는 상태다.
javax.ws.rs.core.Response 클래스에는 javax.ws.rs.core.Response.ResponseBuilder를 사용하여 응답코드에 따라 인스턴스를 쉽게 생성할 수 있다.
댓글
댓글 쓰기