본문 바로가기
네트워크

REST란? REST API란? 일반적인 HTTP API가 REST API가 아닌 이유

by 책 읽는 개발자_테드 2021. 11. 10.
반응형

DEVIEW2015 이응준님의 강연을 정리한 글입니다. (https://tv.naver.com/v/2292653#comment_focus)

학습 목표

· REST란?

· REST의 탄생

· API의 역사 - SOAP과 REST

· REST API

   - REST를 구성하는 제약조건

   - 문제의 발단 - uniform interface

· Uniform Interface의 제약조건

   - self-descriptive messages 

   - HATEOAS

   - Uniform Interface 제약조건이 필요한 이유

· 웹 페이지는 REST 설계가 잘되지만, API는 잘 안되는 이유

   - JSON을 Self-descriptive하게 만드는 방법

   - JSON을 HATEOAS하게 만드는 방법

 


REST란?

· REpresentational State Transfer(대표적인 상태 전달)의 약자

· 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 아키텍처 스타일

- 아키텍처 스타일: 제약조건의 집합이다. 자원을 정의하고 자원에 대한 주소를 지정하는 방법 전반을 일컫는다. 

· 컴퓨터 시스템간의 상호 운용성을 제공하는 방식 중 하나

 

REST의 탄생

·  1991년 WWW(World Wide Web)이 팀버너스리에 의해 탄생하였다.

   이때 인터넷에서 효과적으로 정보를 공유하기 위해 모든 정보를 하이퍼텍스트로 연결했고,

   표현형식을 HTML, 식별자를 URI, 전송 방법을 HTTP로 정했다.

 

· HTTP 설계 작업에 참여자였던 로이필딩은 웹과 HTTP가 이미 사용되고 있는 시점에

  어떻게 웹 호환성을 지키면서 HTTP 기능을 개선(HTTP 명세에 기능 더하고, 기존의 기능 수정 등) 할지에 대한 고민으로 했다.

 

· 이에 대한 해결책으로 HTTP Object Model을 만들었고, 몇 년 후 REST라는 이름의 논문으로 발표하였다.

 

API의 역사 - SOAP과 REST

·  1998년 마이크로소프트는 XML-RPC라는 원격으로 다른 시스템의 메서드를 호출할 수 있는 시스템을 만든다.

   이후 이것은 SOAP이라는 이름으로 바뀐다. 

 

·  2000년 Salesforce는 SOAP 기반의 최초의 API를 만든다.

   하지만 이것은 너무 복잡했다.

 

·  2004년 flickr는 SOAP과 REST 기반의 API를 각각 만들었다.  

   아래 각 API의 이미지를 보면, REST API가 SOAP API에 비하여 단순하고, 규칙이 적음을 알 수 있다.

 

SOAP

 

REST

 

·  이러한 이유로  REST는 SOAP 보다 월등한 인기를 얻게 되었다.

 

REST API

·  REST 아키텍쳐 스타일을 따르는 API

·  REST API의 인기가 뜨거워지면서 많은 사람들이 REST API라는 용어를 사용하게 되었지만,

   사람들이 생각하는 REST API와 창시자 로이필딩이 생각하는 REST API는 달랐다.

 

REST를 구성하는 제약조건

1. client-server

- 클라이언트-서버의 각 파트가 독립적으로 개선될 수 있도록 하자.

2. statelessness

- 각 요청 간 클라이언트의 콘텍스트가 서버에 저장되어서는 안 된다.

3. cache

- 클라이언트는 응답을 캐싱할 수 있어야 한다.

4. uniform interface, 인터페이스 일관성

- 일관적인 인터페이스로 분리되어야 한다.

5. layered system

- 요청된 정보를 검색하는 데 관련된 서버(보안, 로드 밸런싱 등을 담당)의 각 유형을 클라이언트가 볼 수 없는 계층 구조로 체계화하는 계층화된 시스템.

6. Code on demand (선택사항)

- 서버에서 클라이언트로 코드를 보내서 수행시킬 수 있어야 한다. ex) 자바스크립트, 자바 애플릿

 

문제의 발단 - uniform interface

·  6가지 조건 중 uniform interface를 제외한 것들은 HTTP만 잘 따라도 자연스럽게 다 지켜지는 사항이었다.

 

Uniform Interface의 제약조건

1. identification of resources, 자원의 식별

- 리소스는 uri로 식별된다. 요청 내에 기술된 개별 자원을 식별할 수 있어야 한다.

ex) 웹 기반의 REST 시스템에서의 URI의 사용을 예로 들 수 있다. htts://{serviceRoot}/{collection}/{id} 

2. manipulation of resources through representations, 메시지를 통한 리소스의 조작

- HTTP에 표현을 담아 전송하여 리소스를 조작해야한다.

3. self-descriptive messages, 자기서술적 메시지

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

4. hypermedia as the engine of application state (HATEOAS), 애플리케이션의 상태에 대한 엔진으로서 하이퍼미디어

- REST API라고 알려진 거의 모든 API들이 3, 4번 제약 조건은 거의 지키지 못하고 있다.

 

self-descriptive messages 

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

 

▶ 예시: self-descriptive하지 못한 메시지 

 

GET / HTTP/1.1

 

· 위 메시지는 목적지가 빠져있다. 이것을 다음과 같이 수정할 수 있다.

 

GET / HTTP/1.1
Host: www.example.org

 

▶  예시: self-descriptive하지 못한 메시지 

 

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

 

 

· 위 메시지는 어떤 문법으로 쓰였는지 알 수 없기 때문에 클라이언트가 파싱할 수 없기 때문에 self-desciptive하지 못하다.

  이것을 다음과 같이 수정하면, 클라이언트는 대괄호, 중괄호, 큰따움표가 무엇을 의미하는 지 이해할 수 있다.

 

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

[{ "op": "remove", "path": "/a/b/c" } ]

 

· 하지만 이렇게 개선된 위 메시지 또한 self-desciptive하지 못하다. "op", "path"가 무엇을 의미하는 지 알 수 없기 때문이다.

  이 메시지는 JSON Patch(https://en.wikipedia.org/wiki/JSON_Patch) 미디어 타입의 메시지다.

  이것을 명시해야 클라이언트는 JSON Patch 명세를 찾고, 이것을 토대로 메시지를 온전히 이해할 수 있게 된다.

 

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

[{ "op": "remove", "path": "/a/b/c" } ]

 

 

hypermedia as the engine of application state (HATEOAS)

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

 

https://velog.io/@jung2one/RESTful%ED%95%9C-API%EB%8A%94-1

 

▶  예시: HTML을 통한 HATEOAS를 만족하는 메시지

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

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

· <a> 태그의 하이퍼링크를 통하여 다음 상태로 전이할 수 있다.

 

▶  예시: JSON을 통한 HATEOAS를 만족하는 메시지

 

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 헤더는 해당 리소스와 관련된 다른 리소스를 가리킬 수 있는 기능을 제공한다. 

 

Uniform Interface 제약조건이 필요한 이유

· 독립적 진화를 하기 위해서

   - 로이 필딩이 REST를 만들게 된 계기: "How do I improve HTTP without breaking the Web"

   - 서버와 클라이언트가 각각 독립적으로 진화한다.

   - 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다. 

 

· 실제로 REST를 만족하는 사례: 웹

   - 웹 페이지를 변경했다고 웹 브라우저를 업데이트할 필요 없다.

   - 웹 브라우저를 업데이트했다고 웹 페이지를 변경할 필요 없다.

   - HTTP 명세가 변경되어도 웹은 잘 동작한다.

   - HTML 명세가 변경되어도 웹은 잘 동작한다.

 

웹 페이지는 REST 설계가 잘되지만, API는 잘 안되는 이유

· 웹 페이지 vs HTTP API

  웹 페이지 HTTP API
Protocol HTTP HTTP
커뮤니케이션 사람-기계 기계-기계
Media Type HTML JSON

 

· HTML vs JSON

  웹 페이지 HTTP API
Hyperlink a 태그 등을 이용 정의 x
self-descriptive HTML 명세가 존재 불완전

· HTML이 명세가 존재하는 것에 비하여 JSON의 키, 값에 무엇이 있어야하는가는 정의되어 있지 않다.

  때문에 JSON의 의미를 해석하려면 별도로 API 문서가 필요하다.

 

JSON을 Self-descriptive하게 만드는 방법

· 방법 1

   1. 미디어 타입을 정의한다.

   2. 미디어 타입 문서를 작성하고, "title"의 의미와 "contents"의 의미를 정의한다.

   3. IANA에 미디어 타입을 등록하고, 이때 만든 문서를 미디어 타입 명세로 등록한다.

HTTP/1.1 200 OK
Content-Type: application/vnd.todos+json
Link: </articles/1>; rel="previous",
      </articles/3>; rel="next";

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

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

 

· 방법 2

   1.  "title"의 의미와 "contents"의 의미를 정의 명세를 작성한다.

   2. Link 헤더에 profile relation으로 해당 명세를 링크한다.

   3. 메시지를 보는 사람은 명세를 찾아갈 수 있으므로 이 문서의 의미를 온전히 해석할 수 있다.

HTTP/1.1 200 OK
Content-Type: application/vnd.todos+json
Link: </articles/1>; rel="previous",
      </articles/3>; rel="next";
      <http://example.org/docs/todos>; rel="profile"

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

   - 단점:  1. 클라이언트가 Link 헤더(RFC 5988)와 profile(RFC 6906)을 이해해야한다.

               2. Content negotiation을 할 수 없다. 

 

JSON을 HATEOAS하게 만드는 방법

· 방법 1: data에 다양한 방법으로 하이퍼링크를 표현한다.

 

HTTP/1.1 200 OK
Content-Type: application/vnd.todos+json
Link: </articles/1>; rel="previous",
      </articles/3>; rel="next";
      <http://example.org/docs/todos>; rel="profile"

{
    "account": {
        "account_number": 12345,
        "balance": {
            "currency": "usd",
            "value": 100.00
        },
        "links": {
            "deposits": "/accounts/12345/deposits",
            "withdrawals": "/accounts/12345/withdrawals",
            "transfers": "/accounts/12345/transfers",
            "close-requests": "/accounts/12345/close-requests"
        }
    }
}

 

 - 단점: 링크를 표현하는 방법을 직접 정의해야한다.

 

· 방법 1-2: JSON으로 하이퍼링크를 표현하는 방법을 정의한 명세들을 활용한다. ex) JSON API, HAL, Collection+json

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

{
  "links": {
    "self": "http://example.com/articles"
  },
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON:API paints my bikeshed!"
    }
  }, {
    "type": "articles",
    "id": "2",
    "attributes": {
      "title": "Rails is Omakase"
    }
  }]
}

 

   - 단점: 기존 API를 많이 고쳐야한다. (침투적)

 

· 방법 2: Link, Location(Hyperlink를 표현할 수 있는 헤더) 등의 HTTP 헤더로 링크를 표현한다. 

HTTP/1.1 200 OK
Content-Type: application/vnd.todos+json
Location: /todos/1
Link: </todos/>; rel="collection"

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

 

   - 단점: 정의된 relation만 활용한다면 표현에 한계가 있다.

 

출처

https://ko.wikipedia.org/wiki/REST

https://tv.naver.com/v/2292653#comment_focus

반응형

댓글