REST API (1)

sumi Yoo·2022년 10월 7일
0

WEB (1991)

Q: 어떻게 인터넷에서 정보를 공유할 것인가?
A: 정보들을 하이퍼텍스트로 연결한다.
표현 형식: HTML
식별자: URI
전송 방법: HTTP

HTTP/1.0 (1994-1996)

Roy T.Fielding: "How do I improve HTTP without breaking the Web?"
어떻게 하면 웹을 망가뜨리지 않고 HTTP를 진보시킬 수 있을까?

해결책: HTTP Object Model(=REST)

REST(1998)

Roy T.Fielding, Microsoft Research에서 발표

REST(2000)

Roy T.Fielding, 박사 논문으로 발표

REST AIP

REST 아키텍쳐 스타일을 따르는 API
REST: 분산 하이퍼 미디어 시스템(예: 웹)을 위한 아키텍쳐 스타일(제약조건의 집합)

REST를 구성하는 스타일

  • client-server
  • stateless
  • cache
  • uniform interface
  • layered interface
  • layered system
  • code-on-demand (optional): 서버에서 코드를 클라이언트로 보내서 실행할 수 있어야 한다.(자바스크립트)

Uniform Interface의 제약조건

  • indentification of resources
    리소스가 URI로 식별되면 된다.
  • manipulation of resources through representations
    representations 전송을 통해서 리소스를 조작해야한다. 리소스를 만들거나 업데이트 하거나 삭제할 때 HTTP 메시지에 그 표현을 담아서 전송해야한다.
  • self-descriptive messages
  • hypermedia as the engine of application state(HATEOAS: 헤이티오스)

아래 두 조건은 거의 모든 REST API들이 지키지 못하고 있다.

self-descriptive messages

메시지는 스스로를 설명해야한다.

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" } ]

Content-Type 헤더가 반드시 들어가야 한다. 아직은 self-descriptive 하지 못하다.

HTTP.1,1 200 OK
Content-Type: application/json patch+json

[ { "op": "remove", "path": "/a/b/c" } ]
json patch 라는 미디어 타입으로 정의되어 있는 명세를 찾아가서 이걸 이해한 다음에 이 메시지를 해석을 하면 그제서야 이 메시지를 올바르게 이해할 수 있다.

self-descriptive message -> 메시지 내용으로 온전히 해석이 가능해야 한다.

대부분 미디어 타입을 보면 그냥 json이라고 되어있지 이걸 어떻게 해석해야 되는가는 메시지를 보고서는 알 수 없다.

HATEOAS

애플리케이션의 상태는 Hyperlink를 이용해 전이되어야 한다.

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

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

a 태그를 통해서 하이퍼링크가 나와있고, 이 하이퍼링크를 통해서 그 다음 상태로 전이가 가능하기 때문에 HATEOAS를 만족한다.

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..."
}

Link라는 헤더가 이 리소스와 하이퍼링크로 연결되어 있는 다른 리소스를 가리킬 수 있는 헤더이다. HATEOAS를 만족한다.

왜 Uniform Interface가 필요한가?

독립적인 진화를 하기 위해서이다.

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

WEB

웹 페이지들은 REST를 매우 잘 만족하고 있다.

  • 웹 페이지를 변경했다고 웹 브라우저를 업데이트할 필요는 없다.
  • 웹 브라우저를 업데이트 했다고 웹 페이지를 변경할 필요도 없다.
  • HTTP 명세가 변경되어도 웹은 잘 동작한다.
  • HTML 명세가 변경되어도 웹은 잘 동작한다.
    (UI가 깨지는 경우가 있을 수 있지만 동작은 합니다.)

모바일 앱은 위를 만족하지 않는다. 업데이트가 필요하다.
모바일 앱 클라이언트와 서버와 REST 아키텍쳐 스타일을 따르고 있지 않다.

상호운용성(interoperability)에 대한 집착

  • Referer 오타지만 안 고침
  • charset 잘못 지은 이름이지만 안 고침
  • HTTP 상태 코드 416 포기함
  • HTTP/0.9 아직도 지원함 (크롬, 파이어폭스)

Roy T. Fielding
하이퍼텍스트를 포함한 self-descriptive한 메시지의 uniform interface를 통해 리소스에 접근하는 API

왜 API는 REST가 잘 안되나

비교

흔한 웹 페이지HTTP API
ProtocolHTTPHTTP
커뮤니케이션사람-기계기계-기계
Media TypeHTMLJSON

JSON 때문이구나.. 하는 추측을 할 수 있다.

HTMLJSON
Hyperlink됨(a 태그 등)정의되어 있지 않음
Self-descriptive됨(HTML 명세)불완전*

HTML 명세를 보면 HTML에 사용할 수 있는 모든 태그들이 정의되어 있다. 어떤 태그는 어떤 의미인가 하는 명세. JSON 오브젝트 안에 들어갈 수 있는 키 벨류가 어떤 의미를 가져야 하는가를 정의하지 않는다. 불완전 하다는건 적어도 문법은 정의되어 있다. 어떻게 파싱을 하라(중괄호, 대괄호), 어레이 어떻게 해석해라 하는 정의는 되어 있지만, 하지만 그 안에 들어가는 값들이 어떤 의미를 가져야 한다는 정의가 안되어 있다.

  • 문법 해석은 가능하지만, 의미를 해석하려면 별도의 문서가 (API 문서 등) 필요하다.

HTML

GET /tools HTTP/1.1
Host: example.org

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

<html>
<body>
<a href="https://tools/1">회사 가기</a>
<a href="https://tools/2">집에 가기</a>
</body>
</html>

Self-descriptive
1. 응답 메시지의 Content-Type을 보고 media type이 text/html 임을 확인한다.
2. HTTP 명세에 media type은 IANA에 등록되어 있다고 하므로, IANA에서 text/html의 설명을 찾는다.
3. IANA에 따르면 text/html의 명세는 http://www.w3.org.TR/html 이므로 링크를 찾아가 명세를 해석한다.
4. 명세에 모든 태그의 해석방법이 구체적으로 나와있으므로 이를 해석하여 문서 저자가 사용자에 알려 줄 수 있다.
HATEOAS

a 태그를 이용해 표현된 링크를 통해 다음 상태로 전이될 수 있으므로 HATEOAS를 만족한다.

JSON

GET /tools HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Content-Type: application/json

[
	{"id": 1, "title": "회사 가기"},
    {"id": 2, "title": "집에 가기"}
]

Self-descriptive 하지 못하다.
1. 응답 메시지의 Content-Type을 보고 media type이 application/json임을 확인한다.
2. HTTP 명세에 media type은 IANA에 등록되어 있다고 하므로, IANA에서 application/json의 설명을 찾는다.
3. IANA에 따르면 application/json 명세는 draft-ieft-jsonbis-rfc7159bis-04 이므로 링크를 찾아가 명세를 해석한다.
4. 명세에 json 문서를 파싱하는 방법이 명시되어 있으므로 성공적으로 파싱한다. 그러나 id 가 무엇을 의미하고 title이 무엇을 의미하는지 알 방법이 없다.

HATEOAS 하지 못하다
다음 상태로 전이할 링크가 없다.

그런데 Self-descriptive와 HATEOAS가 독립적으로 진화에 어떻게 도움이 될까?

Self-descriptive

확장 가능한 커뮤니케이션
서버나 클라이언트가 변경되더라도 오고가는 메시지는 언제나 self-descriptive 하므로 언제나 해석이 가능하다.

HATEOAS

애플리케이션 상태 전이의 late binding
어디서 어디로 전이가 가능한지 미리 결정되지 않는다. 어떤 상태로 전이가 완료되고 나서야 그 다음 전이될 수 있는 상태가 결정된다. 쉽게 말해서 링크는 동적으로 변경될 수 있다. 서버가 링크를 바꾼다 해도 클라이언트는 영향이 없다. 이동을 해야지만 그 하이퍼링크들을 읽고 다음으로 이동할 수 있는 경로를 알 수 있다.

Self-descriptive 해결

방법1: Media type
1. 미디어 타입을 하나 정의한다.
2. 미디어 타입 문서를 작성한다. 이 문서에 id가 뭐고 title이 뭔지 의미를 정의한다.
3. IANA에 미디어 타입을 등록한다. 이때 만든 문서를 미디어 타입의 명세로 등록한다.
4. 이제 이 메시지를 보는 사람은 명세를 찾아갈 수 있으므로 이 메시지의 의미를 온전히 해석할 수 있다.

Content-Type: application/json patch+json

단점: 매번 media type을 정의해야한다.

방법2: Profile

Link <https://example.org/docs/todos>; rel="profile"
  1. id가 뭐고 title이 뭔지 의미를 정의한 명세를 작성한다.
  2. Link 헤더에 profile relation으로 해당 명세를 링크한다.
  3. 이제 메시지를 보는 사람은 명세를 찾아갈 수 있으므로 이 문서의 의미를 온전히 해석할 수 있다.

단점:
1. 클라이언트가 Link 헤더와 profile을 이해해야한다.
2. Content negotiation을 할 수 없다.

0개의 댓글

관련 채용 정보