HATEOAS는 RESTful 웹 서비스의 설계 원칙 중 하나로 RESTful API을 self descriptive 하게 만들며 client와 server 간의 상호작용을 단순화하고 확장성을 높이는 기술이다.

Hypermedia as the Engine of Application State’ 의 약어로 server와 client의 상호작용을 동적으로 하기 위해 Hpyermedia를 사용하는 것을 원칙으로 한다.
HATEOAS는 API response로 resource와 관련된 Hypermedia link를 제공하여 다음 동작을 제공된 Hypermedia에 따라 동적으로 결정할 수 있도록 한다.

HATEOAS를 왜 쓰는지 이해하기 위해 HATEOAS를 사용하지 않는 경우들의 문제점들을 체크해본다.
비교를 위해 Github의 Repository 조회 api 구현을 예시로 들어본다.
핵심은 하나의 api에서 다른 데이터를 필요로 할 때 데이터가 어떻게 전달되느냐이다.

non-HATEOAS

1. 서버에서 데이터를 채워서 내려주는 경우

Repository를 조회하고 필요한 User에 대한 추가 정보가 필요한 경우 Repository 조회 api에 User의 상세 정보가 추가된다.
client는 Repository와 관련된 작은 User 정보를 같이 받는 것으로 시작하지만 요구사항 변화와 시간이 지나며 Repository 조회를 넘어서는 데이터가 반환되게 된다.

이런 요구사항들이 쌓여, Repository 조회 api는 Repository 조회 뿐 아니라 다른 여러 정보를 같이 내려주는 api가 될 수 있다.

문제점

  • api는 의도된 기능 이상의 역할을 수행하게 된다.
  • api를 이해하기가 어려워진다.
  • server는 api 지원을 위해 여러 도메인 개념들의 예외 처리를 고려해야 한다.
  • server는 해당 api의 어떤 값들이 client에서 사용되고 있는지 파악할 수 없다.
  • 여러 client를 지원하는 경우 client 간에 동일 기능을 제공하는 유사 api set이 생기게 될 수 있다.

2. client와 약속된 데이터의 api를 사용하는 경우

Repository를 조회하고 필요한 User에 대한 추가 정보를 조회하기 위해 client가 약속된 api를 호출하게 된다. 아마 이 api는 get user api일 것이다.
Repository에서 얻은 user의 id를 가지고 약속된 User 정보 조회 api를 호출하는 방식이 된다.

client는 api endpoint를 하드코딩해서 사용해야 한다.
client는 endpoint를 관리하는 주체가 되고 endpoint에 의존하게 된다.

문제점

  • client와 server 간에 endpoint라는 강한 결합이 생기게 된다.
  • client가 가진 endpoint로 생긴 결합으로 인해 server의 api 변경이 둔해진다.
  • server에서 api version update를 할 경우 client에 수정이 필요하다.
  • client와 api를 공유하고 이해시키는 작업이 필요하다.
  • 여러 client를 지원하는 경우 각 client에 api를 설명하는 overhead가 생긴다.

HATEOAS

Github의 Get Repository 조회 예시

 {
   "id": 1296269,
   "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
   "name": "Hello-World",
   "full_name": "octocat/Hello-World",
   "owner": {
     "login": "octocat",
     "id": 1,
     "node_id": "MDQ6VXNlcjE=",
     "avatar_url": "https://github.com/images/error/octocat_happy.gif",
     "gravatar_id": "",
     "url": "https://api.github.com/users/octocat",
     "html_url": "https://github.com/octocat",
     "followers_url": "https://api.github.com/users/octocat/followers",
     "following_url": "https://api.github.com/users/octocat/following{/other_user}",
     "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
     "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
     "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
     "organizations_url": "https://api.github.com/users/octocat/orgs",
     "repos_url": "https://api.github.com/users/octocat/repos",
     "events_url": "https://api.github.com/users/octocat/events{/privacy}",
     "received_events_url": "https://api.github.com/users/octocat/received_events",
     "type": "User",
     "site_admin": false
   }
 }

Github의 api를 참고해서 Repository를 조회하면 Response에 resource를 확인할 수 있는 Hypermedia url를 포함하여 제공한다.
Github api spec을 확인하지 않더라도, 간결한 response에 담겨있는 내용을 명확하게 알 수 있다.
owner의 각각의 resource가 필요하다면 각 응답으로 내려온 Hypermedia url을 통해 조회를 이어갈 수 있다.

api가 제공하는 resource와 api의 역할은 더 명확해지고 이렇게 HATEOAS는 그 자체로 self descriptive api documentation 역할을 할 수 있다.

client와 server 사이에는 client가 원하는 state를 확인하기 위해 확인해야 하는 link가 어느 field의 link인지 알아야 한다는 최소한의 약속만 존재한다.
client는 server가 제공하는 link를 따라가며 필요한 resource를 조회하며 상호작용한다.
따라서 client와 server 간의 결합이 최소화 되고 server는 client에 독립적으로 api를 변경할 수 있게 된다.

단점은 client가 link를 따라가며 state를 조회하므로 network overhead가 발생할 수 있다.