REST: A way of providing interoperability between computer systems on the internet.
컴퓨터 시스템간에 상호운용성을 제공하는 것중 하나의 방법이 REST라고 합니다.
더 자세히 알아보기 위해서 어떻게 나오게 되었는지 역사를 따라가 봅시다.
WEB(1991)
팀 버너스리는 어떻게 인터넷에서 정보를 공유할 수 있을까에 대해서 고민을 하였고 결론은 다음과 같았습니다.
A: 정보들을 하이퍼텍스트로 연결합니다.
이렇게 설계함으로써 HTTP 프로토콜이 탄생하게 되는데,
HTTP/1.0(1994-1996)
Roy T.Fielding: "How do I improve HTTP without breaking the Web?"
이것이 무슨 말이냐면, HTTP/1.0의 설계가 시작되기 전에 이미 HTTP는 전세계의 World Wide Web에서 전송 프로토콜로써 사용되고 있었고, Web은 급속도로 성장하는 도중이였습니다.
HTTP를 정립하고 명세에 기능을 더하고 기존의 기능을 고쳐야 되는 상황에 놓이게 된 것이었죠.
이때 기존에 구축되어 있는 Web과 호환성의 문제를 피하기 어려울 수 있습니다.
여기서 나온 해결책은 HTTP Object Model입니다.
이것이 4년 후에는 REST라는 명칭으로 발표하게 됩니다.
로이 필딩은 2000년에 UC 어바인에서 "Architectural Styles and the Design of Network-based Software Architectures"라는 제목의 2000년 박사 학위 논문에 REST를 정의하였습니다.
한편으론,,
Microsoft에서는 XML-RPC를 1998년도에 만들게 되는데 이것은 원격으로 단일 메서드를 호출할 수 있는 프로토콜입니다. 이것은 나중에 SOAP이라는 이름으로 바뀌게 됩니다.
2000년 2월에는 Salesforce API가 공개됩니다.
이 당시에 SOAP을 사용해서 API를 만들었는데, 복잡하여 잘 사용하지 않았습니다.
4년 후에 flicker에서 API를 만들게 되는데 여러가지 형태로 만들었습니다. SOAP과 REST를 공개했는데, 여기서 SOAP보다 REST가 단순하고 규칙이 적게 보여서 사용자들에게 더 쉽게 보여졌습니다.
그래서 시간이 갈수록 사용자들은 SOAP API에서 REST API로 갈아탔고, 결국 2006년에 AWS가 자사 API의 사용량의 85%가 REST임을 밝혔습니다.
이렇게 REST로 정착될 것 같았지만 2008년에 CMIS라는 것이 나오게 됩니다.
CMIS (2008)
하지만, 로이 필딩은 이것을 보고 이렇게 말합니다.
| "NO REST in CMIS"
다른 경우도 있는데, Microsoft에서 REST API Guildelines를 2016년에 발표하게 됩니다.
간단하게 보자면,
이것을 본 로이 필딩은 이렇게 남깁니다.
| "s/REST API/HTTP API/"
REST API가 아니다. 이것은 HTTP API라고 해야한다. 라는 의미입니다.
로이 필딩은 블로그에 아래 문장을 남겼다고 합니다.
| "REST APIs must be hypertext-driven"
| "REST API를 위한 최고의 버저닝 전략은 버저닝을 안 하는 것"
사람들이 알고 있는 REST와 제작자인 로이 필딩이 말하는 REST는 너무 많이 다릅니다.
무엇이 문제인지 확인해 봅시다.
❗️REST API
REST 아키텍쳐 스타일을 따르는 API
여기서 REST는 무엇일까요?
❗️REST
분산 하이퍼미디어 시스템(예: 웹)을 위한 아키텍쳐 스타일
❗️아키텍쳐 스타일
제약조건의 집합
즉, 제약조건을 모두 만족해야 REST를 만족했다고 할 수 있습니다.
사실 REST는 아키텍처 스타일이면서 하이브리드 아키텍처 스타일이라고 말하는데 그 이유는 아키텍처 스타일이면서 아키텍처 스타일의 집합이기 때문입니다.
REST를 구성하는 스타일
❓code-on-demand
서버에서 코드를 클라이언트에게 보내서 실행할 수 있어야 한다. -> JavaScript
Uniform Interface의 제약조건
위의 두개는 많은 API들이 잘 지키고 있지만 아래 두개는 오늘날 대부분의 REST API라고 지칭하는 것들이 지키고 있지 않은 것들입니다.
이것은 "메시지는 스스로를 설명해야한다"라는 의미입니다.
예를 들어 다음과 같은 메시지가 있다고 해봅시다.
GET / HTTP/1.1
이 HTTP 요청 메시지는 뭔가 빠져있어서 self-descriptive 하지 못한다고 합니다.
"목적지"가 빠져있습니다.
GET / HTTP/1.1
Host: www.example.org
"목적지"를 추가하면 이제 self-descriptive합니다.
또 다른 예시를 보자면 다음과 같은 응답이 있다고 해봅시다.
HTTP/1.1 200 OK
[ { "op": "remove", "path": "/a/b/c" } ]
이것 또한 self-descriptive 하지 못하다고 말하는데 그 이유는 이것이 어떤 문법으로 작성된 것인지 모르기 때문에 해석을 못하기 때문에 다음과 같이 해줘야 합니다.
HTTP/1.1 200 OK
Content-Type: application/json
[ { "op": "remove", "path": "/a/b/c" } ]
이제 self-descriptive 하냐고 물었을 때 아니라고 합니다.
이것을 보고 "op"가 무엇을 의미하고, "path"가 무엇을 의미하는지 알 수가 없습니다. 다음과 같이 해줘야 완전해 진다고 합니다.
HTTP/1.1 200 OK
Content-Type: application/json-patch+json
[ { "op": "remove", "path": "/a/b/c" } ]
json-patch 명세를 찾아가서 메시지를 해석하면 그떄서야 비로소 올바르게 메시지의 의미를 이해할 수 있게 됩니다.
애플리케이션 상태는 Hyperlink를 이용해 전이되어야 한다고 합니다.
다음과 같은 애플리케이션이 있다고 해봅시다.
이러한 형태로 되어 있는 것을 애플리케이션 상태를 전이한다고 합니다.
상태 전이마다 해당 페이지에 있던 링크를 따라가면서 HATEOAS가 된다고 합니다.
말 그대로 Hyperlink를 통한 전이가 된 것입니다.
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라는 헤더가 있는데, 이것이 바로 이 리소스와 하이퍼링크로 연결되어 있는 다른 리소스를 가르킬 수 있는 기능을 제공해줍니다.
이 정보는 Link 헤더가 이미 표준으로 문서화 되어 있기 때문에 이 메시지를 보낸 사람이 온전히 해석해서 이해할 수 있기 때문에 이것 또한 HATEOAS를 만족할 수 있는 것입니다.
왜 필요한 것일까요?
그것은 독립적 진화 때문이라고 합니다.
그렇다면 실제로 이것이 잘 지켜지고 있는가? REST가 잘 지켜지고 있는가?
이것을 알 수 있는 것은 바로 "WEB"입니다.
웹(WEB)
웹 브라우저의 경우 옛날 버전에서는 UI가 좀 깨질 수도 있지만 동작은 됩니다.
웹에서는 웹 사이트가 바뀌었다고 해서 웹 브라우저를 업데이트 해야하거나 옛날 버전은 호환되지 않는다거나 하는 일이 거의 일어나지 않습니다.
하지만 모바일 앱에서는 빈번하게 일어나죠.
이것은 엄밀하게 말하면 모바일 앱 클라이언트와 서버가 REST로 통신하고 있지 않다는 것(REST 아키텍처 스타일을 따르고 있지 않다는 것)을 의미합니다.
웹에서는 어떻게 이것이 가능하게 되었을까요?
W3C Working group에서 html을 만들고, IEFT Working groups에서 http를 만들고, 웹 서버/브라우저 개발자들이 많은 토론을 하며 노력을 합니다.
HTML5 첫 초안에서 권고안이 나오는데 무려 6년, HTTP/1.1 명세 개정판 작업하는데 7년이 걸렸다. HTTP/1.1에 새로운 기능이 추가 되었는가? 아닙니다. 단지, 문서를 다듬기만 하는데 7년이 걸렸다. 절대로 하위 호환을 깨드리지 않기 위해 정말 깊은 토론의 결과였던 것입니다.
상호운용성(interoperability)에 대한 집착
그렇다면 웹이 계속해서 진화할 수 있도록 REST가 어떤 영향을 주었을까요?
그러면 여기서 의심해볼만한 것이 "REST API는 위에서 언급했던 모든 제약조건을 전부 지킬 필요가 있을까?"에 대한 부분입니다.
Roy T. Fielding은 그렇다고 합니다.
"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
Roy T. Fielding은 이에 대해서 아니라고 말합니다.
"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 API를 어떻게 받아들이면 되는 것일까요?
3번에 대해서는 Roy T. Fielding이 다음과 같이 말하면서 싫어합니다.
"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. Fieldin
그래서 1번을 시도해보고자 합니다.
일반적인 웹과 비교를 해봅시다.
흔한 웹 페이지 | HTTP API | |
---|---|---|
Protocol | HTTP | HTTP |
커뮤니케이션 | 사람-기계 | 기계-기계 |
Media Type | HTML | JSON |
커뮤니케이션 부분이 조금 다른데, HTTP API는 사람이 아닌 기계가 해석을 합니다. 그러다보니깐 미디어 타입이 다를 수 밖에 없습니다.
그렇다면 미디어 타입을 비교를 해봅시다.
HTML | JSON | |
---|---|---|
Hyperlink | 됨 (a 태그 등) | 정의되어있지 않음 |
Self-descriptive | 됨 (HTML 명세) | 불완전* |
여기서 Self-descriptive를 보면 HTML은 되는 이유가 명세에 보면 모든 태그의 정의가 다 되어 있습니다.
JSON은 Object안에 들어갈 수 있는 Key-Value에 대한 정의는 아무런 것도 정의되어 있지 않습니다. 단, 문법만 정의되어 있을 뿐입니다.
* : 문법 해석은 가능하지만, 의미를 해석하려면 별도로 문서가(API 문서 등) 필요하다.
예시를 한번 봐봅시다.
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>
이 예시가 Self-descriptive하는지 따져봅시다.
Self-descriptive
HATEOAS
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
HATEOAS
Self-descriptive는 확장 가능한 커뮤니케이션을 가능하게 합니다.
서버나 클라이언트가 변경되더라도 오고가는 메시지는 언제나 self-descriptive 하므로 언제나 해석이 가능합니다.
HATEOAS는 애플리케이션 상태 전이의 late binding을 가능하게 해줍니다.
어디서 어디로 전이가 가능한지 미리 결정되지 않습니다. 어떤 상태로 전이가 완료되고 나서야 그 다음 전이될 수 있는 상태가 결정됩니다.
쉽게 말해서: 링크는 동적으로 변경될 수 있습니다.
좀 더 알기 쉽게 풀어보자면, 링크를 주고 받기 때문에 서버가 링크를 바꾼다고 해도 클라이언트 동작에는 문제가 없습니다. 왜냐하면 수정된 링크를 받고 따라가면 되기 때문입니다.
링크를 따라 페이지 이동을 성공적으로 한 후에야 다음 전이될 수 있는 상태를 보고 결정할 수 있는 것이 late binding입니다.
late binding이 되기 때문에 서버가 동적으로 링크를 바꿀 수 있고 독립적인 진화가 가능한 것입니다.
이것을 토대로 REST API를 수정해 봅시다.
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": "집에 가기"}
]
단점
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": "집에 가기"}
]
단점
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": "집에 가기"
}]
}
단점
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"
}]
}
단점
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 등을 활용한다.
단점
결론적으로, 정의를 해보자면 HATEOAS는 data, 헤더 모두 활용하면 좋습니다.
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
사용하고 있는 회사 즉, 사내에서 이해하고 사용하는데 지장이 없다면 IANA에 등록하지 않아도 상관은 없다고 하네요.
하지만, 장점도 있습니다.
장점
참고한 사이트