그런 REST API로 괜찮은가 정리

김대은·2022년 10월 13일
0

REST API 란?

REST 는 REpresentational State Transfer의 약자이다.

a way of providing interoperability between computer systems on the Internet.

  • REST API 란 ? REST 아키텍처를 따르는 API 이다.
  • REST 란 ? 분산 하이퍼 미디어 시스템 (ex.웹) 을 위한 아키텍처 스타일
  • 아키텍처 스타일 ? 제약 조건의 집합

즉,REST에서 정의한 제약 조건을 모두 지켜야 REST를 따른다고 말할 수 있다는 것이다.

사실 REST는 아키텍처 스타일이면서 하이브리드 아키텍처 스타일 이라고 말한다.
왜냐하면 아키텍처 스타일이면서, 동시에 아키텍처 스타일의 집합이기 때문이다.

REST 아키텍처 스타일

전글을 참조하면 좋다.

  • Client-Server
  • Stateless
  • Cache
  • Uniform Interface
  • Layered System
  • code - on - demand(optioanl)

Code-on-Demand는 서버에서 코드를 클라이언트로 보내서 실행할 수 있어야 한다는 것을 의미하며, 즉 자바스크립ㅂ트를 의미한다.

대체로 REST라고 부르는 것들은 위의 조건을 대부분 지키고 있다.
HTTP만 잘 따라도 Uniform Interface를 제외하곤 다 지킬 수 있기 때문이다.
하지만 Uniform Interface는 잘 지켜지지 않는다고 한다.

Uniform Interface 제약조건

  • Identification or resource : 리소스가URL로 식별된다.
  • Manipulation or resources through representations : represenstation 전송을 통해 resources를 조작해야한다.
    • representation은 HTTP 메소드(PUT,DELETE,GET,POST등)을 의미한다.
  • self-descriptive messages : 메세지는 스스로를 설명할 수 있어야 한다.
  • hypermedia as the enginer of application state (HATEOAS) : 애플리케이션의 상태는 HyperLink를 이용해서 전이가 되어야한다.

위 2가지 조건은 대부분 잘 지켜지는 반면, 아래 2가지 조건이 잘 지켜지지 않는다고 한다.

Self-descriptive message

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

예를들어 아래와 같은 메세지가 있다고 해보자

GET / HTTP/1.1

단순히 루트를 얻어오는 GET 요청이다.
이 HTTP 요청 메세지는 뭔가 빠져 있어 self-descriptive하지 못하다.

우선 목적지가 빠져있다.

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

이 요청이 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" } ]

그렇다면 이제 Self-descriptive하다고 볼 수 있는가는 아니다.
해석했다고 하더라도, op 값은 무슨 뜻이고, path가 무엇을 의미하는지는 알 수 없다.

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

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

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

이처럼 Self-descriptive message라는 것은 메시지를 봤을 때 메시지의 내용으로 온전히 해석이 다 가능해야된다는 것이다.

HATEOAS

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

이렇게 상태를 전이하는 것을 애플리케이션 상태 전이라 하고, 이 상태 전이마다
항상 해당 페이지에 있던 링크를 따라가면서 전이 했기 때문에 HATEOAS라 할 수 있다.

그래서 html 같은 경우는 HATEOAS를 만족하게 되는데,

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

여기서 어떤 1개의 게시물을 표현 했는데, 이전의 게시물 URI가 /articles/1, 다음 게시물은 /articles/3에 있다는 정보를 표현해준 것이다.

또한, 이 정보는 Link 헤더가 이미 표준으로 문서가 나와 있기 때문에 이 메시지를 보낸 사람이 온전히 해석해서 어떻게 링크가 되어 있는가를 이해하고 하이퍼링크를 타고 다른 상태로 전이가 가능하다.

왜 Uniform Interface가 필요한가 ?

독립적 진화

  • 서버와 클라이언트가 각각 독립적으로 진화한다.
  • 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다.
  • REST를 만든 계기가 How do i improve HTTP without breaking the Web 어떻게 웹이 깨지지 않고 HTTP를 개선할 수 있는가 이다.

독립적으로 진화를 한다면 ??
서버의 기능이 바뀌고, API가 추가되고 변동이 생겨도, 클라이언트는 바뀌지 않아도 된다.

이를 위해서는 Unifrom Interface가 필수적이기기에, 이를 만족하지 못하면 REST라고 부를 수 없는 것이다.

실제로 잘 지켜지는 곳은 어디인가 ?

바로 웹이다.

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

하지만 모바일 앱은 많은 문제가 있다고 한다.

서버의 기능이 변경되었는데, 클라이언트가 그거을 지원해주는데 한계가 있을 때
업데이트를 진행하는 것이다.
이는 모바일 앱 클라이언트 와 서버가 REST아키텍처 스타일을 따르지 않고있다 라고 말할 수 있다.

웹은 어떻게 가능한 것인가?

  • 마법은 없다. 엄청난 노력의 결실이다.
  • W3C Working groupsHTML5 첫 초안에서 권고안 나오는데까지 6년
  • IETF Working groups HTTP/1.1 명세 개정판 작업하는데 7년
    기능의 추가 없이, 하위 호환성 문제를 위해 문서를 가다듬기만 했다고 한다.
  • 웹 브라우저 개발자들 의 노력
  • 웹 서버 개발자들 의 노력

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

  • Referer -> 오타가 났지만 고치지 않음
  • charset -> 잘못 지은 이름이지만 고치지 않음
  • HTTP 상태코드 416 포기
  • HTTP/0.9를 여전히 지원(크롬,파이어폭스)

25년전 Referer 오타를 냈다.
charset이라는 이름을 잘 못 지었다.
하지만 고치지 않는다 왜?
이름을 고치게 되면 상호운용성이 깨지기 때문이다.
HTTP 상태코드 416도 만우절때 만들었던 코드인데 http가 아니라 상관이 없었지만, 이미 몇몇 서버들이 이를 HTTP상태 코드로 구현해 버렸다.
그래서 416코드를 아예 영구 결번으로 만들어 버렸다.

이렇게 상호 운용성에 대한 많은 노력과 시행착오가 모여 덕분에 독립적인 진화를 할 수 있다.

원격 API가 꼭 REST API 여야 하는가?


시스템 전체를 통제할 수 있다 ?

  • 클라이언트 개발자를 통제 가능할때 또는, 클라이언트, 서버를다 만들 때

진화에 관심이 없다 ?

  • 매일 업데이트해서 유저의 불만이 나와도 상관이 없다.

하지만 우리는 둘다 어려우니... REST를 따라야 한다.

어떻게 해야 하는가?

  1. REST API를 구현하고 REST API라 부른다.
  2. REST API를 포기하고 HTTP API라 부른다.
  3. REST API가 아니지만, REST API라 부른다. (현재 대부분의 상태)

왜 API는 REST가 어려울까?


웹과 API를 비교해보면,
커뮤니케이션이 다르다.
HTTP API는 사람이 아닌 기계가 해석한다.
또한 미디어 타입이 다르다.

self-descriptive 측면에서 보면 JSON은 불완전하다.
완전 하다는 것은 적어도 문법은 정의되어 있다.
어떻게 파싱하고 array를 어떻게 해석해라 까지는 되어있다.
하지만 안에 들어갈 수 있는 key-value에 대한 의미는 아무도 정의되지 않는다.

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

해결 방법

  1. self-descriptive message

  2. HATEOAS

정리

  • 오늘날 대부분의 REST API는 사실 REST를 따르고 있지 않다.
  • REST의 제약 조건 중 특히 self-descriptive와 HATEOAS를 잘 만족하지 못한다.
  • REST 긴 시간에 걸쳐(수십년) 진화하는 웹 애플리케이션을 위한 것이다.
  • REST를 따를 것인지는 API를 설계하는 이들이 스스로 판단하여 결정해야 한다.
  • REST를 따르겠다면, self-descriptive와 HATEOAS를 만족시켜야 한다.
    • self-descriptive는 custom media type 이나 profile link relation 등으로 만족시킬 수 있다.
    • HATEOAS는 HTTP헤더나 본문에 링크를 담아 만족시킬 수 있다.
  • REST를 따르지 않겠다면, "REST를 만족하지 않는 REST API"를 뭐라고 부를지 결정해야할 것이다.
    • HTTP API라고 부를 수도 있고
    • 그냥 이대로 REST API라고 부를 수도 있다 (roy가 싫어합니다)
profile
매일 1% 이상 씩 성장하기

0개의 댓글