본문 바로가기

Spring

HATEOAS 적용하기

깃허브 레포


Spring Boot를 이용한 RESTful Web Services 개발 강의를 들으며 Level3 단계의 REST API 구현을 위한 HATEOAS 적용 중이었다.

HATEOAS


개념

  • HATEOAS - Hypermedia As the Engine Of Application State
  • 현재 리소스와 연관된(호출 가능한) 자원 상태 정보를 제공한다.

REST API 설계 단계

  • Level 0: The Swamp of POX

    • 사실상 REST API로 판단되지 않는 상태
      • 웹/네트워크 프로토클을 통해 컴퓨터가 가진 자원을 전달하는 상태
  • Level 1: Resources

  • Level 2: HTTP Verbs

  • Level 3: Hypermedia Controls

적용시 장점

  • 하나의 리소스에서 파생할 수 있는 여러가지 추가적인 기능을 확인할 수 있다.
  • 개발한 API를 사용하는 사용자가 다양한 추가 정보를 한번에 알 수 있게 한다.

Build.gradle에 Spring HATEOAS 추가


build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-hateoas:2.6.1'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '2.5.2'
    compileOnly group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: '2.11.3'

}

org.springframework.boot:spring-boot-starter-hateoas를 추가해야한다.


코드 적용


강의를 들으며 확인한 템플릿 코드는 다음과 같다.

import static org.springframework.hateoas.Resource
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

public Resource<User> retrieveUser(@PathVariable int id){
    User user = service.findOne(id);

    if (user == null) {
        throw new UserNotFoundException(String.format("ID[%s] not found", id));
    }
    Resource<User> resource new Resource<>(User);
    ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());

    resource.add(linkTo.withRel("all-users"));

    return resource;
}

똑같이 적용을 하려하니 다음과 같이 Resource 클래스가 임포트되지 않았다.


따라서 Spring HATEOAS 공식 문서를 참조,
hateoas의 버전 업그레이드로 메소드들의 이름이 변경되거나 경로가 달라진게 문제가 되었음을 알게 되었다.

  • ResourceSupport -> RepresentationModel
  • Resource -> EntityModel
  • Resources -> CollectionModel
  • PagedResources -> PagedModel
  • ControllerLinkBuilder는 server.mvc로 이동되었으며 WebMvcLinkBuilder로 대체되었다.

수정된 코드

import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;

public EntityModel<User> retrieveUserV1(@PathVariable int id) {
        User user = service.findOne(id);

        if (user == null){
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        EntityModel<User> model = EntityModel.of(user);
        WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllusers());

        model.add(linkTo.withRel("all-users"));

        return model;
    }
  • EntityModel
    • 기존 개체를 래핑하는 데 사용
  • WebMvcLinkBuilder
    • 컨트롤러 클래스를 가리켜 링크를 생성할 수 있는 기능을 제공
    • methodOn
      • 메서드 호출을 기록하는 컨트롤러 클래스의 프록시를 생성하고 메서드의 반환 유형에 대해 생성된 프록시에 이를 노출한다.
      • add 메서드를 통해 link를 추가할 수 있다.
      • 반환 유형은 메서드 호출을 노출해야 하므로 프록시를 사용할 수 있어야 한다.
      • URI를 구성하기 때문에 메소드에 전달된 매개변수는 일반적으로 무시된다.(@PathVariable 제외)
    • linkTo
      • 해당 API(API 역할을 하는 Class의 메서드)의 URI를 매핑한다.

출처
Spring HATEOAS