HATEOAS (Hypermedia As The Engine Of Application State)
Hateoas란 REST API를 사용하는 클라이언트가 전적으로 서버와 동적인 상호작용이 가능하도록 하는 것을 의미합니다. 이러한 방법은 클라이언트가 서버로부터 어떠한 요청을 할 때, 요청에 필요한 URI를 응답에 포함시켜 반환하는 것으로 가능하게 할 수 있습니다.
🏷️ REST 성숙도 모델
이 모델은 위 이미지와 같이 4가지 수준으로 구성됩니다.
✅ 레벨 0
API 구현은 HTTP 프로토콜을 사용하지만 전체 기능을 활용하지는 않습니다. 또한 리소스에 대한 고유 주소가 제공되지 않습니다.
ex) method: POST URI: /movie
✅ 레벨 1
리소스에 대한 고유 식별자가 있지만 리소스에 대한 각 작업에는 고유한 URL이 있습니다.
ex) method: POST URI: /movie/1/delete
✅ 레벨 2
동작을 설명하는 동사 대신 HTTP 메소드를 사용합니다. 예를 들어 URL 대신 DELETE 메소드를 사용합니다.
ex) method: DELETE URI: /movie/1
✅ 레벨 3
HATEOAS라는 용어가 사용되는데 간단히 말해서 리소스에 하이퍼미디어를 도입합니다. 이를 통해 가능한 작업에 대해 알려주는 응답에 링크를 배치할 수 있으므로 API를 통해 탐색할 수 있는 가능성이 추가됩니다.
ex) method: DELETE URI: /movie/1
완벽한 RESTful API를 사용하려면 HATEOAS를 고려해야 합니다.
잘 설계된 REST API를 구현하기 위한 단계가 존재하는데 , 그 마지막 단계가 Hypermedia Controls (하이퍼미디어 컨트롤) - HATEOAS라는 개념을 통해, 자원에 호출 가능한 API 정보를 자원의 상태를 반영하여 표현하는 것입니다.
✔️여기서 하이퍼미디어라는 용어는 텍스트, 이미지, 영화와 같은 다른 형태의 미디어에 대한 링크를 포함하는 모든 콘텐츠를 의미합니다.
🏷️ 예시 코드
✅ 사용하는 의존성
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
✅ Entity
@Entity(name = "user_datails")
public class User {
@Id
@GeneratedValue
private Integer id;
@Size(min = 2, message = "이름의 최소 길이는 2글자입니다.")
@JsonProperty("user_name") // 응답 필드 커스터마이징(직렬화)
private String name;
@Past(message = "생일은 반드시 과거여야 합니다.")
@JsonProperty("birth_date")
private LocalDate birthDate;
@OneToMany(mappedBy = "user")
@JsonIgnore
private List<Post> posts;
public User(Integer id, String name, LocalDate birthDate) {
this.id = id;
this.name = name;
this.birthDate = birthDate;
}
public User() {
}
// getter setter toString 생략
}
✅ Controller
@RestController
public class UserResource {
private UserDaoService service;
public UserResource(UserDaoService service) {
this.service = service;
}
// GET /users
@GetMapping("/users")
public List<User> retrieveAllUsers() {
return service.findAll();
}
// GET /users/{id}
@GetMapping("/users/{id}")
public EntityModel<User> retrieveUsers(@PathVariable int id) {
User user = service.findOne(id);
if (user == null) {
throw new UserNotFoundException("id: " + id);
}
EntityModel<User> entityModel = EntityModel.of(user);
WebMvcLinkBuilder link = linkTo(methodOn(this.getClass()).retrieveAllUsers());
entityModel.add(link.withRel("all-users"));
return entityModel;
}
// 생략
}
➡️ EntityModel
EntityModel은 RepresentationModel 클래스를 상속받는데 RepresentationModel 클래스는 Link 객체를 담을 수 있고 EntityModel 클래스에서는 해당 링크를 가진 자원 객체(content)를 담을 수 있습니다.
Spring HATEOAS에서는 이 EntityModel을 이용하여 응답에 사용할 객체를 캡슐화하고 링크 객체를 등록하여 클라이언트에게 현재 접근할 수 있는 자원을 명시하는 RESTful 한 응답을 쉽게 생성할 수 있습니다.
➡️ WebMvcLinkBuilder
다른 자원에 접근하는 링크를 일일이 하드코딩으로 지정하면 해당 링크가 변경되었을 때 유연하게 대처하기 힘듭니다. 이때는 WebMvcLinkBuilder 클래스의 linkTo, methodOn 정적 메서드를 사용하여 다음처럼 특정 컨트롤러 클래스의 Mapping 메서드를 기반으로 자동으로 링크를 생성할 수 있습니다.
✅/users/2의 응답
🏷️ 장점
- 서버와 클라이언트를 분리하여 개발자는 클라이언트 중단에 대해 걱정하지 않고 API를 업데이트하고 발전시킬 수 있습니다. ( HATEOAS의 가장 큰 장점 )
- REST API의 HATEOAS는 웹 페이지의 하이퍼링크와 같습니다. 사용자는 도메인 이름에서 웹사이트를 탐색하고 하이퍼링크를 클릭하여 하이퍼링크에 대한 사전 지식 없이 콘텐츠를 탐색할 수 있습니다.
- REST 클라이언트는 API를 사용하기 위해 리소스 URI에 대한 사전 지식이 필요하지 않습니다.
📖 Reference
'코딩 공부 > web & Java' 카테고리의 다른 글
[Spring] Actuator (0) | 2023.11.16 |
---|---|
[Spring] 직렬화(Serialize) (1) | 2023.11.15 |
[Spring] REST API 버전 관리 (0) | 2023.11.12 |
[Spring] 국제화 (0) | 2023.11.12 |
OpenAPI와 Swagger (1) | 2023.11.09 |