[Spring] Spring Hateoas

박준형·2024년 12월 5일
0

Spring

목록 보기
15/17
post-thumbnail

포스팅에 앞서 그런 REST API로 괜찮은가 강의가 Hateoas를 이해하는데 큰 도움이 되었습니다.

Hateoas❓

Hypermedia as the engine of application state의 약자로 HATEOAS를 사용하면 클라이언트는 애플리케이션 서버가 하이퍼미디어를 통해 동적으로 정보를 제공하는 네트워크 애플리케이션과 상호 작용합니다. REST 클라이언트는 하이퍼미디어에 대한 일반적인 이해 외에 애플리케이션 또는 서버와 상호 작용하는 방법에 대한 사전 지식이 거의 필요하지 않습니다. 위키피디아 - Hateoas

영어를 직역해서 무슨 말인지 어렵지만, 여러 영상과 정보를 찾아본 결과 REST API를 이용하여 통신을 할 때 클라이언트는 하이퍼미디어에 대해 이해만 한다면 서버의 상태, 서버의 변경에 상관 없이 동적으로 상호 작용이 가능하고 해당 데이터와 관련한 작업도 하이퍼미디어를 통해 제공 받을 수 있다는 뜻인거 같다.

✅Example

// Request
GET /accounts/12345 HTTP/1.1
Host: bank.example.com
//Response
HTTP/1.1 200 OK

{
    "account": {
        "account_number": 12345,
        "balance": {
            "currency": "usd",
            "value": 100.00
        },
        "links": {
            "deposits": "/accounts/12345/deposits",
            "withdrawals": "/accounts/12345/withdrawals",
            "transfers": "/accounts/12345/transfers",
            "close-requests": "/accounts/12345/close-requests"
        }
    }
}

account_number가 12345인 계좌의 잔액을 조회하는 기능이다. 잔액이 100.00이 있으므로 해당 잔액과 연관된 기능들인 deposits, withdrawals, transfers, close-requests이 하이퍼미디어를 통해 동적으로 제공한다.

// Request
GET /accounts/12345 HTTP/1.1
Host: bank.example.com
//Response
HTTP/1.1 200 OK

{
    "account": {
        "account_number": 12345,
        "balance": {
            "currency": "usd",
            "value": -25.00
        },
        "links": {
            "deposits": "/accounts/12345/deposits"
        }
    }
}

반면에 잔액이 -25.00일 경우엔 deposits만 가능하기 때문에 해당 기능에 대한 하이퍼미디어만 제공된다.

✅ 결론

이런식으로 애플리케이션의 상태의 변화에 따라서 제공되는 하이퍼미디어가 달라지는게 Hateoas 이다. 이러한 Hateoas를 만족시켜야 성숙도가 높은 REST API가 된다.


REST API 성숙도 모델❓

위에서 말한 "성숙도가 높은 RESR API"라는 말이 생소 할 수도 있다. REST API의 성숙도 레벨은 0~3단계로 구성 되어있고, Hateoas는 REST API 성숙도 모델 레벨 3에서 등장한다.

Richardson 성숙도 모델

✅LEVEL 2: HTTP verbs

HTTP method인 GET, POST, PUT, DELETE를 사용해 CRUD를 나타내고 응답에 HTTP status code도 담아서 반환한다. GET은 데이터를 가져오기만 하고(멱등성), POST/PUT은 새 데이터를 만들거나 업데이트하며, DELETE은 데이터를 제거하게 된다. 현재 대부분 자칭 REST API라고 하는 것이 2단계에 속하는 것이다.

✅LEVEL 3: Hypermedia controls

성숙도 모델의 최종 단계로 Hateoas라고 불린다. 이 단계까지 와야 진정한 REST API라고 할 수 있다고 한다. 위에서 설명한 Hateoas에 대한 설명이 이 단계에 해당한다.


📌Spring에서 Hateoas

Spring에서는 Hateoas를 만족하기 위해 Spring Hateoas 기능을 제공한다.

✅Example

// Item 엔티티
@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
@FieldDefaults(level = PRIVATE)
public class Item {

    @Id
    @Column(name = "item_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    String name;

    public Item(String name) {
        this.name = name;
    }
}
// ItemController 메서드
@GetMapping("/detail/{id}")
public EntityModel<Item> detail(@PathVariable("id") Long id) {
    Item item = itemService.getDetail(id);

    return EntityModel.of(item,
            linkTo(methodOn(ItemController.class).detail(id)).withSelfRel(),
            linkTo(methodOn(ItemController.class).list()).withRel("list"));
}

@GetMapping("/list")
public CollectionModel<EntityModel<Item>> list() {
    List<Item> items = itemService.getAllList();

    List<EntityModel<Item>> entityModels = items.stream()
            .map(item -> EntityModel.of(item,
                    linkTo(methodOn(ItemController.class).detail(item.getId())).withSelfRel())
            )
            .toList();

    return CollectionModel.of(entityModels, linkTo(methodOn(ItemController.class).list()).withSelfRel());
}

list() 메서드를 실행한 응답값

{
    "_embedded": {
        "itemList": [
            {
                "id": 1,
                "name": "item1",
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/v1/item/detail/1"
                    }
                }
            },
            {
                "id": 2,
                "name": "item2",
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/v1/item/detail/2"
                    }
                }
            },
            {
                "id": 3,
                "name": "item3",
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/v1/item/detail/3"
                    }
                }
            },
            {
                "id": 4,
                "name": "item4",
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/v1/item/detail/4"
                    }
                }
            },
            {
                "id": 5,
                "name": "item5",
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/v1/item/detail/5"
                    }
                }
            }
        ]
    },
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/v1/item/list"
        }
    }
}

각 항목에 대한 세부 아이템 정보를 볼 수 있는 링크가 제공 되고 self-descriptive를 만족하기 위한 자기 참조 링크도 포함 되어 있다.

장점👍

Spring Hateoas를 사용하면 API에서 하이퍼미디어 링크를 제공하여 클라이언트가 리소스 간의 관계와 작업 흐름을 이해하고 유연하게 탐색할 수 있도록 한다. 이는 클라이언트가 API 구조를 쉽게 파악하고 필요한 작업을 수행할 수 있는 연결점을 찾는 데 도움을 주게 된다. 또한, 하이퍼미디어 링크를 통해 API의 변경에 유연하게 대응하고 새로운 리소스나 기능을 도입할 수 있으며, 버전 관리 시에도 기존 API와의 호환성을 유지하는 데 용이하게 된다.

단점👎

하이퍼미디어 링크를 관리하고 구성하는 과정에서 추가적인 복잡성이 발생할 수 있다. 또한, 하이퍼미디어 링크를 포함한 API 디자인은 매 요청마다 추가적인 링크 정보를 포함하게 되어 데이터 전송량이 증가하고 오버헤드를 초래할 수 있다. 클라이언트 측에서도 하이퍼미디어 링크를 이해하고 해석해야 하므로, 클라이언트 개발자가 추가적인 작업을 수행해야 할 필요가 있다. 마지막으로, 초기 단계에서 하이퍼미디어를 구현하고 관리하는 데 시간이 소요될 수 있어, 이로 인해 개발 초기 단계에서의 생산성이 저하될 수 있다.

profile
으쌰 으쌰

0개의 댓글

관련 채용 정보