REST API
나는 다음 두 가지 조건을 만족하면 REST API라고 생각했다.
- URI는 리소스를 식별하도록 설계
- 행위는 Http Method를 이용해 표현
이렇게 고민하지 않고 API를 설계했다. 그러다 문득 '내가 하고 있는 게 왜 REST API를 만족하는 거지?', '그럼 REST API는 도대체 뭐지?'라는 의문이 생겼다. 그래서 공부하고 정리해 공유하게 됐다.
REST API
REST 아키텍처를 지키도록 설계한 API를 REST API라고 한다. 즉, REST 아키텍처에 대해 공부하면, 내가 외우고 있던 개념의 원리를 이해할 수 있을 것이다.
REST 아키텍처
REST 아키텍처는 분산 하이퍼미디어 시스템을 위한 아키텍처 스타일이다. (웹이 분산 하이퍼미디어 시스템의 대표적인 예다.)
여기서 아키텍처 스타일이란 제약조건의 집합을 의미한다.
REST 아키텍처는 어떻게 나오게 됐을까?
REST 아키텍처는 로이 필딩이 다음을 고민하면서 설계하게 됐다.
How do I improve HTTP without breaking the Web?
Roy T. Fielding
이를 쉽게 표현하면 REST 아키텍처는 웹에서 클라이언트와 서버의 독립적 진화를 가능하게 해주는 제약조건들이다.
그럼 각 제약조건에 무엇이 있는지 알아보겠다.
REST 제약조건들
- client - server
- stateless
- cache
- uniform interface
- layered system
- code - on - demand (optional)
REST API를 만족하려면 이렇게 많은 제약조건들을 지켜야 하는데 왜 나는 위에서 말한
- URI는 리소스를 식별하도록 설계
- 행위는 Http Method를 이용해 표현
두 가지만 신경 쓰자고 알고 있었을까? (이도 틀린 개념이다.)
우리는 보통 API를 만들 때 HTTP 프로토콜을 따른다. 그리고 HTTP 프로토콜을 따르면 자연스럽게 uniform interface를 제외한 나머지 REST 아키텍처의 제약조건들을 만족할 수 있다. 즉, HTTP API를 설계할 때 uniform interface 제약조건을 만족하면, 만든 API를 'REST API'라고 할 수 있다.
위에서 내가 알고 있던 개념은 uniform interface 제약조건에서 만들어진 개념이다.
그럼 uniform interface 제약조건을 자세히 알아보자.
uniform interface
uniform interface 제약조건을 만족하기 위해서는 다음 네 가지 제약조건을 만족해야 한다.
- identificaion of resources
- manipulation of resources through representations
- self - decriptive messages
- hypermedia as the engine of of application state (HATEOAS)
이를 보면 내가 알던 것과 다르다.
맞다 나는 잘못 알고 있었던 것이다. 그럼 각각을 자세히 보면서 REST 아키텍처를 만족하는 API를 설계하려면 어떻게 해야 하는지 알아보겠다.
identificaion of resources
리소스가 URI로 식별돼야 한다. 이는 내가 외우고 있던 'URI는 리소스를 식별하도록 설계'하라고 하는 이유다.
manipulation of resources through reprsentations
리소스에 대한 행위를 Http Method를 이용해 표현한다. 이는 '행위는 Http Method를 이용해 표현'하라고 하는 이유다.
self - descriptive messages
메시지만 보고 메시지를 해석 가능하게 설계해야 한다. 이는 내가 고려하지 않았던 부분이다. 예시를 통해 자세히 이해하자.
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"op": "remove",
"path": "todo/1"
}
]
위 예시는 self - descriptive 하지 않다. 왜냐하면 메시지만 보고 "op", "path"가 정확히 무엇을 의미하는지 알 수 없기 때문이다. 따라서 self - descriptive message 제약조건을 지키기 위해서는 다음과 같이 수정해야 한다.
HTTP/1.1 200 OK
Content-Type: application/json-patch+json
[
{
"op": "remove",
"path": "todo/1"
}
]
메시지를 만들기 전에 application/json-patch+json 콘텐츠 타입 명세를 작성하고, Http Message를 위와 같이 설계하면 self - descriptive 하다고 할 수 있다. 왜냐하면 명세를 통해 "op", "path"가 각각 무엇을 의미하는지 알 수 있기 때문이다. 즉, 메시지만 보고 메시지를 해석할 수 있다.
HATEOS
어플리케이션의 상태는 hyperlink를 이용해 전이돼야 한다. 이도 내가 고려하지 않았던 제약조건이다.예시를 통해 자세히 알아보자.
Request
GET /todos HTTP 1.1
HOST: example.org
Response
HTTP/1.1 200 OK
Content-Type: application/json-get+json
[
{
"title" : "강의하기"
},
{
"title" : "코딩하기"
}
]
예시는 todo 리스트를 조회하는 api의 응답이다. 만약 각 todo를 상세조회하는 상태로 전이하고 싶으면 어떻게 해야 할까?
이렇게 코딩했다면 클라이언트는 서버 개발자가 개발한 api 명세를 보고, 특정 todo를 조회하는 api를 요청하면 될 것이다. 이는 hyperlink를 통한 상태 전이가 아니다. 따라서 HATEOS를 만족하지 못한다. 그럼 HATEOS를 만족하게 수정해 보자.
Request
GET /todos HTTP 1.1
HOST: example.org
Response
HTTP/1.1 200 OK
Content-Type: application/json-get+json
[
{
"title" : "강의하기",
"link" : "https://example.org/todos/1"
},
{
"title" : "코딩하기",
"link" : "https://example.org/todos/2"
}
]
수정 결과 클라이언트는 링크를 통해 다음 상태로 전이할 수 있다. 즉, HATEOS를 만족할 수 있다.
지금까지 uniform interface를 만족하려면 네 가지 제약조건을 지키면 된다는 것을 알았다. 이제 REST API를 설계할 수 있다.
근데 여기서 한 가지 의문이 생겼다.
REST 아키텍처가 나오게 된 배경이 클라이언트와 서버의 독립적 진화라고 했다. 그럼 내가 생각하지 못했던 self - descriptive와 HATEOS는 독립적 진화와 어떤 관계가 있을까?
self - descriptive를 만족하면 메시지만 보고 메시지의 의미를 해석할 수 있다. 즉, 클라이언트는 서버의 진화와 상관없이 서버가 내려준 메시지로 원하는 행동을 할 수 있다.
HATEOS도 비슷하다. 어플리케이션의 상태 전이가 hyperlink를 통해서만 이뤄지면, 클라이언트는 서버의 진화와 상관없이 메시지를 통해 원하는 상태로 전이할 수 있다.
즉, 두 특징이 클라이언트와 서버의 독립적 진화에 도움을 준다.
무조건 REST API ?
REST 아키텍처에 대해 공부하면서, REST API 설계의 까다로움을 느꼈다.
그리고 여기서 의문이 생겼다. REST 아키텍처를 완벽하게 지키는 것이 무조건 좋을까? 이를 판단하기 위해서는 REST 아키텍처를 지킨 API의 장점과 단점을 생각해 봤다.
장점은 클라이언트와 서버가 독립적으로 진화할 수 있다. 이는 강력한 장점이 될 수 있다. 하지만 단점으로 REST API를 설계하기 위해 비싼 비용이 든다는 것이다. 그럼 API를 어떻게 설계하는 게 좋을까?
로이 필딩은 다음과 같이 말했다.
REST emphasizes evolvability to sustain an 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에 대해 따지느라 시간을 낭비하지 마라.' 즉, 우리가 클라이언트와 서버를 완전히 통제할 수 있거나, 서버가 바뀔 때마다 업데이트하겠다는 생각을 한다면 굳이 REST에 때문에 리소스를 사용하지 않아도 된다.
정리
REST API를 설계하려면 우리가 생각하는 것보다 훨씬 더 많은 비용이 든다.
항상 이런 비용을 쓰면서 REST API를 설계할 필요는 없다. 만약 우리가 클라이언트와 서버를 통제 가능하거나, 독립적 진화 대신 업데이트를 통해 진화한다면 굳이 API를 REST 하게 설계하지 않아도 된다. 물론 REST 아키텍처를 지키는데 투자할 충분한 리소스가 있다면 REST API를 작성하는 것이 좋을 것이다.
Reference
'네트워크' 카테고리의 다른 글
HTTP Connection 역사 알아보기 (0) | 2024.02.24 |
---|---|
SSE(Server Sent Event) 개념 정리 (0) | 2024.02.15 |
HTTPS 적용 방법과 동작원리 (1) | 2023.07.14 |
WebSocket과 친해지기 (0) | 2023.05.30 |