‘그런 REST API로 괜찮은가’ 컨퍼런스 정리
REST의 탄생 배경
Web이란
정보들을 하이퍼텍스트로 연결하여, 인터넷에서 정보 공유를 하는 방식이다.
Web의 핵심요소
- 표현 형식
- HTML
- 식별자
- URI
- 전송 방법
- HTTP
Roy T. Fielding 가라사대…
- “이미 전세계에 보급된 Web을 망가뜨리지 않고, HTTP를 어떻게 더 발전시킬 수 있을까?”이라는 의문
- 그것이 바로 HTTP Object Model 이다.
- HTTP Object Model 을 REST라는 이름으로 논문을 발표했다.
- Flickr 라는 서비스가 REST 방식의 API를 제공하기 시작했다.
- 기존 방식인 SOAP보다 쉽고 편리하여, REST가 빠르게 발전하게 됐다.
SOAP: HTTP, HTTPS, SMTP 등을 통해 XML 기반의 메시지를 컴퓨터 네트워크 상에서 교환하는 프로토콜
REST
REST란
- 분산 하이퍼미디어 시스템 (Ex. Web) 을 위한 아키텍처 스타일
- REST는 하이브리드 아키텍처 스타일로, 여러가지 아키텍처 스타일이 합쳐져 하나의 아키텍처 스타일이 된 것이다.
- 아키텍처 스타일?
- 제약조건의 집합
REST API란
- REST라는 아키텍처 스타일이 적용된 API
- REST 아키텍처 스타일은 API뿐만 아니라, 여러 곳에도 적용이 가능하다.
- Ex. 서버와 웹브라우저 등
즉 REST는 일종의 방법론, REST API는 그 방법론이 적용된 API
REST를 구성하는 스타일
- client - server
- stateless
- cache
- layered system
- code-on-demand
- 서버에서 보낸 코드를 클라이언트가 실행할 수 있는 구조 (JavaScript)
- uniform interface
- ‘client - server’, ‘stateless’, ‘cache’, ‘layered system’, ‘code-on-demand’ 는 HTTP를 잘 활용했을 때 자연스럽게 지켜진다.
- 하지만 ‘uniform interface’는 잘 지켜지기 어렵다.
Uniform Interface 아키텍처 스타일
Uniform Interface 아키텍처 스타일의 제약조건
- Identification of Resources
- URI로 자원을 식별할 수 있어야 한다.
- 이미 대부분이 만족하는 조건
- Manipulation of Resources Through Representations
- ‘자원 추가, 삭제, 수정 등의 작업에 대한 표현’이 HTTP 메시지에 포함되어야 한다.
- 이미 대부분이 만족하는 조건
- Self-Descriptive Message
- 메시지 자체가 스스로의 의미를 잘 나타내야 한다.
- HyperMedia as the Engine Of Application State (HATEOAS)
- 애플리케이션의 상태는 HyperLink를 이용해 전달되어야 한다.
가장 중요한 것은 ‘Self-Descriptive Message’와 ‘HATEOAS’임.
하나씩 자세히 설명할 예정임.
Self-Descriptive Message
메시지 자체가 스스로의 의미를 잘 나타내야한다는 제약조건이다.
-
예시 1
GET / HTTP/1.1
위 HTTP 요청 MSG.는 목적지 정보가 빠져있어서 해당 제약조건을 만족시키기 못한다.
GET / HTTP/1.1 Host: www.example.org
위와 같이 수정되어야 한다.
-
예시 2
HTTP/1.1 200 OK [{ "op": "remove", "path": "/a/b/c" }]
위 HTTP 응답 MSG.는 어떤 문법으로 파싱해야 하는지 나타나지 않아서 해당 제약조건을 만족시키지 못한다.
HTTP/1.1 200 OK Content-Type: application/json [{ "op": "remove", "path": "/a/b/c" }]
[
,{
,”
,:
의 의미를 알 수 있어, 파싱이 가능하다.하지만
op
,path
의 의미는 여전히 모른다.HTTP/1.1 200 OK Content-Type: application/json-patch+json [{ "op": "remove", "path": "/a/b/c" }]
application/json-patch+json
이라는 Content-Type 표준에,op
와path
의 의미가 정의되어 있다.따라서, 모든 내용을 정확하게 해석할 수 있다.
HyperMedia as the Engine Of Application State (HATEOAS)
애플리케이션의 상태는 HyperLink를 통해 전달되어야 한다는 제약조건이다.
-
예시 1
각 상태전이가 “해당 페이지에 있는 링크를 따라서 정의”되었기 때문에, 이것은 HATEOAS를 만족시킨다.
-
예시 2
HTTP/1.1 200 OK Content-Type: test/html <html> <head></head> <body> <a href="/test">test</a> </body> </html>
<a>
태그를 통해 하이퍼링크를 만들어 상태를 전이시키므로, 이것은 HATEOAS를 만족시킨다. -
예시 3
HTTP/1.1 200 OK Content-Type: test/html Link: </article/1>; rel="previous", </article/3>; rel="next" { "title": "The Second Article", "content": "Blah~ Blah~" }
Link
헤더를 통해 하이퍼링크로 다른 자원을 가르키므로, 이것 역시 HATEOAS를 만족시킨다.Link
헤더: 이 자원(응답MSG.)과 연결된 다른 자원을 하이퍼링크로 가르키는 헤더,
이미 HTTP 표준에 포함된 헤더임
Uniform Interface 아키텍처 스타일의 필요성
- 지금까지 Uniform Interface 라는 아키텍처 스타일을 살펴보았다.
- 그렇다면 이러한 아키텍처 스타일이 왜 필요한 것일까?
- 그것은 바로, ‘독립적 진화’를 위해서 이다.
독립적 진화
독립적 진화란
- 서버와 클라이언트가 각각 독립적으로 진화한다는 의미이다.
- 즉 서버의 기능이 변경되어도, 클라이언트를 업데이트할 필요가 없다는 의미이다.
- Ex. API가 서버에서 삭제되거나 변경되어도, 웹브라우저는 전혀 변경될 필요가 없다.
독립적 진화와 REST
- 독립적 진화는 REST라는 아키텍처 스타일을 만들게 된 계기와 일맥상통한다.
- REST 아키텍처 스타일을 만들게 된 계기: “이미 보급된 Web을 망가뜨리지 않고, HTTP를 업그레이드시키자.”
- 즉 REST의 목표가 ‘독립적인 진화’이므로, Uniform Interface가 반드시 지켜져야 REST를 제대로 적용했다고 할 수 있다.
Web에 적용된 REST
- 이런 REST가 잘 지켜지고 있는 것이 바로 웹이라고 할 수 있다. (REST API가 아님)
- 웹페이지(Server)를 변경했다고, 웹브라우저(Client)를 업데이트할 필요는 없다.
- 웹브라우저를 업데이트했다고, 웹페이지를 변경할 필요도 없다.
- HTTP 명세가 변경되어도 웹은 잘 동작한다.
- HTTP 2.0 이 출시되어도, 여전히 기존 웹브라우저로 잘 접속할 수 있다.
- HTML 명세가 변경되어도 웹은 잘 동작한다.
- HTML 5.0 이 출시돼 이것으로 작성된 웹페이지를 기존 웹브라우저로 잘 접속할 수 있다.
- 하지만 모바일 앱은 서버가 변경되면 업데이트가 필수적이다.
- 따라서 이 경우, REST 아키텍처 스타일이 아니라고 할 수 있다.
Self-Descriptive와 HATEOAS가 독립적 진화에 있어서 중요한 이유
- Self-Descriptive
- 서버나 클라이언트가 변경되더라도, 오고가는 메시지는 언제나 해석이 가능하다.
- 그 자체로 의미를 알 수 있으므로
- HATEOAS
- 어디서 어디로 전이가 가능한지 미리 결정되지 않는다.
- 어떤 상태로 전이가 완료되고나서야 그 다음 전이될 수 있는 상태가 결정된다.
- 즉, 특정 페이지에 들어가고 나서야 그 다음 상태로 전이가 가능하다.
- 따라서, 서버가 링크를 자유롭게 수정할 수 있으므로 독립적 진화가 가능하다.
REST API
REST가 적용된 API?
- REST API는 REST 아키텍처 스타일을 따라야 한다.
- 하지만 오늘날 스스로 REST API라고 하는 API들은 대부분 REST 아키텍처 스타일을 따르지 않는다.
-
당연하게도, REST의 모든 조건을 따라야 REST API이다.
REST API라고 부르려면 반드시 모두 지켜야 한다고, ‘Roy T. Fielding’가 말했다..!
Web은 REST가 잘 적용되는데, 왜 API는 어려울까
-
Web vs API
흔한 웹 페이지 HTTP API Protocol HTTP HTTP Communication 사람·기계 기계·기계 Media Type HTML JSON 왜냐하면 Media Type에 차이가 있기 때문이다.
-
HTML vs JSON
| | HTML | JSON | | — | — | — | | Hyperlink | 가능 (a 태그 등) | 정의되어 있지 않음 | | Self-Descriptive | 가능 (HTML 명세) | 불완전* |
- Hyperlink
- HTML 에서는 a 태그 등을 통해, 하이퍼링크를 정의할 수 있다.
- JSON 은 아예 지원하지 않는다.
- Self-Descriptive
- HTML 명세에는 각 태그가 어떤 의미인지 정의되어 있기 때문에, Self-Descriptive가 가능하다.
- JSON 의 경우 문법해석은 가능하지만, 의미를 해석하려면 별도의 문서 (API 문서 등)이 필요하다.
- Hyperlink
HTML vs JSON
HTML, JSON
아래는 HTML과 JSON을 비교하기 위해, 사용할 예시이다.
-
HTML 예시
GET /todos HTTP/1.1 Host: example.org HTTP/1.1 200 OK Content-Type: text/html <html> <body> <a href="https://todos/1">회사 가기</a> <a href="https://todos/2">집에 가기</a> </body> </html>
-
JSON 예시
GET /todos HTTP/1.1 Host: example.org HTTP/1.1 200 OK Content-Type: application/json [ {"id": 1, "title": "회사 가기"}, {"id": 2, "title": "집에 가기"} ]
HTML
GET /todos HTTP/1.1
Host: example.org
HTTP/1.1 200 OK
Content-Type: text/html
<html>
<body>
<a href="https://todos/1">회사 가기</a>
<a href="https://todos/2">집에 가기</a>
</body>
</html>
- Self-Descriptive을 만족한다.
- 응답 메시지의 Content-Type을 보고 Media Type이
text/html
임을 확인한다. - HTTP 명세에 Media Type은 IANA에 등록되어있다고 하므로, IANA에서
text/html
의 설명을 찾는다. - IANA에 따르면
text/html
의 명세는 ‘http://www.w3.org/TR/html’ 이므로 링크를 찾아가 명세를 해석한다. - 명세에 모든 태그의 해석방법이 구체적으로 나와있으므로 이를 해석하여, 문서 저자가 사용자에게 해석 방법을 제공해 줄 수 있다.
- 응답 메시지의 Content-Type을 보고 Media Type이
- HATEOAS를 만족한다.
- a 태그를 이용해, 표현된 링크를 통해 다음 상태로 전이될 수 있으므로 HATEOAS를 만족한다.
JSON
GET /todos HTTP/1.1
Host: example.org
HTTP/1.1 200 OK
Content-Type: application/json
[
{"id": 1, "title": "회사 가기"},
{"id": 2, "title": "집에 가기"}
]
- Self-Descriptive을 만족하지 못한다.
- 응답 메시지의 Content-Type을 보고 Media Type이
application/json
임을 확인한다. - HTTP 명세에 Media Type은 IANA에 등록되어있다고 하므로, IANA에서
application/json
의 설명을 찾는다. - IANA에 따르면
application/json
의 명세는 draft-ietf-jsonbis-rfc7159bis-04 이므로, 링크를 찾아가 명세를 해석한다. - 명세에 json 문서를 파싱하는 방법이 명시되어있으므로 성공적으로 파싱에 성공한다.
그러나 “id”와 “title”이 무엇을 의미하는지 알 방법은 없다.
- 응답 메시지의 Content-Type을 보고 Media Type이
- HATEOAS를 만족하지 못한다.
- 다음 상태로 전이할 링크가 없다.
진정한 REST API 로 만들기
Self-Descriptive 만족시키기
-
방법 1: Media Type 활용
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": "집에 가기"} ]
미디어 타입이 변경되었다.
- 미디어 타입을 하나 정의한다.
- 미디어 타입 문서를 작성한다. 이 문서에 “id”와 “title”이 의미하는 바를 정의한다.
- IANA에 미디어 타입을 등록한다. 이 때 만든 문서를 미디어 타입의 명세로 등록한다.
- 이제 이 메시지를 보는 사람은 명세를 찾아갈 수 있으므로, 이 메시지의 의미를 온전히 해석할 수 있다.
하지만 다소 복잡하다.
다음 방법을 보자. -
방법 2: Profile
GET /todos HTTP/1.1 Host: example.org HTTP/1.1 200 OK Content-Type: application/json Link: <http://example.org/docs/todos>; rel="profile" [ {"id": 1, "title": "회사 가기"}, {"id": 2, "title": "집에 가기"} ]
Link
헤더가 추가되었다.- “id”와 “title”이 의미하는 바를 정의한 명세를 작성한다.
- Link 헤더에 profile relation으로 해당 명세를 링크한다.
- 이제 이 메시지를 보는 사람은 명세를 찾아갈 수 있으므로, 이 메시지의 의미를 온전히 해석할 수 있다.
- 단점
- 클라이언트가 Link 헤더(RFC 5988)와 profile(RFC 6906)을 이해해야 한다.
-
Content Negotiation 불가능
Content Negotiation: 동일한 URI에서 다른 버전의 문서를 제공 할 수 있으므로 사용자 에이전트는 자신의 기능에 가장 적합한 버전을 지정하는 것
Ex. 동일한 URI이지만Accept-Language
헤더에 따라, 다른 언어로 제공
HATEOAS 만족시키기
-
방법 1: data 활용
GET /todos HTTP/1.1 Host: example.org HTTP/1.1 200 OK Content-Type: application/json Link: <http://example.org/docs/todos>; rel="profile" [ { "link": "https://example.org/todos/1", "title": "회사 가기" }, { "link": "https://example.org/todos/2", "title": "집에 가기" } ]
- “id” 대신 “link”라는 key가 들어가고, 관련 하이퍼링크를 value로 제공한다.
아래와 같이, 표현할 수도 있다.
GET /todos HTTP/1.1 Host: example.org HTTP/1.1 200 OK Content-Type: application/json Link: <http://example.org/docs/todos>; rel="profile" { "links": { "todo": "https://example.org/todos/{id}" }, "data": [{ "id": 1, "title": "회사 가기" }, { "id": 2, "title": "집에 가기" }] }
- “link”와 “data”를 따로 구분하고, URI를 템플릿 형태로 변경하였다.
-
방법 2: HTTP 헤더 활용
POST /todos HTTP/1.1 Content-Type: application/json { "title": "점심 약속" } HTTP/1.1 204 No Content Location: /todos/1 Link: </todos/>; rel="collection"
- Link, Location 등의 헤더로 링크를 표현한다.
정리
- 오늘날 대부분의 “REST API”는 사실 REST를 따르지 않고 있다.
- REST의 제약조건 중에서 특히 Self-Descriptive 와 HATEOAS 를 잘 만족하지 못한다.
- REST는 긴 시간에 걸쳐(수십년) 진화하는 웹 애플리케이션을 위한 것이다.
- REST를 따를 것인지는 API를 설계하는 이들이 스스로 판단하여 결정해야 한다.
- REST를 따르겠다면, Self-Descriptive 와 HATEOAS를 만족시켜야 한다.
- Self-Descriptive는 custom media type이나 profile link relation 등으로 만족시킬 수 있다.
- HATEOAS는 HTTP 헤더나 본문에 링크를 담아 만족시킬 수 있다.
- REST를 따르지 않겠다면, “REST를 만족하지 않는 REST API”를 뭐라고 부를지 결정해야 할 것이다.
- HTTP API라고 부를 수도 있고
-
그냥 이대로 REST API라고 부를 수도 있다.
REST 창시자는 싫어하지만…
REST를 만족하지 않는 REST API 가이드
- 아래는 Microsoft에서 제공하는 REST API 가이드이다.
- 하지만 이 가이드는 ‘REST를 만족하지 않는 REST API 가이드’ 이며, Microsoft도 인정했다.
- 어떻게 API를 설계해야하는지에 대한 상세한 가이드이다.
https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md