HTML REST API

불꽃남자·2020년 9월 21일
1

이번엔 REST API라는 단어의 의미에 대해 알아보겠다.

REST?

REST API란 것은 별 다른 것이 아니고 REST한 API를 의미한다.
그래서 나는 REST의 의미를 알아내는 것에 집중했다.

REST는 Representational Status Transfer의 약자이다. 무슨소리일까? 알아본 끝에 내린 결론은 Representation의 Transfer을 통한 Status에 대한 접근을 의미하는 것 같다.

도대체 무슨소리인가 싶은데, 아래의 내용을 읽으며 알아보자.

Representation이란

REST API에 대해 알아보며 수도 없이 등장한 용어가 Representation이다. 이 용어가 뜻하는 바가 REST API에 대한 핵심이라고 생각해서, 도대체 Representation이 무엇인가에 대해 알아보았다.

사전적 의미로 Representation은 '표현'이라는 의미이다. 요컨대 REST라는 문맥에서 Representation은 클라이언트와 서버가 요청과 응답을 주고 받을 때 사용하는 무언가 를 일컫는다.
클라이언트가 서버에 GET /programmers/Namespacinator 요청을 보냈다고 하자. 그 결과로 JSON 데이터를 반환받았다.

{
    "nickname": "Namespacinator",
    "powerLevel": 5
}

이것은 programmers의 리소스인가? 답은 "그렇지 않다" 이다.

REST에서 이것은 programmers 리소스의 '표현'이라고 한다. programmers 리소스는 비단 JSON 형식 뿐만 아니라 XML, HTML으로 리소스를 나타낼 수 있고 다른 format의 JSON로 나타낼 수도 있다.
즉, 클라이언트의 요청에 맞게 알맞은 '표현', 즉 Representation을 반환했다고 한다.

이것은 서버에서 클라이언트에게 응답할 때 뿐만 아니라 클라이언트가 서버에 요청할 때에도 마찬가지이다.
클라이언트가 서버에 programmers의 데이터를 포함한 POST 요청을 보냈다고 하자.

POST /api/programmers HTTP/1.1
Host: CodeBattles.io
Authorization: Bearer b2gG66D1Tx6d89f3bM6oacHLPSz55j19DEC83x3GkY
Content-Type: application/json

{
    "nickname": "Namespacinator"
}

클라이언트는 programmers의 자원을 보낸 것이 아니라, 단지 표현을 보낸 것이다. 서버는 이 표현을 해석하고 리소스를 업데이트할 뿐이다.

내 생각에, REST에서 HTTP 메시지는 표현의 일종이다. 서버가 클라이언트에게 어떤 리소스의 GET 요청에 대해 JSON 데이터가 포함된 응답 HTTP 메시지를 반환했다면, 이는 서버가 요청에 따른 해당 리소스에 대한 표현을 반환한 것이다.

Representation State

그리고 Representation State, 이것이 바로 웹 브라우저가 동작하는 방법이다.
위의 내용을 이해했다면, HTML코드는 리소스의 표현 방식중 하나라는 것에 대해 동의할 것이다.

symfonycasts.com의 Representation에 대한 설명에서는 Representation에 대해 이렇게 이야기 하고 있다.

A representation is a machine readable explanation of the current state of a resource.

Representation은 자원의 '현재 상태'에 대해 기계간에 주고 받는 설명이라고 이야기 하고 있다.
그리고 우리는 현재 상태라는 단어에 주목해야 한다.

클라이언트가 GET /programmers/Namespacinator 요청을 해서 얻은 JSON 데이터는 해당 리소스의 현재 상태에 대한 표현이다. 또한 클라이언트가 programmers collection을 업데이트 하게끔 요청을 보내는 것은 해당 리소스를 업데이트 하기 위한 표현을 전송한 것이다.

REST의 구성

REST는 다음과 같은 요소를 가지고 있다.

  1. 자원: URI를 뜻한다.
  2. 행위: HTTP Method를 뜻한다.
  3. 표현: 요청에 대한 적절한 응답을 뜻한다.

REST한 API를 만들기 위해

REST한 API를 만들고 싶다면 주목해야할 가장 중요한 2가지가 있다.

  1. URI를 자원을 표현해야한다. 행위에 대해 표현해서는 안 된다.
  2. 자원에 대한 행위는 HTTP Method가 표현해야한다.

아래의 HTTP 요청 메시지를 살펴보자.

GET /posts/delete?id=2

나는 서버에 있는 posts collection중에 id가 2인 리소스를 삭제하고자 이 HTTP 메시지를 서버에 요청했다.

이것은 전혀 REST하지 못한 api이다. 왜 그럴까?

첫 번째로, URI에 delete라고 하는 행위가 표현되어 있다. URI는 자원만을 표현해야한다.
두 번째로, 특정 자원을 삭제하려고 하면서 GET 요청을 보내고 있다. GET 요청은 특정 자원을 가져오기 위해 사용되어야 한다.

이것을 REST하게 바꾸려면

DELETE /posts/2

이렇게 되어야 할 것이다.
URI는 자원만을 표현하고 있고, 자윈에 대한 행위는 HTTP Method가 표현하고 있다. 알아보기도 더 쉽다.

REST의 6가지 특징

wikipedia에서는 이에 대해 'REST의 6가지 제약' 이라고 표현했다. 여러가지 제약을 걸어 서버와 클라이언트가 응답하고 요청하는 방법을 제한하면 시스템이 여러가지 장점을 얻을 수 있다고 설명하고 있다. 그리고 이러한 제약을 벗어난 API는 REST하지 못한 API라고 이야기 한다. 그런 관점에서 규약이라고 할 수도 있겠다.

1. Uniform Interface

직역하면 일관성 있는 인터페이스이다.
요컨대 리소스에 대한 요청은, 아래의 사항을 지킴으로써 표현되어야 한다는 의미이다.

Uniform Interface는 REST에서 가장 지켜지기 힘든 부분이나 이것이 지켜지지 않는 API는 REST API라고 할 수 없다.

이 일관성 있는 인터페이스를 만들기 위해서 4가지의 규칙을 지켜야 한다.

1.1. Identification of Resources

URI를 통하여 리소스가 식별되어야 한다.
요청을 보내려는 리소스가 무엇인지 URI로 알 수 있어야 한다는 의미이다.

1.2. Manipulation of resources through these representations

리소스에 대한 조작은 해당 리소스의 표현으로 이루어져야 한다.
이는 리소스에 대한 행위가 HTTP Method로 이루어져야 한다는 것과 일맥상통한다.

예를 들어 /programmers/Namespacinator 라는 리소스를 얻고자 하면 GET으로 이를 표현하고, 수정하려 한다면 POST로, 삭제하려 한다면 DELETE로 표현하라는 의미이다.
위에서 살폈던 예시처럼 GET /programmers/delete/Namespacinator 따위의 요청으로 리소스에 대한 행위를 하려 하지 말라는 의미이다.

1.3 Self-descriptive messages

메시지는 자기 자신에 대해 설명할 수 있어야 한다.

예를 들어 클라이언트가 서버에 요청을 보낼 때에, HTTP 요청 메시지는 Header, Startline 명세 등으로 해당 요청에 대한 충분한 정보를 가지고 있어야 한다는 의미이다.

이것은 응답 메시지의 경우에도 마찬가지로 클라이언트에서 해당 응답이 무엇을 의미하는지 이해할 수 있을 정도로 충분한 설명을 가지고 있어야 한다.

1.4 HATEOAS

HATEOAS는 Hypermedia as the engine of application state의 약자로, 어플리케이션의 상태는 항상 하이퍼링크에 의해 바뀌어야 한다는 것을 뜻한다.

무슨 말일까. 예를 들어 <link ref="글목록보기" href="http://myblog.com/post />"라는 코드가 글 목록을 보여주는 버튼으로써 웹에 존재할 때에, 이 버튼을 누르면 클라이언트는 GET http://myblog.com/post 요청을 보내게 된다.
그리고 돌아온 응답을 확인해보니

[
  {
    "_id": "1",
    "content": "어쩌고 저쩌고",
    "links": {
      "self": {
        "ref": "post1",
        "href": "http://myblog.com/post?id=1"
      }
    }
  },
  {
    ...
  }
]

이런 느낌의 JSON 데이터가 반환되었다. 그럼 나중에 글 목록을 보여주는 페이지에서, 첫 번째 포스트로 가는 버튼을 만들려고 할 때에 그냥 <link ref="response.links.self.ref" href="response.links.self.href" />이런 느낌으로 만들기만 하면 된다.
혹은 글쓰기 버튼이나 로그인 버튼 등에 대한 link같이 글목록 페이지에 연관된 link에 대한 정보도 반환하게끔 만들 수 있을 것이다.

이렇게 HATEOAS를 어플리케이션에 적용해놓으면 어떤 리소스에 대한 URI가 바뀌게 되더라도 HTML코드 상의 href URI들을 하나하나 바꾸지 않아도 된다는 등의 장점이 있다.

나는 REST API가 요청에 대해 해당 요청과 관련된 하이퍼링크를 반환하는 것이 HATEOAS라고 생각한다.

그리고 REST API가 반환하는 하이퍼링크가 포함된 JSON이나 XML이 일관된 형식을 갖게 하고자 제안된 것이 바로 HAL이다.

HAL은 다른 것이 아니고 REST API의 리소스 간에 하이퍼링크를 쉽고 일관되게 제공하게끔 만들어진 간단한 형식이다.

2. Client-Server Model

REST API는 Client-Server 구조를 띄고 있어야 한다.
Server는 API를 제공하고, 비즈니스 로직을 관리한다. Client는 API를 요청하고, 반환된 데이터로 UI/UX를 만들어 나간다.
Client와 Server를 분리하여 서로의 의존성을 없애는 것이다. 특정 Client와의 의존성이 없는 Server는 다른 어떤 Client에도 사용될 수 있을 것이다.

3. Stateless

HTTP 프로토콜 표준이 Stateless의 성질을 띄기 때문에, REST 역시 Stateless이다. 여기서 이야기하는 State는 HTTP Session, Cookie등을 이야기한다.
무슨 이야기냐면 REST API는 오직 요청 메시지만을 보고 적절한 응답을 반환한다. 다른 것에 대해 신경쓰지 않고 오직 요청 메시지에 의해 응답을 결정한다.
이는 Uniform Interface의 Self-descriptive message와 일맥상통한다.

4. Cacheable

HTTP 프로토콜 표준이 Caching 기능을 지원하기 때문에, REST API 역시 Caching이 가능하다.
이는 HTTP Header의 Last-Modified나 E-tag를 통해 구현가능하다.
Last-Modified나 E-tag에 대해선 MDN 명세를 찾아보자.

5. Layered System

REST API 서버는 계층형 구조를 가질 수 있다.
API는 실제로 리소스를 가지고 있는 DB로 부터 리소스를 받아 온다. 그리고 API 서버와 클라이언트 사이에는 프록시나 게이트웨이같은 중계자가 있을 수도 있고, API 서버 앞에 사용자 인증이나 보안 시스템, 암호화같은 기능을 수행하는 계층을 추가할 수도 있다.
또한 클라이언트는 자신이 REST API 서버와 통신하고 있는지, 아니면 중간 계층의 서버와 통신하고 있는지 알 수 없고, 사실 알 필요도 없다. 클라이언트는 요청에 대한 올바른 응답만 받으면 된다.

6. Code on Demand(optional)

이것은 충족 하면 좋은 것이고, 충족 하지 않아도 된다.
클라이언트는 요청한 리소스에 대한 표현을 반환받고, 이를 어떻게 사용할 것인지에 대한 스크립트를 작성해야한다. Code on Demand는 이 스크립트를 서버에서 제공하는 것을 이야기한다.

마치며

이 글을 쓰려고 알아보기 전까지 REST API는 그냥 HTTP Method를 이야기하는 줄 알았다. 하지만 REST API는 엄청 거대한 개념이었고... 이걸 면접장에서 어떻게 설명해야 할지에 대해서도 고민해봐야 겠다.

profile
프론트엔드 꿈나무, 탐구자.

0개의 댓글