RESTful API와 HATEOAS

HodaeSsi·2024년 3월 17일
post-thumbnail

1. RESTful API

Representational State Transfer, REST는 Roy Fielding이 제안한 아키텍쳐 스타일로, 일종의 "제약조건"이다. REST는 아래 6개의 제약조건들로 이루어져있다.

  1. client-server
  2. stateless
  3. cache
  4. uniform interface
  5. layered system
  6. code-on-demand (optional)

(*ref. REST 창시자인, Roy Fielding의 논문 : https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)

4번 제약조건인 'uniform interface'는 다시, 아래의 제약조건들로 이루어져있다.

  1. identification of resources
  2. manipulation of resources through representations
  3. self-descriptive messages
  4. hypermedia as the engine of application state (HATEOAS)

"애플리케이션 상태에 대한 엔진으로써의 하이퍼미디어", 번역해봐도 잘 와닿지가 않는다. 우선, HATEOAS에 대해 더 알아보기 전에, 다시 REST로 돌아가보자.

Representational State Transfer, '표현적 상태 전송', 사실 REST부터도 번역이 잘 와닿지는 않지만, 쉽게 표현해보자면 "리소스의 상태를 전달해라."라고 할 수 있겠다.

흔히 우리가 보는 API 형태는 다음과 같다.

[Request]
GET /posts HTTP/1.1
Host: www.example.com

[Response]
HTTP/1.1 200 OK
Content-Type: application/json
[
	{"id": 1, "title": "포스팅1", "contents": ... },
    {"id": 2, "title": "포스팅2", "contents": ... },
    ...
]

요청으로 'post들에 대한 상태 조회'를 요청하였고, 응답으로 'posts들에 대한 상태'를 반환하였다. 대부분의 API는 위의 수준에서 구성되어진다.

notion_api *참고 : Notion API Docs

그럼 우리는 REST 제약조건을 모두 준수한 RESTful API를 구성한 것일까? REST 창시자 Roy Fielding은 아니라고 단언한다. (제발 좀 REST API가 아닌, HTTP API, 혹은 그냥 API라고 불러달라 한다고.)

사실, 위의 노션 API와 같은 수준의 API로도 대부분의 REST 제약조건을 만족한다. 하지만 대부분의 API들이 두 개의 제약조건을 준수하지 못하고 있는데, uniform interface의 self-descriptive messageshypermedia as the engine of application state 가 그것이다. 이 중, HATEOAS에 대해서 알아보자.

2. HATEOAS

HATEOAS를 만족하려면, 리소스에 대한 현재 상태에 더불어서, "리소스가 전이될 수 있는 상태"를 포함하여야 한다. 예를 들면 아래와 같다.

{
  "id": 1,
  "name": "HodaeSsi",
  "email": "unknown@example.com",
  "links": [
    {
      "rel": "self",
      "method": "GET",
      "href": "http://api.example.com/users/1"
    },
    {
      "rel": "users",
      "method": "GET",
      "href": "http://api.example.com/uesrs"
    },
    {
      "rel": "update-user",
      "method": "PUT",
      "href": "http://api.example.com/users/1"
    },
    {
      "rel": "delete-user",
      "method": "DELETE",
      "href": "http://api.example.com/users/1"
    },
    {
      "rel": "create-user",
      "method": "POST",
      "href": "http://api.example.com/users"
    }
  ]
}

links 프로퍼티의 하이퍼미디어, 즉 링크를 통해 현재 리소스로부터 전이 가능한 상태를 표현하고있다.

  • self - 현재 리소스에 대한 링크
  • users - 상위 리소스에 대한 링크
  • update-user, ... - 전이 가능한 상태에 대한 링크

우리가 알고있던 API보다 응답이 더욱 복잡해졌다. REST의 스펙에는 왜 이런 복잡한 제약조건이 걸려있는 걸까.

REST가 제시된 이유는 한참 HTTP와 WEB이 발전하던 시기에, "어떻게 하면 WEB을 해치지 않으면서 HTTP를 발전시킬 수 있을까?"의 고민에서 창안되었다고 한다. 즉 "하위호환성""클라이언트와 서버간의 분리"를 목표로하여 제시된 설계 스타일이다.

위의 예시응답의 HATEOAS와 더불어 또 다른 제약조건인 self-descriptive messages 까지 만족하면, 클라이언트는 서버의 변경으로부터 완전히 자유로워 진다. 그 대표적인 예시가 아주 가까이 있는데, 바로 HTML과 웹이다.

HTML은 <a> 태그와 링크를 통해 리소스의 전이상태를 표현하고, HTML을 구성하는 모든 태그와 프로퍼티는 공통의 명세로 정의되어 있어, 클라이언트인 웹 브라우저는 문서 자체로 그 문서의 해석이 가능하다.(self-descriptive 하다.) 또한, HTML을 응답으로 사용하는 한, 서버의 변경사항이 클라이언트인 웹 브라우저에 영향을 미치지 않는다. 그에 반해 우리가 주로 맞닥뜨리는 API(주로 JSON을 이용한)는 해당 개발자만이 알고있는 임의의 프로퍼티 이름과 구조를 사용하므로 별도의 명세 문서 없이 문서 자체만으로는 해석이 불가능하고, 서버의 응답에 변경사항이 생기면 클라이언트 또한 변경이 강제된다.

결론

그럼 이제까지의 우리의 API는 전부 잘 못 되었고, REST 제약조건을 모두 만족하는 API로 바꿔야 할까? Roy Fielding 본인 또한 그럴 필요는 없다고 한다. REST 제약조건은 서버와 클라이언트간의 분리를 통해 서로 영향을 주지 않고 버전업될 수 있도록, 극한의 하위호환성을 지키고자 하는 과정속에서 나온 결과물이다. 클라이언트 개발자와 백엔드 개발자가 서로 협업하는 일반적인 업무 환경에서는 이 정도의 제약사항은 오히려 복잡함과 어려움 등의 비용을 발생시키는 제약사항으로 작용할 수 있다.

역시나 결론은 진부하지만, "기술과 스펙은 자신의 상황과 목적에 맞게 결정하고 적용하여야 한다."로 끝맺을 수 있겠다.

profile
항상 다같이, 즐겁게 일할 수 있으면 좋겠습니다 😎

0개의 댓글