본문 바로가기
코딩 공부/web & Java

[Spring] HATEOAS

by 현장 2023. 11. 14.

HATEOAS (Hypermedia As The Engine Of Application State)

Hateoas란 REST API를 사용하는 클라이언트가 전적으로 서버와 동적인 상호작용이 가능하도록 하는 것을 의미합니다. 이러한 방법은 클라이언트가 서버로부터 어떠한 요청을 할 때, 요청에 필요한 URI를 응답에 포함시켜 반환하는 것으로 가능하게 할 수 있습니다.

 

🏷️ REST 성숙도 모델

https://grapeup.com/blog/how-to-build-hypermedia-api-with-spring-hateoas

이 모델은 위 이미지와 같이 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