출처: http://greatkim91.tistory.com/


"올바른 성장과 따뜻한 나눔"이 있는 넥스트리

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의 예들이다.

  • 소프트웨어 릴리즈버전 1.0.3
  • 소프트웨어 릴리즈 마지막 버전
  • 2006년 10월 24일 첫번째 웹로그 엔트리
  • 금천구 가산동 넥스트리 근처의 도로지도
  • 해파리(jellyfish)의 약간의 정보
  • 해파리와 관련된 자원 디렉토리
  • 1024 다음 소수(素數)
  • 1024 다섯번째 다음 소수(素數)
  • Q42004의 판매들
  • Alice와 Bob의 교우관계
  • 버그 데이터베이스에 상태가 "오픈"인 버그 목록

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)은 웹 사이트나 웹 애플리케이션에서 가장 중요한 측면이다. 그렇지 않으면 사용하기 어렵다.

"email message about jellyfish" -> addressable하지 않다.
https://mail.google.com/mail/?q=jellyfish&search=query&view=it   -> adressable하다.


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)_상황에서는 언제나 클라이언트에서 요청이 시작되고 요청이 클라이언트로 돌아온다. 모든 요청은 다른 것과 연결되지 않으며 어디에서든지 또는 어느 순서로든지 이루어질 수 있다.

사용자 삽입 이미지

반면 아래 그림과 같은 상태가 유지되는(Stateful) 상황에서는 반드시 정해진 순서에 따라 상태가 진행되어야 한다. 즉 바로 "mice" 검색결과 상태로 갈 수 없고 Search form을 통해 가야한다. 대부분의 데스크탑 애플리케이션이 이런 식이다.
사용자 삽입 이미지

Statefule의 다른 예로 FTP도 stateful하게 작동한다. FTP에는 "working directory" 개념이 있어 working directory를 변경해야 파일을 접근하거나 올릴 수 있다.

상태를 유지하면 검색의 예에서 보듯이 HTTP 요청 자체는 간단해진다(최소한의 정보만 서버쪽에 전달하므로). 그러나 HTTP 프로토콜은 보다 복잡해진다. 클라이언트와 서버간의 세션 상태를 동기화하고 유지하기 때문에 FTP 클라이언트 보다도 HTTP 클라이언트가 더욱 복잡해진다.
이것은 신뢰할만한 네트워크에서도 복잡한 작업인데 인터넷은 신뢰할만 하지도 않다.

프로토콜에서 상태를 없앰으로 많은 실패 상황을 미연에 제거할 수 있다.
서버는 클라이언트 time out에 대해 신경쓸 필요도 없고 클라이언트가 애플리케이션의 "어디"에 있었는지 기억한 정보를 읽어버릴 일도 없다. 클라이언트는 매 요청마다 필요한 모든 정보를 준다.

Statelessness는 load-balance가 가능한 분산 서버 환경을 쉽게 제공한다. 비용이 많이 드는 서버간의 상태정보 유지같은 장치도 필요없다. 서버에 저장된 상태를 사용하기 위해 동일한 클라이언트가 항상 동일한 서버로 라우팅하는 장치도 필요없다. 그냥 서버를 더 추가하기만 하면 된다.

또한 쉽게 북마크를 할 수 있다.

REST에서는 상태가 서버가 아니라 클라이언트에 유지되며 매 요청마다 필요한 정보를 서버에 보낸다.

Application State와 Resource State
Statelessness라는 용어에 문제가 있다. Statelessness에는 한가지의 상태에 대해서만 이야기하고 서버는 그 상태라는 것을 가지면 안된다고 말한다.
그러나 실상은 두 가지의 상태가 있는데 바로 클라이언트에 있는 application state서버에 있는 resource state가 있다. 사실 위의 상태없음에서는 application state만 이야기하였다.

검색엔진을 사용할 때 지금 검색할 질의(query=jellyfish)나 현재 페이지(start=10)는 클라이언트 상태 중 일부이다. 이 상태는 각 클라이언트마다 다르며 이것을 Application state라 한다.
요청이 발생할 때 웹 서비스에서는 이 Application state만을 다룬다. 즉 클라이언트가 요청을 만들때마다 이 요청을 서버가 처리하기 위해 필요한 모든 application state를 담아야 한다. 따라서 URI는 application state를 포함하고 있다.

반면 Resource state는 모든 클라이언트에 대해 동일한 상태이고 이것은 서버에 저장된다. 즉 서버에 저장되는 정보, 사진, 문서들의 의미하며 stateleness에서는 이 resource state를 말하는 것은 아니다.

Represetations (표현)
Resource는 데이터가 아니다. 이것은 오픈된 버그 목록, jellyfish의 정보 같이 단지 서버 설계자의 아이디어 일 뿐이다. 웹 서비스는 아이디어 자체를 전달할 수는 없다. 대신 특정 파일 포맷이나 특정 언어 또는 일련의 바이트를 만들어 전달한다. 이것을 resource의 representation이라 한다.

Resource는 representation의 근원이며 representation은 현재 resource 상태의 일부 데이터일 뿐이다. 버그 목록은 XML로 제공될 수도 있고 웹페이지나 comma-separated text로 제공될 수도 있다.

어떤 표현을 사용할 지 결정
서버가 하나의 resource에 대해 여러 개의 표현을 제공한다면 클라이언트가 어떤 표현을 요청하는지 어떻게 알 것인가? 예를 들어 출판물(press)의 릴리즈가 영어와 스페인어로 제공된다면 클라이언트가 어떤 것을 원할지 어떻게 알 것인가?

REST에서도 여러가지 방법이 있다. 가장 간단하게(그리고 ROa에서 가장 권장한다) resource의 다른 표현에는 서로 다른 URI를 제공하는 것이다. 출판물 릴리즈의 예라면 다음과 같을 것이다.

다른 방법은 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이다.

사용자 삽입 이미지

그림 설명

  • a: 모든 서비스가 하나의 URI로 표현되는 RPC 스타일의 서비스
  • b: 서비스가 잘 정리되어 addressable한 서비스. REST-RPC hybrid(잡종)형 서비스. 뭐 이것도 역시 RESTful한 서비스이다.
  • c: addressable하고 well-connected한 서비스. 이거야 말로 완벽히 RESTful한 서비스이다.

다음은 Connected한 표현(represention)의 예이다.

<?xml version='1.0' encoding='UTF-8'?>
  <ListAllMyBucketsResult xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>
    <Owner>
      <ID>c0363f7260f2f5fcf38d48039f4fb5cab21b060577817310be5170e7774aad70</ID>
      <DisplayName>leonardr28</DisplayName>
    </Owner>
    <Buckets>
      <Bucket>
        <Name>crummy.com</Name>
        <URI>https://s3.amazonaws.com/crummy.com</URI>
        <CreationDate>2006-10-26T18:46:45.000Z</CreationDate>
      </Bucket>
    </Buckets>
</ListAllMyBucketsResult>

위의 Buket의 표현 중 실제 그 자원으로 이동할 수 있는 link가 포함되어 클라이언트는 다음의 URI로 이동함으로써 애플리케이션 상태가 변경된다.

Uniform Interface
웹을 통해 리소스에 할 수 있는 일은 몇 가지 기본적인 것들이 있다. HTTP는 4가지의 가장 일바적인 동작에 대하 4가지 기본 방법(메소드)를 제공한다.

  • 리소스의 표현을 조회: HTTP GET
  • 새 리소스를 생성: 새로운 URI는 HTTP PUT, 이미 존재하는 URI에는 HTTP POST (POST장에 자세히 설명)
  • 존재하는 리소스를 수정: HTTP PUT
  • 존재하는 리소스를 삭제: HTTP DELETE

이 외에 보편화되지 않은 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

  • 표현의 메타데이타만 조회: HTTP HEAD
  • 특정 리소스가 어떤 HTTP 메소드를 지원하는 지 검사: HTTP OPTIONS

클라이언트는 HEAD 메소드를 이용하여 전체 표현을 읽지 않고도 리소스가 존재하는지 검사하거나 리소스의 다른 정보를 찾을 수 있다. HEAD는 entity body 없이도 GET 요청에서 얻을 수 있는 정보를 제공한다.

클라이언트는 OPTIONS 메소드를 이용하여 특정 리소스에 허가된 일(메소드)을 검사할 수 있다. OPTIONS 요청의 응답은 HTTP Allow 헤더를 가지고 있으며 아래는 그 예이다.

Allow: GET, HEAD

이 경우 이 리소스에 대해서는 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만 지원하기 때문이다.

Posted by 이완국
,