DWR (Direct Web Remoting)
- DWR은 Direct Web Remoting 의 약자로서 ajax를 기반으로 하여 client side 의 javascript 로 server side 의 Java Beans을 interactive하게 조작할 수 있는 framework 입니다.
- 현재 stable version 은 1.1 이고 dwr.dev.java.net 에서 development version인 2.0 소스를 checkout 할 수 있습니다. 특히 2.0 에서는 rajax (reverse ajax)라는 개념으로 역으로 Java Beans에서 client side 의 javascript를 조작가능하도록 구현되어 있습니다.
- 또한 Struts, Spring, Webwork, JSF 등 web framwork 와의 연동을 위한 libraries 를 제공하고 있습니다.
- 테스트를 위한 sample 소스를 82서버의 개발cvs repository 에 'dwr_ex' module 로 등록되어 있으므로 checkout 받아 테스트할 수 있습니다.
- 개발자인 Joe Walker가 운영중인 사이트 http://getahead.ltd.uk/dwr/ 에서 online manual과 많은 정보를 구할 수 있습니다.
DWR Architecture
- DWR은 ajax 와 결합된 Client side, DWRServlet, Server side JavaBeans 를 가지는 MVC패턴과 유사한 구조입니다. 이는 Javascript 로 DWREngine(ajax engine)을 통해 view page 갱신없이 실시간 서비스를 호출하여 dhtml로 화면을 조작할 수 있는 기능을 제공합니다.
- Client side 인 view page는 javascript + html 만으로도 충분히 개발가능하나 페이지 로딩 시 서버에서 가져온 데이터를 리스트 또는 상세내역으로 보여주어야 할 때는 JSP 페이지를 사용하여야 합니다.
- DWRServlet의 경우 controller 의 역할로써 /dwr/engine.js, /dwr/util.js, /dwr/interface/TestObject.js 의 요청이 들어오면(uri-mapping) dwr.xml 에 등록된 TestObject을 참조하여 Javascript를 생성해주는 역할을 수행합니다.
- JavaBean인 TestObject의 경우 Business Layer의 component를 생성하여 서비스를 호출할 수 있는 메소드를 Javascript 에서 사용할 수 있도록 제공해주어야 합니다.
- 위 그림에 대한 설명은 다음과 같습니다.
- 1] Init 수행
페이지 로딩 시 /dwr/engine.js, /dwr/util.js, /dwr/interface/TestObject.js 의 요청이 들어오면 DWRServlet은 dwr.xml를 로딩하여 TestObject 에 대한 메소드를 Javascript로 호출할 수 있도록 생성해 줍니다. 이로써 TestObject를 Javascript에서 조작가능하게 됩니다.
Init 수행 후 TestObject.getList() 호출 시 DWREngine 을 호출하여 서버쪽 TestObject JavaBean의 getList() 메소드를 호출하게 됩니다. 만약 getList() 메소드에 return value가 존재한다면 callback function 을 다음과 같이 정의하여야 합니다.
1] callback function 분리 :
// TestObject 의 getList() 메소드 호출.
TestObject.getList(getListCallback);
function getListCallback(data) {
// returned value 처리
};
2] anonymous function 으로 구현 :
// TestObject 의 getList() 메소드 호출.
TestObject.getList( {
callback : function(data) {
// returned value 처리
},
errorHandler : function(message, exception) {
// server message, error 처리
}
});
- 위와 같이 call method 이후 returned value 손쉽게 combo box 및 table에 자동 셋팅할수 있도록 util.js 내에 DWRUtil 을 제공합니다.
dwr libraries 설치
- dwr 라이브러리는 dwr.jar 하나이며 참조하는 라이브러리도 함께 설치되어야 합니다.
- 최소 설치 libraries :
- dwr.jar : dwr core library
- bsf-2.3.jar : Bean Scripting Framework library ( http://jakarta.apache.org/bsf/index.html )
- bsh-2.0b4.jar : Bean Shell Framework library ( http://www.beanshell.org/ )
- commons-logging-1.0.4.jar : Jakarta Common Logging library ( http://jakarta.apache.org/commons/logging/ )
- commons-validator-1.1.4.jar : Jakarta Common Validator library ( http://jakarta.apache.org/commons/validator/ )
- jakarta-oro-2.0.8.jar : Jakarta ORO library ( http://jakarta.apache.org/oro/ )
- log4j-1.2.12.jar : log4j library ( http://logging.apache.org/log4j/docs/index.html )
- web framework 와 연동하기 위한 선택적인 libraries :
- spring.jar : Spring Framework http://www.springframework.org/
- struts.jar : Struts Framework http://struts.apache.org/
- hibernate3.jar : hibernate Framework http://www.hibernate.org/
- webwork.jar : webwork Framework http://www.opensymphony.com/webwork/
- jsf-api.jar, jsf-impl.jar : JSF Framework http://java.sun.com/javaee/javaserverfaces/reference/docs/index.html
등이 필요합니다.
DWRServlet 설정 - web.xml
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<description>Direct Web Remoter Servlet</description>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>dwr-user-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>config-user</param-name>
<param-value>WEB-INF/dwr-user.xml</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>dwr-admin-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>config-admin</param-name>
<param-value>WEB-INF/dwr-admin.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-admin-invoker</servlet-name>
<url-pattern>/dwradmin/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dwr-user-invoker</servlet-name>
<url-pattern>/dwruser/*</url-pattern>
</servlet-mapping>
dwr.xml 작성
- dwr.xml에 javascript 객체로 조작할 JavaBean 을 설정함
- 해당 JavaBean에 대한 Life scope 지정이 가능 (page, script, request, session, application)
- JavaBean에서 사용해야할 메소드 filtering : include, exclude method 지정
- Value object(JavaBean) 사용하기 위한 각종 converter 제공
- ajax 요청을 가로채는 Filter 설정
- 다음은 DwrTest을 Javascript 에서 사용하기 위한 설정입니다.
- Foo 객체의 경우 updateFoo(Foo foo), deleteFoo(Foo foo) 메소드 호출 시 파라미터의 Value Object 로써 사용하기 위해 bean converter 로 설정되어 있음
<dwr>
<init>
...
</init>
<allow>
…
<create creator="new" javascript="DwrTest" scope="request">
<param name="class" value="fullbox.dwr.test.DwrTest"/>
</create>
<convert match="fullbox.dwr.test.Foo" converter="bean"/>
…
</allow>
</dwr>
client page 작성
- client page 내에는 다음을 반드시 작성하여야 합니다.
- DwrTest.js 는 동적으로 생성되는 스크립트로써 DwrTest 객체를 조작하기 위해서는 반드시 아래와 같이 작성해 주어야 함.
- engine.js 는 DWREngine 이 정의된 스크립트임
- util.js 는 DWRUtil 이 정의된 스크립트임
- example (dwr_ex.html 참조)
<html>
<head>
...
<script language="javascript" src="/dwr/interface/DwrTest.js"></script>
<script language="javascript" src="/dwr/engine.js"></script>
<script language="javascript" src="/dwr/util.js"></script>
</head>
<body>
</body>
</html>
dwr_ex 모듈 테스트
- 82서버 개발 cvs 에서 dwr_ex 모듈을 checkout 받습니다.
- tomcat의 web context 로 'dwr_ex'를 설정하여 tomcat을 기동합니다.
- eclipse wtp를 사용하여 web context로 등록하면 tomcat 에 다음과 같이 설정됩니다.
<Context docBase="dwr_ex" path="/dwr_ex" reloadable="true" source="org.eclipse.jst.j2ee.server:dwr_ex"/>
dwr example 개발
에러 메시지 핸들러 구현
- Javascript 로 조작하기 위한 DwrExample 클래스를 다음과 같이 정의합니다.
package fullbox.dwr.test;
import java.util.Date;
public class DwrExample {
public String getData1() {
throw new NullPointerException("NullPointerException occurred!");
}
}
<create creator="new" javascript="DwrExample" scope="session">
<param name="class" value="fullbox.dwr.test.DwrExample"/>
</create>
- 먼저 여러 개의 ErrorHandler function 을 지정합니다. 이 function 들은 error message 를 다양하게 표시하기 위한 로직을 포함하고 있습니다. 아래는 간단히 메시지만 다르게 하여 서로 다른 ErrorHandler function 을 구현한 예제입니다.
// Global ErrorHandler 를 지정하는 function.
function eh0(msg) {
alert("Global ErrorHandler : " + msg);
}
// 새로운 ErrorHandler 를 지정하는 function 1).
function eh1(msg) {
alert("Applied New ErrorHandler : " + msg);
}
// 새로운 ErrorHandler 를 지정하는 function 2).
function eh2(msg, ex) {
// FunkyException 에 존재하는 getWhen() 메소드를 ex.when 으로 호출.
alert(msg + ", date=" + ex.when);
}
- 한 페이지의 모든 메시지의 처리를 동일하게 표시하려면 body태그의 onLoad() 시 init()을 실행하도록 합니다. init() function 은 먼저 Global ErrorHandler function 인 eh0 을 설정합니다.
// body onLoad() 에서 초기화하는 function : Global ErrorHandler를 설정.
function init() {
// ErrorHandler, WarningHandler 설정.
DWREngine.setErrorHandler(eh0);
DWREngine.setWarningHandler(eh0);
// Loading Message 설정.
DWRUtil.useLoadingMessage();
}
- 따라서 객체의 모든 메소드 호출 시 오류가 발생하면 위에서 정의한 대로 메시지가 처리됩니다.
- 객체의 특정 메소드 호출 시(ex : DwrExample.getData1()) 처리 메시지를 다르게 보여주어야 한다면 해당 메소드를 호출하기 전에 DWREngine.setErrorHandler() 를 다음과 같이 재정의하여야 합니다.
function getDataByAppliedErrorHandler() {
// 새로운 ErrorHandler를 적용.
DWREngine.setErrorHandler(eh1);
DwrExample.getData1(getDataCallback);
}
- 단, 위 방법은 DWREngine.setErrorHandler() 에 직접 설정하는 것으로 Global하게 적용되므로 서로 다른 메소드 호출 시 각각 다른 방법으로 메시지를 처리하는 것은 부적절합니다.
function getDataByLocalErrorHandler() {
// 새로운 Local ErrorHandler를 적용.
DwrExample.getData1({
callback : getDataCallback,
errorHandler : function(msg, ex) {
alert("Local ErrorHandler : " + msg);
}
});
}
사용자 정의 Exception 처리
package fullbox.dwr.test;
import java.util.Date;
public class FunkyException
extends RuntimeException {
private final static long serialVersionUID = 1L;
private Date date;
public FunkyException(String msg) {
super(msg);
}
public FunkyException(String msg, Date date) {
super(msg);
this.date = date;
}
public Date getWhen() {
return date;
}
}
- 먼저 구현한 Exception (ex : FunkyException) 을 dwr.xml 의 bean converter로 설정하여야 합니다.
<convert match="fullbox.dwr.test.FunkyException" converter="bean" />
package fullbox.dwr.test;
import java.util.Date;
public class DwrExample {
public String getData1() { ... }
public String getData2() {
Date when = new Date();
// FunkyException 예외 발생.
throw new FunkyException("FunkyException occurred!", when);
}
}
- dwr_ex01.html 에서 객체의 특정 메소드 호출 시(ex : DwrExample.getData2()) 처리 메시지를 다르게 보여주어야 한다면 해당 메소드를 호출하기 전에 DWREngine.setErrorHandler() 를 다음과 같이 재정의하여야 합니다.
function getDataThrownFunkyExceptionByAppliedErrorHandler() {
// 새로운 ErrorHandler를 적용.
DWREngine.setErrorHandler(eh2);
DwrExample.getData2(getDataCallback);
}
- 단, 위 방법은 DWREngine.setErrorHandler() 에 직접 설정하는 것으로 Global하게 적용되므로 서로 다른 메소드 호출 시 각각 다른 방법으로 메시지를 처리하는 것은 부적절합니다.
function getDataThrownFunkyExceptionByLocalErrorHandler() {
// 새로운 Local ErrorHandler를 적용.
DwrExample.getData2({
callback : getDataCallback,
errorHandler : function(msg, ex) {
alert("Local ErrorHandler : " + msg + " , date=" + ex.when);
}
});
}
- DwrExample 클래스에 name 을 셋팅하는 setName() 메소드와, Foo 객체를 셋팅하는 setFoo() 메소드를 추가합니다.
package fullbox.dwr.test;
import java.util.Date;
public class DwrExample {
... 이전 예제의 메소드 ...
private Foo foo;
public void setName(String name) {
System.out.println("### name : " + name);
}
public void setFoo(Foo foo) {
System.out.println("### foo.name : " + foo.getName());
System.out.println("### foo.title : " + foo.getTitle());
System.out.println("### foo.content : " + foo.getContent());
this.foo = foo;
}
public Foo getFoo() {
return foo;
}
}
- Foo 클래스는 name, title, content 필드를 갖고 각 set, get 메소드를 가진 클래스입니다. dwr_ex 모듈 내 src/fullbox/dwr/test/Foo.java 를 참조하십시요.
- dwr.xml 에 보면 Foo 클래스는 다음과 같이 bean converter 로 설정되어 있습니다.
<convert match="fullbox.dwr.test.Foo" converter="bean"/>
- dwr_ex02.html 에서 다음과 같이 function 을 작성합니다.
// name 값을 DwrExample JavaBean에 셋팅.
function setName() {
var name = frm.name.value;
DwrExample.setName(name);
}
// name, title, content 값을 DwrExample JavaBean에 셋팅.
function setFoo() {
var foo = {
name : frm.name.value,
title : frm.title.value,
content : frm.content.value
};
DwrExample.setFoo(foo);
}
// 저장된 Foo 값을 가져오는 function.
function getFoo() {
DwrExample.getFoo(getFooCallback);
}
// getFoo()의 callback function.
function getFooCallback(foo) {
DWRUtil.setValue("data",
"name : " + foo.name +
" , title : " + foo.title +
" , content : " + foo.content);
}
- 입력폼을 생성하고 function setName(), setFoo() 을 호출하여 tomcat 콘솔 로그창에 셋팅된 값이 정상적으로 출력되는지 확인합니다.
- getFoo() 을 호출하면 div 태그 내에 값이 변경되는지 확인합니다.
combobox 동적 구성
- DwrExample 클래스에 주 리스트를 가져오는 메소드와 도시 리스트를 가져오는 메소드를 다음과 같이 정의합니다.
package fullbox.dwr.test;
import java.util.Date;
public class DwrExample {
... 이전 예제의 메소드 ...
private String[][] address = {
{"Florida", "Bryceville"}
,{"Florida", "Elkton"}
,{"Florida", "Glen Saint Mary"}
,{"Michigan", "Birmingham"}
,{"Michigan", "Marine City"}
,{"Michigan", "Chesterfield"}
,{"Minnesota", "Almelund"}
,{"Minnesota", "Castle Rock"}
,{"Minnesota", "Dalbo"}
,{"California", "Los Angeles"}
,{"California", "Orange"}
};
public Map getComboListState() {
Map map = new HashMap();
String curState;
String prevState = "";
for( int i = 0 ; i < address.length ; i++ ) {
curState = address[i][0];
if(!curState.equals(prevState))
map.put(address[i][0], address[i][0]);
prevState = curState;
}
return map;
}
public Map getComboListCity() {
Map map = new HashMap();
for( int i = 0 ; i < address.length ; i++ ) {
map.put(address[i][1], address[i][1]);
}
return map;
}
}
- dwr_ex03.html 에서 다음과 같이 function 을 작성합니다.
// dynamic Combobox 설정 function.
function getComboListState() {
DwrExample.getComboListState(getListComboCallback);
}
// dynamic Combobox 설정 function.
function getComboListCity() {
DwrExample.getComboListCity(getListComboCallback);
}
// callback function.
function getListComboCallback(data) {
DWRUtil.removeAllOptions("listCombo");
var defaultSelect = {"":" -- select -- "};
DWRUtil.addOptions("listCombo", defaultSelect);
DWRUtil.addOptions("listCombo", data);
}
- 빈 select 태그의 name 값을 'listCombo' 로 정의합니다.
- function getComboListState(), getComboListCity() 를 호출하였을 때 listCombo 박스의 내용이 동적으로 변경되는지 확인합니다.
상위 combobox 선택에 따른 하위 combobox 동적 구성
- 이번 예제는 상위 Combobox 에서 특정 주를 선택 시 해당하는 하위 도시 리스트를 sub Combobox에 동적으로 셋팅하는 예제입니다.
package fullbox.dwr.test;
import java.util.Date;
public class DwrExample {
... 이전 예제의 메소드 ...
private String[][] address = { ... };
public Map getComboListState() { ... }
public Map getComboListCityByState(String state) {
Map map = new HashMap();
for( int i = 0 ; i < address.length ; i++ ) {
if(address[i][0].equals(state))
map.put(address[i][1], address[i][1]);
}
return map;
}
}
- dwr_ex04.html 에서 다음과 같이 function 을 작성합니다.
// init() function 에 페이지 로딩 시 stateCombo박스를 셋팅하는 것을 호출합니다.
function init() {
... ErrorHandler 설정...
// dynamic state Combobox 설정 function.
DwrExample.getComboListState(getComboListStateCallback);
}
// dynamic Combobox 설정 function.
function getComboListCityByState() {
// $() 으로 현재 html doucument 내 지정된 id, ids 에 해당하는 element object를 가져옴.
var state = $("stateCombo").value;
DwrExample.getComboListCityByState(state, getComboListCityCallback);
}
// callback function.
function getComboListStateCallback(data) {
DWRUtil.removeAllOptions("stateCombo");
var defaultSelect = {"":" -- select -- "};
DWRUtil.addOptions("stateCombo", defaultSelect);
DWRUtil.addOptions("stateCombo", data);
}
// callback function.
function getComboListCityCallback(data) {
DWRUtil.removeAllOptions("cityCombo");
var defaultSelect = {"":" -- select -- "};
DWRUtil.addOptions("cityCombo", defaultSelect);
DWRUtil.addOptions("cityCombo", data);
}
- 두개의 빈 select 태그를 생성하고 각각 name 값을 'stateCombo', 'cityCombo' 로 정의합니다.
- stateCombo 의 onChange="getComboListCityByState();" 를 설정합니다.
- 작성완료 후 페이지 로딩 시 stateCombo 박스에 정상적으로 값이 셋팅되는지 확인합니다.
- stateCombo박스의 값을 변경하면 정상적으로 cityCombo박스의 값이 변경되는지 확인합니다.
검색어추천 박스 만들기
- 이번 예제는 입력박스에서 하나씩 입력할 때마다 실시간으로 입력된 단어로 시작되는 추천단어들을 검색해주는 예제입니다.
단, 현재 100 ~ 199 까지의 숫자문자열만 배열에 저장되어 있으므로 검색어 범위를 100 ~ 199 사이만 입력하여야 합니다. 그리고 검색어 추천 리스트에서 특정 검색어를 더블클릭하면 div 태그에 선택한 검색어를 표시합니다.
package fullbox.dwr.test;
import java.util.Date;
public class DwrExample {
... 이전 예제의 메소드 ...
private static String[] titles = new String[199];
static {
for( int i = 100 ; i < titles.length ; i++ ) {
titles[i] = "" + i;
}
}
public List getRecommendSearch(String title) {
List result = new ArrayList();
for( int i = 100 ; i < titles.length ; i++ ) {
if(titles[i].startsWith(title)) {
result.add(titles[i]);
}
}
return result;
}
}
- dwr_ex05.html 에서 다음과 같이 function 을 작성합니다.
function recommendSearch() {
var arg = $("title").value;
DwrExample.getRecommendSearch(arg, recommendSearchCallback);
}
function recommendSearchCallback(data) {
// table 내 각 열에 대한 정의를 다음과 같이 각 열대로의 function 을 정의함
var cellFuncs = [
function(data) {
return "<input type='text' name='recommend' style='border:0px;cursor:hand' " +
" value='" + data + "' onClick='selectRecommend(this.value)' readOnly />";
}
];
DWRUtil.removeAllRows("RecommendTable");
DWRUtil.addRows("RecommendTable", data, cellFuncs);
}
function selectRecommend(data) {
DWRUtil.setValue("recommendDiv", data);
}
- 다음은 검색어를 입력할 입력박스와 div태그, 그리고 검색어 추천 리스트를 보여줄 table태그를 다음과 같이 정의합니다.
<form name="frm">
<table width="60%">
<tr>
<td width="50%">
<input type="text" name="title" value="" onKeyUp="recommendSearch();" />
</td>
<td width="50%">
<div id="recommendDiv" style="border:1px solid #000000;">
Recommended argments
</div>
</td>
</tr>
</table>
</form>
<table style="border:1px solid #000000;" cellspacing="0" cellpadding="0">
<tbody id="RecommendTable" border="0" >
</tbody>
</table>
- 위 소스에서 입력박스에 있는 onKeyUp() 이벤트 핸들러를 통해서 키를 누른 후 땔 때마다 검색어 추천리스트를 서버에서 조회하게 됩니다. 정상적으로 작동하는 지 입력박스에 한단어씩 입력하여 테스트합니다.
- 조회된 검색어 추천 리스트에서 더블클릭했을 때 입력박스 옆의 recommendDiv 에 선택한 검색어 추천 단어가 정상적으로 셋팅되는지 확인합니다.
간단한 게시판(CRUD 기능) 만들기
- 이번 예제는 검색폼을 이용하여 검색한 리스트에서 입력,수정,삭제를 하는 간단한 게시판을 구현합니다.
- 이 게시판은 페이지의 갱신없이 CRUD 가 수행되므로 동적인 페이지의 변경 및 리스트의 갱신 등을 필요로 하는 복잡한 화면에서 사용하기에 적절합니다.
- DwrExample 클래스에서 다음과 같이 데이터베이스의 연결없이 static variable 로 CRUD를 처리하는 메소드를 간단히 구현합니다.
package fullbox.dwr.test;
import java.util.Date;
public class DwrExample {
... 이전 예제의 메소드 ...
// 데이터의 선언
private static Map datas = new HashMap();
static {
for (int i = 100 ; i < 199 ; i++ ) {
Foo foo = new Foo();
foo.setName("" + i);
foo.setTitle("title" + i);
foo.setContent("content" + i);
datas.put(foo.getName(), foo);
}
}
// 조회 메소드
public List listFoo(String name) {
List result = new ArrayList();
Iterator it = new TreeMap(datas).values().iterator();
while(it.hasNext()) {
Foo foo = (Foo)it.next();
if(foo.getName().startsWith(name)) {
result.add(foo);
}
}
return result;
}
// 상세내역 조회 메소드
public Foo viewFoo(String name) {
return (Foo)datas.get(name);
}
// 입력, 수정 메소드
public void updateFoo(Foo foo) {
datas.remove(foo.getName());
datas.put(foo.getName(), foo);
}
// 삭제 메소드
public void deleteFoo(Foo foo) {
datas.remove(foo.getName());
}
}
- dwr_ex06.html 에서 다음과 같이 function 을 작성합니다.
// 조회 function
function listFoo() {
var arg = $("search").value;
// 첫번째 매개변수는 실매개변수로, 두번째는 callback function을 셋팅.
DwrExample.listFoo(arg, listFooCallback);
}
// 조회 callback function
function listFooCallback(foos) {
// fooTable tbody 내에서 다시 조회 되었을 때 id 가 'pattern'인 tr태그만 남겨두고 나머지 삭제.
DWRUtil.removeAllRows("fooTable", {
filter : function(tr) {
return (tr.id != "pattern");
}
});
// 루프 돌면서 foo 리스트를 각 span 태그의 id에 해당하는 위치에 값 셋팅.
for (var i = 0; i < foos.length; i++) {
var foo = foos[i];
var name = foo.name;
DWRUtil.cloneNode("pattern", { idSuffix:name });
DWRUtil.setValue("tname" + name, name);
DWRUtil.setValue("ttitle" + name, foo.title);
DWRUtil.setValue("tcontent" + name, foo.content);
// 복사한 tr 태그를 화면에 나타나게 함.
$("pattern" + name).style.display = "";
}
}
// 상세내역 조회 function
function viewFoo(pttnid) {
// pttnid 는 'pattern110' 이런 형태이므로 'pattern' prefix를 잘라낸 나머지가 foo.name 임.
var name = pttnid.substring(7);
DwrExample.viewFoo(name, function(foo) {
DWRUtil.setValue("name", foo.name);
DWRUtil.setValue("title", foo.title);
DWRUtil.setValue("content", foo.content);
// 수정, 삭제 시 name 필드는 readonly로 셋팅.
$("name").readonly = true;
});
}
// 입력,수정 function
function insertFoo() {
var foo = {
name : $("name").value,
title : $("title").value,
content : $("content").value
};
// DWREngine의 batch 단위로 묶어서 일괄처리 함. performance 향상.
DWREngine.beginBatch();
DwrExample.updateFoo(foo);
listFoo();
DWREngine.endBatch();
}
// 삭제 function
function deleteFoo() {
var foo = {
name : $("name").value,
title : null,
content : null
};
// DWREngine의 batch 단위로 묶어서 일괄처리 함. performance 향상.
DWREngine.beginBatch();
DwrExample.deleteFoo(foo);
listFoo();
frmReset();
DWREngine.endBatch();
}
// 폼 리셋 function
function frmReset() {
var val = frm.search.value;
frm.reset();
frm.search.value = val;
frm.name.readonly = false;
}
- 위 스크립트는 기능별로 구현한 것이므로 function 내 주석을 참조하면 쉽게 이해할 수 있습니다.
- 다음은 검색어를 입력할 입력박스와 조회버튼에 대한 태그를 다음과 같이 작성합니다. 이벤트 핸들러는 onclick 입니다.
<table width="60%">
<tr>
<td>
<input type="text" name="search" value="" />
<input type="button" name="listfoo" value="search" onclick="listFoo();" />
</td>
</tr>
</table>
- 다음은 리스트를 표시할 테이블과 상세내역 테이블을 다음과 같이 작성합니다. 리스트 테이블의 tr태그 내의 viewFoo() function 이 onDblClick 이벤트핸들러로 설정되어 있습니다.
<table width="60%" style="border:1px solid #000000;" cellspacing="0" cellpadding="0">
<thead>
<tr>
<th width="20%">Name</th>
<th width="40%">Title</th>
<th width="40%">Content</th>
</tr>
</thead>
<tbody id="fooTable">
<tr id="pattern" style="display:none;cursor:hand;" onDblClick="viewFoo(this.id);" >
<td><span id="tname">Name</span></td>
<td><span id="ttitle">Title</span></td>
<td><span id="tcontent">Content</span></td>
</tr>
</tbody>
</table>
<br />
<table width="60%">
<tr>
<td>Name</td>
<td width="80%"><input type="text" id="name" name="name" value="" /></td>
</tr>
<tr>
<td>Title</td>
<td><input type="text" id="title" name="title" value="" /></td>
</tr>
<tr>
<td>Content</td>
<td><input type="text" id="content" name="content" value="" /></td>
</tr>
</table>
- 위 소스에서 viewFoo() 의 매개변수의 값으로 tr 태그의 id 값을 넘겨주는 데 이는 'pattern' + 'name 값' 이므로 각 tr태그마다 유일하게 정의되어 있습니다. 그러므로 prefix 인 'pattern'을 제외한 'name 값'을 해당 tr태그의 키값으로 하여 각 foo 를 수정하거나 삭제할 수 있습니다.
- 다음은 CUD 버튼을 작성합니다. 각각 function은 button 의 onclick 이벤트 핸들러로 설정되어 있습니다.
<table width="60%">
<tr>
<td>
<input type="button" name="insert" value="insert Foo" onclick="insertFoo();" />
<input type="button" name="update" value="update Foo" onclick="insertFoo();" />
<input type="button" name="delete" value="delete Foo" onclick="deleteFoo();" />
</td>
</tr>
</table>
- dwr_ex06.html 의 코드 작성 완료 후 테스트를 위하여 '100' ~ '199' (데이터값) 의 특정 값을 입력하여 'search' 버튼을 선택합니다.
- 조회된 리스트에서 특정 데이터를 선택합니다. 그리고 아래 상세내역에서 title, content 항목을 수정 후 'update Foo' 버튼을 선택하면 정상적으로 수정되고 조회되는지 확인합니다.
- 조회된 리스트에서 특정 데이터를 선택합니다. 그리고 아래 상세내역에서 'delete Foo' 버튼을 선택하면 정상적으로 삭제되고 조회 후 데이터가 삭제되었는지 확인합니다.
- 아래 상세내역 초기화 상태에서 name, title, content 항목을 입력후 'insert Foo' 버튼을 선택하면 정상적으로 입력되고 조회되는지 확인합니다.
Web Framework Integration
- struts, spring, webwork, jsf 등 web framework 의 bean 설정 파일에 등록된 JavaBeans를 DWR 에서 Javascript 로 조작가능하게 하는 것으로 간단한 설정만으로 연동이 가능합니다.
Struts Integration
- DWR 보다 Struts 의 설정이 먼저 초기화되어야 하므로 web.xml 에 Servlet(Struts 의 ActionServlet, DWR의 DWRServlet) 설정 시 load-on-startup 태그로 초기화 순서를 정해야 합니다.
<web-app>
...
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
...
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
...
<load-on-startup>2</load-on-startup>
</servlet>
...
</web-app>
- Struts 의 설정파일인 struts-config.xml 에 다음과 같이 DwrExample 을 설정합니다.
<struts-config>
..
<form-beans>
<form-bean name="DwrExample" type="fullbox.dwr.test.DwrExample" />
</form-beans>
..
</struts-config>
- DWR 의 설정파일인 dwr.xml 에 struts-config.xml 에 설정된 DwrExample 을 다음과 같이 설정합니다.
<allow>
...
<create creator="struts" javascript="DwrExample">
<param name="formBean" value="DwrExample"/>
</create>
...
</allow>
Spring Integration
- 'dwr_ex' 모듈 내 WebContent/dwr_ex06_spring.html 을 참조하십시요.
- Spring의 설정파일을 로딩하기 위해 web.xml 에 다음과 같이 설정합니다.
<web-app>
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
...
</web-app>
- Spring 의 설정파일인 beans.xml 에 다음과 같이 DwrExample 을 설정합니다.
<beans>
...
<bean id="DwrExample" class="fullbox.dwr.test.DwrExample" />
...
</beans>
- DWR 의 설정파일인 dwr.xml 에 beans.xml 에 설정된 DwrExample 을 다음과 같이 설정합니다.
<allow>
...
<create creator="spring" javascript="DwrExample">
<param name="beanName" value="DwrExample"/>
</create>
...
</allow>
Webwork Integration ( 스터디 중...)
JSF Integration ( 스터디 중...)
reference sites
Comments (0)
You don't have permission to comment on this page.