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로 판단되지 않는 상태
- 웹/네트워크 프로토클을 통해 컴퓨터가 가진 자원을 전달하는 상태
- 사실상 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를 매핑한다.