"생각의 웹"입니다.
구글에서 Open API 설계를 담당하고 있는 샘 고토의 블로그 중 소개하고 싶은 포스트가 있어 아래와 같이 번역하여 공유합니다. 이 연구의 결과가 기계와 사람 간에 스마트한 웹 세상을 만들어 내고 IoT가 꿈꾸는 스마트한 세상을 만드는 데 도움이 되리라고 생각합니다.
의역으로 인해 오역의 가능성이 있으니 더 많은 내용이 궁금하시면 아래 원문 글을 참고하시기 바랍니다. ;-)
http://blog.sgo.to/2014/02/rows.html
리소스 지향 웹 서비스 (ROWS)
리소스 지향 웹 서비스 (ROWS)란 리소스들에 대한 프로그램적인 검색, 기술(description) 그리고 호출이 자동으로 수행 되도록 하는 일련의 기술입니다.
이는 휴먼 웹과 프로그래머블 웹을 연결해 주고 웹이 어떻게 동작하는지에 - URL들과 REST라는 일관된(Uniform) 인터페이스. 여기서 URL의 R이 핵심 영역입니다.- 보다 적합하게 설계된 서비스 지향 아키텍쳐의 대안 중 하나입니다.
이 포스트는 연재 글 중에 하나이며 읽기 전에 이 글과 이 글을 읽어 보길 권합니다.
이 글은 이 기술 명세를 보다 읽기 쉽도록 하기 위해 쓰였습니다.
우리가 다시 풀고자 하는 문제는?
우리는 아직까지 다음 기술들을 도출하지 못했습니다:
- 각각 별개의 서비스에 대한 리소스 설계, 표현(representation) 포맷 그리고 리소스 간의 링크를 클라이언트에게 알려줄 수 있는 보편적인 프레임 워크
- RESTful 과 하이브리드 서비스들의 다양성을 기술할 수 있는 어휘를 가진 언어. 이 언어로 작성된 문서는 보편적인(generic) 웹 서비스 클라이언트 코드를 커스텀하게 작성된 포장개체(wrapper)처럼 동작하도록 생성할 수 있습니다. 보다 자세하게 말하면 우리는 클라이언트에게 다음과 같은 것들을 알려주어야 합니다:
- 수행 가능한 동작의 의미(semantic)가 존재하는가?
- 어떤 HTTP 메소드(method)가 사용되는가?
- 요청의 형식(entity-body)는 어떻게 구성되었는가?
- 호출 후 응답되기를 기대하는 것은 무엇인가?
어떻게 시작하는지부터 살펴보자
휴먼 웹은 브라우저를 이용해 URL이라는 리소스를 GET 요청함으로써 시작됩니다. 만일 콜 택시를 요청하기 위해서 필요한 사례를 들어 설명해보죠.
아래는 브라우저 내부에서 일어나고 있는 일을 보여 줍니다.
GET /mountain-view HTTP/1.1
Host: www.yellowcab.com
그러면 서버가 다음과 같이 응답하도록 구현할 수 있습니다:
HTTP/1.1 200 OK
Content-Type: text/html
- <html>
- <body>
-
- <span>Welcome to Yellow Cab Mountain View!</span>
-
- <a href="/moutain-view/reservations">
- Click here to book a cab!
- </a>
-
- </body>
- </html>
지금 정도로도 사람에겐 충분한 정보겠지만 컴퓨터가 이 웹 페이지 내용과 개구리에 관련된 웹 페이지와의 구분할 수 없습니다.
JSON-LD, microdata와 Schema.Org 삽입하기
컴퓨터가 이 페이지를 이해하도록 만드는 첫번째 과정은 이 페이지가 무엇인지에 대해 명확히 알려 주는 겁니다.
HTML 내에서 연결 데이터(linked-data)를 전송하기 위한 몇 가지 방안이 있는데 제가 선호하는 방식은 JSON-LD와 microdata*입니다.
* 제 생각엔 JSON-LD가 대용량의 복잡한 사례들에 대응하기 위한 보다 확장성 있는 방식이라고 보는 반면 microdata는 예제가 쉬워 쉽게 이해할 수 있습니다. 그래서 전 여기에서는 microdata를 사용할거지만 실제로는 JSON-LD를 훨씬 선호한다는 사실을 알아주셨으면 합니다.
안타깝게도 microdata나 JSON-LD 만으로는 충분치 않습니다. 택시 정거장에 대한 컴퓨터 처리가 가능한 기술이 필요하기 때문이죠. 이것을 schema.org에서 가져올 수 있습니다: shema.org는 세상에서 컴퓨터가 이해할 수 있는 방식으로 사물을 기술하는 어휘들을 제공하는 사이트입니다.
아래는 이 웹 페이지의 모습을 보여줍니다:
HTTP/1.1 200 OK
Content-Type: text/html
- <html>
- <body itemscope
- itemid="/mountain-view"
- itemtype="http://schema.org/TaxiStand" >
-
- <span itemprop="description">
- Welcome to Yellow Cab Mountain View!
- </span>
-
- <a itemprop="reservations"
- href="/mountain-view/reservations"
- itemscope itemtype="http://schema.org/ItemList">
- Click here to book a cab!
- </a>
-
- </body>
- </html>
이제 제가 서두에서 필요로 하다고 제시한 기술들을 리마인드 해보죠. 이 JSON-LD / micro-data + schema.org 를 통해 컴퓨터가 이해할 수 있는 방식으로 사물을 기술하기 위한 보편적인 프레임워크를 제공할 수 있습니다.
그 결과 컴퓨터는 이제 다음을 이해할 수 있습니다:
- 이 리소스는 TaxiStand (택시 승차장) 입니다.
- 이 택시 승차장은 기술(description)을 가지고 있습니다.
- 이 택시 승차장은 예약 아이템 리스트 (ItemList of reservations)를 가지고 있습니다.
그러나 이것으로 할 수 있는 것을 이해하기에는 턱없이 부족합니다.
프로그래머블 웹으로의 연결
API에 존재하는 프로그래머블 웹을 컴퓨터가 발견하도록 하기 어렵습니다.
그래서 이 특정 택시 승차장을 택시 (yellow cab) API에서 찾을 수 있도록 링크를 추가해 보겠습니다:
- <body
itemscope
- itemid="/mountain-view"
- itemtype="http://schema.org/TaxiStand" >
- <meta itemprop="alternate" itemscope
- itemtype="http://schema.org/ApiUrl"
- content="http://api.yellowcab.com/moutain-view"/>
이로서 이 택시 서비스가 특정 API에 연결되어 있음을 컴퓨터가 알 수 있게 되었습니다.
만일 저 URL에 GET 요청을 하게 되면 다음과 같은 일이 일어나게 됩니다:
GET /mountain-view HTTP/1.1
Host: api.yellowcab.com
그리고 서버는 다음과 같이 응답합니다:
HTTP/1.1 200 OK
Content-Type: application/json+ld
- {
- "@context": "http://schema.org",
- "@type": "TaxiStand",
- "@id": "/mountain-view",
- "description": "Welcome to Yellow Cab!",
- "reservations": {
- "@type": "ItemList",
- "@id": "/mountain-view/reservations",
- }
- }
이 응답은 컴퓨터가 이해할 수 있는 형식에 훨씬 가깝습니다.
Content-Type 헤더가 있어 컴퓨터가 어떻게 이것을 분석(parse)할 수 있는지 알려주고
또한 내부의 하이퍼미디어가 이 리소스를 컴퓨터가 이해할 수 있는 정보
(machine-readable information)를 제공하기 때문입니다.
하지만, 컴퓨터가 이 리소스들로 무엇을 할 수 있을지 알려 줄수도 있을까요?
만일 예약을 하고 싶다면 HTTP로 어떻게 해야 하지?
HTTP에서 API 검색 기법에 가장 가까운 방식은 OPTIONS 요청을 보내는 것입니다.
OPTIONS /mountain-view/reservations HTTP/1.1
Host: api.yellowcab.com
그러면 다음과 같이 응답할 수 있겠죠:
HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, POST
POST 요청은 많은 일들을 할 수 있도록 설계되었기에 "RESTful Web API" 책의 저자들
(리처드슨, 샘 그리고 마이크, 이후 LSM으로 명기)이 앞서 지적했던 것처럼
이로써는 충분하지 않습니다. 어떻게 이런 것들을 알 수 있을까요:
- POST 요청의 내용(entity-body)은 무엇을 채워야 하는가?
- POST 요청이 중첩된 POST 요청(예를 들어 RPC-형태의 POST)인지을 어떻게
구분할 수 있을까?
액션 (Actions)를 추가하기
액션은 리소스로 무엇을 할 수 있는지 알려주는 어휘입니다. 이것은 추가 요청을 하는 것이 아닌 리소스에 추가 삽입(attached inline)하는 형태입니다.
이것은 중요한 세 가지의 기법으로 구성됩니다:
- 사물에 액션을 연결하는 기법
- 액션을 분류하고 요청을 강제하기 위한 잘 정의된 의미(semantics)
- 매개 변수가 어떻게 생겼는지를 기술하는 어휘
JSON-LD 응답이 어떻게 생겼는지 보시죠:
HTTP/1.1 200 OK
Content-Type: application/json+ld
- {
- "@context": "http://schema.org",
- "@type": "TaxiStand",
- "@id": "/mountain-view",
- "description": "Welcome to Yellow Cab!",
- "reservations": {
- "@type": "ItemList",
- "@id": "/mountain-view/reservations",
- "operation": {
- "@type": "CreateAction",
- "expects": {
- "@type": "SupportedClass",
- "subClassOf": "http://schema.org/TaxiReservation",
- }
- }
- }
- }
이 정보와 동일한 정보를 HTML 마크업에서도 찾을 수 있습니다:
- <a itemprop="reservations"
- href="/mountain-view/reservations"
- itemscope itemtype="http://schema.org/ItemList">
- <meta itemprop="alternate" itemscope
- itemtype="http://schema.org/ApiAppUrl"
- content="http://api.yellowcab.com/moutain-view/reservations" />
- <div itemprop="operation" itemscope
- itemtype="http://schema.org/CreateAction"/>
- <meta itemprop="expects" itemscope
- itemtype="http://schema.org/SupportedClass"
- content="http://schema.org/TaxiReservation"/>
- </div>
- </div>
- Click here to book a cab!
- </a>
* 이 형태가 꽤나 복잡해 보인다는 점 인정합니다만 이후에 이걸 더 요약하는 법 또한
소개하도록 하겠습니다. 힌트는 링크드 데이터와 연관되어 있습니다.
이제 컴퓨터가 이해하는 데 필요한 *모든 것*을 알려줄 수 있습니다:
- 이것은 택시 승차장입니다.
- 여기에 API 시작 위치가 있습니다.
http://api.yellowcab.com/moutain-view/
- 이 택시 승차장은 예약 아이템 리스트를 가지고 있습니다.
- 이 예약 아이템 리스트에는 *매우* 상세한 의미(semantics)를 갖는 - 이 요청이 갖는
의미 뿐 아니라 세부 상세 내용까지 포함된 - 액션 생성(CreateAction)이라는 동작을
갖습니다. 예를 들어 이렇게 정의된 동작은 엄격한 POST 요청이 되겠지요.
- 예약을 하기 위해서는 택시 예약 객체(TaxiReservation)를 전달합니다.
즉, 이 정보를 통해 컴퓨터는 다음과 같은 요청을 보낼 수 있게 됩니다:
POST /mountain-view/reservations HTTP/1.1
Host: api.yellowcab.com
Content-Type:application/json+d;charset=utf-8
Content-Length:207
- {
- "@context": "http://schema.org/",
- "@type": "TaxiReservation",
- "pickUpLocation":
- "1600 Amphitheatre Parkway, Mountain View, CA",
- "pickUpTime": "2pm",
- "numberOfPassengers": "1"
- }
그러면 서버는 다음과 같이 응답을 하게 되겠지요:
HTTP/1.1 201 Created
Location:
http://api.yellowcab.com/moutain-view/reservations/32523325225
이렇게 다시 하이퍼 미디어가 도래합니다.
하이퍼미디어는 정말 엄청나게 위력있는 개념입니다. 이 개념을 통해 하나의 리소스에서 다음 리소스 링크로 이동할 수 있게 됩니다. 정말 대단한 거죠.
방금 하나의 리소스를 생성했다면 이걸로 해볼 수 있는 최선을 한번 고려해 봅시다:
OPTIONS /mountain-view/reservations/32523325225 HTTP/1.1
Host: api.yellowcab.com
그럼 서버는 이렇게 응답할 수 있겠죠:
HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, PATCH
Accept-Patch: application/json+ld
이 응답의 PATCH HTTP 메소드로 인해 이 리소소는 변경되기 쉬운 (mutable) 리소스라는 걸 알 수 있다는 것이 꽤 흥미로운 점이죠. 또한 이 리소스는 추가로 JSON-LD 패치 문서를 가지고 있다고 알려 주기때문에 이 역시 꽤 유익한 정보가 될 겁니다.
그러나, POST 요청이 그렇듯 PATCH 동작의 어플리케이션 의미(semantics)가 무엇인지
충분히 알 수는 없습니다. 예를 들어 "패치"가 pick up 시간을 업데이트하는 것인지 아니면
location을 전달(drop off) 하는지 말이죠.
액션으로 되돌아 가보죠. 이 리소스를 GET 요청합시다:
GET /mountain-view/reservations/32523325225 HTTP/1.1
Host: api.yellowcab.com
그러면 서버는 이렇게 응답해 줄 수 있습니다:
HTTP/1.1 200 OK
Content-Type: application/json+ld
Accept: application/json+ld
- {
- "@context": "http://schema.org",
- "@type": "TaxiReservation",
- "@id": "/mountain-view/reservations/32523325225",
- "reservationStatus": "CONFIRMED",
- "operation": {
- "@type": "CancelAction"
- }
- }
여기에
취소(CancelAction) - "미래의 사건이나 행위가 더 이상 일어나지 않도록 하는 행위" - 라는 매우 명확한 어플리케이션 의미를 가지고 있고 이 리소스를 "취소" 하는 의미가 매우 명확히 정의(HTTP 용어에서는 "취소"의 정의에 관련된 것은 PATCH 요청입니다.)되어 있으며 따라서 컴퓨터는 다음과 같이 PATCH 요청을 보낼 수 있기 때문입니다:
PATCH /mountain-view/reservations/32523325225 HTTP/1.1
Host: api.yellowcab.com
Content-Type:application/json+d;charset=utf-8
Content-Length:100
Accept: application/json+ld
- {
- "@context": "http://schema.org/",
- "@type": "CancelAction"
- }
이제 취소된 예약 상태를 알 수 있습니다:
HTTP/1.1 200 OK
Content-Type: application/json+ld
- {
- "@context": "http://schema.org",
- "@type": "TaxiReservation",
- "@id": "/mountain-view/reservations/32523325225",
- "reservationStatus": "CANCELLED",
- }
마지막으로 덧붙이면 취소를 완료하게 되면 취소된 예약에는 적용되지 않기 때문에 "취소" 요청의 내용은 사라지게 됩니다.
남은 숙제들...
"악마는 디테일에 있다"는 말처럼 아직 모음(collections), 인증(authentication), 상이한 전송 기법 - 예를 들면 모바일 앱, 이메일 메세지 - 등이 어떻게 되어야 하는지는 숙제로 남겨져 있습니다. 이 하나 하나 모두 풀기에 매우 까다로운 것들이기 때문이죠.
추후 올릴 포스팅에도 계속적으로 관심 가져 주시기를 바랍니다. 감사합니다.