본문 바로가기

WEB&서버

HTTP HATEOAS

하이퍼미디어는 멀티미디어(텍스트, 이미지, 영상 등)를 하이퍼 링크를 통해 이동할 수 있는 비선형 구조를 의미한다.

HATEOAS는 직역하면 "어플리케이션 상태 엔진으로써의 하이퍼미디어"이다. 상태 엔진을 state machine으로 해석하면, 애플리케이션의 상태를 전이(조작)하는데 하이퍼 미디어를 이용한다 정도의 의미가 된다. 구체적으로는 서버가 응답할 때 요청된 자원과 해당 자원과 관련된 링크를 함께 보내는 형식으로 표현된다.

{
  "id": 123,
  "name": "John Doe",
  "age": 30,
  "links": [
    {
      "rel": "self",
      "href": "https://api.example.com/users/123"
    },
    {
      "rel": "update",
      "href": "https://api.example.com/users/123",
      "method": "PUT"
    },
    {
      "rel": "delete",
      "href": "https://api.example.com/users/123",
      "method": "DELETE"
    }
  ]
}

HATEOAS가 사용되지 않는 이유에 대한 고찰

많은 RESTful API는 이 조건을 만족하지 않는다. 다양한 이유가 있겠지만, 가장 큰 이유는 HATEOAS를 만족하지 않더라도 서비스를 구현할 수 있다는 점이 크다고 생각한다.

웹 서비스를 구축하는 경우를 생각해보자. 내부적으로 사용되는 API는 HTML 상의 폼이나 버튼 등을 통해 간접적으로 호출된다. 애초에 주 사용자인 유저가 API를 이용하여 자원을 직접 조작하지 않기 때문에 사용하지도 않을 정보를 페이로드에 포함할 필요가 없다. 오히려 의도하지 않은 자원 조작의 가능성을 막기 위해 API를 숨기는 편이 좋을 수도 있다.

HATEOAS를 만족하도록 API를 구성한다면, 링크에 포함되는 각 동작에 필요한 조건은 어떻게 명시하는 것이 좋을까? delete 작업이 관리자 권한을 요구한다고 생각해 보자. 메시지 자체만으로도 delete 작업을 수행하려면 delete 작업에 필요한 제약 조건을 메시지 수준에서 알 수 있어야 할 것이다.

내가 생각했을 때는 크게 2가지 방법이 있을 것 같다.

 첫번째로, 응답 메시지에 "login": true 또는 "permission":"admin" 등 API 처리에 필요한 조건을 직접 명시하는 방법이다. 이 방법은 API에 적용되는 구체적인 조건을 메시지만 봐도 알 수 있기 때문에 사용자가 별도 문서를 조회할 필요가 없다.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 123,
  "name": "John Doe",
  "position": "Manager",
  "salary": 50000,
  "links": [
    {
      "rel": "delete",
      "href": "/employees/123",
      "method": "DELETE",
      "required_permission": "admin"
    }
  ]
}

그런데, 이런 조건을 전부 명시하는 것이 현실적일까? 예를 들어 네이버 댓글 조회 API는 10개가 넘는 쿼리가 존재한다. 만약 이러한 쿼리 정보를 모두 포함하도록 설계한다면, 응답 내에 자원과 관련된 데이터보다 메타 데이터가 더 많아지는 불상사가 발생한다. 관련된 다른 API "create", "delete" 등의 정보를 포함하면 더더욱.

HTTP/1.1 200 OK
Content-Type: application/json

{
  comments: [
  	{
  	  "id": "ad1044pdg3432",
  	  "username": "blaxsior",
  	  "content": "test comment"
  	}
  ],
  "links": [
    {
      "rel": "self",
      "href": "/comments",
      "method": "GET",
      "query": {
          "sort": {
            "required": false,
            "default": "fav",
            "values": ["fav", "old", "new"]
          },
          "count": {
            "required" false,
            "default": 30
          },
          "page": {
            "required" true
          }
      }
    }
  ]
}

위 예시는 겨우 3개의 쿼리 관련 정보를 포함했음에도 요청한 목적이 되는 comments보다 메타데이터의 비중이 더 높다.

두 번째 방법은 각 메서드에 필요한 메타데이터를 응답에 직접 표현하는 대신, 이를 정리해 둔 API 문서에 대한 링크를 제공하는 것이다. 메타데이터가 메시지에 포함되지 않으므로 위 예시에 비해 응답이 훨씬 깔끔해진다.

HTTP/1.1 200 OK
Content-Type: application/json

{
  comments: [
  	{
  	  "id": "ad1044pdg3432",
  	  "username": "blaxsior",
  	  "content": "test comment"
  	}
  ],
  "links": [
    {
      "rel": "self",
      "href": "/comments",
      "method": "GET",
      "docs": "http://example.org/docs/comments#delete
    }
  ]
}

이 방법은 API에 대한 메타 데이터를 메시지에 직접 표현하지 않으므로, 처음에 제시한 방법에 비해 현실성이 있다. 다만 이러한 구조를 채택했을 때 기존보다 효율이 좋아지는지 묻는다면, 개인적으로 애매한 것 같다. API 공식 문서를 봐야 개발이 가능하다면, 링크 자체만으로는 상태를 바꿀 수 없기 때문에 응답에 명시하는게 큰 장점이 될 것 같지는 않다.


 

 위에서 제시한 내용은 온전히 나의 생각일 뿐, 어떤 집단에서는 HATEOAS 제약조건을 포함하여 훌륭한 API를 작성할 수도 있다. HATEOAS를 포함하는 것이 진정한 REST API다! 라고 생각하기보다는, 각 방법의 트레이드 오프를 고려하여 적절한 API를 구현하는 것이 더 적합하지 않을까 싶다. 개발자의 역할은 좋은 서비스를 제공하는 것이기 때문이다.

'WEB&서버' 카테고리의 다른 글

[WEB] HTTP Method  (0) 2024.04.18
[WEB] REST & REST API  (0) 2024.03.14
[HTTP Status] 429 Too Many Requests  (0) 2023.09.30
[WEB] XPath로 SVG 요소 식별하기  (0) 2023.07.30
[WEB] XPath axes  (0) 2023.07.30