DWR은 쉬운 방법으로 AJAX 와 XMLHttpRequest를 사용하길 원하는 개발자를 위한 오픈 소스 솔루션이다.
DWR 은 Direct Web Remoting 의 약자로 클라이언트 쪽의 AJAX 관련 자바스크립트 라이브러리 뿐만 아니라 서버쪽의 웹 어플리케이션 라이브러리 까지 포함하고 있다.
참고 : http://www.javapassion.com/ajaxcodecamp/#Direct_Web_Remoting_DWR
DWR은 크게 두 개의 파트로 구성되어 있다.
Useful Information DWR웹 어플리케이션은 크게 모든 요청을 처리하는 컨트롤러 역할을 하는 DWRServlet 과 클라이언트의 요청을 처리할 빈들로 구성되어 있다.DWRServlet 은 웹 어플리케이션의 /WEB-INF/web.xml 에서 설정이 가능하고 요청들을 처리할 빈( bean) 객체들은 /WEB-INF/dwr.xml 에서 설정할 수 있다. |
아래 샘플파일은 참고자료에서 다운받은 것을 기준으로 작성되었다.
web.xml
<servlet> <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <description>Direct Web Remoter Servlet</description> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param></servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern></servlet-mapping>
보면 DWR 웹 어플리케이션에서는 모든 요청을 일단 DWRServlet 이 받아서 처리하게 되는데 그 요청을 처리할 객체나 데이터 타입의 맵핑, 보안등의 설정은 모두 dwr.xml 의 설정을 바탕으로 한다
dwr.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"><dwr> <allow> <convert converter="bean" match="com.tmax.spring.domain.Apartment"/> <create creator="new" javascript="ApartmentDAO" > <param name="class" value="com.tmax.spring.ibatis.ApartmentDAO"/> <include method="findApartments"/> <include method="countApartments"/> </create> </allow></dwr>
DWR에서 요청을 처리할 빈(bean) 객체를 지정하는 부분이 <create />이다.
create 요소에는 creator,javascript,scope의 3가지 속성이 지정될 수 있다 creator 속성은 요청을 처리할 빈 객체가 생성되는 방식을 지정하는데 직접 클래스의 이름을 지정하여 생성할 수도 있고 빈 셸 스크립트를 이용하거나 Spring framework 에 객체 생성을 위임할 수도 있다. 기본적으로 가장 많이 사용되는 create 속성인 new 는 지정된 클래스의 인스턴스를 생성하여 클라이언트 쪽에서 오는 자바스크립트 요청과 지정된 인스턴스의 메소드를 맵핑해준다.
Scope 속성 은 creator 로 지정된 객체의 라이프싸이클 범위를 지정하는 것으로 J2EE 서블릿 스펙에서 정의된 scope 와 동일하게 application, page, request,session 중 하나의 값을 가질 수 있다.
creator 는 include 혹은 exclude 요소를 가질 수 있는데 DWR 서비스 빈 객체에서 호출할 수 있는 메소드를 제한하는데 사용할 수 있다.
ApartmentDAO : 쿼리를 가져오는 실제 메소드가 들어있는 클래스
Apartment : id,bedrooms,bathrooms,price,address,city,province를 가진 도메인 객체
search.jsp
<script src='dwr/interface/ApartmentDAO.js'></script> <script src='dwr/engine.js'></script> <script src='dwr/util.js'></script>... <script language="javascript"> function updateTotal() { $("resultTable").style.display = 'none'; var bedrooms = document.getElementById("bedrooms").value; var bathrooms = document.getElementById("bathrooms").value; var price = document.getElementById("price").value; ApartmentDAO.countApartments(loadTotal, bedrooms, bathrooms, price); } function loadTotal(data) { document.getElementById("totalRecords").innerHTML = data; }</script>...
DWR은 클라이언트가 서버단의 서비스 객체를 호출하기 위한 일종의 스텁 스크립트를 자동으로 생성해준다.
dwr/interface/*는 서버단의 서비스 객체를 호출하기 위한 자바스크립트를 만들어주는 URL이다. 그렇기 때문에 ApartmentDAO객체를 클라이언트 쪽에서 사용하기 위해서는 미리 ApartmentDAOrk 정의 되어 있는 자바스크립트를 소스에 포함시켜야 한다.
<script src='dwr/interface/ApartmentDAO.js'></script>
그러면 ApartmentDAO의 메소드를 스크립트 안에서 사용할 수 있다.
ApartmentDAO.countApartments(loadTotal, bedrooms, bathrooms, price);
parameter 중 맨 앞은 countApartment가 수행한 뒤 호출되야 하는 callback 메소드, 그 뒤는 countApartment 메소드의 parameter 이다.
callback 메소드 에서 처리를 할 수 있다.
document.getElementById("totalRecords").innerHTML = data
countApartments 메소드를 수행하고 나온 결과를 totalRecords 라는 태그에 innerHTML로 설정한다.
<dwr> <allow> <convert converter="bean" match="dwr.sample.Apartment"/> <create creator="new" javascript="ApartmentDAO" class="dwr.sample.ApartmentDAO"> <include method="findApartments"/> <include method="countApartments"/> </create> </allow></dwr>
로 되어있다. 하지만 <create/>는 class 를 attribute 로 가지지 않는다. 그래서 에러 발생..
<create creator="new" javascript="ApartmentDAO" class="dwr.sample.ApartmentDAO"> 를
<create creator="new" javascript="ApartmentDAO" >
<param name="class" value="com.tmax.spring.ibatis.ApartmentDAO"/>
</create>
으로 변경한 뒤 정상적으로 수행이 되었다.
수행한 결과
원하는 방 갯수, 화장실 갯수, 가격 대를 선택하면 조건에 맞는 아파트의 수가 나오고 show results 라는 버튼을 클릭하면 주소,방갯수,화장 실갯수,가격등의 정보가 조회된다.
만일 예제와 같은 결과를 얻기 위해 DWR 를 이용하지 않았다면 XMLHttpRequest 객체를 얻는 과정부터 소스를 작성했어야 할 것이다.
하지만 DWR을 사용함으로써 이전의 과정은 모두 DWR 에 맡기고 실제 작동하는 서버 클래스 작성, dwr.xml 작성에만 신경을 쓸 수 있게 되었다.
이전방식
예를 들어
com.tmax.tody.dwr.tody.ProductDwr 클래스가 dwr 에서 사용할 클래스 인 경우
dwr.xml , applicationContext.xml 에 모두 관련 내용을 작성해야 했다.
우선 applicationContext.xml 에 사용하는 클래스에 대한 bean 선언을 해야 한다.
application.xml
<bean id="productDwr" class="com.tmax.tody.dwr.tody.ProductDwr" > <property name="moduleService"> <ref bean="moduleService"/> </property> <property name="versionService"> <ref bean="versionService"/> </property> </bean>
dwr.xml 에 create 요소의 creator 속성 중 spring 을 이용하여 해당 bean 을 가져다 쓸 수 있도록 선언을 해야 한다.
dwr.xml
<create creator="spring" javascript="ProductDwr"> <param name="beanName" value="productDwr"/> </create> <convert converter="bean" match="com.tmax.tody.domain.tody.*"/>
선언을 한 후 실제 사용하는 jsp 에서는
<script type='text/javascript' src='dwr/interface/ProductDwr.js'></script><script type='text/javascript' src='dwr/engine.js'></script><script type='text/javascript' src='dwr/util.js'></script><script type="text/javascript"> function setSubVersion(mainVersionCode, systemCode, userId) { var productCode = $("productCode").value; ProductDwr.findSearchSubVersions(productCode, systemCode, userId, mainVersionCode,listMainVersionProcess); }</script><select name="mainVersionCode" class="input" onchange="*setSubVersion*(this.value, '${sessionScope.currentSystem}', '${sessionScope.user.id}');" style="width:48%;"> <option value="">::: 선택 :::</option> <c:if test="${not empty mainVersions}"> <c:forEach var="mv" items="${mainVersions}"> <option value="${mv.versionCode}" ${(param.mainVersionCode == mv.versionCode)? 'selected':''}>${mv.versionName}</option> </c:forEach> </c:if></select><c:choose> <c:when test="${not empty subVersions}"> <select name="subVersionCode" class="input" style="width:50%;"> <option value="">::: 선택 :::</option> <c:if test="${not empty subVersions}"> <c:forEach var="sv" items="${subVersions}"> <option value="${sv.versionCode}" ${(param.subVersionCode == sv.versionCode)? 'selected':''}>${sv.versionName}</option> </c:forEach> </c:if> </select> </c:when> <c:otherwise> <select name="subVersionCode" class="input" style="width:50%; display:'none';"> <option value="">::: 선택 :::</option> </select> </c:otherwise></c:choose>
DWR2.0+Spring
DWR2.0과 Spring2.0이 나오면서 Spring의 context.xml에 XML Namespace 형식으로 DWR를 적용할 수 있게 되었다.
applicationContext.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd"> <dwr:configuration> <dwr:convert match="com.tmax.tody.domain.tody.*" type="bean"/> </dwr:configuration> <bean id="productDwr" class="com.tmax.tody.dwr.tody.ProductDwr"> <dwr:remote javascript="ProductDwr" /> <property name="moduleService"> <ref bean="moduleService"/> </property> <property name="versionService"> <ref bean="versionService"/> </property> </bean>
설정 면에서도 간편해져서 사용하기에 좀 더 편리하게 되었다.
이 외에도 DWR2.0 이 되면서 더욱더 많은 장점을 가지게 되었다.
그것은.. http://getahead.org/dwr/changelog/dwr20 을 보면 자세히 나와있다.
util.js, engine.js 에서도 다양한 함수들을 사용할 수 있다.
http://getahead.org/dwr/browser/util
참고할 링크