REST - Roy가 입을 열다
연재 목록
- REST - 긴 여정의 시작
- REST - HTML Form에서 GET/POST만 지원하는 이유
- REST - 논문(요약) 훑어보기
- REST - REST 좋아하시네
- REST - Roy가 입을 열다
- REST - 당신이 만든 건 REST가 아니지만 괜찮아
- 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의 부족한 사용성을 보완할 수 있게하겠다..고 했다.
- Tim Bray란 사람이 Sun Cloud용 API를 공개했을 때, 어떤 이가 왜 새로운 VM을 띄우는 데 POST를 사용하냐는 질문이 있었고 이에 대한 설명을 블로그로 남겼다.
REST APIs must be hypertext-driven
그 유명한 “REST APIs must be hypertext-driven“(REST API는 hypertext가 주도해야만 해)를 읽어보자.
요점 정리
난 사람들이 HTTP기반의 인터페이스를 REST API라고 부르는 것이 실망스럽다. “SocialSite REST API”라고 부르는, 그건 RPC다
- 현재 링크는 사라졌으나, 결국 https://www.w3.org/blog/2014/12/opensocial-foundation-moves-standards-work-to-w3c-social-web-activity/ 여기를 의미함
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 구조를 무너뜨리지 마라
- 추가 리소스를 통해 진화하라
- 오래된 양식을 지원하라
- Client
- 어떻게 hypermedia-enable할 수 있는가?
- Step 1. service document
- 서비스/페이지에서 필요한 링크를 모아놓은 문서
- JSON Home
- 이런 문서도 표준화가 진행중
- application/json-home
- Step 2. resource links
- 애초에 리소스 자체에 필요할 수 있는 링크를 추가하기
- Step 3. state transition links
- 어플리케이션의 상태에 따라 필요한 링크를 정해주기
- Step 1. service document
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는 부수효과로 누릴수도 있고
이번에도 별로 대단한 걸 얻은 것 같진 않다면 바로 결론으로 넘어가자.