카샤의 만개시기

마이크로서비스 시작하기 (6편) - HATEOAS 본문

Java/MSA

마이크로서비스 시작하기 (6편) - HATEOAS

SKaSha 2019. 6. 24. 13:13

좋은 REST API는 GET, PUT, POST, DELETE 등과 같은 동사와 HTTP 헤더, 상태 코드 등 HTTP의 기능을 최대한 올바르게 활용하는 것을 말합니다.
REST는 어떤 기술 표준이 아닌 HTTP에 대한 일종의 아키텍처 제약사항이며 레너드 리차드슨 (Leonard Richardson)은 REST 성숙도 모델을 통해 API의 REST 원칙 준수 등급을 정의하였습니다.

REST 성숙도 모델

LEVEL 0. POX(Plain Old XML)의 늪

REST를 도입하기 전 상태를 말합니다.
모든 전송과 응답을 POST로 하며 접근 가능한 엔드 포인트는 하나이며 HTTP의 body에 정보를 넣어 전송하는 기존의 리소스 전송 방식을 사용합니다.

LEVEL 1. 자원

고유의 URI로 각각의 제공하는 자원을 주고 받습니다.
모든 자원을 제공함으로써 클라이언트는 다양한 자원과 포맷(JSON, XML 등)을 제공 받을 수 있습니다.
리소스를 어떻게 나누고 합칠 것인지를 고려해야 합니다.

LEVEL 2. HTTP 동사

URL과 HTTP Method, 상태 코드 등을 적극적으로 활동하며 가장 널리 알려진 방법입니다.

LEVEL 3. 하이퍼미디어 컨트롤(HATEOAS)

하이퍼 미디어의 링크를 이용하여 서비스가 제공하는 자원에 접근하기 위해 아무런 사전 지식도 요구하지 않는 수준의 API를 기술합니다.
서버가 클리이언트에게 자원을 보내면서 다음 작업을 할 수 있는 URL을 링크로 같이 보냅니다.
클라이언트는 링크를 확인하고 다음 작업을 할 수 있는 URL을 확인합니다.
REST API의 URL 변경시 단점을 해결 할 수 있습니다.
어떻게 전달해야 링크를 보고 리소스를 찾아갈 수 있는 문서가 될 수 있는지를 고려해야 합니다.

HATEOAS (Hypermedia As The Engine Of Application State)

하이퍼 미디어 어플리케이션의 상태를 관리하기 위한 매커니즘을 말합니다.
REST API에서 서버가 클라이언트에 리소스를 넘겨줄 때 특정 부가적인 리소스의 링크 정보를 넘겨주어 클라이언트가 이를 참고하여 사용 할 수 있도록 합니다.

HATEOAS를 사용하는 이유는 다음과 같은 REST API의 단점을 보완하기 위해서입니다.

  1. End Point URL 변경 제한.
    엔드 포인트 URL을 변경하면 모든 클라이언트의 URL 역시 변경해야 합니다.
  2. 자원의 상태를 고려하지 않음.
    전달받은 정적 자원의 상태에 따른 로직이 클라이언트에서 처리되어야 함.

위 단점들을 LINK에 사용 가능한 URL을 리소스로 전달하여 클라이언트가 참고하여 사용 할 수 있도록 하여 극복합니다.
이러한 링크정보를 JSON으로 표현한 사실상의 표준이 있는데, 바로 HAL(Hpyertext Application Language)이며 컨텐트 타입은 application/hal+json입니다.
HAL을 사용하면 REST 자원을 표시하기 위한 자료 구조를 그때그때 따로 만들지 않아도 HATEOAS를 구현할수 있습니다.

스프링 부트는 HAL브라우저라고 하는 아주 편리한 HAL 클라이언트를 지원합니다.
org.springframework.boot:spring-boot-starter-actuator
org.springframework.data:spring-data-rest-hal-browser를 의존 관계로 추가하면 됩니다.
스프링 HATEOAS는 스프링 MVC 위에 존재하며 자원에 대한 정보와 관련 링크를 기술하고 사용하는 데 필요한 기능을 제공합니다.
데이터와 관련 링크를 담는 Resource 클래스는 T타입의 객체를 ResourceAssembler를 이용해 Resource나 Resources로 전환하여 사용합니다.

HATEOAS는 Link 구조를 가지고 있어서 자신과 연관 된 다른 MicroService들을 연결할때 이용됩니다.

또한 마이크로서비스간 RestTemplate를 이용하여 API를 호출할때도 링크 경로를 받아 링크를 따라가고 최종 결과를 반환해주는 Traverson 클라이언트를 제공합니다.
아래와 같이 base URI와 클라이언트가 처리해야할 미디어타입을 지정해서 traverson bean을 등록합니다.

@Bean
@Lazy
Traverson traverson(RestTemplate restTemplate) {
    Traverson traverson = new Traverson(this.baseUri, MediaTypes.HAL_JSON);
    traverson.setRestOperations(restTemplate);
    return traverson;
}

Traverson의 follow 메소드가 링크를 연쇄적으로 따라가는 일을 간단하게 만들어주며 toObject 메소드에 ParameterizedTypeReference의 서브 클래스를 파라미터로 넘겨서 결과값을 제네릭을 포함하는 Resources로 변환할 수 있습니다.

Resources<Actor> actorResources = this.traverson
   .follow("actors", "search", "by-movie")
   .withTemplateParameters(Collections.singletonMap("movie", nameOfMovie))
   .toObject(new ParameterizedTypeReference<Resources<Actor>>() {
   });

NEXT

마이크로서비스 시작하기 (7편) - 라우팅

Comments