REST - 당신이 만든 건 REST가 아니지만 괜찮아

연재 목록

  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을 이 관점에서 바라보면 재미있을 것 같다

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