REST API: 리소스 설계

xellos·2023년 2월 26일
0

REST

목록 보기
2/2

2. 리소스 설계

REST 리소스 패턴


컨텐츠 협상

컨텐츠 협상이란, 동일한 URI 리소스를 여러가지 표현형으로 제공하여 클라이언트가 원하는 하나를 선택할 수 있게 하는것을 말한다.

컨텐츠 협상에는 두 가지 패턴이 있다.

  • HTTP 헤더를 이용(권장): 복잡한 비즈니스 문제들을 따로 분리하기가 용이하고, 여러가지 표현형을 리소스 메서드 하나로 모두 처리할 수 있다는 장점도 있다.

  • URL 패턴 이용

1) HTTP 헤더를 이용한 콘텐츠 협상 (권장)

리소스를 생성/수정하려고 할 때, 클라이언트는 종단점으로 어떤 형태의 페이로드(payload)를 전송해야 한다. 반대로 서버가 다시 클라이언트로 응답할 때에도 역시 페이로드가 전달된다. 이 과정에서 페이로드는 HTTP 메세지 본문과 함께 전달된 HTTP 요청/응답 엔티티가 처리한다.

엔티티는 HTTP POST나 PUT 메서드로 서버에 요청하고 응답을 반환받는 과정에서 오간다. Content-Type 헤더는 서버가 보낸 엔티티의 MIME 타입을 가리킨다. 많이 사용되는 컨텐츠 타입을 다음과 같다.

  • text/plain
  • application/xml
  • text/html
  • application/json
  • image/gif
  • image/jpeg

클라이언트는 서버에 자신이 처리할 수 있는 미디어 타입을 명시하고 Accept 헤더에 그 우선순위를 나열한다. Accept-Language 헤더에서 원하는 언어를 지정할 수도 있다. Accept 헤더가 없다면 서버는 임의로 표현형을 선택한다.

만약, 클라이언트가 요청한 MIME 타입으로 리소스에서 결과를 보내줄 수 없다면 406(Not Acceptable) 에러를 반환한다.

스프링에서 특정 컨텐츠 소비

  • Mapping 애노테이션에서 consume 속성을 지정하여 특정 Content-Type 을 소비할 수 있다.
@PostMaping(value="/mapping-consume", consumes="application/json")
public String mappingConsumes() {
	log.info("mappingConsumes");
	return "ok";
}

2) URL 패턴을 이용한 콘텐츠 협상

API에서 자주 보는 방법으로, 리소스 URL 의 확장자를 보고 리소스 표현형을 결정하는 것이다. 서버는 사이한 URL을 구분하여 처리할 수 있게 2개의 메서드를 가지고 구분해야한다. 리소스는 동일한데 표현형만 다른 것이다.

  • XML
@PostMapping("/orders/xml")
public XmlResponse<Order> xmlOrders() {
	//...
}
  • JSON
@PostMapping("/orders/json")
public JsonResponse<Order> jsonOrders() {
	//...
}


엔티티 제공자와 여러가지 표현형

여기서는 현시점에서 가장 일반적으로 사용되는 JSON 표현형만 알아본다.

1) JSON 바인딩

implementation 'org.json:json:20210307'

POJO 기반의 JSON 바인딩

모든 자바객체를 JSON으로 매핑시킬 수 있는, 아주 포괄적인 방법이다. 매핑을 잭슨(Jackson) 라이브러리를 사용한다. 스프링에서는 기본적으로 사용한다.

ObejctMapper mapper = new ObjectMapper();
Coffee coffee = mapper.readValue(jsonData, Coffee.class);

String 형태의 JSON을 Object 매핑

//String 형태의 JSON 데이터를 가져온다.
String result = requestApi.getWeather(url);

//결과 데이터를 JSON 데이터로 파싱한다.
JSONObject jsonObject = new JSONObject(result);

mapper.readValue(
	//getJSONObject() 메서드에 접근하여 원하는 데이터 String 으로 가져온다.
    jsonObject.getJSONObject("coord").toString(),
    
   	//파싱할 타입의 클래스를 넘긴다.
    WeatherDto.Coord.class
);


API 버저닝

애플리케이션이 점진적으로 발전할 수 있도록, URI 설계 단계에서부터 버전을 명확히 구별할 수 있도록 틀을 마련해야 한다. 애플리케이션 운영과정에서 자주 수정될 리소스를 미리 예측하기란 사실살 불가능하다. API 버저닝의 목적은 리소스의 종단점과 주소를 정의하고 여기에 버전을 부여하는 것이다.

API 개발자는 버전 변경시 사람이 개입하여 수작업을 하지 않아도 HTTP 메서드의 의미와 상태코드가 계속해서 동작할 수 있게 해야한다. 애플리케이션이 운명하기 전까지는 API 버전은 계속 업데이트 되면서, 일부 구버전 API는 폐기되어야 한다.

따라서, 구 API 를 경유한 요청은 최신 API로 자동 리다이렉트 되도록 하던지, 아지면 적당한 에러 코드로 해당 API가 폐기되었음을 클라이언트에 알려야 한다.

API 버저닝은 어디에 버전을 지정하느냐에 따라 다음과 같이 분류할 수 있다.

  • URI 에 지정
  • 요청 쿼리 파라미터에 지정
  • Accept 헤더에 지정

1) URI에 버전 지정

리소스에 URL 버전을 명시하는 것이다. 예를 들어, 다음 URL에서 'v2' 라는 버전이 리소스 경로의 일부로 포함되어 있다.

http://localhost:8080/api/v2/books/1234

API 개발자는 기본적으로 최신 버전의 API 경로를 제공해야 한다. 다시 말해 V2이 최신 버전이라면 다음 두 URI는 동일한 리소스를 가리켜야 맞다.


클라이언트가 구버전 API에 접속하면 HTTP 에러 코드를 반환하여 최신버전 API로 옮겨갈 수 있도록 조치해야하 한다.

  • 301 Moved Permanatly: 요청한 경로의 리소스가 다른 URL로 영구이동 되었음을 의미한다. 구버전 또는 현재 지원하지 않는 버전의 API를 클라이언트가 사용하려고할 때 다른 리소스 퍼머링크로 대체되었음을 알린다.

  • 302 Found: 요청한 URI는 아직 유효하지만, 해당 리소스가 임시로 다른 경로에 옮겨졌을을 뜻한다.


2) 요청 쿼리 파라미터에 버전 지정

쿼리 파라미터에 버전을 추가하여 보내는 방법이다. 리소스 메서드는 요청 객체와 함께 전달된 버전 정보를 보고 어떤 코드를 실행할지 판단한다. 예를 들어 URL이 http://localhost:8080/books/1234?v=v2 라면 끝 부분의 쿼리 파라미터에서 요청버전이 v2 임을 알 수 있다.

단, 이 방법은 응답이 캐싱되지 않는다는 단점이 있다. 게다가 코드 레벨에서 쿼리 파라미터에 실려온 버전에 따라 흐름을 분기시켜야 하므로 그다지 직관적이지 않을 뿐더러 차후 유지보수에도 좋지 않다.


3) Accept 헤더에 버전 지정

다음과 같이 Accepter 헤더에 버전을 넣을 수도 있다.

  • 여기서 vnd 는 벤더 고유의 MIME 타입이다. 이렇게 하면 더이상 URL 에 버전 정보를 넣을 필요가 없는데, 이 방식을 선호하는 개발자들도 있다.
Accept: application/vnd.foo-v1+json


응답 코드와 REST 패턴

0개의 댓글