진정한 REST API

REST API가 무엇이고 진정한 Restful은 무엇인지 공부해보자.

REST API란?

  • REST는 Representational State Transfer의 약자로 로이필딩에 의해 만들어졌다.
  • REST 아키텍처의 제약 조건을 준수하는 애플리케이션 프로그래밍 인터페이스이다.
  • REST 아키텍처는 애플리케이션 사이의 결합도를 낮춰 설계되어있는 아키텍처이다. 결합도를 낮춰 애플리케이션끼리 별도로 구축되고 결합할 수 있도록한다.

REST API 제약조건

API가 아래의 6가지 제약조건을 만족하면 Restful하다고 한다.

  • Client-Server
  • Stateless
  • Cacheable
  • Uniform Interface
  • Layered System
  • Code-On-Demand

Client-Sever

  • 이 조건의 기본 원칙은 관심사의 명확한 분리이다.
  • 사용자들에게 제공하는 Interface인 User Interface와 데이터 스토리지(DB와 같은), 비즈니스 로직 등 서버 내부의 작업을 분리함으로써 User Interface는 여러 플랫폼에서의 이식성을 향상 시킬 수 있으며 서버는 그 구성요소를 단순화하여 확장성을 단순화 할 수 있다.
  • 이 조건을 따르면 클라이언트와 서버는 서로 의존하지 않고 진화할 수 있다. 클라이언트는 서버의 URI만 알고 있으면 되기 때문이다.

Stateless

  • 무상태성
  • 서버에서 클라이언트의 상태 정보를 저장하지 않고 들어오는 요청에 대해서만 처리하여 구현을 단순화 하는 것이다.
  • 따라서 클라이언트의 모든 요청은 서버가 요청을 알아드는데 필요한 모든 정보를 담고있어야 한다.
  • 세션 상태도 클라이언트 쪽에 보관되어 클라이언트 요청 시 세션을 같이 보내 서버의 데이터베이스와 같은 서비스로 전송되어 일정 기간동안 지속 상태를 유지하여 인증하는데 사용하게 된다.

Cacheable

  • 요청에 대한 응답 내의 데이터에 해당 요청은 캐시가 가능한지 불가능한지 명세를 해줘야한다.
  • 캐시 사용이 가능하면 클라이언트는 응답 데이터를 재사용 할 수 있다.
  • cache-control 헤더를 통해 캐시 여부를 명시해줘야 한다.
  • Last-Modified, E-Tag를 이용해서 캐시 구현이 가능하다.

Uniform Interface

  • URI로 지정된 리소스에 균일하고 통일된 인터페이스를 제공해야한다.

  • 아키텍처를 단순하게 분리하여 독립적으로 만들 수 있다는 장점이 있다. 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다. (배포가 어려운 모바일 앱에서 특히나 큰 장점 중 하나이기도 하다.)

  • Resource Identification in request

    • 요청의 자원식별이라는 의미로 요청 부분에서 개별 자원에 대한 식별을 할 수 있어야 한다.
    • 예를들어 URI가 있다. http://localhost:8080/resource/1
    • 서버에서는 요청된 리소스 보고 HTML, XML, JSON 등 형태로 보낼 수 있게 된다.
  • Manipulation of resources through Representations

    • 메시지를 통해 자원을 조작한다는 의미이다.
    • 클라이언트의 요청에 어떤 자원에 대한 적절한 표현과 작업을 수행하기 위한 메타데이터를 충분히 갖추고 있다면, 서버 상에서 해당 자원을 변경, 삭제할 수 있는 충분한 정보를 가지고 있다는 의미이다.
    • HTTP의 GET, POST, PUT, DELETE등을 말한다.
  • Self-Descriptive Message

    • 자기 서술적 메시지라는 의미이다.

    • 각 메시지는 자신을 어떻게 처리해야 하는지 충분한 정보를 포함해야 한다는 의미이다.

    • 예를 들어 요청의 경우를 살펴보자.

      GET / HTTP/1.1
      

      위에 요청 메시지는 전혀 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하지 않다.

      이유는 클라이언트가 해석하려고 하면 어떤 문법으로 작성된 것인지 모르기 때문에 해석에 실패한다. 그렇기 때문에 Content-Type 헤더가 반드시 들어가야한다.

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

      이제 Content-Type 헤더에서 대괄호, 중괄호, 큰 따옴표의 의미가 무엇인지 알 수 있게 되어 파싱이 가능하고 문법을 해석할 수 있게된다.

      하지만 아직 Self-Descriptive하지 않다. op 값이 무슨 뜻이며 paht가 무엇을 의미하는지 알 수가 없다.

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

      이렇게 명시를 하면 완전해진다. 이 응답은 json-patch+json 이라는 미디어 타입으로 정의된 메시지이기 때문에 json-patch라는 명세를 찾아가서 이해한 다음, 이 메시지를 해석을 하면 그때 올바르게 메시지의 의미를 이해할 수 있게된다.

      이렇게 메시지를 봤을 때 메시지의 내용으로 온전히 해석이 다 가능해야 한다는 것이 Self-Descriptive이다.

    • 오늘날의 REST API는 상당히 이를 만족하지 못하고 있으며 대부분 미디어 타입에는 json이라고만 되어있고 이를 어떻게 해석해야되는지는 명시가 되어있지 않다.

  • Hypermedia As the Engine Of Application State(HATEOAS)

    • 단순히 결과 데이터만이 아닌 결과 리소스 정보를 포함해야 한다는 의미이다.

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

    • HTML 같은 경우 HATEOAS를 만족한다.

      예를 들어 HTML을 보자

      HTTP/1.1 200 OK
      Content-Type: text/html
          
      <html>
      <head> </head>
      <body> <a href="/test"> test </a> </body>
      </html>
      

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

      json으로도 표현해보자.

      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라는 헤더를 이용하여 리소스와 하이퍼링크로 연결되어 있는 다른 리소스를 가르킬 수 있는 기능을 제공한다.

      클라이언트 입장에서 Link 헤더(RFC 5988)를 이해해야하는 어려움과 Content negotiation이 불가하다는 단점이 있다.

      아래와 같이 json에 포함 시켜도 된다.

      HTTP/1.1 200 OK
      Content-Type: application/json
      {
          “title” : “The second article”,
          “contents” : “blah blah..."
          "links" :
          {
              previous : /article/1
              next : /article/3
          }
      }
      

      이 방법도 링크를 표현하는 방법을 직접 정의해야 된다는 단점이 있긴하다.

      (헤더의 방법과 데이터로 넘기는 방법을 적절히 사용하는 것이 좋다.)

Layered System

  • 계층화된 시스템 아키텍처를 사용하여 각 구성들 간의 계층을 마음대로 상호작용 할 수 없도록 제한 하는 제약조건으로 Interface를 일원화한다.

Code-On-Demand

  • 선택이 가능한 제약 조건이다.
  • 클라이언트는 서버에서 자바 애플릿, 자바스크립트 실행 코드를 전송받아 기능을 일시적으로 확장할 수 있다는 의미이다.
  • Code on demand 는 서버가 클라이언트에 보내는 데이터로 바로 실행 가능한 코드를 보내서 클라이언트에서 실행하는 것을 의미한다.

실제로 지켜지지 않는 Restful

  • 대부분 Rest API라고 불리는 API들은 Rest 제약 조건 중에 Uniform Interface 규칙에서 Self-Descriptive MessageHATEOAS 규칙이 지켜지고 있지 않다.

  • 아무래도 이 이유는 json의 사용에서 오게됐는데 태그에 맞도록 사용하면 알아서 위에 규칙이 지켜지는 HTML과 달리 json은 불완전하다.

  • 따라서 별도로 문서(API 문서 등)이 필요하게 되어 규칙을 지키기가 쉽지않다.

  • 원격 API가 꼭 REST API여야 할 필요는 없다.

    따라서 API를 개발하는 본인의 상황이 클라이언트 개발자를 통제할 수 있거나 클라이언트, 서버를 다 만들 수 있는 경우에는 단순 HTTP API로 개발을 해서 시간을 아낄수 도 있다.

    또는 Uniform Interface를 통한 상호운용성과 진화에 관심이 없다면 계속 업데이트를 하면서 개발을 해도 좋다.

    하지만 사실상 우리는 시스템 전체를 통제할 수 없기도 하고 개발자로서 진화에 관심을 두어야 하기에 REST를 따르자.

Reference

https://www.redhat.com/ko/topics/api/what-is-a-rest-api

https://youtu.be/RP_f5dMoHFc

https://meetup.toast.com/posts/92

https://wallees.wordpress.com/2018/04/19/rest-api-hateoas/

https://sabarada.tistory.com/26

⤧  Next post Spring Security - 1 ⤧  Previous post HTTP 버전과 SPEC