Dojo의 데이터 추상 계층인 dojo.data는 다양한 백엔드 서비스에 액세스하기 위해 Dojo 애플리케이션에 표준 API를 제공한다. Dojo 확장 라이브러리 DojoX에 위치한 JsonRestStore를 통해 애플리케이션을 백엔드 REST(Representational State Transfer) 서비스에 빠르게 연결할 수 있다.
JsonRestStore는 Dojo를 REST 서비스로 링크하기 위한 훌륭한 솔루션이다. 하지만, 기본적으로 JsonRestStore는 서비스로 전달되고 리턴되는 정보의 형식에 대한 예상이 있다. 이러한 상호작용을 사용자 정의하기 위한 SMD(Service Mapping Description) 설비는 아무리 낙관적으로 봐도 이해하기에 어렵다.
Dojo 애플리케이션 작성자가 전에 존재하는 백엔드 REST 저장소와 통신해야 할 때, 저장소의 통신이 변경될 수 없으면 사용자 정의 dojox.rpc.Service 구현 방식을 사용한다.
이 기사에서는 비표준 REST 서비스를 JsonRestStore로 연결하기 위한 간단한 일반 서비스 구현 방식을 학습한다. 실제적인 예제를 연습하여 자체 서비스에 이를 사용하고 확장하는 방법을 탐색한다.
이 기사의 소스 코드를 다운로드하려면 다운로드 섹션을 참조한다.
EasyRestService는 네 가지 REST 연산인
POST(작성), GET(읽기), PUT(업데이트) 및 DELETE(삭제)에 구현 방식을 제공한다. 각 활동에 대해
이는 서비스 메소드의 이전과 이후 호출에서 후크하는 기능을 제공한다. 호출 라이프사이클은 다음과 같다.
- URL, 인수 및 데이터 형식 메타데이터가 들어 있는 표준 Dojo XHR 호출 구조를 구성한다.
- 메소드에 대해 인수 변환자를 호출한다. 이 메소드는 호출 구조(경로 포함)를 변경할 수 있고 REST 서비스의 예상에 일치하기 위해 호출 구조를 변환하는 데 사용될 수도 있다.
- 컨텐츠 노드를 JSON 표현으로 변환하여 호출 구조의 putData/postData/deleteData/getData 노드를 입력한다.
- XHR 메소드를 변경된 XHR 호출 구조로 호출한다.
- 결과 변환자를 XHR 콜백에 추가한다. 이 메소드는 결과의 데이터 구조를 변경할 수 있다. 이를 사용하여 결과를 JsonRestStore가 예상하는 구조로 변환한다.
목록 1은 예제 코드를 보여준다.
목록 1. EasyRestService 소스 코드 소스
dojo.provide("com.ibm.developerworks.EasyRestService");
(function() {
var pa = com.ibm.developerworks.EasyRestService = function (path, serviceImpl, schema) {
// Enforce the dojox.rpc.Rest trailing slash functionality
path = path.match(/\/$/) ? path : (path + '/');
// A dojox.rpc.Service implementation is a function with 3 function members
var service;
// GET function
service = function(id, args) {
return _execXhr("get", id, args);
};
// POST function member
service['post'] = function(id, value) {
return _execXhr("post", id, value);
};
// PUT function member
service['put'] = function(id, value) {
return _execXhr("put", id, value);
};
// DELETE function member
service['delete'] = function(id) {
return _execXhr("delete", id);
};
// Generic XHR function for all methods
var _execXhr = function(method, id, content) {
// Transform the method string
var methodCapitalised = method.substring(0,1).toUpperCase()
+ method.substring(1).toLowerCase();
var methodUpperCase = method.toUpperCase();
var methodLowerCase = method.toLowerCase();
// Get the transformer functions
var argumentsTransformer = service["transform" + methodCapitalised + "Arguments"];
var resultTransformer = service["transform" + methodCapitalised + "Results"];
// Construct the standard query
var serviceArgs = {
url : path + (dojo.isObject(id) ? '?' + dojo.objectToQuery(id) :
(id == null ? "" : id)),
handleAs : "json",
contentType : "application/json",
sync : false,
headers : { Accept : "application/json,application/javascript" }
};
// Transform the arguments
// NOTE: argumentsTransformer has a reference to "service"
serviceArgs = argumentsTransformer(serviceArgs, arguments);
// Copy the content into the appropriate *Data arg
// getData, putData, postData, deleteData
// NOTE: If you want your arguments transformer to edit the *Data arg directly,
// move the arguments transformer invocation to after this call
serviceArgs[methodLowerCase + 'Data'] = content;
// Kick off the call
var xhrFunction = dojo['xhr' + methodCapitalised];
var deferred = xhrFunction(serviceArgs);
// Add our result transformer
// NOTE: resultTransformer has a reference to "service" too
deferred.addCallback(dojo.partial(resultTransformer, deferred));
return deferred;
};
// Mix in the service hooks
// Uses a "default" implementation that does nothing
// Service hooks will have a reference to the "service" object in their context
dojo.mixin(service,
new com.ibm.developerworks.EasyRestService.DefaultHooks(),
serviceImpl);
// Now remove any default _constructor() methods
// This is necessary as the JsonRestStore stack uses _constructor() differently
delete service['_constructor'];
// Remove the declaredClass member if it has been added
delete service['declaredClass'];
// Save the path away
service.servicePath = path;
// Save the schema
service._schema = schema;
return service;
};
})();
dojo.declare("com.ibm.developerworks.EasyRestService.DefaultHooks", null, {
transformGetArguments: function(serviceArgs) {
// Alter serviceArgs to provide the information the backend
// service requires
return serviceArgs;
},
transformPutArguments: function(serviceArgs) {
// Alter serviceArgs to provide the information the backend
// service requires
return serviceArgs;
},
transformPostArguments: function(serviceArgs) {
// Alter serviceArgs to provide the information the backend
// service requires
return serviceArgs;
},
transformDeleteArguments: function(serviceArgs) {
// Alter serviceArgs to provide the information the backend
// service requires
return serviceArgs;
},
transformGetResults: function(deferred, results) {
/*
* JsonRestStore expects the following format:
* [
* { id: "1", ... },
* { id: "2", ... },
* ...
* ]
*/
return results;
},
transformPutResults: function(deferred, results) {
/*
* JsonRestStore does not expect any specific content here
*/
return results;
},
transformPostResults: function(deferred, results) {
/*
* JsonRestStore expects:
* 1) A "Location" response header with location of the new item.
* From the Dojo API:
* The server’s response includes a Location header
* that indicates the id of the newly created object.
* This id will be used for subsequent PUT and DELETE
* requests. JsonRestStore also includes a
* Content-Location header that indicates the temporary
* randomly generated id used by client, and this
* location is used for subsequent PUT/DELETEs if no
* Location header is provided by the server or if
* a modification is sent prior to receiving a response
* from the server.
* NB: There is no JS method for altering response headers.
* You might wish to try overriding the
* deferred.ioArgs.xhr.getResponseHeader() method with your
* own implementation.
* 2) The new item in the following format:
* { id: "1", ... }
*/
return results;
},
transformDeleteResults: function(deferred, results) {
/*
* JsonRestStore does not expect any specific content here
*/
return results;
}
});
|
목록 1의 코드는 dojox.rpc.Rest의 기본 함수를 복제한다. 이는 JsonRestStore로 사용될 수 있으며, 이는 목록 2에 표시된다.
목록 2. JsonRestStore로
EasyRestService의 기본 사용
dojo.require("com.ibm.developerworks.EasyRestService");
dojo.require("dojox.data.JsonRestStore");
var store = new dojox.data.JsonRestStore({
service: new com.ibm.developerworks.EasyRestService("https://mydomain.com/restservice"),
idAttribute : "id"
}); |
기본값으로 EasyRestService 인스턴스는
어떠한 방식으로든 인수와 결과를 변경하지 않는다. 필수 인수와 결과 변환을 수행하기 위해
DefaultHooks를 변경하는 대신에
EasyRestService는 인스턴스 단위로 이러한 변환을 대체하기 위한
메커니즘을 제공한다.
EasyRestService는 사용자 정의 변환자의 공급에 간단한
메커니즘을 제공한다. 목록 3의 예제는 실행하기 전에 GET 호출 구조를 로그하기 위해 EasyRestService의
작동을 변경한다.
목록 3.
EasyRestService 사용자 정의하기
dojo.require("com.ibm.developerworks.EasyRestService");
var transformers = {
transformGetArguments: function(args) {
console.log(args);
return args;
}
};
var service = new com.ibm.developerworks.EasyRestService(
"https://mydomain.com/restservice", transformers);
|
마찬가지로, DefaultHooks에서 모든 변환자는 인스턴스 단위로
대체될 수 있다.
EasyRestService의 두 가지 인스턴스를 작성한다. 즉, 하나는
준수 서비스에 대한 것이고 하나는 비준수 서비스에 대한 것이다. 해당 예제는 이를
JsonRestStore의 두 가지 인스턴스의 서비스 제공자로 사용하고, 저장소에 대해 기본 페치를
실행한다.
목록 4와 목록 5에서 서비스는 JSON 구조가 들어 있는 두 개의 읽기 전용 파일을 사용하여 실물처럼 된다.
목록 4. 준수 REST 서비스, compliantService.json
[
{ id: 1, name: "Phil" },
{ id: 2, name: "John" }
] |
목록 5. 비준수 REST 서비스, noncompliantService.json
{ items : [
{ id: 1, name: "Phil" },
{ id: 2, name: "John" }
] } |
JavaScript는 목록 6에서 코드를 사용하여 저장소를 인스턴스화하고 쿼리할 것이다.
목록 6. 저장소와 상호작용하기 위한 JavaScript 코드
// Create a store using a service that needs no transformations
compliantStore = new dojox.data.JsonRestStore({
service : new com.ibm.developerworks.EasyRestService(
"./compliantService.json"),
idAttribute : "id"
});
// Cause an async fetch from the compliant service
dojo.create("p", {
innerHTML : "Requesting from compliant service"
}, dojo.body(), "last");
compliantStore.fetch({
onComplete : function(items, request) {
console.log(items);
// Log the number of items fetched
dojo.create("p", {
innerHTML : "Got " + items.length + " items from compliant service."
}, dojo.body(), "last");
}
});
// Create a store using a service which needs transformations
// to interpret the results
noncompliantStore = new dojox.data.JsonRestStore({
service : new com.ibm.developerworks.EasyRestService(
"./noncompliantService.json", {
transformGetResults : function(deferred, results) {
// This store wraps its results in an items object
// so return the items object
return results.items;
}
}),
idAttribute : "id"
});
// Cause an async fetch from the noncompliant service
dojo.create("p", {
innerHTML : "Requesting from noncompliant service"
}, dojo.body(), "last");
noncompliantStore.fetch({
onComplete : function(items, request) {
console.log(items);
// Log the number of items fetched
dojo.create("p", {
innerHTML : "Got " + items.length
+ " items from noncompliant service."
}, dojo.body(), "last");
}
}); |
이 기사에서는 자체적인 REST 서비스를 JsonRestStore로 연결하는 방법을 학습했다. 해당 예제는 JsonRestStore가 필요한 서명을 제공하기 위해 서비스 인터페이스를 변환하는 간단한 메소드를 보여 주었다. JsonRestStore가 예상하는 전체 데이터 구조에 대한 정보는 DefaultHooks의 주석, DojoCampus의 JsonRestStore 문서 및 API 문서를 참조해야 한다.
JsonRestStore가 예상하는 전체 데이터 구조에 대한 정보는 참고자료 섹션을 참조한다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| EasyRestService code sample - project archive | EasyRestServiceExample.zip | 6.5KB | HTTP |
교육
- DojoCampus JsonRestStore용 문서를 읽어보자.
- JsonRestStore용 Dojo API를 살펴보자. JsonRestStore는
모든 저장된 수정사항이 REST 명령(GET, PUT, POST 또는 DELETE)을 사용하여 서버로 전송되도록
한다.
제품 및 기술
- IBM 제품 평가판을 다운로드하거나 IBM
SOA Sandbox의 온라인 시험판을 살펴보고 DB2®, Lotus®,
Rational®, Tivoli® 및 WebSphere® 애플리케이션 개발 도구와 미들웨어 제품을 사용해 볼 수 있다.
토론
- 지금 My developerWorks 프로파일을 작성하고 Dojo 및 REST에 대한
관심 목록을 설정하자. My developerWorks에서 최신 정보를 자주 확인하자.
- 웹 개발에 관심이 있는 다른 developerWorks 구성원을 찾아보자.
- 지식 공유: 웹 주제를 중점적으로 다루는 developerWorks 그룹 중 하나에 참여하자.
- Roland Barcia가 자신의 블로그에서 Web 2.0 및 미들웨어에 관해 설명한다.
- developerWorks 멤버가 공유해 놓은 웹 주제관련 책갈피를 따라가 보자.
- 빠른 해답: Web 2.0 Apps forum을 방문하십시오.

Nick Maynard는 영국 허슬리의 IBM Software Solutions Transformation 팀에서 재직 중이다. 그의 전문 분야로는 웹 프로그래밍, Linux, 웹 서비스 및 비즈니스 통합 기술이 있다. 이메일 주소는 nick.maynard@uk.ibm.com이다.