제공 :
한빛 네트워크
저자 : Andrew Sterling Hanenkamp
역자 : 이대엽
원문 :
Developing RESTful Web Services in Perl
여러분이 웹 개발자이고 "REST"라는 용어가 친숙하게 느껴 지지 않는다면 여러분이 늘상 "REST"라는 소프트웨어 아키텍처상에서 일하고 있다는 사실을 알아야만 한다. REST는 월드 와이드 웹(World Wide Web)상의 서버와 클라이언트간에 일어나는 상호작용을 설명해 줄 수 있는 용어이다. 하지만 수많은 용어가 그렇듯 REST의 원래 정의도 실제로는 여러 단체에서 그 용어를 사용해 왔기 때문에 원래 의미 이상의 의미를 지니고 있는 듯 하다. 나는 이 기사에서 REST를 구체적으로 서버와 클라이언트간에 발생하는 통신을 기술하는데 보다 적합한 특정한 API의 일부로 가정할 것이다.
나는 여기에서 여러분만의 RESTful API 개발을 어떻게 시작할지에 관한 가이드를 제시할 것이다. 이 가이드는 두 부분으로 나뉘는데, 여기에서는 이번 가이드에 맞게 REST에 대해 매우 간략하게 설명할 것이다. 그 다음에는 CGI 스크립트(FastCGI나 mod_perl, 혹은 다른 웹 프레임워크에서도 손쉽게 참고할 수 있도록)를 이용하여 여러분만의 RESTful 서버를 구축하는 법을 설명할 것이다. 다음 기사에서는 libwww-perl을 이용하여 작성한 RESTful 클라이언트에서 이러한 서버에 접근하는 방법을 설명할 것이다. 그리고 고려해 보아야 할 두 가지 유용한 확장기능(extension)으로 기사를 마무리하고 기타 중요한 참고자료를 공유할 것이다.
REST란 무엇인가?
가장 일반화된 용어로 REST (
Representational State Transfer)는 원래 로이 필딩(Roy Fielding)이 자신의
논문에서 공개한 소프트웨어 아키텍처이다. 보다 구체적으로 말하자면
이 용어는 HTTP로 생성하고(create), 읽고(read), 갱신하고(update), 삭제(delete)할 수 있는 자원을 관리하는데 사용되는 웹 서비스 API를 정의하는데 사용되고 있다. 바로 이것이 이번 기사에서 초점을 두는 부분이다. 여러 이름있는 기업에서도 RESTful API를 사용하고 있는데, 금방 머릿속에 떠오르는 것은 아마존(
Amazon Web Services), 인튜잇(
Quickbase), 페이스북(
Facebook) 모두 자사 애플리케이션에서 RESTful 인터페이스를 제공하고 있는 것으로 안다. 물론 그 밖에도 많이 있다.
이 기사에서는 자원(resource)에 관해 논의하는데 많은 시간을 할애할 것이다. RESTful 웹 서비스에서 자원은 단순히 여러분의 사이트에서 의미 있는 데이터 단위를 의미한다. 이것은 SQL 데이터베이스에 들어 있는 레코드일 수도 있고, LDAP 서버상의 계정이나, XML 데이터 파일의 일부, 아니면 여러분이 다른 이들과 공유하고자 하는 다른 어떠한 데이터 단위일 수도 있다. 가령 예제 서버에서는 디스크상의 파일을 읽고 쓰게 될 것이다.
RESTful 원칙
몇 가지 원칙에 관해 이야기하기 전에 먼저 나는 REST를 전적으로 실용적인 배경에서 이해하는 것은 부인하고자 한다. REST는 소프트웨어 아키텍처 스타일의 하나이며, 그러한 점에서 REST에 관련된 논문과 이론은 상당히 많으며 여러 순수주의자들(purist)도 REST에 많은 관심을 보였다. 본 가이드의 내용은 펄에서 뭔가를 해보는 것이다. 만약 내가 REST 자체의 이론적 취지를 제시하는데 실패한다면 다시는 그렇게 하지 않겠다고 하여 용서를 빌겠다. 여러분이 아키텍처의 하나로서 REST에 관해 보다 자세한 정보가 필요하다면 RESTful 참고자료 섹션에 나열되어 있는 내용을 참고하도록 한다.
우선 이런 이야기는 접어 두고 몇 가지 원칙에 관해 이야기해 보자. 여러분은 "REST의 삼요소(Triangle), [그림 1] 참고"를 빼놓고는 REST에 관한 여러 문헌을 이해할 수 없을 것이다. 기본적으로 REST에는 세 가지 핵심 개념이 존재하는데, 바로 명사(noun)와 동사(verb), 그리고 콘텐츠 타입(content type)이다. 아래에서 이러한 세 가지 요소에 관해 간략히 설명하도록 하겠다.
[그림 1] REST의 삼요소(Triangle)
명사(Noun): 네 자신의 URI를 알라
명사(noun)는 한 자원(resource)에 대한 식별자(indentifier)이다. 일반적으로 우리가 HTTP상의 REST에 관해 이야기할 때에는 URL(자원을 가져오는(GET)하는 링크)을 명사로 볼 수 있다. 명사는 URN(HTTP 를 통해 사용할 수 있는 자원의 이름이나 기타 자원을 식별해 주는 것)이나 다른 종류의 URI(간혹 URL과 URN은 REST 명사로 사용될 때 URI가 되기도 한다)가 될 수도 있다. 아마 여러분은 자원을 유일하게 식별해 주는(URI : Unique Resource Identifier) 여러 명사나 하나의 명사라도 필요할 수가 있는데, 그러한 상황에서 유일하지 않은 명사를 제공했을 지도 모른다. 가령 아래와 같은 명사를 이용하여 동일한 레코드를 식별하는 인터페이스가 있을 수 있다.
- http://example.com/=/model/person/id/157
- http://example.com/=/model/person/last_name/Hanenkamp
- http://example.com/=/model/person/irc_name/zostay@irc.freenode.net
위 명사는 제각기 내가 제공하는 인터페이스에서는 URL일 수도 있다. 첫 번째 예제는 시스템 내에서 유일한 계정 ID일 수 있으므로 유일한 ID이다. 그 다음 예제에서는 하넨캄프(Hanenkamp)라는 내 성을 이용했는데, 이러한 성은 미국에서는 꽤 특이하겠지만 세계적으로 보았을 때에는 전혀, 그리고 확실히 유일하지 않다. 마지막 예제는 외부 인증 기관(external authority)을 통해 유일성을 보장받은 명사의 예를 보여 준다. 여러분의 명사를 어떻게 식별할지 결정하는 것은 고려해 볼 문제다.
명사와 URL을 다루는 것에 관련된 보다 상세한 정보가 필요하다면
URL Construction을 참고하도록 한다.
동사(Verb): 네 자신의 CRUD를 알라
CRUD는 데이터에 일반적으로 행해지는 변경, 즉 생성(Create), 조회(Read), 갱신(Update), 삭제(Delete)를 나타내는 약어이다. 일반적으로 이러한 일련의 오퍼레이션은 데이터에 이루어질 수 있는 모든 작업을 포괄한다. 내가 REST 맥락에서 이러한 오퍼레이션을 언급할 때에는 구체적인 HTTP 요청 메소드를 이용하여 각각을 구현할 것이다. REST 명명 기준에서 이러한 오퍼레이션은 아키텍처의 동사(verb)로 불린다.
GET
GET 요청은 조회(read) 오퍼레이션을 수행하는데 사용된다. 이것은 자원의 내용을 반환하는데 사용될 것이다.
POST
POST 요청은 생성(create) 오퍼레이션을 수행하는데 사용된다. POST는 서버에 자원을 생성하고, 생성된 자원에 명사를 할당할 것이다.
PUT
PUT 요청은 갱신(update) 오퍼레이션을 수행하는데 사용된다. PUT 오퍼레이션은 GET과는 반대되는 오퍼레이션을 수행하는데, 즉 클라이언트에서 서버로 콘텐츠를 보내는(push) 경우 서버상의 자원을 갱신하는 역할을 한다.
DELETE
DELETE 요청은 삭제(delete) 오퍼레이션을 수행하는데 사용된다. DELETE는 더 이상 설명할 게 없다.
이론적으로 모든 RESTful 웹 서비스에서 실제로 이러한 오퍼레이션이 모두 필요하지는 않다. 자원이 변경되는 것을 허용하지 않는다면 여러분은 GET만 사용할 수도 있다. REST 인터페이스가 단순하다면 필요에 따라 GET과 PUT이나 GET과 POST만을 제공할 수도 있다. 이 밖에도 사용할 수 있는 동사는 더 있다.
보다 자세한 정보가 필요하다면
HTTP Methods를 참고하도록 한다.
콘텐츠 타입(Content Type): 네 자신의 MIME을 알라
REST의 삼요소 중 마지막 요소는 자원의 콘텐츠 타입(content type)이다. 콘텐츠 타입은 RESTful에 관한 논의의 일부를 차지하는 데이터의 형식을 제공해 준다. 여러분은 요청(클라이언트 측)과 응답(서버 측)에 들어 있는 "Content-Type" 헤더를 이용하여 콘텐츠 타입을 지정할 것이다. 월드 와이드 웹(World Wide Web)이라는 정보 고속도로(information superhighway)를 여행할 때 여러분은 HTML의 몇몇 변종을 주요 문서 형식으로 사용하도록 제약 받게 된다. 그러나 웹 서비스에서는 여러분의 애플리케이션에 적합한 것이라면 어떤 형식도 사용할 수가 있다.
여러분이 사용하고자 하는 형식은 애플리케이션의 요구 사항에 따라 확연하게 달라질 것이다. 여러분이 이 기사에 포함되어 있는 예제 서버 및 클라이언트와 같은 조직화된 데이터를 교환할 것이라면 XML이나 YAML, JSON, 혹은 CVS와 같은 데이터 상호 교환 형식(interchange format)을 사용하고자 할 것이다. 여러분의 애플리케이션에서 문서를 다루어야 한다면 애플리케이션에 관련된 형식, 즉 HTML과 DocBook, SGML, ODF, PDF, PostScript와 같은 문서 형식을 사용하고자 할 것이다. 여러분의 애플리케이션에서는 사진(JPG, PNG, BMP)이나 달력 정보(iCal), 혹은 분류화된 링크(OPML) 등 어떠한 형식을 다루어야 할 지 모른다. 여러분은 마이크로포맷(microformat)을 비롯하여 선호하는 것은 무엇이든 사용할 수 있다.
여러분이 정말로 제대로 하길 원한다면 갖가지 형식으로 데이터가 서술될 수 있게 할 수가 있다. 가령 여러분은 전송된 요청에 들어 있는 "Content-Type" 헤더를 검사한 다음 주어진 데이터에 따라 처리하여 데이터에 대한 갱신 내역이 XML이나 YAML, JSON으로 가게 할 수 있을 것이다. 여러분은 "Accept" 헤더를 검사하여 클라이언트측의 구성 설정을 토대로 형식을 선택함으로써 클라이언트에서 요청한 데이터를 사용자가 정의한(custom) 형식으로 받게 할 수도 있다. 궁극적으로 여러분의 데이터가 클라이언트 입장에서 편리하게 활용할 수 있는 형식으로 요청되고 게시(post)될 수 있다면, 아마 여러분의 클라이언트는 좀 더 행복해 질 것이다.
콘텐츠 타입을 고려하는 것과 관련된 보다 풍부한 논의 내용은
Which Content Type를 참조하도록 한다.
RESTful 서버
나는 CGI 스크립트를 이용해서 매우 간단한 RESTful 웹 서비스를 작성해 보았다. 이제는 이론에서 약간 벗어나, 이 서버가 어떻게 작동하는지를 확인해 보면 실용적 관점의 개념을 설명하는데 도움될 것이다. 이 서버의 역할은 내 서재에 있는 책을 관리하는 것이다. 각 책의 정보는 특정 폴더에 YAML 파일로 저장되어 있다. 본 가이드에서는 데이터베이스를 이용해서 정보를 저장하지는 않았는데, 데이터에 대한 직렬화(serialization)와 역직렬화(unserialization)에 신경 쓰고 싶지 않기 때문이다. 여기에서는 되도록 REST 프로토콜 자체에만 집중하고자 한다.
여기서 작성한 REST 서버의 인터페이스는
Jifty 웹 애플리케이션 프레임워크에서 맡고 있는 작업 결과에 토대를 두고 있다. Jifty 웹 애플리케이션 프레임워크에는 RESTful 구현에 관련된 괜찮은 아이디어가 여럿 포함되어 있다고 생각한다. 여러분이 Jifty에 익숙하다면 내가 작성했던 코드와 정책 결정은 매우 친숙하게 다가설 것이다.
예제 서버 URL
구현을 시작하기 전에 먼저 예제 서버에서 사용할 URL을 기록해 두고 싶다. 이 URL은 부분적으로 파악하기도 쉽고 확장하기도 쉽게 만들어 질 것이며 Jifty REST 플러그인에서 선택한 것과 아주 유사하다.
첫 번째로 모든 REST 인터페이스 URL은 "/="으로 시작하게 하였다. 얼핏 보기에는 다소 이상하게 보일 수도 있겠지만 이렇게 하면 REST URL을 사이트의 나머지 부분과 매우 간단하게 구별할 수가 있다. 나는 이것이 "
IM IN YR API!"에 대한 매우 멋진 이디엄이라 생각한다.
역자 주) LOLCODE 언어에서는 "IM IN YR"가 반복문의 시작을 의미한다.
두 번째로는 API의 다음 컴포넌트인 "/model"을 만들었다. 이것은
MVC의 "모델(Model)" 개념에서 따온 것이다. 이렇게 한 이유는 누군가 이러한 REST API에 원격 프로시저를 실행하기 위한 "/action/"이나 데이터 검색을 실행하기 위한 "/search/" 등의 추가 기능을 삽입할 수도 있기 때문이다. 이것들이 꼭 RESTful해야 하는 것은 아니지만 유용한 것만은 분명하다.
세 번째로는 API의 다음 컴포넌트인 "/book"을 만들어 현재 사용하고 있는 데이터 유형의 명칭을 지정하였다. 다시 말하지만 차후 확장 기능으로 저장에 대한 부가 모델을 추가하여 API가 향상되는 것을 예상할 수 있겠다. 나는 저자 약력을 "/author"에 저장하거나 아니면 책을 빌려 준 친구에 대한 정보를 "/loan"에 저장할 수도 있을 것이다.
이러한 정책 결정은 여러분이 피할 수만 있다면 이미 만들어 진 기능을 손상시키지 않고도 API가 차후 향상 기능에 유연하게 대처하게 하기 위해 가장 먼저 고려해야만 하는 것이다. 이는 Jifty 개발자들도 같은 목적에서 비슷한 이유로 골랐던 것들이다.
GET으로 문서 가져오기
나는 API에서 자체적으로 어느 정도의 문서화는 제공해 주기로 하였다. 여러분이 library.cgi 스크립트를 로컬 cgi-bin 디렉터리에 설치하고 브라우저에서 최상위 레벨의 URL인 http:// localhost/cgi-bin/library.cgi/= (주소는 여러분이 어디에 스크립트를 설치했느냐에 따라 달라진다)으로 이동하게 되면 인터페이스의 작동 방식을 문서화한 HTML 응답을 받게 될 것이다.
내 생각에 자기 문서화 서비스는 괜찮은 아이디어인 것 같다. 만약 내가 이 서버를 제품화할 목적이고 프로젝트 기한이 그렇게 할 수 있을 정도로 넉넉했다면, 나는 발생하는 다양한 오류 메시지에 대한 문서를 더 많이 추가하여 인터페이스의 사용법을 문서화했을 것이다. 그렇게 하면 여러분은 문제 해결 방법에 관해 개발자(혹은 잘못된 위치로 이동한 최종 사용자에게도)에게 조언해 줄 수도 있을 것이다.
GET으로 목록 가져오기
우리가 다룰 API의 첫 번째 실질적인 측면은 바로 가장 기본적인 것인 목록 가져오기(listing)이다. 이것은 실제로 위에서 논의했던 CRUD의 한 가지 측면은 아니지만 여러분은 어떠한 자원이 사용 가능한지 알 수 없다면 자원을 가져오거나(fetch) 갱신하는 작업이 어려울 수도 있다.
이러한 목록에 접근하기 위한 URL은 "/=/model/book/id"이고 책 모델의 모든 ID를 나열한다.
예제 서버의 코드는 꽤 간단하다. 서버는 디스크에 YAML로 저장되어 있는 사용 가능한 자원을 모두 찾는다. 그런 다음 발견된 자원에 대한 링크를 포함하는 HTML 파일을 출력한다.
print $q->header("text/html");
# 사용 가능한 파일을 모두 찾는다
my @items;
for my $filename (glob get_local_path("*")) {
my ($id) = $filename =~ m{([d-]+).yaml$};
next unless defined $id;
push @items, $q->li(
$q->a({ href => absolute_url("/=/model/book/id/".$id) }, $id),
);
}
# 항목을 나열한다
print $q->ul( @items );
책의 ID (차후에도 살펴보겠지만 통상 ISBN인)는 파일명에 들어 있으며, 참고 자료에 링크되어 있는 파일에도 저장되어 있다. 위의 펄 코드는 내 서재에 있는 책에 대한 정렬되지 않은 링크 목록을 HTML에 출력한다.
여러분은 브라우저에 이를 직접 시도해 볼 수가 있다. URL은 아마 다음과 같을 것이다:
http://localhost/cgi-bin/library.cgi/=/model/book/id
만약 자원 서재가 비어 있다면(즉, 단순히 설치만 하고 클라이언트를 이용해서 책을 아무것도 추가하지 않았다면), 페이지는 비어 있을 것이다. 만약 책을 하나 이상 저장하면 링크된 ID가 포함된 목록을 보게 될 것이다.
만약 그러한 링크 가운데 하나를 클릭하게 되면 여러분은 해당 서적의 YAML 설명에 접근하게 될 것이다.