진정한 REST API
진정한 REST API
REST API가 무엇이고 진정한 Restful은 무엇인지 공부해보자.
REST API란?
- REST는 Representational State Transfer의 약자로 로이필딩에 의해 만들어졌다.
- REST 아키텍처의 제약 조건을 준수하는 애플리케이션 프로그래밍 인터페이스이다.
- REST 아키텍처는 애플리케이션 사이의 결합도를 낮춰 설계되어있는 아키텍처이다. 결합도를 낮춰 애플리케이션끼리 별도로 구축되고 결합할 수 있도록한다.
REST API 제약조건
API가 아래의 6가지 제약조건을 만족하면 Restful하다고 한다.
- Client-Server
- Stateless
- Cacheable
- Uniform Interface
- Layered System
- Code-On-Demand
Client-Sever
- 이 조건의 기본 원칙은 관심사의 명확한 분리이다.
- 사용자들에게 제공하는 Interface인 User Interface와 데이터 스토리지(DB와 같은), 비즈니스 로직 등 서버 내부의 작업을 분리함으로써 User Interface는 여러 플랫폼에서의 이식성을 향상 시킬 수 있으며 서버는 그 구성요소를 단순화하여 확장성을 단순화 할 수 있다.
- 이 조건을 따르면 클라이언트와 서버는 서로 의존하지 않고 진화할 수 있다. 클라이언트는 서버의 URI만 알고 있으면 되기 때문이다.
Stateless
- 무상태성
- 서버에서 클라이언트의 상태 정보를 저장하지 않고 들어오는 요청에 대해서만 처리하여 구현을 단순화 하는 것이다.
- 따라서 클라이언트의 모든 요청은 서버가 요청을 알아드는데 필요한 모든 정보를 담고있어야 한다.
- 세션 상태도 클라이언트 쪽에 보관되어 클라이언트 요청 시 세션을 같이 보내 서버의 데이터베이스와 같은 서비스로 전송되어 일정 기간동안 지속 상태를 유지하여 인증하는데 사용하게 된다.
Cacheable
- 요청에 대한 응답 내의 데이터에 해당 요청은 캐시가 가능한지 불가능한지 명세를 해줘야한다.
- 캐시 사용이 가능하면 클라이언트는 응답 데이터를 재사용 할 수 있다.
- cache-control 헤더를 통해 캐시 여부를 명시해줘야 한다.
- Last-Modified, E-Tag를 이용해서 캐시 구현이 가능하다.
Uniform Interface
URI로 지정된 리소스에 균일하고 통일된 인터페이스를 제공해야한다.
아키텍처를 단순하게 분리하여 독립적으로 만들 수 있다는 장점이 있다. 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다. (배포가 어려운 모바일 앱에서 특히나 큰 장점 중 하나이기도 하다.)
Resource Identification in request
- 요청의 자원식별이라는 의미로 요청 부분에서 개별 자원에 대한 식별을 할 수 있어야 한다.
- 예를들어 URI가 있다.
http://localhost:8080/resource/1
- 서버에서는 요청된 리소스 보고 HTML, XML, JSON 등 형태로 보낼 수 있게 된다.
Manipulation of resources through Representations
- 메시지를 통해 자원을 조작한다는 의미이다.
- 클라이언트의 요청에 어떤 자원에 대한 적절한 표현과 작업을 수행하기 위한 메타데이터를 충분히 갖추고 있다면, 서버 상에서 해당 자원을 변경, 삭제할 수 있는 충분한 정보를 가지고 있다는 의미이다.
- HTTP의
GET
,POST
,PUT
,DELETE
등을 말한다.
Self-Descriptive Message
자기 서술적 메시지라는 의미이다.
각 메시지는 자신을 어떻게 처리해야 하는지 충분한 정보를 포함해야 한다는 의미이다.
예를 들어 요청의 경우를 살펴보자.
GET / HTTP/1.1
위에 요청 메시지는 전혀 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하지 않다.
이유는 클라이언트가 해석하려고 하면 어떤 문법으로 작성된 것인지 모르기 때문에 해석에 실패한다. 그렇기 때문에
Content-Type
헤더가 반드시 들어가야한다.HTTP/1.1 200 OK Content-Type: application/json [ { "op": "remove", "path": "/a/b/c" } ]
이제
Content-Type
헤더에서 대괄호, 중괄호, 큰 따옴표의 의미가 무엇인지 알 수 있게 되어 파싱이 가능하고 문법을 해석할 수 있게된다.하지만 아직 Self-Descriptive하지 않다.
op
값이 무슨 뜻이며paht
가 무엇을 의미하는지 알 수가 없다.HTTP/1.1 200 OK Content-Type: application/json-patch+json [ { "op": "remove", "path": "/a/b/c" } ]
이렇게 명시를 하면 완전해진다. 이 응답은
json-patch+json
이라는 미디어 타입으로 정의된 메시지이기 때문에json-patch
라는 명세를 찾아가서 이해한 다음, 이 메시지를 해석을 하면 그때 올바르게 메시지의 의미를 이해할 수 있게된다.이렇게 메시지를 봤을 때 메시지의 내용으로 온전히 해석이 다 가능해야 한다는 것이 Self-Descriptive이다.
오늘날의 REST API는 상당히 이를 만족하지 못하고 있으며 대부분 미디어 타입에는
json
이라고만 되어있고 이를 어떻게 해석해야되는지는 명시가 되어있지 않다.
Hypermedia As the Engine Of Application State(HATEOAS)
단순히 결과 데이터만이 아닌 결과 리소스 정보를 포함해야 한다는 의미이다.
즉, 애플리케이션의 상태는 Hyperlink를 이용해 전이되어야 한다는 것이다.
HTML 같은 경우 HATEOAS를 만족한다.
예를 들어 HTML을 보자
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 헤더(RFC 5988)를 이해해야하는 어려움과 Content negotiation이 불가하다는 단점이 있다.
아래와 같이 json에 포함 시켜도 된다.
HTTP/1.1 200 OK Content-Type: application/json { “title” : “The second article”, “contents” : “blah blah..." "links" : { previous : /article/1 next : /article/3 } }
이 방법도 링크를 표현하는 방법을 직접 정의해야 된다는 단점이 있긴하다.
(헤더의 방법과 데이터로 넘기는 방법을 적절히 사용하는 것이 좋다.)
Layered System
- 계층화된 시스템 아키텍처를 사용하여 각 구성들 간의 계층을 마음대로 상호작용 할 수 없도록 제한 하는 제약조건으로 Interface를 일원화한다.
Code-On-Demand
- 선택이 가능한 제약 조건이다.
- 클라이언트는 서버에서 자바 애플릿, 자바스크립트 실행 코드를 전송받아 기능을 일시적으로 확장할 수 있다는 의미이다.
Code on demand
는 서버가 클라이언트에 보내는 데이터로 바로 실행 가능한 코드를 보내서 클라이언트에서 실행하는 것을 의미한다.
실제로 지켜지지 않는 Restful
대부분 Rest API라고 불리는 API들은 Rest 제약 조건 중에 Uniform Interface 규칙에서 Self-Descriptive Message와 HATEOAS 규칙이 지켜지고 있지 않다.
아무래도 이 이유는 json의 사용에서 오게됐는데 태그에 맞도록 사용하면 알아서 위에 규칙이 지켜지는 HTML과 달리 json은 불완전하다.
따라서 별도로 문서(API 문서 등)이 필요하게 되어 규칙을 지키기가 쉽지않다.
원격 API가 꼭 REST API여야 할 필요는 없다.
따라서 API를 개발하는 본인의 상황이 클라이언트 개발자를 통제할 수 있거나 클라이언트, 서버를 다 만들 수 있는 경우에는 단순 HTTP API로 개발을 해서 시간을 아낄수 도 있다.
또는 Uniform Interface를 통한 상호운용성과 진화에 관심이 없다면 계속 업데이트를 하면서 개발을 해도 좋다.
하지만 사실상 우리는 시스템 전체를 통제할 수 없기도 하고 개발자로서 진화에 관심을 두어야 하기에 REST를 따르자.
Reference
https://www.redhat.com/ko/topics/api/what-is-a-rest-api
https://meetup.toast.com/posts/92