일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- CDN
- 성능최적화
- styled-component
- Modal
- 회고
- component
- scrapping
- 웹팩
- react
- 웹크롤링
- npx
- go
- cicd
- 반응형웹
- typescript
- javascript animation
- express
- graphql
- 정규표현식
- socket.io
- docker
- sequelize
- 포트포워딩
- Redux
- AWS
- route
- Recoil
- Today
- Total
프로그래밍 공부하기
그런 REST API로 괜찮은가 본문
REST API란 정확히 무엇을 의미하는 것일까? 나는 REST API를 URI를 통해 리소스를 식별하고 GET, POST, DELETE 등의 메소드로 동작을 처리하는 구조 정도로만 이해하고 있었다. 위 영상을 통해 REST API가 무엇인지, 사용 목적이 무엇인지 구체적으로 이해할 수 있었다.
REST API란 REST 아키텍처 스타일을 따르는 API이다. REST 스타일은 Client-server, stateless, cache, uniform interface, layered system, code-on,demand(optional)로 구성이 되어 있으며 이들을 모두 만족해야 REST라고 표현할 수 있다. 그런데 자칭 REST API라 하는 많은 API들이 이들을 모두 만족하지 않으면서 REST API라고 주장하고 있다.
많은 자칭 REST API들이 REST 스타일 중 Uniform Interface라는 제약 조건을 따르지 않는다. Uniform Interface의 제약 조건은 다음과 같다.
- Identification of resources: 리소스가 URI로 식별
- Manipulation of resources through representation: 리소스를 처리할 때 URI의 메세지에 해당 내용이 포함
- self-descriptive messages: 메시지는 스스로를 설명해야함
- hypermedia as the engine of application state(HATEOAS): 애플리케이션의 상태는 Hyperlinke를 통해 전이됨
이 중 3, 4번째 조건인 self-descriptive와 HATEOAS를 대부분 지키지 못하고 있다. 이 것이 어떤 것을 의미하는지 알아보자.
*Self-Descriptive
//1번 응답 -> [], {} 의 의미 알 수 없음
HTTP/1.1 200 OK
[{"op": "remove", "path": "/ihl/trash"}]
//2번 응답 -> op, path의 의미 알 수 없음
HTTP/1.1 200 OK
Content-Type: application/json
[{"op": "remove", "path": "/ihl/trash"}]
//3번 응답 -> Self descriptive 만족!
HTTP/1.1 200 OK
Content-Type: application/json-patch+json
[{"op": "remove", "path": "/ihl/trash"}]
Self-descriptive message란 메시지의 내용 자체만으로도 온전한 해석이 가능해야한다는 것이다. 위 3가지 응답 메시지에서 1번과 2번은 self-descriptive를 만족하지 않는다. (보통은 추가적인 API문서를 따로 찾아봐야한다.) 3번 처럼 구체적으로 미디어 타입을 등록하여야 메시지를 본 사람이 미디어 타입 명세를 보고 메시지의 의미를 해석할 수 있다.
*HATEOAS
HATEOS란 애플리케이션의 상태는 Hyper link를 이용해 전이되어야 하는 것이다. HTML의 경우 대부분 이를 만족하지만 JSON 메시지는 이를 만족하지 못하는 경우가 대다수이다.
Uniform Interface
왜 Self-Descriptive와 HATEOAS를 지켜야하는 것일까? 바로 독립적으로 진화하기 위해서이다. 이는 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다는 의미와 동일하다. REST를 잘 지켜서 독립적 진화가 가능한 대표적인 예가 웹이다. 웹 페이지가 변경했다고 웹 브라우저를 업데이트할 필요가 없으며 웹 브라우저를 업데이트했다고 웹 페이지를 변경할 필요가 없다. 브라우저에 따라 UI가 조금 깨질 수는 있지만 동작에는 전혀 지장이 없다.
Self-descriptive의 경우 서버나 클라이언트가 변경되어도 오고가는 메시지는 스스로를 잘 설명하고 있기 때문에 언제가 해석이 가능해진다. HATEOS의 경우 애플리케이션 상태 전이가 나중에 결정(late binding)되므로 링크가 동적으로 변경할 수 있다. 따라서 서버가 링크를 바꾸면 클라이언트는 바뀐 링크를 보고 그대로 따라가기만 하면 된다.
REST API화
API를 지속적으로 업데이트 할 예정이라면 REST API를 지켜주는 것이 상호 운용성(interoperability)을 위해 좋은 방향일 것이다. 그렇다면 Self-descriptive와 HATEOAS를 지키기 위해 JSON 메시지를 어떤 방식으로 구현하면 될지 알아보자.
//1. MediaType
GET /IHLScores HTTP/1.1
Host: ihl.com
HTTP/1.1 200 OK
Content-Type: application/vnd.IHLScores+json
[{"id": "수학", "Score": 88}, {"id": "영어", "Score": 87}]
//2. Profile
GET /IHLScores HTTP/1.1
Host: ihl.com
HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://ihl.com/docs/IHLScores>; rel="profile"
[{"id": "수학", "Score": 88}, {"id": "영어", "Score": 87}]
Self-description의 경우 2가지 방법이 있다. 첫 번째는 IANA에 내가 사용할 Media Type을 등록하는 것이다. 이 방법의 단점은 매번 Media Type을 정의해야한다는 점이다. 두 번째는 Profile을 사용하는 것이다. 이 경우 클라이언트가 Link와 profile을 이해할 수 있어야 한다는 조건이 생긴다. 또한 Content negotiation이 불가능한 단점이 있다.(정확히는 이해하지 못했지만 Client가 요구스펙을 만족하지 못할 때 Server가 알 수 없다. 는 의미인 것 같다)
//방법1-1. 직접 여러가지 방식으로 하이퍼링크를 넣는다.
//예시1
GET /IHLScores HTTP/1.1
Host: ihl.com
HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://ihl.com/docs/IHLScores>; rel="profile"
[
{
"link": "https://ihl.com/id/1",
"Score": 88
},
{
"link": "https://ihl.com/id/2",
"Score": 87
}
]
//예시2
GET /IHLScores HTTP/1.1
Host: ihl.com
HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://ihl.com/docs/IHLScores>; rel="profile"
{
"links": {"Score": "https://ihl.com/id/{id}"},
"data": [{"id": "수학", "Score": 88}, {"id": "영어", "Score": 87}]
}
//방법2. 헤더에 링크를 넣는다.
HTTP/1.1 204 No Content
Location: /id/1
Link: </id/>; rel="collection"
HATEOAS를 만족시키는 첫 번째 방법은 메시지에 직접 하이퍼링크를 넣는 것이다. 위와 같은 여러 방식으로 하이퍼링크를 객체에 포함시키면 된다. 혹은 JSON으로 하이퍼링크를 표현하는 방법을 정의한 JSON API, HAL, UBER, Siren 등의 명세를 활용하여 하이퍼링크를 표현할 수 있다. 두 번째 방법은 Link, Location 등의 헤더를 이용하여 링크를 표현하는 것이다.
이 외에 내가 추가적으로 공부한 부분은 리소스를 나타내는 방식이다. 리소스를 나타낼 때 다음과 같은 규칙을 지켜야한다.
1. 리소스를 나타내기 위해 명사를 사용하라
//1. document
/user-management/users/{id}
//2. collection
/user-managements/users/{id}/accounts
//3. store
/cart-management/users/{id}/carts
//4. controller
/cart-management/users/{id}/cart/checkout
공식문서에서 리소스는 크게 document/collection/store/controller로 분류한다. 각 분류에 다라 위와 같이 이름을 지어야 한다.(예를들어 collection은 복수형을 사용한다...) 이 때 CRUD와 같은 동작 이름 등을 URI에 사용하지 않아야 한다.
2. 일관적인 이름을 지어라
다음과 같은 규칙을 지켜 일관적인 이름을 짓는 것이 좋다.
- 계층구조를 나타낼 때 /사용하기
- URI 끝에 / 붙이지 않기
- URI의 가독성을 높이기 위해 -사용
- _는 사용하지 않음
- URI에 소문자 사용
- 파일확장자는 사용하지 않음
3. filterring해야한다면 query component를 사용하라
/user-management/users?id=32&®ion=USA
리소스를 필터링해야할 때가 있다. 이 때는 위와 같이 query component를 활용하자.
사실 위의 사항을 지키지 않아도 웹을 당장 서비스하는 것에는 문제가 없기 때문에 지나쳐가기 쉬운 부분인 것 같다. 그러나 API는 한 번만들고 끝이 아니라 끊임 없이 수정되야 하므로 힘들고 낯설더라도 REST API로 충족시키는 방향으로 가야한다고 생각이 들었다. Media Type, Profile, Header 등의 사용 예시들을 더 찾아보고 활용할 수 있도록 연습해봐야겠다.
'감상문' 카테고리의 다른 글
코어 자바스크립트 (0) | 2021.02.07 |
---|---|
영어 변수명을 잘 지어보자 (0) | 2021.02.07 |
어쨌든 이벤트 루프는 무엇입니까? (0) | 2021.02.03 |
유저동향분석시스템 (0) | 2021.01.07 |
좋은 쿠키 있으면 소개 시켜줘 (0) | 2021.01.06 |