영화다운위젯을 블로그에 설치하고, 위젯을 통해 다운로드가 되면 금액을 적립해 준다고 한다.
두가지 방식이 있는데 하나는. HTML방식, 두번째는 링크방식이다.
첫번째
|
두번째 : '국가대표 완결판'무비위젯
|
두번째 : '국가대표 완결판'무비위젯
Web Service는 몇년간 이 세계를 구원할 기술로 각광을 받아왔다. 웹서비스하면 주위의 누구에게라도 물어보면 SOAP을 먼저 이야기할 것이다. 그런데 최신기술의 총아였던 SOAP이 공격받기 시작했다. 그리고 REST가 화두에 올랐다.
실제 구글에서는 더 이상 SOAP을 이용한 서비스를 지원하지 않는다고 하고(물론 기존 서비스는 유지하겠지만) 아마존에서 제공하는 SOAP과 REST 서비스에서 REST가 우월하게 많이 사용한다는 이야기를 보았다.
이미 SOAP이 CORBA와 같은 기술과 같이 서서히 지고 있다고 믿고 있는 사람들도 많다. (http://www.ibm.com/developerworks/kr/library/x-xml2007rvw.html?ca=dnn-krt-20080220)
REST(Representational State Transfer)는 HTTP의 주요 저자인 Roy Fielding의 2000년 논문에 의해 소개가 된 네트워크 아키텍처를 위한 구조이다. REST가 화두가 되면서 RET의 정체를 알아보기 위해 "RESTfull Web Services"를 읽고 이 책에서 말하는 Resource-Oriented Architecure(이하 ROA)를 나름대로 정리해 보았다.
---
ROA는 REST 기반(이하 RESTful) 웹서비스를 만들기 위한 문제점을 해결하는 방법을 제공한다.
ROA는 RESTful 아키텍처이다.
이 시점에 Resource Oriented라니?
안 그래도 수 많은 아키텍처 때문에 머리도 아픈데 ROA라는 새로운 용어는 왜 들이대나?
ROA 역시 RESTful이다. 그러나 RET 그 자체느 아키텍처느 아니다. REST는 네트워크 아키텍처를 위한 설계 규범들의 집합일 뿐이다. 사람들은 RET에 대한 자기들만의 이해를 바탕으로 서비스를 설계하고 RESTful 하다고 주장한다. 그 명백한 현상은 그들이 RESTful하다고 주장하는 다양한 REST-RPC hybrid(잡종^^) 서비스들의 출현이다.
Ray Fielding이 말하는 REST는 매우 포괄적이다. 특히 REST는 Web과 직접적인 관련이 없다. 즉 REST는 HTTP나 URI 같은 기술에 의존적이지 않다. 따라서 ROA로 Web 기술과 접목할 것이다.
ROA를 들이대는 이유는 전통적인 REST 정의는 오픈공간에서 이미 멀어졌다. 많은 사람들이 RESTful을 이야기하지만 저마다 그 정의가 조금씩 변질되었다.
ROA를 제안하는 마지막 이유는 REST용어는 이미 기술적인 종교전쟁에 사용되고 있다. (저마다 주장이 너무 많아 그것을 피하기 위해 새로운 용어를 쓴다??)
"resource-oriented"와 "resource-oriented architecure"라는 말은 포괄적인 RESTful 아키텍처를 설명하기 위해 사용할 것이다.
Resource란?
Resource란 참조하거나 그 자체로 충분히 중요한 무엇이다. (음.. 그냥 사용할 수 있는 모든 자원으로 이해해도 무리가 없다.) 보통 resource는 문서, 데이터베이스의 row또는 알고리즘 실행결과 등과 같이 컴퓨터에 저장되고 비트의 스트림으로 표현된다.
아래는 가능한 resource의 예들이다.
URI
뭐가 Resource를 진짜 의미있는 Resource로 만들까? Resource는 적어도 하나의 URI를 가져야한다. URI는 Resource의 주소와 이름이다. URI를 가지지 않는다면 여기서 정의하는 진정한 Resource가 아니다.
URI는 자신을 충분히 설명해야 한다. (URIs Should Be Descriptive)
Resource와 이것을 나타내는 URUI는 직관적이어야 한다. 아래는 좋은 URI 예들을 나열한 것이다.
URI와 Resource와의 관계
동일한 시점에 두 개의 Resource가 동일한 URI를 가질 수 없다. 동일한 시점이란 뜻은 시간에 따라 하나의 URI가 서로 다른 Resource를 가질 수도 있다는 이야기이다.
위의 Resource의 예에서 소프트웨어 릴리즈 마지막 버전은 이전 달에는 버전이 1.0.2인 소프트웨어 릴리즈 였지만 오늘은 1.0.3인 소프트웨어 릴리즈가 될 수도 있다는 이야기이다.
그러나 동일한 시점에는 하나의 URI는 하나의 Resource를 가르켜야 한다.
반대로 Resource는 하나 이상의 URI를 가질 수 있다.
Addressability
애플리케이션이 특정 데이터집합을 resource로 내 놓을 때는 주소다워야(addressable) 한다.
최종 사용자 입장에서 주소같이 계층적인 접근(addressability)은 웹 사이트나 웹 애플리케이션에서 가장 중요한 측면이다. 그렇지 않으면 사용하기 어렵다.
Statelessness (상태없음)
상태없음(Stateleness)이란 모든 HTTP 요청이 완전히 독립적으로 발생한다는 뜻이다.
Stateleness 상황에서 검색의 경우 http://www.google.com/search?q=jellyfish의 다음 페이지를 검색하기 위해서는 앞의 요청에 의존하지 않고 독립적으로 완전한 URI를 통해 요청이 이루어져야 한다. (http://www.google.com/search?q=jellyfish&start=10)
서버가 상태를 유지하는 stateness 상황이라면 아마 start=10만 보내주면 될 것이다. 질의 문자열인 jellyfish는 서버의 상태(서버 세션)에 저장되어 또 다시 받을 필요가 없을 것이다.
아래 그림과 같이 상태가 유지되지 않는(Stateless)_상황에서는 언제나 클라이언트에서 요청이 시작되고 요청이 클라이언트로 돌아온다. 모든 요청은 다른 것과 연결되지 않으며 어디에서든지 또는 어느 순서로든지 이루어질 수 있다.
다른 방법은 content negotiation이라고 부른다. 이것은 HTTP request 헤더에 어떤 표현을 요청하는지 표시하는 방법이다. 일반적으로 브라우저는 요청 시 헤더에 Accept-Language를 보낸다. 이것을 이용하여 요청하는 표현을 알아낼 수 있다.
클라이언트가 어떤 표현을 요청하는지에 대한 정보를 HTTP 헤더에 유지하는 것도 RESTful하고 URI에 유지하는 것도 RESTful 하다. 이 중 사용자끼리 요청에 대한 정보를 쉽게 교환할 수 있고 더 명확한 URI에 표현하는 방법을 권장한다.
Link와 Connectedness
어떤 표현들은 그냥 데이터 구조일 ㅃㄴ이다. 이 표현들은 사용되고 버려진다. 그러나 가장 RESTful한 서비스의 표현은 하이퍼미디어(hypermedai)이다. 문서는 데이터뿐만 아니라 다른 resource의 연결(link)도 가지고 있다. 일반 웹페이지를 상상해 보면 이해하기 쉽다.
"하이퍼미디어는 application state의 엔진의 역할을 함"
표현이 다른 resource의 link를 포함할 때 그 문서는 또 다른 가능한 애플리케이션 상태(application state)로 갈 수 있는 URI를 가진다. (URI는 애플리케이션 상태를 포함한다고 위에서 이야기 했다.)
여기에서 말하고자 하는 것은 리소스들은 그 표현들을 통해 서로 연결되어야 한다는 것이다. 이게 바로 Connectednes이다.
다음은 Connected한 표현(represention)의 예이다.
위의 Buket의 표현 중 실제 그 자원으로 이동할 수 있는 link가 포함되어 클라이언트는 다음의 URI로 이동함으로써 애플리케이션 상태가 변경된다.
Uniform Interface
웹을 통해 리소스에 할 수 있는 일은 몇 가지 기본적인 것들이 있다. HTTP는 4가지의 가장 일바적인 동작에 대하 4가지 기본 방법(메소드)를 제공한다.
이 외에 보편화되지 않은 HEAD와 OPTIONS라는 두 가지의 HTTP 메소드가 있다.
GET, PUT, DELETE
이미 존재하는 리소스를 조회하거나 삭제하기 위해서는 GET가 DELTE를 사용한다.
GET 요청의 경우 서버는 응답 body로 리소스의 표현을 돌려준다. DELETE 요청의 경우 삭제 후 응답 body에 상태 메시지 또는 비어있는 body를 돌려준다.
리소스를 생성하거나 수정하기 위해서는 요청 body에 클라이언트가 제안하는 리소스스의 표현을 담고 PUT 요청을 보낸다. 그 포맷은 서비스에 따라 달라진다.
요점은 어떤 application state가 서버로 이동하고 resources state로 변경되는가이다.
즉 URI를 포함한 요청에는 application state를 가지고 있다. 이것이 서버로 전송되고 서버는 이를 모든 클라이언트가 동시에 동일한 형태로 가질 수 있는 resource가 되도록 저장한다.
HEAD와 OPTIONS
클라이언트는 HEAD 메소드를 이용하여 전체 표현을 읽지 않고도 리소스가 존재하는지 검사하거나 리소스의 다른 정보를 찾을 수 있다. HEAD는 entity body 없이도 GET 요청에서 얻을 수 있는 정보를 제공한다.
클라이언트는 OPTIONS 메소드를 이용하여 특정 리소스에 허가된 일(메소드)을 검사할 수 있다. OPTIONS 요청의 응답은 HTTP Allow 헤더를 가지고 있으며 아래는 그 예이다.
이 경우 이 리소스에 대해서는 GET과 HEAD 요청만 가능하다는 이야기이다. 이것으로 간단한 접근관리가 가능하다.
이론적으로 서버는 OPTIONS 요청에 대한 응답에 추가적인 정보를 반환할 수 있다. 그리고 클라이언트는 서버의 능력에 대해 특수한 질문을 보낼 수 있다.
그러나 현실은 대부분의 웹서버와 프레임들이 OPTIONS 지원이 빈약하다.
POST
POST 메소드에 대해서는 가장 많이 잘못 이해하고 있다. 이 메소드는 본질적으로 두 가지 목적을 가지고 있다. 하나는 REST의 제약에 딱 들어맞는 것이고 다른 하나는 REST 하지 않고 RPC 스타일에 가깝다.
이렇게 복잡한 경우는 POST 정의의 원문으로 돌아가 보는 것이 최선이다. 아래가 바로 HTTP 표준 RFC 2616에서 말하는 POST이다.
POST는 다음의 기능을 제공하는 uniform 메소드를 만들기 위해 설계되었다.
- 이미 존재하는 리소스에 주석달기
- 게시판, 뉴스그룹, 메일링 리스트 또는 간단한 기사 그룹에 메시지 달기
- 폼 제출(submiting)의 결과 같이 데이터 처리 프로세스에 데이터 블럭을 제공하기 위하여 사용
- 기능 추가로 데이터베이스 확장
실제 POST 수행 기능은 서버에 의해 결정되고 보통 요청 URI에 의존한다. 추가되는(posted) 엔티티는 그 URI의 하위에 붙는 것이다. (이 말은 다음 장에서 이해할 수 있다.)
하위(subordinate) 리소스 생성
RESTful 설계에서, 대개 하위 리소스를 생성할 때 POST를 사용한다. 이 리소스는 다른 "부모"리소스와 관계를 가진다.
웹로그 프로그램에서 각 웹로그 리소스는 "/weblogs/myweblog"와 같다. 그리고 각각의 웹로그 엔트리들은 부수적인 리소스로 "/weblogs/myweblog/1"과 같을 것이다.
웹 기반(web enable) 데이터베이스는 테이블을 리소스로 노출할 것이다. 그리고 각각의 데이터베이스 row들은 테이블의 하위 리소스이다.
웹로그 엔트리나 데이터베이스 row를 생성하기 위해서는 웹로그나 테이블 부모에 POST한다.
추가하는 데이터가 무엇인지나 그것의 포맷이 무엇인지는 서비스에 달려있다. 그러나 PUT에서는 application state를 resource state로 변경하는 것이 요점이고 POST와 차이가 있다.
이렇게 POST를 "추가하는(append)" 용도로 사용할 경우 POST(a)라고 부르겠다. 여기에서 이야기하는 "POST"는 대개 POST(a)를 말한다.
PUT과 POST의 차이점은 다음과 같다. 새로운 리소스의 URI가 클라이언트에 의해 결정될 경우 PUT을 사용한다. 새로운 리소스의 URI가 서버에 의해 결정될 경우 POST를 사용한다.
PUT을 사용하는 경우 클라이언트에서 서버에게 보내주는 정보(application state)로 새로운 리소스의 URI가 예측되고 결정된다. 즉 리소스의 대표이름(아이디나 키)이 클라이언트에 의해 결정된다.
반면 웹로그 프로그램의 경우를 보면, 클라이언트는 웹로그 엔트리 생성을 위해 필요한 정보를 알 수는 있다. 그러나 자원이 생성된 후 그 자원의 URI가 어떻게 될지 클라이언트는 알지 못한다.
아마 서버 기준으로 정렬을 기준으로 하거나 내부 데이터베이스 ID로 URI가 결정될 것이다. 최종 URI는 리소스가 추가되는 시점에 결정될 것이다. (/weblogs/myweblog/entiries/1 또는 /weblogs/myweblog/entries/1000 와 같이 1이나 1000은 서버 내부의 메커니즘에 의해 생성된다. 클라이언트는 이것을 결정할 수 없다.)
클라이언트는 서버가 언제 이 일을 하는지에 대해 알 필요가 없다.
POST 메소드는 클라이언트가 새로 생성될 리소스의 URI를 정확하게 할 필요가 없을 때 사용한다. 대부분의 경우 클라이언트는 "부모"나 "factory 리소스"의 URI만 알면 된다.
이런 종류의 POST 요청에 대한 응답은 보통 HTTP 상태코드로 201을 갖는다. 201응답의 "Location" 헤더에는 새로 추가된 리소스의 URI를 담는다.
이제는 리소스가 실제로 존재하고 클라이언트는 리소스의 URI를 알게 되므로 앞으로는 그 리소스를 수정하기 위해서 PUT 메소드를 사용할 수 있으며 리소스의 표현을 조회하기 위해서 GET 메소드를 사용할 수 있다. 또한 삭제하기 위해서 DELETE 메소드를 사용할 수 있다.
아래는 PUT과 POST의 생성, 수정에 대해 정리한 것이다.
새로운 리소스를 PUT | 이미 존재하는 리소스를 PUT | POST | |
/weblogs | N/A (리소스가 이미 존재) | 영향 없음 (weblogs 수정) | 새로운 weblog 생성 (추가) |
/weblogs/myweblog | myweblog 생성 | myweblog 값 수정 | 새로운 웹로그 엔트리 생성 |
/weblogs/myweblog/entiries/1 | N/A (이 URI를 미리 알 수 없다) | 이 웹로그 엔트리 편집 | 이 웹로그 엔트리에 주석 추가 |
리소스 상태 추가
POST로 리소스에 정보를 전달한 후 반드시 완전한 새로운 추가 리소스가 생성될 필요는 없다. 즉 별도의 새로운 리소스 생성와 그 URI가 생성 될 필요가 없다.
어떤 경우는, 데이터를 부모 리소스에 POST하면 새로운 하위 리소스가 생성되는 것이 아니라 이 정보는 부모 리소스 자체에 추가되기도 한다.
"log"라는 이름으로 하나의 리소스를 노출한 이벤트 로깅 서비스를 생각해보자. 이것의 URI는 "/log"이며 로그를 보기 위해서 /log URI를 이용하여 GET 방식을 이용한다.
그러면 클라이언트는 어떻게 끝에 로그를 추가할 수 있을까? 클라이언트는 /log에 PUT을 보낼 수도 있다. 그러나 PUT 메소드는 새로운 리소스를 생성하는 의미를 가지거나 기존의 값을 새것으로 덮어쓰는 의미를 가진다.
클라이언트는 이것을 하려는 것은 아니다. 단지 로그의 끝에 새 정보를 추가하기 위한 것이다.
여기서 POST 메소드를 사용한다. 각 로그 엔트리가 별도의 리소스로 노출된다면 POST가 이 경우 적당할 것이다. 클라이언트가 이미 존재하는 리소스에 부가적인 정보를 추가하는 것 이 두가지 모두 POST의 의미론은 동일하다.
웹로그와 웹로그 엔트리 사이의 다른 점은 웹로그 엔트리의 경우 추가정보가 새로운 리소스(URI를 가짐)로 나타나게 된다는 것이고, 웹로그의 경우는 부모 리소스의 표현에서 새로운 추가된 데이터로 나타나게 된다는 것이다.(새로운 URI가 생기는 것은 아니다.)
Overloaded POST: 그다지 uniform 하지 않은 interface
아직 설명하지 않은 POST의 다른 용도는 아마 많이 익숙할 것이다. 왜냐하면 이 방법이 대부분의 웹 애플리케이션에서 사용되고 있다.
그것은 폼을 submit 한 결과 같이 데이터 처리 프로세스에 데이터 블럭을 전송하기 위해 사용하는 것이다.
"데이터 처리 프로세스"란 이 방법으로 POST를 사용하면 리소스를 작은 메시지 프로세스로 취급하여 XML-RPC 서버와 같이 작동한다. 리소스는 POST 요청을 받고 그 요청을 살펴보고 무엇을 할 지 결정한다. 그런 다음 클라이언트에게 어떤 데이터를 제공할 지 결정한다.
이런 용도로 POST를 사용하는 것을 프로그램 언어의 연산 재정의(operator overloading)와 비슷한 의미에서 overloaded POST라고 부른다.
하나의 HTTP 메소드에 몇 개의 표준이 아닌 HTTP(non-HTTP) 메소드의 의미를 갖게 하기 위해 사용하니 재정의(overload)한다고 볼 수 있다. 언어에서 연산 재정의(operator overloading)가 혼란스러울 수 있는 기능인 것 같이 이 방법 역시 혼란스럽다. (언어에서 +와 같은 연산의 작동을 재정의 할 수 있는데 이것은 재정의 하는 프로그래머에 의해 그 동작이 결정되어지므로 일관성-uniform-을 깨뜨릴 수 있어 혼란스럽다는 이야기이다. 물론 언어의 overloading 기능을 폄하하려는 것은 아니다.)
이미 HTTP POST가 하는 일을 알고 있으나 이 경우의 POST는 알 수 없는 목적을 달성하기 위해 사용한다. 이렇게 "process"를 위해 사용하는 overloaded POST를 POST(p)라고 부를 것이다.
모든 HTTP 요청은 메소드 정보를 가지고 있어야 한다. 그러나 overloaded POST를 사용하면 HTTP 메소드를 가지지 않는다. POST 메소드는 단지 서버에게 "진짜 실행할 메소드를 찾기 위해서는 HTTP 요청의 내용에서 찾으세요"라고 말하면서 어느 메소드를 실행할 지 방향만 지시한다. 실제 정보는 URI, HTTP 헤더 또는 entity body에 있을 수 있다. 어쨌거나 이렇게 사용하는 경우는 빈번히 발생하고 있으며 이러한 것들은 RPC 스타일의 서비스가 되어가고 있다.
빈약한 리소스 설계를 만회(cover)하려고 Overload POST를 사용하지는 말아야 한다. 리소스는 어떤 것이든 될 수 있다는 것을 잊지 말라. 이것은 보통 리소스 설계를 마구 섞어버리는 경향이 있어 결국 일관된(uniform) 인터페이스에도 영향을 주게 된다. Overloaded POST 사용을 자제하지 않을 경우 당신의 서비스는 결국 RPC 스타일로 가게 될 것이다.
안전성(Safety)과 멱등(Idempotence)
충실하게 설계한 일관된 HTTP uniform 인터페이스를 노출한다면 여러가지 유용한 속성을 꽁짜로 얻게 된다. HTTP 메소드를 바르게 사용했다면 GET과 HEAD 요청은 안전할 것이다. GET, HEAD, PUT 그리고 DELETE 요청은 멱등일 것이다. (멱등은 아래에 설명)
안전성
GET이나 HEAD 요청은 데이터를 읽기만 하고 서버 상태를 바꾸지 않는다. 따라서 클라이언트는 GET이나 HEAD 요청을 처음 보는 URI에게 보내도 안전하다고 느낄 것이다. (그 서버에 어떤 영향도 주지 않을 것이라 확신하므로..)
멱등
멱등이란 한 번 적용하나 여러 번 적용하나 항상 같은 결과를 나타내는 것을 이야기한다. 곱셈의 예를 들면 0으로 계속 곱하는 것은 멱등이다. ( 4 x 0 x 0 x 0 ...) 0으로 곱해도 마찬가지 결과이다. ( 4 x 1 x 1 x 1 ... 도 마찬가지이다.)
비슷하게 리소스에 동일한 오퍼레이션을 여러 번 요청해도 같은 결과일 경우 멱등이라 한다.
PUT과 DELETE는 멱등이다. 만일 어떤 리소스를 DELETE하고 또 다시 DELETE 한다면 이미 지워진 내용을 또 지워도 결과는 같으므로 멱등이다. PUT으로 새 리소스를 생성하고 또 동일한 내용으로 PUT을 리소스에 요청해도 두 번째는 이미 존재하고 동일한 내용이므로 결과는 같다. 이미 존재하는 리소스에 PUT을 보낼 경우 동일한 내용으로 몇 번을 보내더라도 동일한 내용으로 상태가 저장되므로 리소스 상태는 변하지 않는다.
왜 안전성과 멱등이 중요할까?
안전성과 멱등은 신뢰할 수 없는 네트워크 상의 HTTP를 신뢰할 수 있게 만들어 준다. 만일 GET 요청을 보내고 응답을 못 받았을 경우 그냥 한 번 더 보내면 된다. 이 동작은 안전하다. 먼저 보낸 요청이 벌써 처리되었다 하더라도 서버에 다른 영향은 없다. 마찬가지로 PUT 요청을 보내고 응답을 못 받았다면 단지 한 번 더 동일한 요청을 보내면 된다.
그러나 POST는 안전하지도 않고 멱등도 아니다. 따라서 POST 메소드는 주의해서 사용해야 한다.
왜 Uniform 인터페이스가 중요한가
중요한 것은 일관성(Uniformity)이다. 모든 서비스는 동일한 방법으로 HTTP 인터페이스를 사용한다.
Uniform 인터페이스가 없다면 매번 각 서비스가 어떻게 정보를 주고 받는지 배워야 한다. 하나의 서비스에서도 서로 다른 리소스에 대해 규칙이 서로 다를 것이다.
어떤 애플리케이션은 HTTP uniform 인터페이스를 확장했다. 그 예로 WebDAV는 8개의 새로운 HTTP 메소드를 추가했다.
그러나 실제 WebDAV를 사용하지 않는 이유는 다른 RESTful 서비스와 호환이 되지 않을 수 있기 때문이다. 따라서 당신의 고유한 HTTP 메소드를 만드는 것은 아주 아주 좋지 않은 생각이다.
또 다른 uniform 인터페이스는 단지 HTTP GET과 overloaded POST로만 구성된다. 리소스의 표현을 fetch하기 위해서는 GET을 사용한다. 리소스를 생성, 수정, 삭제하기 위해서는 POST를 사용한다. 이 인터페이스는 완벽하게 RESTful이다. 그러나 여기에서 말하는 ROA를 기반으로 하지는 않는다. 이 인터페이스는 안전한 오퍼레이션과 안전하지 않은 오퍼레이션을 구분하기에는 충분하다. 일부(사실 대부분) Resource-Oriednted 웹 애플리케이션은 이 인터페이스를 사용할 수 있다. 왜냐하면 오늘날 HTML 폼이 오직 GET과 POST만 지원하기 때문이다.