그런 REST API로 괜찮은가?

Junha Kim·2021년 2월 21일
32

개발 취준 톡방에서 ajax api가 REST API인지? 에 관한 질문에 어떤 분이 REST API의 정의를 다시 한 번 생각해보라면서, 아래의 동영상을 남겨주셨다.

https://www.youtube.com/watch?v=RP_f5dMoHFc

이에, 위 동영상 내용을 정리하면서 REST API란 무엇이고, RESTful하다고 불리기 위해서는 어떻게 설계를 해야하는지 알아보자.

1. 서두

개발자라면 REST라는 이름은 한번쯤은 들어보았을 것이다. 특히 웹 개발라면 100프로다. 한번쯤은 과연 우리들이 작성한 api는 RESTful한 api인가?라는 이야기를 한다. 문제는 정확하게 REST가 뭔지 딱 확실히 말할 수 있지 못한다는 것이다. 머리로는 이해를 하고 있지만, 뭔가 잘 설명이 되지 않아 RESTful하지 않다는 것에 반박을 하지 못한 채로 남은 경우가 많다. 그렇다면 REST의 정의는 무엇인가?


1) REST란?

REST는 REpresentational State Transfer의 약자이다. 이것을 보고 어떤 것인지 알 수 있겠는가? 잘 모르겠다. 좀 더 자세한 정의를 가져와 보자.

a way of providing interoperability between computer systems on the Internet.

interoperablility는 상호 운용성이라는 뜻으로, 컴퓨터 시스템와 인터넷 사이에 상호 운용성을 제공하는 방법이라고 한다. 아직도 잘 모르겠다.

그렇다면 REST가 어떠한 배경에서 나오게 되었는지 그 계기를 봐보면 좀 더 이해가 될 수 있을 것이다.


2) REST의 출현 계기

시작은 웹이다. 1991년에 www가 팀 버너스 리에 의해 탄생했다. 그런데 하나의 고민이 있었다.

어떻게 인터넷에서 정보를 공유할 것인가?

이에 대한 해답으로 "웹"이 출범하게 된다. 이에 팀 버너스 리의 답은 아래와 같다

정보들을 하이퍼 텍스트로 연결한다.

  • 표현 형식 : HTML
  • 식별자 : URI
  • 전송방법 : HTTP

그래서 이제 HTTP라는 프로토콜을 여러 사람들이 설계를 하게 되었다. 그 중에 1명, 대학원생이었던 로이 필딩(Roy T. Fielding)이라는 사람이 이 프로토콜 작업에 참여하게 된다.

그 와중에 고민이 생긴다. 이미 94년도에 로이는 http 1.0 작업에 참여했다. 이 명세가 나오기 전에 이미 http는 당연히 www의 전송 프로토콜로서 이용이 되고 있었다. 그리고 또한 웹은 이미 급속도로 성장하는 도중이었다.

이 시점에서 로이은 http를 정립하고 이 명세에 기능을 더하고 기존의 기능을 고쳐야하는 상황에 놓이게 된다. 그러나 무작정 http 프로토콜을 고치게 된다면, 기존 구축된 웹하고 호환이 안되는 가능성이 존재 했다. 이에 로이는 고민을 한다.

"How do I improve HTTp without breaking the Web?"

"웹을 망가뜨리지 않고 어떻게 http 기능을 증가시킬 수 있을까?"

로이는 고민 끝에 HTTP Object Model이라는 것을 만든다. 아직 REST는 아니다. 이는 4년 후 "Representational State Transfer: An Architectural Style for Distributed Hypermedia interaction"에서 REST를 최초로 공개한다. 이후 2년 후, "Architectural Styles and the Design of Network-based Software Architectures"을 박사 논문으로 발표하게 된다. 이 박사 논문이 바로 그 REST라는 것을 정의한느 논문이다. → 약 120페이지


3) API

그런 한편, API라는 것이 있다. 98년에 마이크로소프트에서 원격으로 다른 시스템에 메소드를 호출할 수 있는 XML-RPC( 1998) 프로토콜을 만든다. 이는 곧 SOAP이라는 이름으로 바뀌고, Salesforce라는 회사가 인터넷에서 최초로 공개하게 된다. 당시 SOAP을 사용해서 api를 만들었는데 이는 상당히 복잡하다.

너무 복잡하여 Salesforce의 api는 많이 쓰이지 않게 된다. 그리고 flickr에서 SOAP을 사용하여 API를 발표하는데 훨씬 간단해 졌다.

논문에 나온 내용을 바탕으로 작성하여 REST라는 이름으로 발표했다.

딱 보더라도 훨씬 짧다. 사람들이 아래와 같이 느끼게 된다

SOAP → 복잡, 규칙 많음, 어렵다. REST → 단순, 규칙 적음, 쉽다.

결국 아래와 같은 결과를 낳게 된다.

결국, 2006년에 AWS가 자사 API의 사용량의 85%가 REST임을 밝혔다.

또한, 2010년에 Salesforce.com은 REST API를 추가했다.

이렇게 WWW의 API가 REST로 정착이 되나 싶었다.

하지만...


4) 그것은 REST가 아니다.

CMIS(2008)

CMS를 위한 표준으로 CMIS가 나왔다. IBM, 마이크로소프트와 같은 기업들이 함께 작업했으며, REST 바인딩을 지원한다고 발표했다.

하지만, 이것을 본 로이는 아래와 같은 말을 한다.

"No REST in CMIS"

다른 사람들이 볼 때는 충분히 REST처럼 보였지만, 정작 논문 저자인 로이는 아니라고 말한다.

Microsoft REST API Guidelines(2016)

2016년 말, 마이크로소프트가 REST API 가이드 라인을 만들었다.

  • URI는 https://{serviceRoot}/{collection}/{id} 형식이어야 함.
  • GET, PUT, DELETE, POST, HEAD, PATCH, OPTIONS를 지원해야함.
  • API 버저닝은 Major.minor로 하고 URI에 버전 정보를 포함시킨다.
  • ...

하지만 역시 로이는 아래와 같이 말한다.

"s/REST API/HTTP API/"

이것은 그냥 http API다.

그리고 또한 블로그에 아래와 같은 말을 남긴다.

"REST API must be hypertext-driven"

"REST API를 위한 최고의 버저닝 전략은 API 버저닝을 안하는 것"

사람들이 알고 있던 REST API와 정작 만들었던 로이의 REST API은 너무나도 다르다.

무엇이 문제인가? 왜 이런 차이가 있는건가?


2. 본문


1) REST API

REST API → REST 아키텍처를 따르는 API

REST → 분산 하이퍼미디어 시스템(ex. 웹)을 위한 아키텍처 스타일

아키텍처 스타일 → 제약 조건의 집합

즉, REST에서 정의한 제약 조건을 모두 지켜야 REST를 따른다고 말할 수 있다는 것이다.

사실 REST는 아키텍처 스타일이면서 하이브리드 아키텍처 스타일이라고 말한다. 왜냐하면 아키텍처 스타일이면서, 동시에 아키텍처 스타일의 집합이기 때문이다.


2) REST를 구성하는 스타일

  1. Client-Server
  2. Stateless
  3. Cache
  4. Uniform Interface
  5. Layered System
  6. Code-on-Demand (optional)

대체로 REST라고 부르는 것들은 위의 조건을 대부분 지키고 있다. 왜냐하면 HTTP만 잘 따라도 Client-Server, Stateless, Cache, Layered System은 다 지킬 수 있기 때문이다. Code-on-Demand는 서버에서 코드를 클라이언트로 보내서 실행할 수 있어야 한다는 것을 의미, 즉 자바스크립트를 의미한다. 이는 필수는 아니다.

하지만 로이가 REST가 아니라고 말하는 것들은 Uniform Interface를 만족하지 못하는 것이 대부분이다. Uniform Interface 역시 아키텍처 스타일이기 때문에 안에 뭐가 들어있는지 보자.


3) Uniform Interface 제약 조건

  • Identification of resources
  • Manipulation of resources through representations
  • Self-descriptive messages
  • Hypermedia as the engine of application state(HATEOAS)

Identification of resources은 URI로 리소스가 식별되면 된다는 것이고, Manipulation of resources through representations는 representation 전송을 통해서 리소스를 조작해야된댜는 것이다. 즉, 리소스를 만들거나 삭제, 수정할 때 http 메시지에 그 표현을 전송해야된다는 것이다. 위 2가지 조건은 대부분 잘 지켜지고 있다. 하지만 문제는 아래 2개이다. 이 2가지는 사실 우리가 REST API라고 부르는 거의 모든 것들은 지키지 못하고 있다.


4) Self-descriptive messages

메시지는 스스로를 설명해야한다.

예를 들어 아래와 같은 메시지가 있다고 해보자

GET / HTTP/1.1

단순히 루트를 얻어오는 GET 요청이다. 이 HTTP 요청 메시지는 뭔가 빠져 있어서 Self-descriptive 하지 못하다.

우선 목적지가 빠져있다.

GET / HTTp/1.1
Host: www.example.org

이 요청이 www.example.org 라는 도메인으로 간다라는 목적지가 빠져있기 때문에 이런 경우는 Self-descriptive하지 않다고 한다.

또 이런 것도 생각해볼 수 있다. 200 응답 메시지이며, JSON 본문이 있다.

HTTP/1.1 200 OK
[ { "op": "remove", "path": "/a/b/c" } ]

이렇게 되면 당연히 Self-descriptive 하지 않는데, 그 이유는 이걸 클라이언트가 해석하려고 하면, 어떤 문법으로 작성된 것인지 모르기 때문에 해석에 실패한다. 그렇기 때문에 Content-Type 헤더가 반드시 들어가야한다.

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

[ { "op": "remove", "path": "/a/b/c" } ]

Content-Type 헤더에서 대괄호, 중괄호, 큰따옴표의 의미가 뭔지 알게 되어, 파싱이 가능하여 문법을 해석할 수 있게 된다.

그렇다면 이제 Self-descriptive하다고 볼 수 있는가? 아니다.

그걸 해석했다고 하더라도, op 값은 무슨 뜨시고, path가 무엇을 의미하는지는 알 수 없다.

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

[ { "op": "remove", "path": "/a/b/c" } ]

이렇게 명시를 하면 완전해진다. 이 응답은 json-patch+json이라는 미디어 타입으로 정의된 메시지이기 때문에 json-patch라는 명세를 찾아가서 이해한 다음, 이 메시지를 해석을 하면 그제서야 올바르게 메시지의 의미를 이해할 수 있게 된다.

이처럼 Self-descriptive message라는 것은 메시지를 봤을 때 메시지의 내용으로 온전히 해석이 다 가능해야된다는 것이다.

하지만 오늘날의 REST API는 상당히 이를 만족하지 못하고 있다. 대부분 미디어 타입에는 그냥 json이라고만 되어있고 이를 어떻게 해석해야되는지는 명시가 되어있지 않다.


5) HATEOAS

애플리케이션의 상태는 Hyperlink를 이용해 전이되어야 한다.

예를 들어 아래와 같은 사이트가 있다고 해보자

루트 홈페이지 → 글 목록 보기 GET → 글 쓰기 GET → 글 저장 POST → 생성된 글 보기 GET → 목록 보기 GET → ...

이렇게 상태를 전이하는 것을 애플리케이션 상태 전이라고 하고, 이 상태 전이마다 항상 해당 페이지에 있던 링크를 따라가면서 전이했기 때문에 HATEOAS라고 할 수 있다. 말 그대로, 하이퍼 링크를 통한 전이가 되는 것이다.

그래서 html 같은 경우를 보면 HATEOAS를 만족하게 되는데,

HTTP/1.1 200 OK
Content-Type: text/html

<html>
<head> </head>
<body> <a href="/test"> test </a> </body>
</html>

a 태그를 통해서 하이퍼링크가 나와 있고, 이 하이퍼 링크를 통해서 그 다음 상태로 전이가 가능하기 때문에 만족한다고 볼 수 있다.

Json으로 표현하면 어떻게 할 수 있을까?

HTTP/1.1 200 OK
Content-Type: application/json
Link: </articles/1>; rel="previous",
			</articles/3>; rel="next";

{
	"title": "The second article",
	"contents": "blah blah..."
}

Link라는 헤더가 있는데, 이것이 바로 이 리소스와 하이퍼링크로 연결되어 있는 다른 리소스를 가르킬 수 있는 기능을 제공해준다.

여기서 어떤 1개의 게시물을 표현 했는데, 이전의 게시물 URI가 /articles/1, 다음 게시물은 /articles/3에 있다는 정보를 표현해준 것이다.

또한, 이 정보는 Link 헤더가 이미 표준으로 문서가 나와 있기 때문에 이 메시지를 보낸 사람이 온전히 해석해서 어떻게 링크가 되어 있는가를 이해하고 하이퍼링크를 타고 다른 상태로 전이가 가능하다.


6) 왜 Uniform Interface?

그럼 왜 이러한 Uniform Interface가 필요한지 의문이 든다. 그것은 바로 아래의 내용 때문이다.

1. 독립적 진화

  • 서버와 클라이언트가 각각 독립적으로 진화한다.
  • 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다.
  • REST를 만든 계기 : "How do I imporve HTTP without breaking the Web"

독립적으로 진화한다면 어떻게 될까? 서버의 기능이 바뀌었다. 새로운 API가 추가되고 기존 API가 변경/삭제되고 URI가 바뀌고 변동이 있다. 하지만 클라이언트가 바뀌지 않아도 된다.

이게 바로 REST를 만들게 된 계기이다. 로이가 초반에 http 1.0을 만들 당시 고민했던 것, "웹을 망가뜨리지 않고 어떻게 수정할 것인가?"에 대한 결과가 REST였다고 했다.

그렇기 때문에 REST가 목적하는 바가 독립적인 진화이다. 이를 달성하기 위해서는 Unifrom Interface가 필수적이기기에, 이를 만족하지 못하면 REST라고 부를 수 없는 것이다.

2. 실제로 잘 지켜지고 있는가?

REST를 아주 잘 지키고 있는 사례는 바로 웹이다.

  • 웹 페이지를 변경했다고 웹 브라우저를 업데이트할 필요는 없다.
  • 웹 브라우저를 업데이트 했다고 웹 페이지를 변경할 필요도 없다.
  • HTTP 명세가 변경되어도 웹은 잘 작동한다.
  • HTML 명세가 변경되어도 웹은 잘 작동한다.

우리가 들어가는 웹 페이지가 변경되었다고 해서 그 웹 페이지를 접근할 때 웹 브라우저를 업데이트 할 필요 없다. 그냥 잘 들어가진다.

반대로 크롬을 업데이트 했다고 해서, 웹 페이지에 안들어가지는 것도 아니다.

심지어 HTTP 명세가 바뀌어도 그렇다. 2014년 HTTP 2.0이 나오고 여러가지 웹 페이지들이 HTTP 2.0을 적용한다. 하지만 적용을 하지 않은 사이트들도 여전히 잘 된다.

HTML 5.0이 HTML 5.2로 바뀌어도 똑같이 잘 된다.

물론 옛날 버전에서는 페이지가 좀 깨질 수는 있다.

하지만 페이지가 깨진다고 기능은 작동하지 않는가? 아니다. 웹에서 제공하는 기능들은 다 정상적으로 작동을 한다.

그런데 우리가 만든 모바일 앱은 많은 문제들을 겪고 있다.

이것은 무슨 이야기일까? 앱이 강제 업데이트 하는 경우가 있다. 서버가 기능이 변경되어 있는데, 클라이언트가 그것을 지원해주는데 한계가 있을 때 업데이트를 진행하는 것이다. 그런데 이게 되게 잦다는 것이다.

분명히 웹에서는 이런 경우가 잘 없다. 하지만 모바일 앱에서는 자주 있는 일이다. 사실은 엄밀히 말하면 우리가 만든 모바일 앱 클라이언트와 서버가 REST 아키텍처 스타일을 따르고 있지 않다고 말할 수 있다.

웹은 어떻게 이렇게 가능하게 했을까? 당연히 많은 노력을 했다. W3C Working group에서 html을 만들고, IEFT Working groups에서 http를 만들고, 웹 서버/브라우저 개발자들이 많은 토론을 하며 노력을 한다.

HTML5 첫 초안에서 권고안이 나오는데 무려 6년, HTTP/1.1 명세 개정판 작업하는데 7년이 걸렸다. HTTP/1.1에 새로운 기능이 추가 되었는가? 아니다. 새로운 기능은 하나도 없었다. 문서를 다듬기만 하는데 7년이 걸렸다. 절대로 하위 호환을 깨드리지 않기 위해 정말 깊은 토론의 결과였던 것이다.

3. 상호운용성(interoperability)에 대한 집착

  • Referer → 오타지만 안 고침
  • charset → 잘못 지은 이름이지만 안 고침
  • HTTP 상태 코드 418(I'm a teapot) 포기
  • HTTP/0.9 아직도 지원(크롬, 파이어폭스)

25년전 Referer 오타를 냈다. 하지만 이를 고치지 않았다. 이를 고치는 순간 웹이 깨지기 때문이다. 또 charset이란 이름은 잘못 지었다. 원래 encoding이라고 이름을 지어야하는 데 그때 encoding 개념이 부족하여, character set이랑 같은 줄 알고 지었다. 하지만 그대로 고치지 않고 갔다. 이름을 고치면 상호 운용성이 깨지기 때문에!

또한, http 상태 코드가 하나씩 추가되다가 418번을 추가할 때가 되었는데, 문제가 생겼다. 만우절 때 만들었던 이상한 spec HTCPCP 상태 코드가 있는데, 이것은 http가 아니라서 무시를 해도 됐었다. 하지만 수많은 node.js와 같은 서버들의 http 서버 구현체들이 http 상태 코드로 구현을 해버렸다. 그래서 처음에는 http 위장이 프로젝트마다 돌아다니면서 418 지원을 제거해야된다는 이슈를 올렸는데 맹비난을 받고 포기했다. 그래서 418은 http 상태코드에서 영구 결번으로 만들게 되었다. 이미 그런 구현체가 존재하고, 잘못 구현체들과 상호 운용성을 지켜주기 위해(안그러면 웹이 깨지기때문에) 이러한 결정을 한것이다.

HTTP 버전이 올라가면서 예전 버전을 빼려고 시도했지만 몇몇 프록시에서 오작동 하는 것이 발견되어서 HTTP/0.9를 유지하게 되었다.

그래서 이렇게 많은 사람들이 상호 운용성에 대한 노력 덕분에 독립적으로 진화될 수 있었다.


7) REST가 웹의 독립적 진화에 도움을 주었나

그렇다. HTTP에 지속적으로 영향을 주었다.

  • Host 헤더 추가

  • 길이 제한 다루는 방법 명시 (414 URI Too Long 등)

  • URI에서 리소스의 정의가 추상적으로 변경: "문서의 위치" → "식별하고자 하는 무언가"

  • HTTP/1.1 명세 최신판에서 REST에 대한 언급이 들어감 → representaion의 개념도 있음

    → 로이 필더 : HTTP와 URI 명세 저자 중 1명 ⇒ 당연히 영향을 줄 수 박에 없다.

1. 그럼 REST는 성공 했는가?

  • REST는 웹의 독립적 진화를 위해 만들어졌다.
  • 웹은 독립적으로 진화하고 있다. → 성공!

2. 그런데 REST API는?

  • REST API는 REST 아키텍처 스타일을 따라야 한다.
  • 하지만 오늘날 스스로 REST API라고 하는 API들의 대부분이 REST 아키텍처 스타일을 따르지 않는다.

3. REST API도 제약 조건들을 다 지켜야 하는건가?

그렇다!

"An API that provides network-based access to resources via uniform interface of self-descriptive messages containing hypertext to indicate potential state transition might be part of an overall system that is a RESTful application. - Roy T. Fielding

REST API라고 하는 것은 하이퍼 텍스트를 포함한 self-descriptive 메시지의 uniform interface를 통해 리소스에 접근하는 API라고 강조했다.

즉, 모든 제약 조건을 지켜야한다고 했다.

이렇게 보니.. REST는 꽤나 복잡하고 어려워 보인다.


8) 원격 API가 꼭 REST API여야 하는건가?

아니여도 된다.

"REST emphasizes evolvability sustain on uncontrollable system. If you think you have control over the system or aren't interested in evolvability, don't waste your time arguing about REST. - Roy T. Fielding

시스템 전체를 통제할 수 있다고 생각하거나, 진화에 관심이 없다면, REST에 대해 따지느라 시간을 허비하지 마라.

시스템 전체를 통제할 수 있다 → 회사에서 서버 개발자인데, 클라이언트 개발자를 통제 가능할 때 or 클라이언트, 서버를 다 만들 수 있을 때

진화에 관심이 없다 → 매일 업데이트 해서 유저들에게 불만을 들어도 난 상관이 없다.

하지만 우리는 시스템 전체를 통제할 수 없기도 하고, 개발자로서 진화에 관심을 두어야 하기에 REST를 따라야 한다.


9) 그럼 이제 어떻게 해야되는가?

  1. REST API를 구현하고 REST API라고 부른다.
  2. REST API 구현을 포기하고 HTTP API라고 부른다.
  3. REST API가 아니지만 REST API라고 부른다. (현재 상태)

"I am getting frustrated by the number of people calling any HTTP-base interface a REST API... Please try to adhere to them or choose some other buzzword for your API." - Roy T. Fielding

제발 제약 조건을 따르던지 아니면 다른 조건을 쓰라고 로이 필딩은 말한다.


10) 진짜 REST API를 구현하자

1. 왜 API는 REST가 잘 안되나?

웹하고 비교해보자.

커뮤니케이션이 좀 다르다 HTTP API는 사람이 아닌 기계가 해석한다. 그러다 보니 미디어 타입이 다르다. Json이나 XML같이 기계가 의미를 이해할 수 있는 포맷을 쓰게 된다.

그렇다면 문제의 원인이 미디어 타입이라고 생각이 된다.

self-descriptive 측면에서 보면 JSON은 불완전하다. 불완전 하다는 것은 적어도 문법은 정의되어 있다. 어떻게 파싱하고 array를 어떻게 해석해라 까지는 되어있다. 안에 들어갈 수 있는 key-value에 대한 의미는 아무도 정의되지 않는다.

즉, 문법은 해석 가능하지만 의미를 해석하려면 별도로 문서(API 문서 등)가 필요하다.

HTML과 JSON을 비교해보자

GET /todos HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Content-Type: text/html

<html>
<head> </head>
<body> 
<a href="https://todos/1"> 회사 가기 </a> 
<a href="https://todos/2"> 집에 가기 </a> 
</body>
</html>
  • HTML → Self-descriptive
    1. 응답 메시지의 Content-Type을 보고 미디어 타입이 text/html 확인
    2. HTTP 명세에 미디어 타입은 IANA에 등록되어 있다고 함 → IANA에서 text/html 명세 찾음
    3. IANA에 따르면 text/html 명세는 http://www.w3.org/TR/html 이므로 링크를 찾아가 명세 해석
    4. 명세에 모든 태그의 해석 방법이 있으므로 이를 해석, 사용자에게 보여줄 수 있음
    5. Success
  • HTML → HATEOAS
    1. a 태그를 이용해 표현된 링크를 통해 다음 상태로 전이될 수 있으므로 만족
    2. Success
GET /todos HTTP/1.1
Host: example.org

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

[
	{"id": 1, "title": "회사 가기"},
	{"id": 2, "title": "집에 가기"}
]
  • JSON → self-descriptive
    1. 응답 메시지의 Content-Type을 보고 미디어 타입이 application/json 확인
    2. HTTP 명세에 미디어 타입은 IANA에 등록되어 있다고 함 → IANA에서 application/json 명세 찾음
    3. IANA에 따르면 application/json 명세는 draft-ietf-jsonvis-rfc7159bis-04 이므로 링크를 찾아가 명세 해석
    4. 명세에 JSON 문서 파싱 방법이 명시 → 성공적으로 파싱, 그러나 "id"가 무엇을 의미하고, "title"이 무엇을 의미하는지 알 방법 없음.
    5. FAIL
  • JSON → HATEOAS
    1. 다음 상태로 전이할 링크가 없다.
    2. FAIL

2. 그런데 self-descriptive, HATEOAS가 어떻게 독립적인 진화에 도움이 되는가?

self-descriptive → 확장 가능한 커뮤니케이션

서버나 클라이언트가 변경되더라도 오고가는 메시지는 언제나 self-descriptive 하므로 언제나 해석이 가능하다.

HATEOAS → 애플리케이션 상태 전이의 late binding

어디서 어디로 전이가 가능한지 미리 결정X. 어떤 상태로 전이가 완료 되어야, 그 다음 전이될 수 있는 상태 결정 ⇒ 링크는 동적으로 변경될 수 있다.

3. 그럼 REST API로 바꿔보자

  1. Self-descriptive

    1. Media type 정의
      1. 미디어 타입을 하나 정의
      2. 미디어 타입 문서 작성 → "id", "title"의 의미 정의
      3. IANA에 미디어 타입을 등록. 이때 만든 문서를 미디어 타입의 명세로 등록
      4. 이제 이 메시지를 보는 사람은 명세를 찾아갈 수 있음 → 메세시지 의미 온전히 해석 가능
      5. Success
    GET /todos HTTP/1.1
    Host: example.org
    
    HTTP/1.1 200 OK
    Content-Type: application/vnd.todos+json
    
    [
    	{"id": 1, "title": "회사 가기"},
    	{"id": 2, "title": "집에 가기"}
    ]

    ⇒ 단점 : 매번 media type 정의

    1. Profile 이용

    2. "id", "title" 의미 정의한 명세 작성

    3. Link 헤더에 profile relation으로 명세 링크

    4. 이제 메시지를 보는 사람은 명세를 찾아갈 수 있음 → 의미 해석 가능

    5. Success

    GET /todos HTTP/1.1
    Host: example.org
    
    HTTP/1.1 200 OK
    Content-Type: application/json
    Link: <https://exmaple.org/docs/todos>; rel="profile"
    
    [
    	{"id": 1, "title": "회사 가기"},
    	{"id": 2, "title": "집에 가기"}
    ]

    ⇒ 단점

    • 클라이언트가 Link 헤더(RFC5988)와 profile(RFC 6906)을 이해해야함.
    • Content negotiation 불가
  2. HATEOAS

    1. data에 다양한 방법으로 하이퍼링크 표현
    GET /todos HTTP/1.1
    Host: example.org
    
    HTTP/1.1 200 OK
    Content-Type: application/json
    Link: <https://exmaple.org/docs/todos>; rel="profile"
    
    [
    	{
    		"link": "https://exmplae.org/todos/1, 
    		"title": "회사 가기"
    	},
    	{
    		"link": "https://exmplae.org/todos/2, 
    		"title": "회사 가기"
    	},
    ]
    GET /todos HTTP/1.1
    Host: example.org
    
    HTTP/1.1 200 OK
    Content-Type: application/json
    Link: <https://exmaple.org/docs/todos>; rel="profile"
    
    {
    	"links" : {
    		"todo" : "https://example.org/todos/{id}"
    	},
    	"data": [{
    		"id": 1, 
    		"title": "회사 가기"
    	}, {
    		"id": 2,
    		"title": "집에 가기"
    	}]
    }

    ⇒ 단점 : 링크를 표현하는 방법을 직접 정의

    1. JSON으로 하이퍼링크를 표현하는 방법을 정의한 명세들을 활용
    GET /todos HTTP/1.1
    Host: example.org
    
    HTTP/1.1 200 OK
    Content-Type: application/vnd.api+json
    Link: <https://exmaple.org/docs/todos>; rel="profile"
    
    {
    	"data": [{
    		"type": "todo",
    		"id": 1, 
    		"attributes": {"title": "회사 가기"},
    	"links" : { "self": "https://example.org/todos/1"
    	},{
    		"type": "todo",
    		"id": 2, 
    		"attributes": {"title": "집에 가기"},
    	"links" : { "self": "https://example.org/todos/2"
    	}]
    }

    ⇒ 단점 : 기존 API를 많이 고쳐야한다. (침투적)

    1. HTTP 헤더로 → Link, Location 등
    POST /todos HTTP/1.1
    Content-Type: application/json
    
    {
    		"title": "점심 약속"
    }
    
    HTTP/1.1 204 No Content
    Location: /todos/1
    Link: </todos/>, rel="collection"

⇒ data, 헤더 모두 사용하면 좋다.

4. Media type 등록은 필수인가?

NO

"A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience(i.e., expected to be understood by any client that might user the API). — Roy. T. Fielding

의도한 저자가 이해할 수만 있다면 상관 없다. 예를 들면 사내에서만 쓰는 API고 이를 이해하고 있다면 굳이 등록하지 않아도 된다.

등록의 장점

  • 사전에 알고 있지 않은 사람도 누구나 쉽게 사용할 수 있게 됨
  • 이름 충돌을 피할 수 있음
  • 등록이 별로 어렵지 않다(고 주장)

3. 정리

  • 오늘날 대부분 "REST API"는 사실 REST를 따르고 있지 않다.
  • REST의 제약 조건 중 특히 self-descriptive와 HATEOAS를 잘 만족하지 못한다.
  • REST 긴 시간에 걸쳐(수십년) 진화하는 웹 애플리케이션을 위한 것
  • REST를 따를 것인지는 API 설계하는 이들이 스스로 판단하여 결정
  • REST를 따르겠다면 Self-decriptive와 HATEOAS를 만족시켜야한다.
    • Self-decriptiv는 custom media type, profile link relation으로 만족 가능
    • HATEOAS는 HTTP 헤더나 본문에 링크를 담아 만족 가능
  • REST를 따르지 않겠다면, "REST를 만족하지 않는 REST API"를 뭐라고 부를지 결정해야할 것이다.
    • HTTP API라고 부를 수도 있고
    • 그냥 이대로 REST API라고 부를 수도 → 로이가 싫어함

1개의 댓글

comment-user-thumbnail
2022년 12월 23일

안녕하세요.

포스팅에 정리를 잘 해두셔서 많이 신세지고 있습니다 :)

다만, 서론에 영어 문장해석에서 혼동을 줄 수 있을만한 내용이 있어서 댓글 남깁니다.

a way of providing interoperability between computer systems on the Internet.

이부분에서 '컴퓨터 시스템와 인터넷 사이에 상호 운용성'이라 포스팅을 해주셨는데, '인터넷 상에서 컴퓨터 시스템 간의 상호 운용성'이라 하는 것이 더 좋을 것 같습니다.

감사합니다 :)

답글 달기