관리 메뉴

백엔드 엔지니어 이재혁

진짜 REST API와 HATEOAS 본문

흥미로운 것들

진짜 REST API와 HATEOAS

alex00728 2025. 7. 13. 15:39

사전 안내: 이 글은 현재 널리 사용되고 있는 REST API의 의미를 바꾸자는 글이 아니다.

 

 

얼마 전, GeekNews에 올라온 글을 보고 REST API의 원래 의미를 알게 됐다.

 

대부분의 RESTful API는 실제로 RESTful하지 않음 | GeekNews

Roy Fielding의 REST 원조 논문에서는 HTTP 메서드나 CRUD 중심의 API 사용을 명확히 규정하지 않으며, REST는 원래 하이퍼미디어 기반(HATEOAS) 시스템 설계를 강조함많은 이들이 RESTful API라 부르는 것들은

news.hada.io

 

REST의 진짜 의미를 확인하고 어떤 점에서 의미 있는지 확인해보자.

 

오해. REST와 HTTP의 관계

REST는 원래 프로토콜 종속적이지 않다. API의 동작을 HTTP 메서드로 정의해야 할 필요도 없다.

 

다만, 그렇게 규정한다고 해서 REST가 아닌 것은 아니다.

따라서, 이 오해를 바로 잡는다고 해서 현재 널리 쓰이는 REST API들이 REST가 아닌 것은 아니다.

 

지금의 REST API를 엄격히 REST라고 부를 수 없는 이유는 self-descriptive하지 않고, HATEOAS가 적용되지 않았기 때문이다.

 

REST: self-descriptive

REST 원칙 중 self-descriptive는 응답에 상태 코드, 미디어 타입, 링크 정보 등 클라이언트가 추가 문서 없이도 API 사용 방법을 이해할 수 있도록 정보를 포함해야 한다는 원칙이다.

하지만 Swagger 등 별도의 문서화 도구 발전하면서, 실제 응답에는 이러한 설명이 생략되는 경우가 많다.

 

self-descriptive 부분은 이정도만 확인하고 핵심 차이인 HATEOAS 내용으로 넘어가자.

 

REST의 핵심 제약: HATEOAS

HATEOAS(Hypermedia As The Engine Of Application State)는 클라이언트가 전적으로 서버와 동적인 상호작용이 가능하게 해준다. 이를 통해 클라이언트-서버 결합도를 최소화할 수 있다.

 

예를 들어 서버는 다음과 같은 응답을 줄 수 있다.

{
  "orderId": 123,
  "\_links": {
    "self": { "href": "/orders/123" },
    "cancel": { "href": "/orders/123/cancel", "method": "POST" }
  }
}

 

클라이언트는 위 응답을 통해, 주문번호 123의 정보를 확인하는 방법과 주문을 취소하는 방법을 알아낼 수 있다.

 

HATEOAS 적용 전후 비교

서버측에서 주문 정보를 "수정"할 수 있는 기능을 추가했다고 가정해보자.

 

현재 일반적인 REST API

서버측에서는 수정 기능이 있는 API 엔드포인트를 추가하고 끝낸다.

가령 `/orders/{id}/modify`라는 엔드포인트를 추가할 수 있다.

 

이 때, 클라이언트 측에서 직접 주문을 수정하는 API에 접근할 수 있는 기능을 추가해야 작업이 완료된다.

즉, 서버-클라이언트 결합도가 높다.

 

이번에는 HATEOAS를 통해 결합도를 낮춰보자.

 

HATEOAS가 적용된 REST API

클라이언트 측에서는 API 응답에서 `\_links` 목록을 배열로 사용해, 동적으로 `<button>`을 생성한다.

버튼을 클릭하면 전달받은 `href`와 `method`를 통해 서버 측에 요청하도록 만든다.

 

API 응답

{
  "orderId": 123,
  "\_links": {
    "self": { "href": "/orders/123" },
    "cancel": { "href": "/orders/123/cancel", "method": "POST" }
  }
}

 

클라이언트에서 동적으로 버튼 생성

 

 

서버 업데이트 후 API 응답

{
  "orderId": 123,
  "\_links": {
    "self": { "href": "/orders/123" },
    // REST에 따르면 modify 동작을 method: PUT을 통해 정의할 의무는 없다.
    "modify": { "href": "/orders/123/modify", "method": "POST" },
    "cancel": { "href": "/orders/123/cancel", "method": "POST" }
  }
}

 

클라이언트에서 동적으로 버튼 생성

서버가 업데이트된다고 해서 클라이언트도 업데이트할 필요가 없어졌다. 즉, 결합도가 낮아졌다.

 

사실 이렇게 하면 API에 버저닝도 필요 없다.

서버측 수정사항이 발생하면, 클라이언트는 응답에 기반해 동적으로 작동할 수 있기 때문이다.

 

실무에서 REST API가 HATEOAS를 잘 사용하지 않는 이유

REST 원칙의 초기 학습 부담이 높고, 동적 링크 파싱의 복잡성 등 실용적 장벽이 크다.
바로 위 예시만 해도 동적으로 작동하게 만드는 비용이 있는데,

현대의 복잡한 클라이언트-서버 간 상호작용을 모두 동적으로 만드는 비용은 어마무시하다...

 

내부 전용 API라면 단순한 RPC 스타일도 충분히 실용적이다.

 

정리

HATEOAS를 통해 클라이언트와 서버 간의 결합도를 낮추는 방법을 확인해볼 수 있었다.

 

하지만 반드시 HATEOAS를 도입할 필요는 없다. 부분적으로만 도입하는 것도 방법이라고 생각한다.

원칙은 좋은 방향성을 제시하는 것이지 실제 개발 환경에서는 비용도 고려하며 사용해야 한다.