ha-ah

로그, 게으른 로그

bsidesoft 블로그에 Jobs에 대한 내용이 올라왔다.

여튼, Jobs에 대한 글 중간에

<!--소용없음. 그냥 async 선언과 동일-->
<script async="false" src="1.js"></script>

“안타깝게도 이 옵션은 직접 태그에 쓰면 소용없고 동적으로 스크립트를 만든 경우에만 적용할 수 있습니다.”라고 나와있어서,

스펙에서는 어디에 나와있는가 질문을 드렸더니 다음과 같은 답변이 달렸다.

www.w3.org/TR/html51/semantics-scripting.html

여기에 가시면 script엘리먼트객체의 속성인 async IDL attribute에 대한 작동에 대해 자세히 기술하고 있습니다.

The async IDL attribute controls whether the element will execute in parallel or not. If the element’s “non-blocking” flag is set, then, on getting, the async IDL attribute must return true, and on setting, the “non-blocking” flag must first be unset, and then the content attribute must be removed if the IDL attribute’s new value is false, and must be set to the empty string if the IDL attribute’s new value is true. If the element’s “non-blocking” flag is not set, the IDL attribute must reflect the async content attribute.

분명 훑어보았던 부분인데 IDL attribute를 이해 못해서 그냥 넘어갔던 부분이다.

반성의 의미로 스펙을 좀 더 읽고 내 질문에 대한 답을 정리해본다.

async attribute

우선 <script async="false" src="1.js"></script>에서 async가 false로 설정 안 되는 이유.

script element 스펙에는 “The async and defer attributes are boolean attributes“라는 말이 나오는데,

boolean-attribute란 attribute로 명시하는 것 만으로 true가 된다.

즉, async=”true”든 async=”false”든 모두 async가 설정된 것으로 본다.

checked, disabled 같은 애들이 다 boolean-attribute다.

따라서 스크립트를 추가된 순서대로 실행하려면 attribute에서 async를 생략해야 하지만,

그렇게 하면 또 병렬(parallel) 로 로드가 안 되는 문제가 생기게 된다.

그럼 동적으로 삽입된 스크립트에서 가능한 이유는 뭘까?

//js에서 동적로딩하는 경우
const s = document.createElement('script');
s.src = '1.js';
s.async = false; //효과 있음
document.body.appendChild(s);

script element 스펙에 나오는 문장을 이해하기 위해 IDL attribute부터 알아보자.

content attribute와 IDL attribute

스택오버플로우에서 답을 찾아봤다.


IDL이란 말은 Web IDL spec에서 왔습니다.

이 문서는 Web IDL이라는 인터페이스 정의 언어를 정의하고, 이는 웹 브라우저 내에서 구현하려는 인터페이스를 설명하는데 사용될 수 있습니다.
Web IDL은 IDL의 일종으로 웹 플랫폼에서의 공통적인 스크립트 객체의 동작을 보다 쉽게 정의될 수 있게 해주는 많은 기능을 갖추고 있습니다.
Web IDL로 설명된 인터페이스가 ECMAScript 실행 환경 내의 구조에 어떻게 대응되는지 또한 이 문서에서 자세히 설명합니다.

Contents attibute란 마크업에서 나타나는 attribute입니다.

<div id="mydiv" class="example"></div>

여기서 id와 class는 attribute죠. 일반적으로 content attribute는 이에 상응하는 IDL attribute를 갖고 있습니다.

…생략

JavaScript 안에서 IDL attribute는 종종 property라고 불리는데, JavaScript에게는 DOM 객체의 property로써 노출되기 때문입니다.

보통 content attribute와 IDL attribute는 짝을 이루지만, 반드시 둘이 호환되는 건 아닙니다.

…생략 (<option> element의 초기값을 예로 든다)


결국 우리가 JavaScript 개발자로서 인식하는 property가 바로 IDL attribute란 개념을 가리키고 있다.
contents attribute라 함은 일반적으로 attribute라고 생각하는 그 마크업 상에 노출되는 바로 그것.

script element

이제 async에 관한 HTML5.1의 script쪽 명세를 다시 보자.

The async IDL attribute controls whether the element will execute in parallel or not.
Async IDL attribute는 element가 병렬로 실행될 지를 결정한다.


If the element’s “non-blocking” flag is set, then, on getting, the async IDL attribute must return true, and on setting, the “non-blocking” flag must first be unset, and then the content attribute must be removed if the IDL attribute’s new value is false, and must be set to the empty string if the IDL attribute’s new value is true.
Element의 “non-blocking” 플래그가 설정되면, 값을 가져올 때는 async IDL attribute가 true를 반환해야만 하고, 설정할 때는 “non-blocking” 플래그는 일단 해제한다. 이후 IDL attribute의 새로운 값이 false이면 content attribute는 제거, true이면 빈 값을 설정한다.


If the element’s “non-blocking” flag is not set, the IDL attribute must reflect the async content attribute.
Element의 “non-blocking” 플래그가 설정되지 않았다면, IDL attribute는 반드시 async content attribute를 반영(연동)해야 한다.

참고로 값을 가져올 때 현재 content attribute 값을 리턴해야 하는 경우 reflect란 용어를 쓰고 있는데 나는 이를 ‘반영(연동)’이라고 번역했다.

간단히 console 창에서 테스트해보자.

var s = document.createElement('script');를 통해 생성한 후 바로 s.async를 보면 true가 나온다.

처음 스크립트가 생성될 때는 “non-blocking” 플래그가 설정되기 때문이다.

Element의 “non-blocking” 플래그가 설정되면, 값을 가져올 때는 async IDL attribute가 true를 반환해야만…

이제 한 문장은 이해한 것 같은데, 어려운 용어가 더 생겼다.

“non-blocking” 플래그라는 게 뭘까?

parsing script tag

“non-blocking” 상태를 이해하기 위해 우선 HTML 도큐먼트의 파싱과정을 보자.

parsing-model-overview

“non-blocking”에서 block하는 대상은 HTML의 파싱이다.

보통 네트웍 계층에서 input stream으로 데이터를 넘겨주지만, 실행중인 스크립트로부터 받아들이기도 한다.

document.write가 대표적인데, 여기서 쓰는 데이터는 input stream에 섞여 들어간다.

<ul>
<script>document.write('<li>hello</li>');</script>
</ul>

ul 사이에 li가 제대로 자릴 잡기 위해선 input stream으로부터 가 나타나기 전까지만 데이터를 읽어들이고,

document.write가 포함된 스크립트 구문 실행하면 <li>hello</li> 부분이 input stream에 추가되고,

파서가 non-block 상태가 되면 다시 input stream으로부터 문자를 받아오는데,

<li>hello</li>\n</ul> 순으로 가져오게 된다.

이런 이유로 script 태그는 기본적으로 HTML 파싱을 막는다.

외부 스크립트도 마찬가지인데,

<script src="external.js"></script>

파서는 를 만나면 src에 명시된 파일을 다운로드하고, 실행하기를 기다린 후에야 파싱을 재개할 수 있다.

“non-blocking” flag

그럼 “non-blocking” 상태는 스펙에서 어떻게 설명하는지 보자.

Initially, script elements must have this flag set.
Script element는 초기 플래그 값으로 non-block으로 설정된다.


It is unset by the HTML parser and the XML parser on script elements they insert.
HTML/XML 파서가 script element를 삽입하면서 이 플래그가 해제된다.

그러니까 HTML 파서에 의해 생성된 스크립트 태그는 기본적으로 fetch/실행하는 동안 HTML 파서를 block 블럭하게 된다.

In addition, whenever a script element whose “non-blocking” flag is set has an async content attribute added, the element’s “non-blocking” flag must be unset.
추가적으로, “non-blocking” 플래그가 설정된 script element에 async content attribute가 추가되면, element의 “non-blocking” 플래그는 해제된다.

이 부분은 script element 쪽에서도 언급이 되니 같이 살펴보자.

다시 script element

If the element’s “non-blocking” flag is set, then, on getting, the async IDL attribute must return true, and on setting, the “non-blocking” flag must first be unset, and then the content attribute must be removed if the IDL attribute’s new value is false, and must be set to the empty string if the IDL attribute’s new value is true.
Element의 “non-blocking” 플래그가 설정되면, 값을 가져올 때는 async IDL attribute가 true를 반환해야만 하고, 설정할 때는 “non-blocking” 플래그는 일단 해제한다. 이후 IDL attribute의 새로운 값이 false이면 content attribute는 제거, true이면 빈 값을 설정한다.

최초 script element를 생성할 때는 “non-blocking” 플래그가 설정되기 때문에 async가 true를 반환하는 것은 이미 확인했다.

…설정할 때는 “non-blocking” 플래그는 일단 해제한다.

이 글을 한참이나 쓰고 있는 이유가 이 때문인데, async를 추가하는데 왜 “non-blocking”을 해제하는 걸까? 이런 의문으로 너무 혼란스러웠다.

그러니까, async가 “non-blocking” 상태 아님?하는 의문이다.

나만 빼고 다들 잘 이해를 하고 있는지, “non-blocking” 플래그에 대한 질문도 찾기 쉽지 않다.

여러가지 가정을 하고 스펙을 여러번 읽어본 결과,

브라우저는 “non-blocking” 플래그가 없어도 async로 병렬처리 여부를 확인할 수 있다라고 나름의 결론을 냈다.

왜 병렬로 다운로드 받는가에 대해선 스펙상에서 찾을 수는 없었는데, 어쩌면 다운로드를 병렬로 받는 건 중요하지 않겠다는 생각도 든다.

다운로드는 왜 병렬로 처리되는 걸까?

스펙에서 찾은 건 아니지만, 원래 다운로드는 그냥 알아서 병렬로 다운받는 거 아닌가?

브라우저 별로 동시 다운로드 받을 수 있는 개수가 다르긴 한데, 어쨌든 아래와 같은 일반적인 script 태그를 써도 동시에 다운로드 받는 걸 볼 수 있다.

<script src="small.js"></script>
<script src="small2.js"></script>
<script src="small3.js"></script>
<script src="small4.js"></script>

현대 브라우저에서 외부 리소스를 다운로드할 때는 fetcher라는 놈에게 위임하고,
(아마 이런 놈들)

Fetcher는 캐시를 확인하거나 각종 다운로드 받을 파일을 나름의 우선순위로 다운로드 한다.

그럼 fetch는 어차피 병렬이 기본인 거고(혹은 브라우저 마음일테고),

async도 “non-blocking” 플래그와 마찬가지로 HTML 파서가 스크립트를 해석하는 순서, 즉 실행 순서를 결정 짓는 것 뿐이란 말인가.

아직 자신은 없다.

차근 차근 각 상황을 되짚어 보자.

우선 async가 없는 일반적인 동적 스크립트 생성 시

  1. 스크립트에서 동적으로 script element를 생성하면 기본적으로 “non-blocking” 플래그 설정됨
  2. xxx.async를 찍어보면 true를 리턴하는 상태
  3. 다운로드 자체는 fetcher가 알아서 동적으로 다운로드 받겠지
  4. “non-blocking” 상태라서 HTML 파서는 나중에 추가된 스크립트의 존재를 알고 있겠지
  5. 그럼 준비된(먼저 들어온) 놈부터 실행하라고 JS 엔진에게 던질 수 있겠지.

다음으로 async가 true로 설정되는 경우

  1. 스크립트에서 동적으로 script element를 생성하면 기본적으로 “non-blocking” 플래그 설정됨
  2. xxx.async = true하면 “non-blocking” 플래그 해제
  3. HTML 내에 async attribute를 넣어 정적으로 삽입된 것 혹은 동적 스크립트 생성의 경우과 동일하게 동작함
  4. 그래서 다운로드 되는 놈부터 실행

다음으로 스크립트로 xxx.async = false로 처리하면 왜 병렬로 fetch하고 실행은 순서대로 하는지 살펴보자.

스펙에 의하면 async는 병렬로 ‘실행’할지를 제어한다.

bigScript.async = false;
document.body.appendChild(bigScript);
smallScript.async = false;
document.body.appendChild(smallScript);
  1. 그럼 script는 bigScript, smallScript 순으로 추가가 될 것이고
  2. xxx.async = false;로 했으니 “non-blocking” 플래그 해제된 상태
  3. smallScript가 먼저 로드가 되거나 말거나 blocking 상태에선 현재 script 블럭을 처리하기 전까지는 파서는 다음 script를 읽어들이지 않는다
  4. 그러니 bigScript의 소스를 js 엔진이 지지고 볶은 후, 파서는 다음 스크립트인 smallScript를 해치운다

OK. 실행이 순서대로 되는 건 알겠다.

결국 async는 비동기 다운로드와는 관계가 없고, 애초에 실행 순서만 관련있었던 것 같다.

async - Execute script in parallel

스펙에 써있는 그대로다.

참고

연재 목록

  1. REST - 긴 여정의 시작
  2. REST - HTML Form에서 GET/POST만 지원하는 이유
  3. REST - 논문(요약) 훑어보기
  4. REST - REST 좋아하시네
  5. REST - Roy가 입을 열다
  6. REST - 당신이 만든 건 REST가 아니지만 괜찮아
  7. REST - 논문 읽기(To Do)

To do

로이 필딩의 논문을 읽지 못해 생긴 부채를 여기 한장에 남겨둔다.

언젠가 돌아보다 생각나면 진짜 논문을 읽을 날이 오겠지.

그럼 그 때까지, REST in peace!

연재 목록

  1. REST - 긴 여정의 시작
  2. REST - HTML Form에서 GET/POST만 지원하는 이유
  3. REST - 논문(요약) 훑어보기
  4. REST - REST 좋아하시네
  5. REST - Roy가 입을 열다
  6. REST - 당신이 만든 건 REST가 아니지만 괜찮아
  7. REST - 논문 읽기(To Do)

Update: “Your API isn’t RESTful — And That’s Good”에 대한 요약번역본은 숨겨놨다가, 원문을 쓴 Trevor Reed에게 요약본을 번역하여 퍼뜨리는 것에 대해 댓글로 문의한 후, 재배포함.


Hypertext, HATETOAS for API

여러 동영상을 찾아보다, 2016년에 진행중인 API 스펙들에 관해 이야기하는 강연을 봤다

  • REST의 핵심은 hypertext이다
  • 2016년의 API 스펙들
    • JSONAPI
    • HAL
    • Hydra (JSON-LD)
    • Collection+JSON
    • GraphQL

이들 스펙 만으로는 RESTful하다고 볼 수는 없지만, hypertext에 대한 관점에 공감을 하며 이를 표준 스펙을 통해 풀어나가려는 시도가 이어지고 있다.

좋은 시도이고 꾸준히 발전하길 기원한다.

그러나 지금까지 다루었던 개발자들의 몰이해는 이런 실질적인 구현체가 없었기 때문인가?

튜토리얼 몇 번 따라하고 실무에 적용할 만 한가? 그러면 RESTful API가 될 수 있나?

어렵다고 본다.

REST or not

사람들이 싸우는 것은 항상 이런 식이다.

  • 제약 사항을 지키지 않았다면 그것은 REST가 아니다. 세상은 REST와 REST가 아닌 것으로 분류된다
  • 현실적으로 그걸 다 지키기는 어렵다. Roy 지도 못하면서! 난 내 작품을 REST라고 부를거야

수많은 논쟁을 지켜보면 든 생각은, REST에 대해 잘못 알고있는 사람들(어쩌면 당신과 나)을 비난하며 선을 긋지 말자는 것이다.

인터넷에 돌아다니는 99%의 글이, 그 좋은 회사(트위터, 페이스북…)에서 통용되는 용어들이, 단지 몇가지 URL 규칙과 HTTP 메소드가 필요했을 뿐인 사람들이 REST란 용어를 납치했다.

비슷한(?) 예로, 선풍기를 쐬고 자면 죽는다는 미신은 증명하기는 너무나 어렵고 오랜기간 광범위하게 퍼졌으며 전문가들도 경고했으며 모두가 믿기 때문에, 많은 이가 오늘도 타이머를 맞추고 잔다.

그러니까 난 지금 선풍기를 쐬고 자도 안 죽는다는 미신을 숭배하고 있다. 여전히 난 어떤 게 미신인지 증명할 수 없다.

REST뿐 아니라, IT뿐 아니라, 우리는 많은 것을 블랙박스 안에 넣어놓거나 혹은 추상화된 개념으로 이해하고 넘어간다.

블랙박스를 열어보니 다른 게 들어있었다면, 그저 예상과 달랐다고 담백하게 말해보자.

REST를 잘못 알고 있다고 해서, 그가 실력도 없고 뭔가를 자세히 알아볼 생각도 없는 겉멋든 사람이라고 할 수는 없다.

‘난 XX를 안다고 착각하는 부류를 알고 있어. 그런 사람들은 보통 XX한 사람들이지’라며 선풍기 타이머를 돌리지 말자는 이야기다. (물론 난 다른 이유로 타이머를 애용한다)

특정 관점이 다른 사람들을 경계 밖으로 몰아내 모든 교류를 끊지 않았으면 한다.


로이 필딩은 REST에 관한 논문은 전문가를 위한 논문이라고 언급한 적이 있다.

REST는 설계를 위한 지침이지 초심자도 따라할 수 있는 best practice를 제공하는 것이 아니다.

쉽게 말해, 어렵다.

RESTful -> RESOURCEful

사실 간단한 해결책이 있다.

이 모든 혼란은 REST이라는 용어를 쓰지 않으면 해결된다.

RESTful하지 않다는 것이 실패한 디자인이라는 뜻이 아니다.

다만, 그동안 통용됐던 의미의 REST를 통칭할 수 있는 새로운 용어가 필요하다는 말이다.

마지막으로 이 글을 읽어보자.

이 글에서 그동안 REST라고 잘못 명명된 몇가지 구현 원칙을 RESOURCEful API라는 새로운 이름으로 재정의하고 있다.

Your API isn’t RESTful — And That’s Good

2016년 3월 31일에 발행된 Trevor Reed이란 사람의 블로그를 읽어보자.

https://medium.com/@trevorhreed/you-re-api-isn-t-restful-and-that-s-good-b2662079cf0e

현 상황을 가장 명확하게 정리한 글로 보인다.

The Background

  • 2000년에 로이 필딩은 Architectural Styles and the Design of Network-based Software Architectures라는 논문을 발표한다
  • 비슷한 시기(1999년)에 Web 2.0이니 시맨틱 웹이니 자신들의 아이디어를 더욱 혁신적으로 보이게 만들어주는 유행어가 등장한다
  • 이 아이디어는 웹 서비스로 이어지고 SOAP이나 XML-RPC같은 웹 서비스를 구현하기 위한 많은 사양이 나타나기 시작했다
  • 하지만 이는 구현하기 복잡하고 성능 문제도 발생했다
  • 이런 API 제공을 위한 더 간단한 방식으로 월드 와이드 웹이 제기되었다
  • 분산 하이퍼미디어 시스템을 위한 고도로 정교한 아키텍처 스타일이 대중적인 아이디어의 원시적인 힘과 충돌한 시점이 바로 이쯤이다.
  • 사람들은 SOAP 같은 프로토콜 대신 간단하고 표준적인 대안을 원했다
  • REST의 아이디어 중 일부는 필요에 잘 부합했지만 전부는 아니었다
  • 그래서 사람들은 필요한 것을 가져갔고 나머지는 무시했다.
  • REST라는 용어는 무시하지 않고 잘도 가져갔다. 그들은 새로운 웹 API 개념을 위해 그 용어를 빌려갔다
  • 로이 필딩의 노력에도 불구하고, 인터넷은 REST라는 용어를 납치하여 도망쳤다
  • REST, RESTful, REST API 등의 용어는 모든 종류의 API 구현체에 사용되고 있다.

The Problem

  • API 개발을 하려는 많은 사람들이 REST를 API 구축을 위한 일련의 베스트 프랙티스로 인식한다
  • REST를 제대로 이해한다면, 그게 자신들이 예상한 것과 다르다는 걸 깨닫는다
  • 이해하는 것과는 상관없이, 그게 자신들이 필요로 하는 것보다 많다는 걸 깨닫는다
  • 그래서 그냥 ‘나름 RESTful’한 API를 만들어 버린다
  • REST 유스 케이스는 서버와 클라이언트가 강하게 결합하지 않아야만 한다
  • 클라이언트는 서버가 제공하는 하이퍼미디어를 탐색하는 방법만 이해하면 된다
  • 가장 일반적인 예는 월드 와이드 웹이다
  • 브라우저는 클라이언트이고, URL은 응용프로그램 상태를 나타내며 사용자는 하이퍼미디어를 탐색하는 방법을 결정한다(링크 클릭 등)
  • 많은 웹 API는 이와는 대조적으로 클라이언트와 강하게 결합되어 있고 하이퍼미디어가 부족하기 때문에 REST의 원칙을 위반한다
  • API의 엔드포인트는 클라이언트가 개발될 때 이미 알고 있고, 클라이언트 안에 하드코딩 되어 있기 때문에, API의 엔드포인트를 발견하기 위한 하이퍼미디어 사용은 불필요하다

The Solution

  • 우리가 필요로하는 것은 밀접하게 결합 된 웹 API의 일반적인 사용 사례를 공식적으로 정의하고 표준화하는 것이다
  • 혼란을 종식시키기 위해서는 이 개념을 새로 명명해야한다
  • 이를 통해 커뮤니티는 “REST”의 개념과 혼란과는 별개로 강력한 표준 및 모범 사례를 구축 할 수 있다

RESOURCEful APIs: A New Name, An Old Concept

  • RESOURCEful API : 새로운 이름, 오래된 개념
  • 다음의 조건을 만족할 때 RESOURCEful하다고 간주된다
    • API는 HTTP를 전송 레이어로 사용해야만 한다 (MUST)
    • 모든 API 엔드 포인트(URL)은 명사로 구성되어야 하며, 리소스, 리소스 모음 또는 리소스 속성을 나타낸다
    • 컬렉션에 속한 리소스는 컬렉션 URL을 접두어로 하는 URL에서 접근할 수 있어야 한다
      • 리소스에 속한 서브 컬렉션이나 속성은 리소스의 URL을 접두어로 해서 접근할 수 있어야 한다
    • API 버전 및 리소스 식별 매개 변수는 URL 경로에 있어야 한다
      • 다른 모든 매개변수는 쿼리 문자열 혹은 헤더에 있어야 한다
    • 실제 동작을 나타내는 API 호출은 작업큐 혹은 작업 대행자로 표시되어야 한다. 이 둘은 여전히 명사이다. 이들의 동작을 트리거하는 데는 HTTP POST 메소드가 사용되어야 한다
      • 역주) 의역하자면 “동작을 나타내는 API는 그 동작 자체를 명사로 나타내야 하며, 이때 HTTP 메소드는 POST를 사용한다” 정도로 하면 될 것 같다
    • 구현된 HTTP 메소드는 HTTP Method spec에 따라 정의된 목적으로 사용되어야 한다
    • 구현된 HTTP 메소드는 안전함(Safe)과 멱등성(Idempotent) 원칙을 준수해야 한다
    • HTTP 상태코드는 정의된 용도에 따라 모든 응답에서 사용되어야 한다
      • 200은 다른 가능한 상태코드가 있을 지라도 모든 성공적인 요청 결과의 응답 코드로 사용 수 있다
    • 요청이나 응답의 본문은 자원, 자원의 모음 혹은 자원의 특성을 나타내야 한다
    • 요청이나 응답의 본문의 형식은 Content-Type 헤더로 지정해야 한다
    • API가 요청 본문의 형식을 지원하지 않으면 415(Unsupported Media Type) 상태를 반환해야 한다
    • 응답 본문의 형식은 가능하다면 Accept 헤더에서 요청한 형식이어야 한다
      • API가 요청 본문의 형식을 지원하지 않으면 406(Not Acceptable) 상태를 반환해야 한다
  • 또한 아래와 같은 우수 사례를 권장한다
    • URL을 소문자로 만들기
    • 특정 HTTP 메소드나 상태코드를 지원하지 않는 client를 위한 대체 수단을 만들기
      • 예를 들어 JSONP는 모든 응답에 200 상태 코드를 필요로 한다
    • JSON 형식은 항상 지원할 것 - XML은 선택 사항
    • 항상 HTTPS를 통해 호스팅할 것
    • API가 새로운 리소스를 생성할 때는 응답 어딘가에 해당 자원의 식별자를 반환해야 함

Examples & References

  • 일반적인 HTTP 메소드
    • GET — safe, idempotent —요청된 URL에서 리소스의 representation을 검색한다
    • POST — not safe, not idempotent — 제출된 리소스의 representation을 요청된 URL 아래 새로운 위치에 생성한다
    • PUT — not safe, idempotent — 제출된 리소스의 representation을 요청된 URL의 위치에 저장하고 이미 존재한다면 대체한다
    • PATCH — not safe, not (necessarily) idempotent — 제출된 정보를 통해 요청된 URL의 리소스를 부분적으로 업데이트한다. 존재하지 않는 리소스에 대한 PATCH 시도는 400 상태 코드와 함께 실패해야 한다
    • DELETE — not safe, idempotent — 요청된 URL에 있는 리소스를 삭제한다
  • 비동기 작업 큐에서 HTTP 메서드의 동작
    • GET - 큐 또는 큐의 특정 태스크에 대한 정보 검색
    • POST - 큐에 작업 추가
    • PUT - 큐의 기존 작업 업데이트
    • PATCH - 큐에 있는 기존 작업을 부분적으로 업데이트
    • DELETE - 큐에서 작업 제거
  • 작업 대행자(agent)에 대한 HTTP 메소드의 동작
    • GET - 에이전트 또는 그의 동작 중 하나의 상태를 검색
    • POST - 원하는 동작 트리거
    • PUT - 정의되지 않음
    • 패치 - 정의되지 않음
    • DELETE - 가능한 경우 이전에 트리거 된 동작을 취소
  • 공통 HTTP 상태 코드 - 2XX 레벨 상태 코드를 제외한 모든 것이 실패로 간주된다
    • 200 OK - 응답 성공
    • 201 Created - 요청 본문에 제출된 엔티티가 (동기적으로) 생성되었다
    • 202 Accepted - 요청 본문에 제출된 엔티티가 생성될 것이다(비동기식으로)
    • 204 No Content - 뷰를 업데이트할 필요가 없음
    • 205 Reset Content - 클라이언트가 뷰를 재설정해야 한다
    • 206 Partial Content - 반환된 부분 콘텐츠 (예 : 구간 설정된 혹은 페이지 별 콘텐츠)
    • 400 Bad Request - 요청이 잘못되었음
    • 401 Unauthorized - 클라이언트가 서버에 인증되지 않음
    • 403 Forbidden - 클라이언트는 서버에 인증되었지만 요청된 자원에 대해 요청된 작업을 수행할 수있는 권한이 없음
    • 405 Method Not Allowed - 요청된 URL에 사용된 HTTP 메소드가 허용되지 않음
    • 409 Conflict - 작업을 수행할 때 충돌이 발생함. 예를 들어 요청이 이미 변경된 자원을 업데이트하려고 시도함
    • 500 Internal Server Error - 서버의 오류가 발생했으나 처리되지 않음
    • 501 Not Implemented - 요청된 자원에 대해 HTTP 메소드가 현재 구현되지 않음
    • 503 Service Unavailable - 서버 또는 그것의 종속성 중 하나(예 : 데이터베이스)가 과부하, 정전 등으로 인해 응답 할 수 없음

Example: Doctors Office API

Resource API Endpoints:

GET /customers
POST /customers
GET /customers/<id>
PUT /customers/<id>
PATCH /customers/<id>
DELETE /customers/<id>
GET /customers/<id>/appointments
POST /customers/<id>/appointments
GET /customers/<id>/appointments/<id>
PUT /customers/<id>/appointments/<id>
PATCH /customers/<id>/appointments/<id>
DELETE /customers/<id>/appointments/<id>
GET /customers/<id>/cell_phone
PUT /customers/<id>/cell_phone


Asynchronous Task Queue API Endpoints:

GET /outgoing-emails
POST /outgoing-emails
GET /outgoing-emails/<id>
PUT /outgoing-emails/<id>
PATCH /outgoing-emails/<id>
DELETE /outgoing-emails/<id>


Synchronous Agent API Endpoints:

GET /patient-discharger
GET /patient-discharger/<id>
POST /patient-discharger
DELETE /patient-discharger/<id>

더 읽을 거리

미신

여기부터는 REST를 알지 못하는 사람들을 이해해보려 좀 더 검색한 내용을 적어볼텐데, 대단한 근거는 없다.

나는 그토록 뛰어난 개발자들이, 게다가 영어권에 살면서 얼마든지 로이의 논문을 비오는 일요일 오후에 슬쩍 읽어볼 수 있을 사람들이 왜 이렇게 일관된(?) 오해를 하고 있는지 궁금했다.

그 기원을 좀 더 찾아보려고 뒤져본 바, ROA(Resource-Oriented Architecture)에서 찾을 수 있었다.

앞에서 잠시 언급했던 행복한 아빠님의 블로그를 보자.

REST(Representational State Transfer)는 HTTP의 주요 저자인 Roy Fielding의 2000년 논문에 의해 소개가 된 네트워크 아키텍처를 위한 구조이다. REST가 화두가 되면서 RET의 정체를 알아보기 위해 “RESTfull Web Services”를 읽고 이 책에서 말하는 Resource-Oriented Architecure(이하 ROA)를 나름대로 정리해 보았다.

ROA는 REST 기반(이하 RESTful) 웹서비스를 만들기 위한 문제점을 해결하는 방법을 제공한다.
ROA는 RESTful 아키텍처이다.

(충분히 못 찾은 것일 수도 있지만) 난 2008년도에 쓰인 이 글이 한국어로 된 것 중에 REST에 대해 이해하기 가장 좋은 글이라고 생각한다.

이 분이 공부했다는 것이 Leonard Richardson의 RESTful Web Services인데
한국어 번역서은 절판 상태.

대신 마틴 파울러가 이를 소개한 블로그를 썼는데, 마침 지앤선이 번역을 해놓았다(중간중간 이해하기 어려운 문장이 보인다면 원문을 참조하기 바란다).

여기까지 와도 아직 REST가 잘못 이해되진 않았다. 제대로 이해하고 구현하려는 시도로 보인다.

위에 소개한 “Your API isn’t RESTful — And That’s Good”에서 언급된 대로,

REST를 이루기 위한 여러 복합적인 요소/방법 중 자신의 결과물과 몇 가지 구현상의 공통점을 발견하고 REST란 용어만 납치했다고 봐야할 것 같다.

구현하기 쉬운 부분만 골라서 best practice set이 만들어진 게 아닐까.

하나만 기억하자,

서버와 클라이언트가 서로의 눈치를 보며 변화/발전을 두려워 하고 있다면(즉, 서로에게 강한 의존성을 갖고 있다면),

그건 REST가 아니다.(하지만 괜찮다. REST라고 안 부르면 되니까.)

서버와 클라이언트는 변화에 대응하기 위해 프로토콜/표준을 통한 약한 의존성을 가져야 한다.

아니 이건 수십년 간 소프트웨어 엔지니어링이 강조하고 있는 것이 아닌가!

마지막으로 아래의 관점도 생각해볼 만 하다.

REST API Design - Resource Modeling

  • 현실적으로 REST API를 어떻게 다루어야 하는지 이야기하고 있다
  • “REST without PUT”이라고도 하는 기법은, 상태 변화를 위한 요청(mutation)에서 PUT을 통한 새로운 상태를 전송하지 않고, 상태 변화 자체를 리소스로 올려first class citizen noun) 사용하자는 의미이다.
  • 예를 들어, 고객 주소를 갱신할 때의 대상 리소스는 customer가 아닌 ChangeOfAddress라고 정의한다
  • 이것이 CQRS 적용에 있어 부가적인 효과를 얻을 수 있다고도 하지만, 내가 CQRS나 이벤트 소싱을 알지 못하기 때문에 더 이상의 언급은 피해야겠다
  • 다만 앞으로 다룰 예정인 GraphQL을 이 관점에서 바라보면 재미있을 것 같다

혹시라도 이 긴 여정을 충실히 따라오신 독자가 있다면, 진심으로 고마움과 존경을 표한다.

연재 목록

  1. REST - 긴 여정의 시작
  2. REST - HTML Form에서 GET/POST만 지원하는 이유
  3. REST - 논문(요약) 훑어보기
  4. REST - REST 좋아하시네
  5. REST - Roy가 입을 열다
  6. REST - 당신이 만든 건 REST가 아니지만 괜찮아
  7. REST - 논문 읽기(To Do)

Untangled

‘RESTful APIs, the big lie’의 댓글 마지막 쯤 누군가 추천해 준 컨텐츠 몇 개를 소개해본다.

(이번에도 여전한 오역과 거친 의역에 대해 양해를 구하며…)

우선 당사자인 Roy 자신이 쓴 글이 꽤 재미있다.

“REST APIs must be hypertext-driven”이란 글이 꽤 유명한 것 같지만

이것 말고도 Roy가 2008년도에 REST에 관해 쓴 글을 보면 각기 나름의 재미가 있다.

예를 들어 이런 글, “POST 써도 괜찮아”

  • http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post
    • Tim Bray란 사람이 Sun Cloud용 API를 공개했을 때, 어떤 이가 왜 새로운 VM을 띄우는 데 POST를 사용하냐는 질문이 있었고 이에 대한 설명을 블로그로 남겼다.
      재부팅할 때는 상태도 계속 변화하고, 뭔가 내부적으로 많은 일이 일어나는데 이때 POST를 쓰겠다는 이야기가 나온다.
    • Roy도 여기에 자신도 POST를 쓰겠다는 이야기를 한다.
      REST의 논문에선 CRUD에 대한 언급을 안 했으며, 모든 자원에 대해 균일하게 정의되어야 한다 정도를 언급했다. 다만 이 메소드가 본래의 정의에 맞게 사용되기는 해야한다. 모든 상태변화에 PUT만 쓸 필요가 없다.
      POST는 다른 메소드와 다르게 캐시나 중개자의 오류처리 등을 위한 방법은 제공하지 않지만 여전히 유용한 메소드이다.
      자신은 그 리부팅을 위한 버튼에는 POST만 쓸 것이고, 클라이언트가 그 응답으로부터 상태가 변경됐음을 알 수 있도록 함으로써 POST의 부족한 사용성을 보완할 수 있게하겠다..고 했다.

REST APIs must be hypertext-driven

그 유명한 “REST APIs must be hypertext-driven“(REST API는 hypertext가 주도해야만 해)를 읽어보자.

요점 정리

난 사람들이 HTTP기반의 인터페이스를 REST API라고 부르는 것이 실망스럽다. “SocialSite REST API”라고 부르는, 그건 RPC다

API가 hypertext 주도로 동작하지 않는다면, RESTful하지 않고 REST API가 아니다. 어디 잘못된 매뉴얼이라도 돌아다니는 거야?

API 디자이너들은 당신의 창작물을 REST API라고 부르기 전에 아래 규칙들을 확인하기 바란다

  • REST API는 어떤 특정 통신 프로토콜에 종속되면 안 된다
    • 일반적으로 URI를 식별자로 사용하는 프로토콜 요소는 URI를 식별하기 위해 어떤 URI scheme이든 허용해야 한다
    • 실패하는 경우 : 식별 기능이 interaction과 분리되지 않을 때 실패한다
  • REST API는 표준화되지 않은 프로토콜의 일부를 채우거나 수정하는 것을 제외하면, 프로토콜 자체에 수정을 가해서는 안 된다
    • 잘못된 구현(HTML이 HTTP 메소드를 정의하고 있다고 믿을 정도로 멍청한 브라우저들 같은)에 관한 해결방법은 별도로 분리해야한다. (그게 결국 쓸모없게 될 것을 가정하고)
    • 실패하는 경우 : 리소스 인터페이스가 포괄적인 의미가 아닌 오브젝트에 종속되는 경우 실패한다
  • REST API는 이를 설명하려는 대부분의 노력을, 리소스의 representation과 어플리케이션의 상태를 주도할 media type 이나, 존재하는 media type을 활용해 hypertext가 가능한 마크업이나 관계를 정의하는데 써야 한다
    • 어떤 상황의 URI엔 어떤 메소드를 써야한다고 정의하는데 쓰는 노력은 media type을 처리하는 규칙의 범위 안에 완전히 정의되어야 한다
    • 실패하는 경우 : hypertext 대신, 서버와 클라이언트 간 주고받는 범위 밖의 정보(out-of-band information)를 통해 인터렉션을 주도하면 실패한다
  • REST API는 고정된 리소스나 계층구조를 고정해서는 안 된다(client와 server 간 명백한 결합이다)
    • 서버는 반드시 스스로의 namespace를 제어할 수 있는 자유가 있어야 한다. 대신 client에게 어떻게 URI를 구성해야할 지 알려주도록 하자. HTML form이나 URI Template에서 동작하는 방식으로, media type과 link relation을 통해
    • 실패하는 경우 : client가 (RPC의 기능적 결합에 해당하는 데이터 지향적인) domain-specific 표준 같은 것으로부터 리소스 구조를 추측하게 만들 때
  • REST API는 client에 중요한 리소스에 type을 지정해서는 안 된다
    • 명세를 만드는 사람들은 인터페이스 뒷단의 서버 구현체를 묘사하기 위해 resource type을 사용할 지도 모른다. 하지만 그런 타입은 client에게 불필요하고 보이지 않는다
    • client에게 중요한 단 하나의 타입은 현재 representation의 media type과 표준화된 relation 이름 뿐이다
  • REST API는 초기 URI (bookmark) 및 의도된 대상에게 적합한 표준화 된 미디어 유형 세트 이외에는 사전 지식없이 입력해야한다. 그걸 사용할 client가 이해할 수 있도록
    • 이 관점에서, 모든 어플리케이션 상태의 전이는 서버가 제공한 선택지 안에서 client의 선택으로부터 주도돼야 한다. 그 선택지는 전달받은 representation 안에 있거나 이를 client가 조작한 것에서 나와야 한다
    • 상태 전이는 client의 media type이나 리소스 교환 메커니즘에 대한 지식에 의해 결정되거나 제한될 수 있다
    • 실패하는 경우 : hypertext 대신, 서버와 클라이언트 간 주고받는 범위 밖의 정보를 통해 인터렉션을 주도하면 실패한다

댓글

Bjorg : 왜 hypermedia 대신 hypertext란 용어를 사용하는가?

  • 로이:
    • hypermedia에 대한 정의는 매우 다양하다
    • Ted Nelson의 원래의 정의에선 비선형 문서에 초점을 맞췄다
    • 이후 특정 UI 메커니즘으로 설명되고 있다
    • 내가 hypertext라고 이야기 하는 건, 정보와 통제(control)를 동시에 제공함으로써, 그 정보가 사용자에 선택권을 주고 동작을 결정하는 수단이 되는 것을 말한다
    • hypertext는 브라우저 위에서 HTML로 이루어질 필요는 없다. 기계는 data format과 relation type만 이해하면 링크를 따라갈 수 있다

SocialSite REST API에 대해 쓴 블로거(snoopdave)가 와서 댓글을 달았고, 로이가 그건 여전히 RPC 결과를 wrapping한 것 뿐이라고 반박한다

  • 매번 하는 이야기인 media type, relation, 사용자의 선택권 같은 이야기를 하는데, 정확히 번역하기 어려움

BillHiggins : 사람들이 자신이 REST를 만든다고 생각하는 이유는 뭐라고 생각해, 로이?

  • 로이:
    • 일부는 내가 논문에서 media type 설계에 대한 세부사항을 충분히 넣지 못했기 때문인 것 같아. 그건 시간이 부족했기 때문이지, REST의 다른 부분에 비해 중요하지 않아서가 아니야
    • 마찬가지로 많은 사람들이 권위있는 출처를 기반으로하지 않는 Wikipedia 항목 만 읽었기 때문이기도 하다
    • 그러나, 사람들은 단순한 것을 디자인하기 위해 단순하게 생각하는 건 실수라고 생각한다. 무언가를 설계한다는 것은 결과의 단순함과는 반비례한다
    • REST의 모든 세부사항은 소프트웨어의 수명과 독립적인 진화를 촉진하기 위함이다
    • 많은 제약사항은 단기 효율에 직접적으로 거부하고 있다
    • 사람들은 단기적 설계에는 능숙해도 장기적 설계에는 형편없으며, 현재의 릴리즈 이후의 설계를 하지않는다

nkallen : 내가 생각하는 게 맞는 지 좀 알려줘.

  • 행위는 media type에 의해 결정된다. 그렇다면 GET / POST / PUT / DELETE는 미디어 유형에 따라 의미가 부여되는가
    • 로이:
      • 모든 media type은 기본 동작 방식이 정의되어 있다. 그건 GET / POST / PUT / DELETE와 같은 메소드와 전혀 상관없다
      • 식별자, 메소드, media type은 관심사가 교차하지만 메소드는 media type으로부터 의미가 부여되지 않는다
      • 대신 media type은 client에 어떤 메소드를 사용할 지 결정하는 방법을 알려준다
  • 조작을 위한 인터페이스는 발견되어야 한다. 예를 들어, 이미지 리사이즈를 위해 width,height를 파라미터로 넘기는 등
    • 로이: 발견될 필요는 없다. 그건 이미 hypertext안에 존재하니까. representation은 어플리케이션의 다음 상태로 가기 위한 모든 변화를 어떻게 만들어 낼지 알려준다
  • root resource로부터 하위 리소스로의 링크를 가져올 수 있어야 하는가? 우리는 하위 리소스의 위치를 미리 알아둘 필요는 없을 것 같다. API 문서에 있으니까
    • 로이: 리소스 모델링의 목적은, 네가 식별하고 representing하고 조작할 가치가 있는 리소스를 찾아내기 위함이다. 리소스 이름 구조에 의존하는 client를 만들어선 안 된다

TheOtherMarcus: 리소스와 타입, 가능한 동작을 식별하는 유명한 hypertext media type을 하나 소개해줄 수 있을까? 내가 새로운 아이템을 추가하는 POST를 작성한다고 할 때, 어떤 미디어 타입이 적당한 지 어떻게 표현해야 하지?

  • Roy :
    • HTTP의 동작은 일반적이다. 리소스 별로 혀용되고 안되고 차이는 있지만 모두 사용 가능하다. hypertext는 네가 사용 가능한 모든 동작을 말해주진 않는다. 단지 각각 사용 가능한 동작이 무엇인지 말해줄 뿐이다. client는 어떤 인터페이스를 사용할 것인가가 아니라 어떤 작업을 할지를 결정하는 것이다
    • HTML은 type 명세가 필요없다. RESTful 설계도 그런 건 필요없다. POST가 어떤 의미인 지 네가 결정할 일은 없다. 그건 리소스가 정하는 거니까. 네 환경에서 이해할 수 있는 리소스 타입을 가진 hypertext representation이 POST의 결과로 어떤 걸 기대할 수 있는 지 알려준다. HTTP 응답은 결과로서 어떤 일이 일어났는지 알려준다

jdubray : 보통 하이퍼텍스트는 특정 리소스에서 허용되는 모든 작업을 알려주지 않아. 잠재적인 전환을 알려줄 뿐이지. 이런 전환에 대한 정확한 사전 지식이 없다면 코드를 짜는 게 얼마나 어려울 지 상상이 가. 리소스의 생명 주기(상태, 전환 등)가 변경되었을 때도 내 코드가 문제가 없을지 어떻게 알겠어?

  • Roy :
    • 물론 클라이언트는 사전 지식을 갖기 마련이고, 모든 프로토콜, 미디어 유형 정의, URI 스킴, link의 relation 유형들도 클라이언트가 알아야할 사전 지식 중 하나지. REST는 그 단서가 필요없다고 하진 않아. REST가 하는 일은 사전 지식의 필요성을 쉽게 표준화할 수 있는 형태로 집중하는 것이지. 이건 데이터 지향 - 컨트롤 지향 통합 간 주요한 차이이지
    • 표현과 관계 유형을 표준화하는 것이 오브젝트와 오브젝트 특정 인터페이스를 표준화하는 것보다 쉽지. 알아야 할 것은 더 적고, 예상치 못한 방식으로도 재조직되면서도 여전히 클라이언트는 잘 이해할 수 있지
    • 하지만 모든 사람들이 REST 설계 방식에 따라 자체 시스템을 재설계해야 한다고 생각하진 않아. REST는 여러 조직에 걸쳐있는 수명이 긴 네트워크 기반 응용 프로그램을 대상으로 하니까. 제약조건이 필요없다면 안 쓰면 돼. 다만 그걸 REST API라고는 하지 말아야지. 자신의 설계 스타일에 맞는 시스템에다가는 뭐라 하고 싶지 않아

tlainhart : 이 아키텍쳐 스타일에 대한 부정적인 반응 중 하나는 어떻게 그걸 효율적으로 구성할 수 있을까이다. 이를 위해 캐싱 서비스를 사용하는 것이 좋을 거라고 가정해도 될까?

  • Roy :
    • 캐싱은 중요하고 바람직하지만, 배치 작업에는 잘 맞지 않지. 사람들은 리소스의 범위를 이해 못하기 때문에 배치 작업이 필요하다고 생각한다
    • 리소스는 스토리지에 있는 항목과 항상 동일하진 않아. XML 처럼 동일한 자원 상태가 여러 자원으로 겹쳐질 수도 있지
    • 마찬가지로 단일 리소스가 데이터베이스의 stored 프로시저와 동일할 수 있고, 이는 수많은 저장 항목에 대한 상태 변경을 추상화한 것일 수 있지
    • 배치 작업이 필요하다고 생각이 들면, 리소스를 충분히 정의하지 않은 탓일 지도 몰라

TheOtherMarcus :

  • 내가 REST에 대한 내 생각을 말해보고 여전히 남아있는 모순에 대해 물어볼게
  • 클라이언트가 하이퍼텍스트에 제시된 동작 중 하나를 골라 현재 응용 프로그램 상태를 변경했다고 하자
  • CreateItem POST /random/uri
  • 클라이언트는 이 CreateItem가 뭔 지 알아야 하지. 이건 media type의 일부이니까
  • /random/uri를 POST로 보내면 새로운 프로그램 상태가 반환되지(201 응답)
  • 역주) ….뭐 이런 내용이 나오는데 별로 중요하진 않음
  • Roy :
    • 응용 프로그램의 상태와 자원의 상태를 헷갈리면 안돼
    • 그건 그렇고. 블로그 댓글 안에서 디자인에 대한 추가 지침을 줄 수는 없다고 생각해. 내 짐작으로는 네가 OOD나 ER 모델링을 리소스 공간에 투영하려는 것 같은데, 애플리케이션 요구 사항 및 컨텍스트에 대한 광범위한 지식 없이는 적절한 디자인이 뭔지 말할 수 없다

Robert Cernysays: 주어진 API가 RESTful한 지 알 수 있게, 또 글로만 적어줘서 고맙다. 지능이 발달한 인간이야 인터페이스의 추상적인 설명을 보고 REST인가를 결정할 수 있지만, 기계는 가능한가? 샘플 소스라도 좀 보내줄 수 있을까? 못 한다면, 왜?

  • Roy :
    • REST에 대한 추적 기준을 제공하기 여렵다면, 클라이언트가 전이를 결정하는 방법이 중요하다고 생각해라
    • 명세를 테스트할 때 가정 어려운 부분은 RESTful 프로그램이 사실 대역 외 정보에 의존하고 있거나 작성자가 문서 작성 목적으로 지나치게 명세를 만들어 놓는 경우이다. 내가 찾는 건 미디어 유형에 대한 명세 밖에 정의된 어떤 행위에 대한 요구 사항이다. 가장 쉬운 예로는 프로토콜이 일반 미디어 유형을 요청하고 API 별로 특수하게 처리하는 경우를 들 수 있다. 그것들이 일반적인 형식의 의미를 확장하는 XML 네임스페이스 선언처럼 content 내에서 어떤 고유한 것을 강화하고 있다면 괜찮다. 그러나 일반적으로 OPENSOCIAL같은 애들이 하는 건, 응답 안에 미디어 유형에 의해 정의된 것 이상의 특정한 구조를 가지고 있다고 가정한다. 구조가 변경되면 클라이언트는 망가지겠지

30번 nkallen에 대한 답변

  • Roy :
    • 미디어 유형은 representation이 어떻게 처리될 지에 정의하는 명세를 가리킨다. 이건 대역 외 정보가 맞고 모든 통신은 사전 지식에 의존하고 있다. 네가 놓치고 있는 것은 각각의 representation이 제공되는 서비스와의 특정 지침을 포함하고 있다는 사실이다. 미디어 유형이란 건 모든 에이전트가 배울 수 있는 일반적인 처리 모델이다. representation은 agent가 실행하는 어플리케이션에 따라 다르다. 따라서 각 representation은 어플리케이션의 해당 시점에서 사용할 수 있는 전환(transition)을 제공한다
    • representation이 relation이 지정된 hypertext로 제공된다면 agent는 사람과 다를 바 없이 이 어플리케이션을 따라갈 수 있다. 내게 중요한 건, 인간을 위한 좋은 웹 디자인이 반영된 동일한 디자인이다. 동일한 아키텍쳐 스타일로 기계와 인간 모두를 지원하는 프로토콜을 설계할 수 있다

역주) 이후 링크가 깨진 트랙백이 다수…

Dima : 공개된 API 중에 실제로 RESTful하다고 생각한 게 있어? 내가 가진 문제는 보안을 수행하며 동시에 RESTful하게 유지하는 것이야. 보안이 필요한 요청을 보낼 때는 보통 이렇게 하지. GET /resource/11231231/token=KJGHY7687JKGH. POST를 쓰지 않는 한, 이보다 더 좋은 방법이 있을까?

  • Roy :
    • 난 네가 그런 모호한 URI 형식이 보안이 필요한 요청을 가능하게 한다고 생각하는 지 잘 모르겠어. RESTful 시스템은 다른 메시지 전송 프로토콜과 동일한 방식으로 안전한 동작을 하고 있어. 메시지 스트림을 캡슐화하거나 메시지 자체를 암호화 하면서 말이지. 실제로 많은 예제가 존재하고, 브라우저가 다른 인증 메커니즘을 익히면 더 많이 생기겠지

역주) 이후엔 Roy의 답변이 없고, 블로그 몇 개를 더 썼다. 아래 colinjack이란 사람이 한 말은 의미있다

colinjack : DDD와 비교를 하면서, DDD도 오해를 겪고 사람들은 때로 중요하지 않은 부분에 집중을 하지만, Evans의 책이 살아있는 예제를 제공하고 그의 아이디어가 왜 중요한지를 보여준다. 그러나 REST에서는 웹에 많은 컨텐츠가 있지만, REST 전문가가 썼다 하더라도 새로 접하는 사람들에게는 거의 쓸모가 없다. 사실 좋은 REST 예제와 토론은 줄어들고 있고, 아마도 이런 REST라는 용어를 납치하게 된 이유가 된 것 같아. 여기에서 (RESTful하진 않지만) 현실적인 예제가 있긴 하다. (이 링크의 댓글을 보면 ‘REST’에서 ‘RE’는 빼고 ‘ST’에 대한 좋은 글이라는 비아냥이 있다)

REST: I don’t Think it Means What You Think it Does

GOTO 2014 • REST: I don’t Think it Means What You Think it Does • Stefan Tilkov

이것도 ‘RESTful APIs, the big lie’의 댓글에서 추천을 받은 동영상이다.

‘그건 REST가 아니야’류의 발표나 글은 참 많은데, 제대로 REST를 이야기하는 몇 안 되는 강연 중 하나라고 생각한다.

REST의 주요 특징

  • URI를 통해 식별하고
  • Representation을 통해 실제 데이터와 전달받는 정보를 분리
  • 흐름을 제어하기 위해 hypermedia를 활용
  • self-descriptive한 메시지
    • 표준화된 방식으로 메타데이터를 통해 전달함으로써 캐싱 등의 이점이 있다
    • 나머지는 제대로 요약을 못하겠음…

REST 미신

REST는 좋은 URL에 관한 것이다 -> 땡

  • RESTful한 URI 같은 건 없다
  • 중요한 건 hypermedia context
    • 그 URI가 client에게 어떻게 노출되는가
    • 서버는 지가 알아서 잘 한다

REST = URI 패턴 + GET, PUT, POST, DELETE -> 비슷하지만, 땡

  • /customers/{id}/orders
    • 주문을 하기위해 회원 URI에 /orders를 붙이는 것보다는 /customers/{id}의 응답 안에 주문을 할 수 있는 링크를 넣어주는 게 좋다
  • /v1/이런 버저닝은 바보같은 짓이다 하지말자!
    • Data는 괜찮다. document도 괜찮다. 그것도 data니까
    • 하지만 API에서는 버저닝은 아무 쓸모가 없다. 그 버전이 얼마나 자주 바뀌는 것 같아? API의 정체성은 거의 바뀌지 않는다. 버전을 URI에 넣는다는 것은 API가 아무 이유없이 계속 바뀐다는 의미로 보인다
    • 버전이 바뀐다는 것은 URI 구조가 깨진다는 의미이다
    • 버저닝을 하고 싶을 때
      • 다른 것을 리턴하고 싶으면 다른 미디어타입을 사용할 것
      • URI에 버전을 넣고 싶으면 리소스 자체에 버전을 매겨라. document 같이
      • API에 새로운 관점이 생긴다면 새로운 리소스를 만들어라. 새로운 걸 만들고 링크를 걸어라. 그게 웹이 잘하는 거잖아
      • 링크를 위한 공간을 확보할 것
      • API가 아닌 document에 버저닝을 할 것
      • 청중의 질문 : 그럼 헤더에 버전을 심어서 쓰는 건 어떤가?
        • 괜찮다. 네 회사 안에서나 한정된 곳에서는 self-descriptive하고 좋다. 그런데 그건 표준화된 건 아니다. 그래서 누군가 그걸 빼먹거나 이해 못할 가능성이 있다
  • 안정적인 API를 위해 지켜야할 것
    • Client
      • URI 구조에 의존하지 말것
      • 모르는 링크를 지원하고 모르는 컨텐츠는 무시할 것
      • 새로운 링크에 대해선 열려있어야 하고, 내가 모르는 필드가 추가됐다면 그냥 무시하면 된다. API는 바뀌기 마련이니
    • Server
      • 의미없이 URI 구조를 무너뜨리지 마라
      • 추가 리소스를 통해 진화하라
      • 오래된 양식을 지원하라
  • 어떻게 hypermedia-enable할 수 있는가?
    • Step 1. service document
      • 서비스/페이지에서 필요한 링크를 모아놓은 문서
      • JSON Home
        • 이런 문서도 표준화가 진행중
        • application/json-home
    • Step 2. resource links
      • 애초에 리소스 자체에 필요할 수 있는 링크를 추가하기
    • Step 3. state transition links
      • 어플리케이션의 상태에 따라 필요한 링크를 정해주기

hypermedia apis = Responses with links -> 그게 다가 아니다
* Rule #1. Don’t have clients build URIs using string concatenation
* 대신, 레시피를 제공하라. 이것도 역시 hypermedia이다.
* 다음에 요청할 URI를 만들 수 있는 템플릿을 제공
* 이것도 표준이 있다 : https://tools.ietf.org/html/rfc6570
* 여기서 더 나아가면 default data를 제공하는 폼을 만들 수 있다. (43:20 지점)

REST = 서비스 인터페이스에 대한 색다른 접근 방식 -> 너무 단순한 표현이다

  • 우리는 다수의 client가 다수의 server에 접근하는 서비스 인터페이스를 만드는데, 이때 특정 서버에 종속되지 않은 hypermedia 타입을 만들어야 한다.
  • 그러기 위해선 아래와 같은 표준을 사용할 수도 있다
    • application/atom+xml
    • application/vnd.collection+json
  • 그런데 그냥 text/html을 쓰면 어떨까
    • 여기저기 퍼져있고
    • 잘 알려져있고, hypermedia control을 잘 지원하고
    • 좋은 표준 client가 존재하고
    • 좋은 프로그래밍 tool이 존재한다
    • UI는 부수효과로 누릴수도 있고

이번에도 별로 대단한 걸 얻은 것 같진 않다면 바로 결론으로 넘어가자.

연재 목록

  1. REST - 긴 여정의 시작
  2. REST - HTML Form에서 GET/POST만 지원하는 이유
  3. REST - 논문(요약) 훑어보기
  4. REST - REST 좋아하시네
  5. REST - Roy가 입을 열다
  6. REST - 당신이 만든 건 REST가 아니지만 괜찮아
  7. REST - 논문 읽기(To Do)

RESTful APIs, the big lie

이제 개발자들 사이에 널리 퍼진 REST에 대한 오해를 들춰볼 차례이다.

독자 입장에선 이 정성으로 그냥 논문을 읽지 그러냐는 의문도 들 수 있겠지만,

이 바닥의 수많은 오해를 발견하는 재미도 있었다.

REST에 관한 끊이지 않는 논쟁

자신을 “SPA (UI/UX/server) architect and author”라고 소개하고 있는
Michael S. MikowskiRESTful APIs, the big lie라는 자극적인 글과 댓글을 읽고 한번 정리해볼 필요가 있다고 생각했다.

댓글로 달려드는 모두가 REST나 API를 바라보는 관점이 다르고, 댓글을 읽으면서 ‘그래, 그렇지’하면서 나의 관점도 계속 흔들렸다.

내가 흔들렸던 이유는 이 글을 읽을 때는 막 REST에 대해 공부하기 시작했을 때였기도 했고, RESTful한 API를 만들기 위한 현실적인 고민들이 남일 같지 않았기 때문이기도…

그냥 REST라는 용어만 포기하면 편하다는 결론은 내 후속 블로그 글에서 다시 확인하시고, 일단 개싸움부터 들여다보자.

사실 블로그 자체는 REST를 이해하는데는 별로 도움이 안 되는 글이긴 한데, 이런 개싸움이야 말로 진짜 현실 세계(반 REST 진영이 가장 많이 언급하는 단어가 ‘real world’이다)의 문제인 듯!

주요 논점을 정리해보고, 딸린 댓글을 개발 새발 정리한 버전은 페이지 마지막에 붙여놓겠다.

블로그 본문

우선 블로그에서 저자가 주장하는 바를 짧게 정리해보자.

Problem #1: There is little agreement on what a RESTful API is

HTTP 메소드와 응답코드는 모호해서 그 뜻이 정확히 뭘 의미하는지 사람마다 다르게 인식한다.

어떤 응답코드를 줄 지는 회사/사람마다 다른 기준이 있을 수 있어서, client 입장에선 정확히 예측하기 어렵다.

Problem #2: The REST vocabulary is not fully supported

브라우저는 PUT과 DELETE같은 문법을 지원하지 않는다.

307 Temporary 같은 애들은 브라우저마다 상이하게 처리되기도 한다. 제대로 지원하는 건 200과 500뿐이다.

Problem #3: The REST vocabulary is not rich enough for APIs

HTTP의 문법은 풍부하지 못해서 모든 어플리케이션의 요청과 응답을 표현할 수 없다.

Problem #4: RESTful APIs are very hard to debug

그래서 REST로 통신할 때 문제가 생기면 7가지나 되는 조합으로 원인을 파악해야 한다.

  • HTTP method (GET/POST)
  • URI
  • 실제로 사용하고 싶은 HTTP method (PUT/DELETE..) in payload
  • 진짜 메시지 in payload
  • 응답코드
  • 진짜 받아야 하는 응답코드 in payload
  • 진짜 메시지 in payload

이런 한계 때문에 어떤 문제가 발생했을 때, 디버깅을 위해 고려해야할 게 너무나 많다.

Problem #5: RESTful APIs are usually tied to HTTP

HTTP에 너무 종속되어 있다.
HTTP가 아닌 다른 전송 방식으로 전환하려면 또 저 7가지 조합을 고려해서 만들어야 한다.

The way forward: JSON-Pure APIs

그래서 제안하는 건, JSON-Pure APIs

  • 이 글이 쓰인 지 3주 후 JSON-Pure APIs에 관한 후속 블로그가 올라온다

논쟁

블로그 본문과 댓글에서 발화한 여러 논쟁을 크게 묶어 정리해본다.

HTTP 메소드와 응답코드는 모호해서 그 뜻이 정확히 뭘 의미하는지 사람마다 다르게 인식한다.

따지고 보면 그걸 모르는 사람의 문제지만, 현실적으로는 이해가 되는 부분도 있다.

어떤 응답 로직을 구현하기 위해 적합성 여부를 여러번 생각해야하는 것은 어쨌든 일종의 비용이라고 생각한다.

물론 이것은 REST의 문제가 아닌 구현상의 어려움일 뿐이다.

  • 이 글을 비판하는 진영에서는 ‘아니, 그게 왜 어렵다는 거야?’같은 반응을 보이기도 한다
  • 사족이지만, npm 개발자인 Isaac Z. Schlueter는 세미콜론을 최대한 안 쓰는데, 이런 방식은 버그를 양산할 수 있고 혼란스러울 수 있다고 비판하는 사람들에게 ‘네가 JavaScript 문법을 몰라서 그렇다’고 일침하기도 했다

요청 메소드와 응답 코드가 모든 상황을 담아낼 수 없다는 의견도 있는데, 이 글에 동조하는 사람들은 거대한 엔터프라이즈 환경(ERP, 병원 시스템 등)에서 다양하고 복합적인 리소스를 다루는 상황에 대해 언급한다.

논쟁을 지켜보며 생각이 드는 것은 - 추측이지만,

메소드가 충분치 않다는 감정은 특히 RPC를 실행하는 경우,
그러니까 내부적으로 복잡한 로직이 섞여있을 때 발생하는 것 같다.

예를 들어 리퀘스트를 날려서 설거지와 빨래와 아이 목욕을 시킨다면, 여기서 리소스란 무엇일까? 이 때는 어떤 메소드를 써야하는가?

이 지점부터 한차례 혼란이 오는 것 같은데, 그 리퀘스트를 보내는 대상 리소스가 우리가 흔히 인식하는 리소스, 그러니까 CSS나 이미지같은 소위 정적 리소스 개념을 벗어나는 지점부터 어려워지는 것 같다.

HTTP에서 리소스란 뭐든 지 될 수 있다.

또한 REST를 CRUD로 치환하는 순간 이견은 좁혀지지 않는다.

조회/등록/수정/삭제를 GET/POST/PUT/DELETE로 처리하는 게 REST라고 생각하면 더 이상의 이성적인 논쟁은 불가능해진다.

HTTP 메소드가 부족하다고 생각이 드는 건 모든 RPC 요청의 성격을 CRUD에 매핑하려는 시도에 원인이 있다고 본다.

어떤 기분인 지는 알 것 같다.

REST API는 GET/POST/PUT/DELETE를 쓰는 것 -> 곧 조회/등록/수정/삭제를 의미 -> RPC 업무는 조회/등록/수정/삭제 네 가지로 단순하게 떨어지지 않아!

각 HTTP 메소드가 정확히 어떤 의미인지는 각자 (제발 신뢰할 만한 출처에서) 찾아보기로 하고,

REST 논문에는 애초에 CRUD나 어떤 HTTP 메소드를 써야 한다는 언급이 없었다.

‘REST API는 GET/POST/PUT/DELETE를 쓰는 것이다’라는 공식은 웹 프레임웍의 잘못된 가이드에도 원인이 있기도 하다.

Ruby on Rails의 예를 보자.

  • PUT을 썼더니 방화벽에서 막혔던 사례를 통해 이때 발생할 수 있는 문제를 다루고 레일스의 잘못된 가이드에 대해 언급한다
  • 마지막 결론쯤에 Rails는 Rails일 뿐이라는 대목이 나온다
  • Best practice를 쫓아가면 좋긴하지만, 이해용이성을 조금 포기해야 하기도 한다

PHP의 Laravel은

  • 5.2 버전까지 RESTful Resource Controllers라는 용어를 사용했으나,
  • 5.3 버전부터는 typical "CRUD" routes를 제공한다는 설명과 함께 Resource Controllers라고 변경했다. 하지만 여전히 PUT은 UPDATE에 대응이 된다.

307 Temporary 같은 애들은 브라우저마다 상이하게 처리되기도 한다. 제대로 지원하는 건 200과 500뿐이다.

좀 오버하는 감은 있긴 하다.

하지만 REST에서는 Uniform Interface가 중요한데도 경험적으로 (적어도 HTTP 쪽에서는) 인프라가 충분치 않다는 인식이 있는 것 같다.

위에 Ruby on Rails의 예에서 언급한 것처럼 보안을 이유로(?) PUT을 지원하지 않는 방화벽이랄지,

HTTP 응답 코드를 각기 다르게 해석하는 클라이언트 등 현실적인 제약은 다반사인 것 같다.

구현을 하는 입장에선 아무래도 이런 부분에선 부담이 되는 게 사실이다. (IE 대응을 위해 버려진 수많은 나날을 생각해보자)

HTTP의 문법은 풍부하지 못해서 모든 어플리케이션의 요청과 응답을 표현할 수 없다

이것도 사실 각자의 상황에 맞는 Best Practice의 부재 탓이라고 본다.

HTTP가 끝없는 업계의 요구사항에 매번 응답코드를 추가해줄 수는 없다.

답답하겠지만 결국 개발자는 결정을 해야한다. 현실 세계의 문제는 원래 그렇다.

RESTful API는 디버깅 하기 힘들다

이에 대한 반론 중에는 개발자도구나 HTTP client 툴을 사용하면 된다고들 하지만,

이 블로그 글쓴이의 의도는 요청/응답 시 신경써야 하는 부분이 많다는 것이다.

난 맞는 지적이라고 생각한다.

SOAP이나 GraphQL처럼 응답 메시지 본문에서 모든 상황을 지켜보고 싶은 마음을 나도 십분 이해한다.

하지만 REST는 쉽게 개발하려고 만든 아키텍처 스타일이 아니다.

웹의 인프라를 충분히 활용해서 효율적으로 동작하고, 독립적으로 진화하고, 확장할 수 있게 설계하기하기 위함이다.

디버깅을 편하게 하기 위해 설계 원칙을 바꾸는 순간 본래의 목적에서 멀어진다.

HTTP에 너무 종속되어 있다

웹에서의 주요 프로토콜이 HTTP이긴 하고, 그 때문에 REST를 구현했다는 사람들이 HTTP를 많이 쓸 뿐이지 REST는 HTTP에 한정되지 않는다.

5.3.2 Connector View 참고

아무리 이론적으로는 맞는 말일 지라도, 현실적으로는 대부분 HTTP 위에서 REST를 구현할 수 밖에 없잖아

맞다. 웹개발자 입장에선 대부분의 API 설계를 HTTP 위에서 하게 된다.

하지만 그것이 REST가 HTTP에 종속됐다는 의미는 아니다.

HTTP를 재발명 하자는 게 아니다. 단지 어플리케이션 레벨의 메시지를 HTTP 안에 억지로 끼워넣지 말고 JSON payload에 담자는 말이다.

그래도 된다고 생각한다. 다만 그렇게하면 REST라고 부르지 말자는 것이다. REST라고 부를 수 없다고 나쁜 설계는 아니다.

글쓴이가 제시하는 JSON-Pure API는 “json-ld/hydra”와 같은 hypermedia를 사용하는 REST(중의적인 의미) 사람들을 따라하고 있을 뿐이라는 의견도 있다. (지원하는 클라이언트가 얼마 없지만…)

hypermedia를 구현하기 위한 수많은 API 표준이 존재하는데, 후속 블로그에서 더 다뤄보겠다.

JavaScript를 비활성하면 REST를 쓸 수가 없다. HTML은 GET/POST밖에 지원하지 않는다.

계속되는 HTML의 PUT/DELETE 지원 논쟁.

XHR을 쓰면 된다는 부류와 XHR을 못쓰는 환경이 있는데 어떡하냐는 부류가 자주 부딪힌다.

미안하지만 REST는 Architectural Styles일 뿐 당신들이 request 하나하나를 어떻게 구현하는지 관심이 없다.

내 개인적인 생각으로는 어느 시스템이든 각각의 제약 사항은 존재하기 마련이다.

부족하면 부족한대로 해결해내는 것이 개발자가 할 일이라고 본다.

그러다보면 REST의 제약사항을 어길 수밖에 없는 상황도 분명 올 수 있겠지.

다시 말하지만, 그럼 REST라고 안 부르면 된다!

가장 적당한 디자인을 설계하자.

가서 SOAP으로 입 좀 행구고 오지 그래

네가 하려는 건 SOAP과 다를 바 없다는 중의적 의미.

댓글에서 SOAP과의 비교를 하는 사람이 참 많다. 여기 REST와 비교한 글도 참고하자.

(이 블로그는 국내에서 보기 드물게 REST에 대해 정확한 표현을 하고 있음에도 이 글에서는 제대로 REST를 설명하고 있지 않는데, 그 이유는 ROA에 관한 그 분의 다른 글에서 볼 수 있다. 여기서 REST는 아키텍처 스타일, RESTful Web Service는 아키텍처로 구분하고 있는데, 이게 얼마나 신방성 있는지는 좀 더 찾아봐야겠다. ROA는 나중에 다시 다뤄보겠다.)

‘소위 REST’가 SOAP을 대체해 나간 것처럼 기술이 오래 살아남기 위해선 사람들이 얼마나 쉽게 받아들일 수 있는가도 무시할 수 없는 것 같다.

결국 누군가 나서서 논쟁을 정리한다

(좋은 내용인데 이렇게 밖에 해석할 수 밖에 없어서 미안하다)

  • REST는 API가 아니야. API를 디자인 하기 위한 패턴이야. 그것은 매우 엄격하게 또는보다 실용적인 방식으로 고수 될 수 있으며 현실 세계에서 구현하기 위한 모범 사례가 무엇인지에 대해 의견 차이가 있지.
  • XMLHttpRequest는 모든 CRUD문법을 지원하고 있어. 10년 정도를 넘지 않은 모든 메이저 브라우저는 REST를 완벽하게 지원해.
  • REST API는 도메인에 종속된 메시지 교환을 충실히 지원하는게 목적이 아니야. HTTP 레이어가 트랜잭션의 상태에 대한 정보를 갖고 있지. 즉, 일반적인 HTTP client와 프로토콜을 구현할 의무가 있는 서버에게 유용한 방식이야. 이 컴포넌트들이 도메인 특정의 어플리케이션이 도메인에 종속된 로직을 처리하도록 메시지 페이로드를 전달하고 있지. 이건 대부분의 메시지 교환 방식에서 사용하고 있어. 프로토콜에서 하나의 역할을 하는 시스템은 자신이 필요한 부분만 읽고 서브 시스템에게 넘겨주지. TCP/IP 위에서의 HTTP 응답을 예로 들면 IP packet과 TCP 헤더는 HTTP client가 읽기도 전에 TCP client에서 읽히고 처리되지. 단순히 다음 프레임으로 넘어가서 메시지를 해석할 수 있는 어플리케이션 코드에게 메시지를 전달하면 되는데, 왜 굳이 HTTP 프로토콜을 확장해야 하는거지? 모든 REST 원칙은 URL이나 HTTP 동사와 같은 일반적인 개념을 제공하는 HTTP 프로토콜을 이용하고, 이를 페이로드 안에서 재발명하지 않을 것을 권고하고 있어. 그렇게 함으로써 페이로드의 내용을 더 가볍고 간단하게 만들 수 있고, 대부분의 HTTP client가 공짜로 제공해주는 캐싱 같은 이점도 누릴 수 있지.
  • 네가 언급한 7가지 영역은 사실 모두 요청과 응답이라는 2개의 조각에 나뉘어 있어. 커스텀 JSON에 모두 묻어버릴 때와 같이 말이지. 다른 점이 있다면, 대부분의 개발자는 이미 이 형식에 익숙해서 HTTP 요청과 응답 안의 정확히 어디를 봐야할 지 안다는 사실이지. HTTP 인터렉션을 디버깅을 위한 (네 브라우저에도 있는) 훌륭한 툴을 모두 언급하지는 않을게.
  • 내가 이미 지적했지만, REST는 API설계를 위한 패턴이지 HTTP로 제한된 건 아니야. RESTful한 디자인 관점은 네 API를 대부분의 전송 프로토콜 안에서 깨끗하고 일관되고 성능도 좋고 캐시하기도 좋게 도와주지. HTTP에서 적용하기 쉬운 이유는 REST의 많은 원칙들이 HTTP로부터 영향을 받았기 때문이야.
  • 넌 프로토콜과 컨텐츠에 대해 혼란스러워하고 있어. 네가 말하고 있는 건, HTTP 프로토콜의 3가지 information 조각이야. 리소스 식별자, 명령어 동사, 응답 코드. 응답코드가 메시지 컨텐츠로 절대로 볼 수 없다고는 말할 순 없지만, 트랙잭션의 상태를 나타내는 표준 코드는 완전히 프로토콜의 일부야. 나머지 것들도 메시지 교환에서의 보편적인 개념이고 프로토콜 레이어 상에서 이를 명시하는 것은 아무런 해가 되지 않아.

결론

많은 사람들은 REST가 좋은 설계 패턴이라고 생각하면서 REST를 제대로 이해 못한 채 API/서비스를 만들고 여기에 RESTful이라는 표현을 쓰고 있다.
그건 REST가 아니라고 말을 해줘도, 그럼 어떻게 구현하냐고 따지고 든다.

마치 REST라는 용어를 반드시 써야만 하는 사람들처럼.

2008년 논문의 저자 Roy도 사람들이 REST에 대해 오해하는 것에 대해 한탄하며 글을 썼는데, 이는 다음 포스트에서 다룰 예정이다.

이 논쟁에서 등장한 근거와 여러 링크가 궁금하다면 제일 하단 추천 링크도 확인해 볼 것!

댓글 정리

양이 너무 많아서 정말 대충 막 그냥 해석해서 써갈겼지만, 써 놓고 보니 또 아깝고 해서 쓸데 없지만 붙여 넣어본다.

각 댓글 앞에는 이 글에 대한 찬반 표시를 했다

  • 이 글에 찬성하는 편 &#128077;
  • 이 글에 반대하는 편 &#128078;
  • 쓸데없는 소리 &#128169;

&#128078; 너 정말 REST를 제대로 이해하는 거 맞어?
&#128078; 그 문법이 좀 혼란스러운 건 맞어. 그런데 그건 HTTP의 문제 아니냐?
&#128078; REST는 HTTP에 종속된 게 아니야

  • &#128169; 난 왜 이걸 (머릿속에서) 화난 목소리로 읽고 있는거야
  • &#128077; 무슨 소리야. HTTP의 문법이 사용되고 있는데. 그리고 현실에선 99%는 HTTP위에서 쓴다고
    • &#128078; 로이 필딩의 논문 Section 5.2.1.2는 너랑 생각이 좀 다른 것 같은데? REST는 아무 representation이나 사용할 수 있어. 바이너리 프로토콜이라도 말이지.
      • &#128077; 원래 논문이 어쨌든 현실에선 아무 의미가 없어. 단지 몇 가지 아이디어와 이름만 가져왔을 뿐이라고.
        • &#128078; 아닌데? 최초 거기서 설명된 것이 바로 핵심이야. HTTP 동사와 상태 코드에 어플리케이션 관점의 의미를 부여하는 게 사실상의 REST의 의미가 되고 있지만, 내 생각엔 그건 REST의 핵심이 아니야. 하지만 오늘날의 이른바 REST를 원래 묘사된 것보다 더 관련있고 유용하고 정확하게 만들어주는 그런 구현이 없긴하지.
        • &#128078; 어떤 면에서는 반대로 생각해. 어플리케이션 도메인의 의미를 HTTP 도메인으로 확장하려고 하면 좋은/우수 사례는 존재하지 않아. 단지 다양한 의견, 느슨한 명세와 설명의 묶음이 될 뿐이지. 사실 그 중 어느것도 확실하게 실용적인 주장이라고 정당화하긴 어려워. 요즘 널리 퍼진 소위 RESTful 베스트 프랙티스는 대부분은 개발자들 기분 좋으라고 있는거지, 원래의 의미를 기초로 하거나 필요로 하지 않는 것들이야.

&#128078; 인터넷 이야기를 해보자고. 그 거대하게 연결된 웹 자체가 이미 REST가 잘 동작하는 훌륭한 예라고. 20만개의 사이트가 20년 후에도 잘 돌아갈 거야.

  • &#128077; 뭐가 잘 동작한다는 거야? 평이한 링크들은 관심도 없거니와 여기서 다루는 문제가 아니야. 여기선 REST가 어떻게 사용되고 있는가에 대해서 이야기 한다고. 특히 IPC같은 것들.
  • &#128077; 20만개의 사이트가 잘 돌아가는 건 그동안 문서의 저장소로서 존재했기 때문이야.

&#128078; 오해를 한 것 같은데, REST는 전송 프로토콜과 언어에 대해서는 언급하고 있지 않아. 위키디피아를 봐 “RESTful systems typically, but not always, communicate over the Hypertext Transfer Protocol “
&#128077; 당신이 아무리 철학적으로 올바른 말을 해도, HTTP위에서 REST를 구현하려는 시도는 어쩔 수 없는 현실이야. 쉽고 객관적으로 써보려고 노력했는데 잘 안된 것 같군. 곧 JSON-pure API에 관한 추가 포스팅을 올리도록 하지

  • &#128078; 신경질적으로 반응한 건 미안한데, 글 제목이 좀 자극적이긴 하잖아. 당신의 그 json-pure api는 어떻게 전송하는지 말해줄래?

    • &#128077; 우선 다음 포스트(링크)를 확인하면 많은 부분 답이 될 거야
    • &#128077; REST에 관한 나의 묵상..이렇게 제목을 지어놓으면 누가 보기나 하겠어?
    • &#128077; 난 HTTP를 재발명하자고 제안하는 게 아냐. http는 훌륭한 매커니즘이고, json은 GET이나 POST를 사용해 표준 application/json contents-type으로 HTTP위에서 전송이 되지. 난 대부분의 어플리케이션 레벨의 메시지를 http안에 억지로 끼워넣지 말고 json안에 넣자는 거야. 그게 훨씬 api를 간편하고 신뢰할 수 있게 만들어 주니까.
    • &#128077; 어쨌든 이미 많은 사람들이 이 문제를 지적했다고 확신해. 이게 뭐 대단한 것도 아니고. 난 웹개발자의 관점에서 전형적인 RESTful API 구현체가 왜 종종 안 좋은 방법인가에 대해 간략하게 정리하고 싶었을 뿐이야.
    • &#128077; 추가: 내가 일반적인 http를 예로 들었지만, 이 방법은 쉽게 웹소켓이나 다른 전송 방식에서 구현할 수 있어. json에 요청과 응답에 필요한 거의 모든 것을 넣어놨기 때문이지.
    • &#128078; 사실 너는 “json-ld/hydra”와 같은 hypermedia를 사용하는 REST(중의적인 의미) 사람들을 따라하고 있을 뿐이야. 그 사람들이 더 잘하고, 표준도 만들고 있고, http를 해치지도 않지. 이미 존재하는 것을 이용하는 게 더 좋긴 하지만, 이 방식의 가장 큰 문제는 hypermedia의 개념이 아직 널리 퍼지지 않아서 지원하는 클라이언트가 엉망이라는 점이야.
    • &#128077; 뭐하러 직렬화한 것을 전송하는데 contents-type까지 붙여야 하지?…(역주) 이렇게 시작하는 답글이 있으나 문장을 끊어쓰지 않아서 뭔 소린 지 잘 모르겠음. 어쨌든 다양한 클라이언트를 대응하고 클라이언트가 기대하는 대로 응답하기에는 좋은 생각인 것 같다는 말인 것 같음.
  • &#128077; 정말 재밌게 읽었어. api를 설계할 때 나눠야할 깊은 논의에 있어 좋은 출발점이 될 것 같아.

    • &#128077; 고마워 넌 다음 글도 좋아할 것 같아
  • &#128169; 에헴 graphql 에헴 (역주 graphql이란 게 있는데…란 )

&#128078; 디버깅하기 어렵고 DELETE/PUT이 지원하지 않는다고? 지금 몇년도에 살고 있는거야?

  • &#128077; 2016에 html form에서 DELETE/PUT이 지원하나보지? 지원 안하면 javascript를 disable시킨 환경에서 어떻게 rest를 사용할건데? - 이건 인터뷰 질문으로 쓰기 좋겠구만

    • &#128078; 누가 2016년에 request 보내는데 form을 쓰냐? 누가 2016년에 js를 끄고 브라우저를 쓰고 누가 js가 제공하는 기능을 모두 기대하겠냐고. js를 켜는 것은 2016년에는 필수라고.
      • &#128077; 많은 큰 기업에서 보안을 이유로 xhr을 끄고 form을 쓰도록 하고 있어. 이름을 댈 수는 없지만 많은 기업이 여전히 IE7을 쓰고 있고 심지어 일부는 xp에 ie6을 쓰고 있지.
        • &#128077; 그리고 ajax는 일반적인 브라우저의 네비게이션 히스토리를 따르고 있지 않아. 그렇다면 history api를 써야겠지? 그러다 이런 생각이 들거야. 왜 여전히 form이 html의 가장 주요한 스펙으로 남아있을까.
        • &#128077; 나도 요즘엔 form이 덜 쓰인다고 생각해. 내가 하고싶은 말은, 당신이 어떤 길을 걸어갔던지 모든 문제는 항상 그렇게 간단하지만은 않다는 거야.
      • &#128169; 별 바보같은 댓글 다 보겠네 ㅋ 그럼 html5나 css도 호환성 문제가 있으니 갖다 버리라고 그래.
      • &#128077; 많은 큰 기업들이 보안을 이유로 PUT과 PATCH를 막고 있어. 최근 일했던 곳에서 이 둘을 허가받기 위해 엄청 설명을 해야했어. 큰 기업 환경에선 완전한 RESTful은 아직 쉽지 않지.
  • &#128078; 내가 오해를 하고 있나? 확인해보니 HTML에서 둘 다 잘 지원하는데?

    • &#128077; GET, POST, PUT and DELETE 이런 거는 다 HTTP 표준인데, HTML에서 지원하는 건 GET/POST 밖에 없어. 최신 브라우저는 지원할지도 모르겠지만, 대부분의 개발환경은 2010~2013년 시대에 머물러 있어. http://laraveldaily.com/theres-no-putpatchdelete-method-or-how-to-build-a-laravel-form-manually/ 이런 걸 봐.
      • &#128169; 1970년대에 살고 있으면 비주얼 베이직이나 어셈블러나 하시지. 지금 2017년이야 정신차려.

&#128169; 가서 비누(역주 : 그거 SOAP 아니냐는 중의적 의미)로 입 좀 행구고 오지 그래

  • &#128077; 웃긴 건 둘째치고, 이건 SOAP이랑 다른 거야. SOAP의 문제는 복잡성과 망할 스펙이지. 내가 해봐서 알아. 이건 너네의 HTTP 기반의 RESTful보다 훨씬 쉽다고.

&#128078; 마이클! 로이와 그의 논문에 대한 항목은 좀 축소하거나 삭제할 필요가 있어. 당신이 설명하는 것은 HTTP 위에서의 CRUD이지 로이가 설명했던 것과는 달라. 사실 그게 REST라고 불리고는 있지만, 로이의 논문과 관련있는 것은 단지 사람들이 완전히 다른 것을 가져오기 위해 납치한, 그가 사용한 REST란 이름 뿐이라고. REST에 대한 현대적 관행의 결점을 그의 탓으로 돌리는 건 공정하지 않아.

  • &#128077; 나도 당신의 의견에 동의하고, 당신이 쓴 그 내용을 서론에 추가해도 좋을 것 같아. 괜찮아? 내가 RESTful web services에 대해 논의했던 모든 웹개발자는 위에 쓴 그대로 REST를 설명하고 있었어. 맞아, 메소드들과 응답코드를 사용하긴 해도 기본적으로는 HTTP위에서의 CRUD일 뿐이지. 다음주에 더 설명해볼게.
    • &#128078; 고마워. hypermedia api를 한번 살펴봐 “RESTful Web API 웹 API를 위한 모범 전략 가이드”란 책이나 https://groups.google.com/forum/#!forum/hypermedia-web 을 아직 모른다면. 거기선 사람들이 CRUD를 REST와 헷갈리지 않거든. 너도 아마 좋아할거야.

&#128078; 난 네가 JSON을 사용하는 SOAP을 쓰자고 하는 것 같은데. 세세한 제약이 있는 RPC스타일 API가 어려운 건 다 이유가 있어. 많은 이가 지적했지만, 보통 오해하는 것처럼 REST가 CRUD는 아니야. HTTP 동사를 CRUD로 단순화하면 representation의 개념을 놓치게 돼. 이는 그 패턴이 잘 돌아가게끔 도메인 엔티티를 추상화하지. 나아가 각자가 정의한 의미들을 JSON payload에 담아보내는 것이 HTTP동사/응답 패턴보다 쉽다고 하는게 이해가 안돼. 적어도 HTTP동사/응답에 관해선 표준적인 사용법이 있잖아.

  • &#128078; 하나 더 이야기하자면, REST의 HATEOAS관점은 매우 중요해. 로이도 그렇게 이야기 했고. 그걸 지키지 않은 건 REST라고 할 수 없지. 이건 SOAP의 정적인 발견가능성을 넘어서, 제대로만 만든다면 API를 실시간으로 인터렉티브하게 만들어줄 수 있지.

&#128078; 이런 끔찍한 글을 쓰기 전에 REST나 공부해. 틀린 점을 말해줄게. #1. 코드의 의미는 API개발자들이 잘 이해하고 있어. #2. 동사들을 지원하기 위해 API 설계자들은 그걸 다 구현해야 하지(모든 웹 서비스가 다 그런 거잖아. 이상한 소릴 하는구만). 이건 API설계에 대한 이야기야. HTML이 몇몇 동사를 지원하지 않겠지만, API에 HTML을 사용해선 안돼. 그래서 JSON이나 XML을 써야하지. #3. 추가적인 동사는 필요없어. 리소스 기반 설계에 대해 공부 좀 해봐. 내가 아래에 몇가지 알려줄게. REST는 특히 캐싱에 대해 고려하거나 이로써 대역폭에 관해 효율적으로 동작해. #4. REST는 디버깅이 존* 쉬워. 브라우저나 Firebug, postman 등… 사실 REST가 가진 장점이라고 할 수 있지. 다른 웹서비스는 훨씬 더 어렵다고. #5. REST가 HTTP에 강력하게 묶일 필요는 없어. 다른 프로토콜을 섞어 쓸 수도 있고, 그게 안티 패턴도 아냐. 더 이상 나쁜 소리하기 싫으니까 조만간 내가 추천하는 걸 좀 찾아보길 바라. API-craft google group, Books: REST in practice, RESTful Web APIs, RESTful Webservices

  • &#128077; 트위터나 그런 10~20개 정도의 리소스를 매핑할 수 있는 아주 제한된 기능을 가진 사이트에서는 아주 훌륭한 이야기들이야. 그런데 이런 사이트들은 실제 비지니스 시스템에 비해 아주 원시적인 기능들만 제공할 뿐이야. 내가 10년이상 REST를 경험해봤는데, REST를 제대로 할 수 있는 시스템은 본적이 없어. 다른 사람들도 내가 하고있는 의료 분야처럼 복잡한 어플리케이션을 다루고 있어. 200개의 테이블이 있고, 150개의 트랜잭션을 위한 테이블이야 말할 것도 없고…REST를 사랑하시는 여러분이 내가 하고 있는 시스템에서 ‘적절한 REST’ 인터페이스를 정의해나가는 게 얼마나 걸릴지 궁금하군. 당연히 JSON RPC API가 유지보수하기에 훨씬 쉽고, 네가 REST로부터 멀어질 수록 더 단순하고 유지보수 비용이 줄게 되지. REST는 작은 서비스에서 쓰기 좋은 거라고.
    • &#128077; 브라우저에서 돌아가게 만든 ERP중에 REST로 구현할 놈은 하나도 없을 거라 생각해. 불행히도 많은 웹 개발자들은 ERP가 뭔지도 잘 몰라. 맨날 리소스, 확장성같은 거만 생가하지. 고객들은 빠른 응답과 브라우저에서 데스크탑 프로그램의 룩앤필을 갖는 것 등을 원하는데 말야. RESTful은 이걸 절대 이룰 수 없어. 요구사항이 적을 때나 가능한 일이라고.
      • &#128077; ERP에 대한 설명이 웃기네. 완전 맞는 말이야. 이해관계자들은 REST가 아닌 비지니스와 회계를 신경쓰지. 빠른 개발과 응답을 원하고. 아무도 백엔드가 어떻게 생겼는지 신경 안 써. 어마어마한 요구사항에는 SOAP이 더 낫지. 난 SOAP이 싫지만 의료업계의 회계같은 곳에선 더 간단하다고 볼 수 있지.

&#128077; 내가 본 대부분의 json api는 심지어 REST가 아냐. twitter의 ‘REST’ api를 봐. 물론 몇 개는 REST라고 할 수 있지만, 저건 RPC에 가깝지.
&#128078; 결국 넌 REST의 목적을 이해하지 못하기 때문에 REST가 나쁘다고 말하는 거구나.

  • &#128077; 아니, 난 REST를 시도때도 없이 사용하는 게 안 좋다는 거야. (곧 릴리즈될) Facebook Relay and Netflix Falcor도 - 전통적인 RESTful API를 벗어나는 것에 대한 - 같은 주제를 다루고 있지.

  • &#128078; xhr 클라이언트는 request를 위한 모든 환경을 제공하고 있어.

    • &#128077; 맞아. 그러나 그건 XHR뿐이지, 다른 영역에선 여전히 GET/POST만 제공하거든. 그리고 응답코드에 대해 지원하는 수준도 천차만별이야. 오래된 브라우저는 더 심하고. 게다가 네 client가 patch 메소드를 보낼 수 있다고 쳐도 서버가 그걸 구현했다는 의미는 아냐.
  • &#128078; 네가 이런 혼동에서 빠져나오기를 바란다

    • &#128077; 첫째, 난 혼란스럽지 않아. 둘째, 인신공격이 널 더 똑똑하게 만들어주지 않아.
      • &#128078; 인신공격이라고? 넌 혼동하는 게 맞아. 그건 팩트라고. 다른 코멘트도 모두 네 논리에 대한 결함을 이기 하고 있고. 넌 네가 이해하지 못하거나 구현할 수 없는 것에 대해 배척하는 것 뿐야.
      • &#128169; (역주) 이후 개싸움이라 생략…

&#128169; 2 Years later … GraphQL is rising. And GraphQL only use 200 and 500.
&#128077; API 제공자가 아닌 소비자로서의 나의 문제는, 어떻게 파라미터를 전달할 지, 서비스를 어떻게 노출할 지, 리턴 데이터가 어떻게 되어야 할지 일관된 방법이 없다는 것이다

  • &#128077; 기본적으로 HTML을 통한 REST는 form에서 PUT, POST 등을 지원하지 않기 때문에 완전하지 않다. 왜 RESTful하기 위해 굳이 form이 아닌 xhr을 써야하나? 삭제는 어떻게 해야하는거야? HTTP+HTML만으로는 실무에서 제대로 구현하는 게 불가능하지.

&#128078; 내 생각에 REST의 유일한 문제는 REST 자체가 아니라 그걸 쓰는 사람들의 문제야. 왜냐면 REST는 종종 HTTP와 혼동하거나 HTTP없이는 구현할 수 없다고 생각하거든. 너도 똑같은 실수를 하는 것 같다고 생각하는 이유는, 넌 HTTP의 상태코드나 동사에 대해 불만이 있다고 말을 하지만 그건 REST랑 상관이 없는 얘기거든. 그건 HTTP에 정의된 거라고. 물론 네가 REST를 HTTP를 통해 구현할 수 있기도 하고, REST를 구현한다는 서비스들이 99%는 http를 쓰고 그 속성을 상속받게 되긴 하지.

&#128078; 네가 지적한 건 뭐 하나 제대로 된 게 없어

  • 1 . REST는 API가 아니야. API를 디자인 하기 위한 패턴이야. 그것은 매우 엄격하게 또는보다 실용적인 방식으로 고수 될 수 있으며 현실 세계에서 구현하기 위한 모범 사례가 무엇인지에 대해 의견 차이가 있지.
  • 2 . XMLHttpRequest는 모든 CRUD문법을 지원하고 있어. 10년 정도를 넘지 않은 모든 메이저 브라우저는 REST를 완벽하게 지원해.
  • 3 . REST API는 도메인에 종속된 메시지 교환을 충실히 지원하는게 목적이 아니야. HTTP 레이어가 트랜잭션의 상태에 대한 정보를 갖고 있지. 즉, 일반적인 HTTP client와 프로토콜을 구현할 의무가 있는 서버에게 유용한 방식이야. 이 컴포넌트들이 도메인 특정의 어플리케이션이 도메인에 종속된 로직을 처리하도록 메시지 페이로드를 전달하고 있지. 이건 대부분의 메시지 교환 방식에서 사용하고 있어. 프로토콜에서 하나의 역할을 하는 시스템은 자신이 필요한 부분만 읽고 서브 시스템에게 넘겨주지. TCP/IP 위에서의 HTTP 응답을 예로 들면 IP packet과 TCP 헤더는 HTTP client가 읽기도 전에 TCP client에서 읽히고 처리되지. 단순히 다음 프레임으로 넘어가서 메시지를 해석할 수 있는 어플리케이션 코드에게 메시지를 전달하면 되는데, 왜 굳이 HTTP 프로토콜을 확장해야 하는거지? 모든 REST 원칙은 URL이나 HTTP 동사와 같은 일반적인 개념을 제공하는 HTTP 프로토콜을 이용하고, 이를 페이로드 안에서 재발명하지 않을 것을 권고하고 있어. 그렇게 함으로써 페이로드의 내용을 더 가볍고 간단하게 만들 수 있고, 대부분의 HTTP client가 공짜로 제공해주는 캐싱 같은 이점도 누릴 수 있지.
  • 네가 언급한 7가지 영역은 사실 모두 요청과 응답이라는 2개의 조각에 나뉘어 있어. 커스텀 JSON에 모두 묻어버릴 때와 같이 말이지. 다른 점이 있다면, 대부분의 개발자는 이미 이 형식에 익숙해서 HTTP 요청과 응답 안의 정확히 어디를 봐야할 지 안다는 사실이지. HTTP 인터렉션을 디버깅을 위한 (네 브라우저에도 있는) 훌륭한 툴을 모두 언급하지는 않을게.
  • 내가 이미 지적했지만, REST는 API설계를 위한 패턴이지 HTTP로 제한된 건 아니야. RESTful한 디자인 관점은 네 API를 대부분의 전송 프로토콜 안에서 깨끗하고 일관되고 성능도 좋고 캐시하기도 좋게 도와주지. HTTP에서 적용하기 쉬운 이유는 REST의 많은 원칙들이 HTTP로부터 영향을 받았기 때문이야.
  • 넌 프로토콜과 컨텐츠에 대해 혼란스러워하고 있어. 네가 말하고 있는 건, HTTP 프로토콜의 3가지 information 조각이야. 리소스 식별자, 명령어 동사, 응답 코드. 응답코드가 메시지 컨텐츠로 절대로 볼 수 없다고는 말할 순 없지만, 트랙잭션의 상태를 나타내는 표준 코드는 완전히 프로토콜의 일부야. 나머지 것들도 메시지 교환에서의 보편적인 개념이고 프로토콜 레이어 상에서 이를 명시하는 것은 아무런 해가 되지 않아.
  • &#128077; 여기에 블로그 글쓴이가 논점에 벗어난 댓글을 담
    • &#128078; 여기에 다른 이가 항의함
      • 네가 말하는 건 모두 DSL interface를 만들기 위해 REST의 원칙들을 왜곡하려는 것 같은 일이야. 난 개인 감정으로 네 주장에 대해 지적하는 게 아냐. 넌 너무 방어적이라고. 네가 지적한 기술적 문제는 REST에 대한 납득할 만한 비평이 아냐. 모두가 지적하는 부분에 대해 그저 동의하는 척 끝내버리고 있어. 넌 기술적인 원칙들과 네 안 좋은 경험들을 분리하는 데 문제가 있는 것 같아. REST가 의도하지 않은 방향으로 구현된 걸로 REST를 까지말고, 구체적으로 네가 생각하는 이상적인 기술적인 세부사항을 그려보지 그래? 네 블로그 글은 REST의 결함에 대한 이해는 되지 않고, 왜 그것이 종종 잘못 구현됨으로써 만능 해결책이 되지 않는지에 대한 내용 뿐이야.

&#128169; facebook이 GraphQL 내놓았어 링크

  • &#128169; 그럼 이것도 읽어보지 그래? 링크
    • 역주 : facebook의 글도 REST를 제대로 이해하고 쓴 건 아닌 것 같고, 이를 비판하는 글도 GraphQL을 제대로 이해하는 것 같지는 않음

댓글에서 언급된 추천 링크

로이 필딩이 이야기하는 HATEOAS에 관한 이야기

API Conf Panel: The Future of Media API

GOTO 2014 • REST: I don’t Think it Means What You Think it Does • Stefan Tilkov

JSON-RPC

Facebook의 GraphQL

Netflix의 Falcor

RESTful Web API 웹 API를 위한 모범 전략 가이드

Hypermedia Web discussion group

API Craft google group

Hypermedia Client Tutorial

From Zero to Hyper in 30 Minutes: Live Coding a Hypermedia Client

CODE COMPLETE 2

Your API isn’t RESTful — And That’s Good

연재 목록

  1. REST - 긴 여정의 시작
  2. REST - HTML Form에서 GET/POST만 지원하는 이유
  3. REST - 논문(요약) 훑어보기
  4. REST - REST 좋아하시네
  5. REST - Roy가 입을 열다
  6. REST - 당신이 만든 건 REST가 아니지만 괜찮아
  7. REST - 논문 읽기(To Do)

논문

REST의 사전적인 의미부터 알아보자.

‘바쁜 개발자들을 위한 REST 논문 요약’을 요약 + WhatIsRest.com + 지난 며칠간 블로그와 댓글을 뒤져가며 얻은 지식 + 내 생각을 적어보려고 한다.

REST 제약 조건

논문에서는 아무것도 없는 웹 스타일에서 시작해, 제약 조건을 하나씩 추가해 나가며 REST 스타일을 유도해 나가는 과정을 통해 REST를 설명한다.

  1. Starting with the Null Style
  2. Client-Server
  3. Stateless
  4. Cache
  5. Uniform Interface
  6. Layered System
  7. Code-On-Demand

각 제약 조건을 취하면서 발생하는 trade-off도 언급하고 있으나 여기서는 넘어가기로 한다.

Client-Server

보통 생각하는 서버-클라이언트를 의미.

여기서 의문이 들었던 게, Client-Server라는 게 과연 제약조건이라고 할 수 있는 건가? 그러니까, 제약 조건을 어기는 게 과연 가능한가?

이 제약조건은 클라이언트와 서버의 역할을 명확하게 구분한다 정도로 생각하면 될 것 같다.

서로의 관심사를 분리하여 각각의 로직의 독립적인 진화를 지원해야 한다(중요).

Stateless

모든 요청은 필요한 모든 정보를 담고 있어야한다.

요청 하나만 봐도 바로 뭔지 알 수 있으므로 가시성이 확보된다.

API 문외한 입장에서 가장 이해가 안 됐던 부분 중 하나였다. 인증을 위해 쿠키를 심고 세션에서 필요한 정보를 가져오는 경우, 이는 stateless인가 아닌가?

  • 정답 : Session 정보 활용한다면 stateless가 아니다

REST에서는 각각의 요청에 서버가 필요한 정보가 모두 담겨야 한다.

Cache

모든 서버 응답은 캐시 가능한 지 그렇지 않은 지 알 수 있어야 한다.

캐시를 고려한 설계가 필요하다.

Uniform Interface

구성요소(클라이언트, 서버 등) 사이의 인터페이스는 균일(uniform)해야한다.

  • 이것만으로는 무슨 말인지 모르겠다

WhatIsREST.com의 내용을 구글 번역으로 돌려보면

  • 소비자는 많은 소비자와 서비스에서 표준화 된 방법, 미디어 유형 및 공통 리소스 식별자 구문을 통해 서비스 기능에 액세스합니다라고 나오는데,
    미디어 유형이나 리소스 식별자 등을 의미하는 문법이 구성요소나 호환 시스템 간 동일해야 한다는 의미

균일한 인터페이스가 필요한 이유는

  • 서버 입장에선 어떤 클라이언트든 상관없이(내가 보내주는 것을 이해할 수 있을까 걱정없이) 표준에 의한 응답을 해줄 수 있고
  • 클라이언트 입장에선 hypertext를 통해 다음 상태로의 이동을 해야하는데, 표준화된 방식을 통해 해당 서버의 특성을 알 필요가 없어진다
  • 서로의 특성을 알지 못해도 커뮤니케이션이 가능할 수 있어야, 각각 독립적으로 진화할 수 있는 유연한 시스템이 만들어진다

REST는 네가지 Interface 제약조건으로 정의된다

네가지 Interface 제약조건에서 언급된 내용을 구글 번역으로 좀 더 상세히 살펴보자.

  • 이 자원의 추상적 정의는 웹 아키텍처의 핵심 기능을 가능하게 합니다
  • 유형이나 구현에 따라 인위적으로 구별하지 않고 많은 정보 소스를 포괄함으로써 보편성을 제공합니다
  • 표현에 대한 참조의 늦은 바인딩을 허용하여 요청의 특성에 따라 내용 협상을 수행 할 수 있습니다
  • 저자는 개념의 단수 표현보다는 개념을 참조 할 수 있으므로 표현이 바뀔 때마다 기존의 모든 링크를 변경할 필요가 없습니다

1. identification of resources

리소스를 식별하는 방법이 동일해야 한다. 우리는 보통 URI를 쓰기 때문에 어쩐지 당연한 말처럼 느껴진다.

2. manipulation of resources through representation

representation의 개념은 이응준님 블로그에서 다루고 있다.

리소스의 표현계층(representation)을 리소스의 식별자(URL)로부터 분리한 것은 REST에서 아주 주요한 관점이다.

RESTful 응용 프로그램은 동일한 URI에서 동일한 자원의 표현을 둘 이상 지원할 수 있다.

3. self-descriptive messages

요청이나 응답 메시지에는 이를 이해하기 위한 모든 정보가 포함되어야 한다.

4. hypermedia as the engine of application state

줄여서 HATEOAS라고도 하지만 REST를 사랑하는 이들은 종종 이렇게 줄여부르는 걸 싫어하기도 한다(똑똑하고 재수없게 보이려면 ‘HATEOAS라고 줄여 부르는 건 바보같다’라고 말하면 된다).

훗날 블로그에서 Roy는 이 부분을 가장 강조하는데, 어쩌면 사람들이 가장 안 지키고 있기 때문일 지도 모르다는 생각을 했다.

그리고 이를 언급할 때 주로 hypertext라는 표현을 사용한다.

논문에서 hypermedia라고 한 것은 단지 text가 아닌 매체를 고려했기 때문이고,

일반적인 API에서는 거의 hypertext라고 표현하면 된다.

음? 우리가 HTML 페이지에서 맨날 보는 그 hypertext라는 용어가 맞나?

맞다. 제시된 hypertext 위에서 application 상태를 변경하는 주체가 client가 되어야 한다는 게 핵심이다.

조대협님의 블로그에는 HTTP Response에 다음 Action이나 관계되는 리소스에 대한 HTTP Link를 함께 리턴하는 것이라고 언급이 되어 있다.

  • 뭔가…이상한데…틀렸다고 하기는 뭣 하지만 온전히 이해가 되는 문장은 또 아니다. 다른 사람의 의견을 더 들어보자

What HATEOAS actually means

  • 다들 혼란스러워하는 것 같은데 직접 로이 필딩에게 물어보지 그래?
  • 로이의 문장은 명확하고, 모호하지도 않으니 그냥 읽어보길 바란다
  • 로이가 HTTP 표준의 기여자라는 것을 잘 생각해보자
  • 이 논문의 주제는 WWW가 잘 동작하고 확장성도 좋고, REST가 WWW 아키텍쳐 위에서 동작한다면, 이 역시 잘 동작하고 확장성이 좋다는 것이다
  • 4가지의 제약 조건은 결국 URI를 사용해서 MIME-typed 문서를 GET/PUT 등으로 전송하는 것 + HATEOAS
  • WWW := URI + HTTP + MIME + Hypermedia
  • Hypermedia라는 건 hypertext를 확장한 개념
  • 좀 더 정확하게 쓰자면, HATEOAS := hypermedia documents as the state of state
  • 로이의 배경을 생각하면 그는, 하이퍼링크가 없는 HTML 도큐먼트를 상상조차 못할 인물이다
  • 그가 언급한 Hypermedia란 문서 자체를 의미한다
  • 로이는 HTTP통신에 Link header를 넣어도 여전히 RESTful 할 수 있지만, 합당한 이유가 있어야 한다고 했다
    • 역주) 헤더에 넣으면 이를 처리할 수 없는 클라이언트도 있을 수 있기 때문에..라고 했는데 당시에는 LINK가 정식 표준이 아닌 때였기 때문인 것 같음
  • HATEOAS를 정확히 구현한 것은 문서안에 있는 링크다
  • HATEOAS라는 다음 상태 변경을 위한 링크(form 링크 등)를 제공해야 한다는 것

이 문서도 좀 제멋대로 해석한 부분이 있는데, Roy는 단지 HTTP만을 염두에 둔 게 아니다. HTTP이거나 HTML 문서이거나 그런 게 중요한 게 아니다.
HATEOAS라는 걸 응답에 다음 액션에 관한 링크를 전달하는 것 정도로 생각하면 안 된다.

  • 응답에 링크를 주렁주렁 달고 다니면 HATEOAS가 되는가? NO!
  • 서버는 Hypermedia를 통해 다음 액션에 대한 선택지를 클라이언트에게 줘야한다
  • 클라이언트는 서버의 독자적인 진화에 영향을 받아선 안 되며, 서버 상의 구현에 의존하면 안 된다

결국 HATETOAS의 목적은 (서버-클라이언트 간 의존성을 분리해야만 가능한) 독자적인 진화와 확장을 보조하는 것이며, hypermedia는 그 목적을 이루는 데 기여해야 한다

Layered System

클라이언트든 서버든 미들웨어 구성요소를 추가할 수 있는 구조.

하지만 Client-Server 사이에선 그 구성요소가 추가되는지, 다른 서비스와 추가로 통신하는 지 여부는 관심없다.

서버와 클라이언트 간 상호 작용을 일관성있게 유지해야 한다.

Code-On-Demand (Optional)

서버가 네트워크를 통해 클라이언트에 프로그램을 전달하면 그 프로그램이 클라이언트에서 실행될 수 있어야한다. (Java applet이나 Javascript 같은 것을 말함).

다만 이 제약조건은 필수는 아니다.

thanks to

로이 필딩의 논문

이응준님의 블로그에서 많은 영감을 얻음

WhatIsREST.com

네가지 제약조건에 대한 짧은 설명

HATEOAS가 진짜로 의미하는 것

연재 목록

  1. REST - 긴 여정의 시작
  2. REST - HTML Form에서 GET/POST만 지원하는 이유
  3. REST - 논문(요약) 훑어보기
  4. REST - REST 좋아하시네
  5. REST - Roy가 입을 열다
  6. REST - 당신이 만든 건 REST가 아니지만 괜찮아
  7. REST - 논문 읽기(To Do)

HTML Form에서 GET/POST만 지원하는 이유

쉬어가는 마음으로 다음 장을 위한 사전 지식을 하나 쌓아보자.

왜 PUT과 DELETE를 지원하지 않는가에 대한 정리

  • 일본어다. 구글 번역기를 돌려보자

PUT/DELETE를 Form에 넣으려는 시도는 위 글에서 가장 자세히 나오긴 했다.

(구글 번역이 훌륭하긴 해도) 일본어라 원본을 알아볼 수가 없으므로,
대신 stackexchange에 올라온 글로 짧게 정리해봤다.

  • 초기 HTML5 draft에는 포함되어 있었고 Firefox의 베타 버전에서는 잠시 구현하기도 했었다.
  • W3C는 이 문제를 bug report 10671에서 논의를 했는데,
  • Mike Amundsen이란 사람이 언급하길
    • 리소스를 수정하기 위해 PUT과 DELETE를 실행하는 건 모던 브라우저의 XmlHttpRequest 객체를 사용하면 간단한데, 스크립트를 사용하지 않는 상황에선 쉽지 않다.
    • 이 패턴은 많은 일반적인 웹 프레임웍/라이브러리에서 기본으로 구현하고 있다.
    • (그리고 이를 우회하는 방식에 대한 문제를 언급함)
  • Tom Wardrop란 사람도 흥미로운 관점을 제시함
    • HTML은 HTTP와 떼려야 뗄 수 없는 관계다. HTML은 인간을 위한 HTTP 인터페이스다. 따라서 왜 HTTP에서 필요한 메소드를 모두 제공하지 않는 것인가에 대해 자동으로 의문을 가질 수 밖에 없다. 기계는 PUT과 DELETE를 할 수 있으면서 인간은 왜 안 되는가?
    • HTML이 시맨틱한 마크 업을 보장하는 데 많은 시간을 할애하면서, 시맨틱한 HTTP 요청을 보장하기 위한 노력을 하지 않았다는 것은 모순이다.
  • 결국 이 버그는 (HTML editor인) Ian Hickson에 의해 고치지 않는 것으로 결정났다.
    • “PUT을 form 메소드로 사용한다는 것은 말도 안 된다. 네가 form payload를 통해 PUT을 해야할 일은 없을 것이다. DELETE도 payload가 없어야만 말이 된다. 그러니 역시 form에서 제공할 이유가 없다.”
  • 하지만 포기하지 않고 추가 드래프트가 올라와 있다

일본어로 정리된 글에서도 왜 PUT과 DELETE가 Form에서 적합하지 않은지는 생각하진 않은 것 같다.

추가된 드래프트도 결국 애초의 HTML Form이 가진 기능/목적을 우회하여 Form을 활용한 PUT/DELETE를 실행할 수 있도록 Form의 기능 확장(extension)을 요구하고 있다.
여기에서 DELETE는 크게 이질감이 없지만, PUT의 경우 payload를 통해 헤더를 구성하는 방식을 제안하고 있다. 그 방식은 꽤나 어색해서 과연 HTML 에디터나 커뮤니티 차원에서 받아들일 수 있을지는 미지수이다.

이슈를 닫아버린 Ian Hickson 입장에선 Form에서 PUT/DELETE를 지원하는 게 너무 당연하게 말도 안 되는 일이라고 생각했을 것 같다. 이유는,

  • Form은 서버에 정보를 제출하기 위해 존재한다
  • GET과 POST는 form에 존재하는 대화형 컨트롤에 입력된 값을 보낸다
  • POST와 PUT의 가장 큰 차이
    • RFC2616에서는 ‘요청 URI의 다른 의미’라는 표현을 썼고
    • RFC 7231에서는 ‘동봉된 representation에 대한 다른 의도’라는 표현을 썼다
    • POST에서의 요청 대상 리소스는 동봉된 representation을 ‘처리하는’ 주체이고
    • PUT에서는 동봉된 representation은 대상 리소스의 상태를 대체한다. 즉, 리소스 자체가 되어야 한다
  • 따라서
    • GET은 form에 있는 정보를 줄테니 나에게 리소스(representation)를 줘
    • POST는 form에 있는 정보를 줄테니 이걸로 처리 좀 해줘
  • 라는 의미가 되지만,
    • DELETE에서 URI는 리소스를 정확하게 식별하고 요청을 보내기 때문에 form을 통해 값을 전달할 게 없으며
      • 권한을 획득하기 위해선 header를 이용해야할 것 같다
    • PUT도 전송해야할 것은 대상 리소스를 대체할 representation 그 자체인데, 대상 리소스더러 조각난 form 데이터를 처리하라고 보낼 수는 없는 노릇

연재 목록

  1. REST - 긴 여정의 시작
  2. REST - HTML Form에서 GET/POST만 지원하는 이유
  3. REST - 논문(요약) 훑어보기
  4. REST - REST 좋아하시네
  5. REST - Roy가 입을 열다
  6. REST - 당신이 만든 건 REST가 아니지만 괜찮아
  7. REST - 논문 읽기(To Do)

Representational State Transfer

바쁜 개발자들을 위한 REST 논문 요약

REST는 Representational State Transfer의 줄임말로, 웹을 위한 네트워크 기반 아키텍처 스타일이다. REST는 Roy T. Fielding이 그의 박사학위 논문 “Architectural Styles and the Design of Network-based Software Architectures” 에서 처음 소개하였다.

아하.

가장 중요한, Roy의 실제 논문을 아직 읽지 못했다. 논문의 대략적인 내용은 위에 링크를 걸어둔 이응준님의 블로그로부터 이해를 했다.

REST와 HTTP를 이해하기 위해 필수적인 representation의 개념도 그 분의 블로그에서 얻을 수 있으니 미리 보고 올 것을 권한다.

  • representation이란 용어를 단순히 ‘표현’이라고만 번역하기에는 뭔가 놓치는 의미가 있는 것 같아서, 일단은 계속 representation이라고 부르겠다

몇 가지 이해 안 되던 부분은 실제 논문으로부터 갈증을 해소하기도 했지만,

논문 전체를 자세히 해석해 나가기에는 지금 좀 지쳤으니 (언젠가) 후속으로 정리를 해야겠다.

지금 시점에는 내가 RESTful하게 설계할 일이 없을 것 같긴 하지만,

웹을 이해하기에는 꽤 좋은 관점이 들어있(다고 추측이 된)다.

REST 아키텍처 스타일

바쁜 개발자들을 위한 REST 논문 요약을 읽기에도 바쁜 개발자들을 위해 요약하자면,

논문에서 Roy는 소프트웨어 아키텍처, 네트워크 기반 어플리케이션 아키텍처, 네트워크 기반 아키텍처 스타일 등을 설명하고,

네트워크 기반 아키텍처의 요구사항, 해결해야할 문제 등을 제시하는데 이에 대한 해결책으로 REST 아키텍처 스타일을 제안한다.

REST는 소프트웨어 아키텍처가 아닌 아키텍처 스타일(architectural style)이라고 정의한다.

후속으로 올릴 REST에 대한 논쟁에서도 REST는 아키텍처 스타일이라고 여러번 강조한다.

많은 IT 용어가 건축에서 왔는데(건축 뿐이겠는가..), 영어권에서는 동일한 단어를 사용하는 서로 다른 용어(동음이의)가 우리말로 번역되면서 각각 다른 단어로 표현되곤 한다.

따라서 어떤 IT 용어가 가진, 원래의 단어에서 오는 뉘앙스나 메타포를 놓치는 경우가 많아 비영어권 국민으로서 참 아쉽다.

많은 REST 논쟁에서 REST를 이해 못하는 사람들이 좀 더 구체적으로 예시를 들어줄 것을 요구하면, 사람들은 REST가 아키텍처 스타일이라고 강조하는 경우가 많았던 것 같다.

아키텍처 스타일(architectural style)은 건축양식을 의미한다.

중세에 고딕양식이 유행을 했다는 말은 고딕양식에 기반해 건물을 설계하고 이 설계를 구현한 건물이 많았다는 말이고(내가 건축 양식의 문외한임은 미리 고백한다),

고딕 성당을 지으려는 설계자는 이 양식을 지키는 선에서 ‘알아서’ 설계를 해야한다.

시장은 설계나 세부 구현에 대한 이해없이 고딕 양식이라는 틀 안에서 요구사항을 제시할 것이고 그러다보면 설계에서도, 구현에서도 best practice가 퍼지게 되겠지.

수십개의 성당을 지어야 하는 작업 반장의 커뮤니티에서는 이 세부 구현이 곧 고딕 양식이다라고 인식될 수 있다. 그가 얼마나 뛰어난 작업 반장인지와는 상관없이.

물론 이런 식의 건축 양식이 시간을 두고 확립되었다면, REST는 태초에 명확한 제약사항이 존재했다는 차이가 있긴 하다.

기와집이라는 건축양식이 있다고 할 때, 청기와 주유소는 과연 기와집인가? (기와는 맞는데 집이 아닌가? 흠..)

아니라면 기와집이라는 건축양식을 현대에는 의미를 확장해야할 필요가 있지 않은가?

어쩌면 사회적 합의를 봐야할 지도 모르겠다.

REST처럼 작성자가 확실하며, 애초에 확실한 제약 사항이 있는 분야도 합의의 대상인가?


REST에 관해서는 온갖 블로그와 구현체에서 서로 다르게 해석하고 있다.

작성자의 역량 + 독자의 역량이 오해를 재생산하기도 한다.

그러니 지금 이 정리를 본 누군가도 역시 오해를 얻어가리라 믿는다. 나 때문이든 당신 때문이든.

우선 REST에 대해 알아보자.

API Design

생각해보니 내가 API를 제대로 만들어 본 적이 없었더랬다.

GraphQL이 재미있어 보이니 써보고는 싶은데,

사실 난 인증을 붙인 프로덕션 레벨의 코드를 만들어 본 적이 없다.

왜 좋은가를 설명하려면 널리 퍼진 아키텍처 스타일을 기반으로 설명하면 좋을텐데,

나는 REST는 설명할 수 있던가? HTTP는?

나는 정확히 설명할 수 없다.

그럼 해야지.

지금까지는 지식 습득의 드라이브를 발표를 통해 얻었다면, 이제 블로그에 정리하면서 익혀보려고 한다.

What to do

‘정확히 알기’ 그 첫번째 과정으로 API 디자인에 도전해본다.

이 시리즈가 끝날 즈음이면 나는

  • API 서버를 구축해서 인증을 붙여본다
  • 전통적인 RESTful API와 GraphQL 서버 모두 구현
  • REST에 대해 제대로 설명하게 된다
  • 여러 인증 과정을 경험해본다
  • HTTP에 대해 설명을 할 수 있게 된다

동시에

  • HTTP 완벽가이드 릴레이 세미나
    • HTTP 완벽가이드로 적당히 완벽히 알기(금새 또 적당히 적당히 하려는 버릇이..)
    • 여기에 몇가지 간단한 책(그림으로 배우는 시리즈)도 참고로 읽기

And how?

검색과 블로그와 책으로부터 얻은 지식을 에버노트에 거칠게 정리한 다음 블로그에 옮겨 담기로..

내가 은연 중 피하려고 했던 것들

기술 블로그와 신세 한탄이 섞여있겠지만,

익숙하지 않은 것을 시도해보기로 했다.

그리고 2017.05.18 오늘

오랜만에 제창된 임을 위한 행진곡

RIP. Chris Cornell

0%