[RESTful API] 그런 REST API로 괜찮은가?

2021. 3. 20. 21:03RESTful API

1. REST

1) REST 란?

  • REpresentational
  • State
  • Transfer
  • REST is a way of providing interoperability between computer systems on the Internet

 

아무리 REST라는 단어를 풀어보아도 이해가 안되는 경우가 많다. 하지만 REST가 어떠한 계기들을 통해서 나오게 되었는지 접근해보면 이해할 수 있다.

2) WEB (1991)

Q. 어떻게 인터넷에서 정보를 공유할 것인가?

A. 정보들을 하이퍼텍스트로 연결한다. (표현 형식 - HTML, 식별자 - URI, 전송 방법 - HTTP)

2) HTTP / 1.0 (1994 - 1996) 

Roy T.Fielding HTTP 설계에 참여하면서 기존의 웹을 망가트리지 않고 향상 시킬 수 있을지 고민한다.

 

Q. How do i improve HTTP without breaking the Web?

A. HTTP Object Model

3) REST (1998)

설계에 참여한 4년 후 Roy T.Fielding은 REST를 Microsoft Research에서 발표한다.

"Representational State Transfer : An Architectural Style for Distributed Hypermedia Interaction"

4) REST (2000)

2년 후 다시 Roy T.Fielding은 REST를 박사 논문으로 발표한다. 이는 우리가 REST를 정의하는 논문이 된다.

"Architectural Styles and the Design of Network-based Software Architectures"

 

 

 

2. API

1) REST API의 등장

  • 1998년 Microsoft XML-RPC, SOAP 등장했다.
  • 2000년 Salesforce에서 API 공개했지만 단순한 API임에도 굉장히 복잡했다.
  • 2004년 flickr에서 API를 SOAP의 형태와 논문의 이름을 가져온 REST라는 이름의 API를 공개했다.

 

그 결과, REST는 SOAP에 비해 굉장히 단순하고 규칙이 적으며 쉽다는 특징으로 급부상하게 된다.

  • 2006년 AWS가 자사 API의 사용량이 85%가 REST임을 공개
  • 2010년 Salesforce.com에서도 REST API를 추가함을 공개

 

2) Roy T.Fielding의 반박

2008년 EMC, IBM, Microsoft가 함께 작업하여 CMS를 위한 표준인 CMIS를 제작했다. 이 CMIS에는 REST 바인딩을 지원한다고 했지만, Roy T.Fielding은 CMIS에 REST가 없다고 반박을 했다.

 

또한 2016년 많은 사람들이 합리적이라고 생각했던 Microsoft에서 제작한 REST API 가이드라인마저 Roy T.Fielding은 REST가 아닌 그저 HTTP API라고 이야기를 하게 된다.

  • URI는 https://{serviceRoot}/{collection}/{id} 형식이어야 한다.
  • GET, PUT, DELETE, POST, HEAD, PATCH, OPTIONS를 지원해야한다.
  • API 버저닝은 Major.minor로하고 URI에 버전 정보를 포함시킨다.
  • 등등

 

이 후 Roy T.Fielding은 반박과 동시에 다음과 같은 말을 했다.

  • REST API는 반드시 hypertext-driven이여야한다.
  • REST API를 위한 최고의 버저닝 전략은 버저닝을 안하는 것이다.

 

 

 

3. REST API

1) REST를 따른 다는 것은 ?

그렇다면 여기서 "도대체 무엇이 문제인가?" 에 대한 의문이 들 수 있다.

 

REST API를 풀어 쓴다면 다음과 같다.

  • REST 아키텍쳐 스타일을 따르는 API
  • REST는 분산 하이퍼미디어 시스템(ex. 웹)을 위한 아키텍쳐 스타일
  • 아키텍쳐 스타일이란 제약 조건의 집합
  • 즉, 제약 조건들을 모두 지켜야 REST를 따른다고 할 수 있다.

 

REST 아키텍처를 구성하는 스타일은 다음과 같다.

스타일 설명
Client - Server - 클라이언트 - 서버 구조를 이루어야한다.
- 일관적인 인터페이스로 분리되어야 한다.
Stateless - 상태가 유지되어선 안된다.
- 각 요청 간 클라이언트의 컨텍스트가 서버에 저장되어서는 안된다.
- 데이터를 주고 받는 것으로 끝나야한다.
- 세션과 같은 컨텍스트 정보를 신경쓸 필요가 없어 구현이 단순해진다.
Cache - www에서와 같이 클라이언트는 응답을 캐싱할 수 있어야 한다.
Layered System - 계층형 시스템 구조가 되어야한다.

서버는 클라이언트가 모르게 API 서버에 여러 계층을 추가하여 유연한 구조로 개발될 수 있다. 예를 들어 서버에서 Proxy나 Gateway, 로드밸런싱, 암호화 등의 기능들을 사용해도 클라이언트에서는 몰라야 한다.
Code-on-demand (optional) - 서버에서 코드를 클라이언트로 보내서 실행할 수 있어야한다. (ex. JavaScript)
Uniform interface - 아키텍처를 단순화시키고 작은 단위로 분리함으로서, 클라이언트 - 서버의 각 파트가 독립적으로 개선될 수 있도록 해준다.

대체로 오늘날 사용되는 API는 대부분의 위의 조건을 만족시킨다. 하지만 이 중 Uniform interface를 만족시키지 않은 경우가 많다.

2) Uniform Interface의 제약 조건

  • Identification of resources 
  • Manipulation of resources through representations 
  • Self-descriptive messages
  • Hypermedia as the engine of application state (HATEOAS)

 

조건 설명
Identification of resources - 자원이 URI로 식별되면 된다.
Manipulation of resources through representations - Representations (PUT, GET, DELETE 등) 전송을 통해 자원을 조작해야한다.
Self-descriptive messages - 메시지가 스스로 설명해야한다. 즉, 메시지만 보고 무슨 뜻을 의미하는지 알아야한다.
HATEOAS - 애플리케이션의 상태는 HyperLink를 이용해서 전이되어야 한다.

오늘날 'Identification of resources'와 'Manipulation of resources through representations'의 조건은 대체로 잘 만족한다. 하지만 나머지 2개의 조건은 거의 지키지 못하고 있다.


Self-descriptive messages 의 조건은 예시를 통해 이해할 수 있다.

만족하지 않는 경우의 예

- 목적지가 생략되어 있기 때문에 Self-descriptive 조건을 만족하지 않는다.

GET / HTTP /1.1

- 클라이언트가 해석하지 못하기 때문에 Self-descriptive 조건을 만족하지 않는다.

HTTP/1.1 200 OK
[ {"op": "remove", "path": "a/b/c/" }

 

만족하는 경우의 예

- 목적지를 정의했기에 만족

GET / HTTP /1.1
Host: www.test.com

- Content-Type의 application/json-patch+json 명세를 통해 Body 내용을 해석할 수 있어 만족

HTTP/1.1 200 OK
Content-Type: application/json-patch+json  
[ {"op": "remove", "path": "a/b/c/" } 

HATEOAS의 조건은 HyperLink를 이용해서 전이되어야 한다.

- <a> 태그를 통해 HyperLink를 사용한 상태 전이가 가능하기 때문에 HTML은 이 조건을 만족한다.

HTTP/1.1 200 OK
Content-Type: text/html

<html>
<head></head>
<body><a href="/test">test</a></body>
<html>

- JSON은 HTTP의 Link Header를 통해 만족시킬 수 있다.

HTTP/1.1 200 OK
Content-Type: application/json
Link: </article/1>; rel="previous",
      </article/3>; rel="next";

{
      "title": "The second article",
      "contents": "blah blah.."
}

HATEOAS 상태 전이


제약 조건을 이해했다면, 왜 Uniform Interface를 왜 만족해야하는지 알아야한다.

 

Uniform Interface를 만족해야하는 이유는 독립적 진화때문이다.

  • 서버와 클라이언트가 각각 독립적으로 진화한다.
  • 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다.
  • REST를 만들게 된 계기 "How do I improve HTTP without breaking the Web"

3) REST를 사용한 사례 (웹)

  • 웹 페이지를 변경했다고 웹 브라우저를 업데이트할 필요가 없다.
  • 웹 브라우저를 업데이트했다고 웹 페이지를 변경할 필요가 없다.
  • HTTP 명세가 변경되어도 웹은 잘 동작한다.
  • HTML 명세가 변경되어도 웹은 잘 동작한다.

 

REST가 웹의 독립적 진화에 도움을 준 것들은 다음과 같다.

  • HTTP에 지속적으로 영향을 줌
  • Host 헤더 추가
  • 길이 제한을 다루는 방법이 명시 (414 URI Too Long 등)
  • URI에서 리소스의 정의가 추상적으로 변경됨 ("식별하고자 하는 무언가")
  • 기타 HTTP와 URI에 많은 영향을 줌
  • HTTP / 1.1 명세 최신판에서 REST에 대한 언급이 들어감

4) 왜 REST가 잘 안되는가

일반적인 웹과 비교하면 다음과 같다.

  흔한 웹 페이지 HTTP API
Protocol HTTP HTTP
커뮤니케이션 사람-기계 기계-기계 (ex. 모바일)
Media Type HTML JSON
  HTML JSON
HyperLink (HATEOAS) 가능 (<a> 태그) 정의되어있지 않음
Self-descriptive 가능 (HTML 명세) 불완전 (문법은 정의되나 값들의 의미는 정의되지 않음)

즉, 문법 해석은 가능하지만, 의미를 해석하려면 별도의 문서가 필요하다. 이를 위해 API 명세서를 작성할 필요가 있다.

 

REST API를 적용하는 방법에는 두가지가 존재한다.


Media Type 방식

GET /todos HTTP/1.1
Host: test.com

HTTP/1.1 200 OK
Content-Type: application/vnd.todos+json

[
   {"id": 1, "title": "회사 가기"},
   {"id": 2, "title": "집에 가기"},
]
  • Media Type 정의
  • Media Type 문서 작성 ('id'가 뭔지, 'title'이 뭔지 의미 정의)
  • IANA에 Media Type을 등록 (단, 만든 문서를 Media Type의 명세로 등록)

 

번거로운 작업이지만 이 메시지를 보는 사람은 명세를 찾아갈 수 있으므로 메시지의 의미를 온전히 해석 가능해진다. 물론 Media Type 등록은 필수가 아니다.


Profile 방식

GET /todos HTTP/1.1
Host: test.com

HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://test.com/docs/todos>; rel="profile"

[
   {"id": 1, "title": "회사 가기"},
   {"id": 2, "title": "집에 가기"},
]
  • "id"가 뭐고 "title"이 뭔지 의미를 정의한 명세를 작성
  • Link 헤더에 profile relation으로 해당 명세를 링크

 

이 메시지를 보는 사람은 명세를 찾아갈 수 있으므로 이 문서의 의미를 온전히 해석할 수 있다. 단, 클라이언트가 Link 헤더(RFC 5988)와 profile(RFC 6906)을 이해해야하며 Content negotiation을 할 수 없다는 단점을 가지고 있다.


HATEOAS 방식

GET /todos HTTP/1.1
Host: test.com

HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://test.com/docs/todos>; rel="profile"

[
   {"link": "https://test.com/todos/1", "title": "회사 가기"},
   {"link": "https://test.com/todos/2", "title": "회사 가기"},
]
GET /todos HTTP/1.1
Host: test.com

HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://test.com/docs/todos>; rel="profile"

[
   "links": {
      "todo": "https://test.com/todos/{id}"
   },
   "data": [
      {"id": 1, "title": "회사 가기"},
      {"id": 2, "title": "회사 가기"}
   ]
]
GET /todos HTTP/1.1
Content-Type: application/json

{
   "title": "점심 약속"
}

HTTP/1.1 204 No Content
Location: /todos/1
Link: </todos/>; rel="collection"

데이터에 다양한 방법으로 하이퍼링크를 표현한다. 단, 링크를 표현하는 방법을 직접 정의해야한다.

5) JSON으로 하이퍼 링크를 표현하는 방법을 정의한 명세들을 활용

  •   JSON API
  •   HAL
  •   UBER
  •   Siren
  •   Collection+json

 

 

 

5. 정리

  • 오늘날 대부분의 API는 REST를 따르지 않고 있다. (그 중, Self-descriptive, HATEOAS)
  • REST API는 모든 제약조건을 지켜야한다. (Roy T.Fielding)
  • REST는 긴 시간에 걸쳐 진화하는 웹 애플리케이션을 위한 것이다.

[참고] www.youtube.com/watch?v=RP_f5dMoHFc

728x90