IT/Flex

제5회 Flex의 체제, Cairngorm로 검색 어플리케이션 작성

craveu 2008. 3. 11. 19:28
출처 : http://2005elc.elancer.co.kr/eTimes/page/eTimes_view.html?str=c2VsdW5vPTU0NzU=

1. BusinessDelegate의 작성
2007/01/31  

 전회(Flex의 체제, Cairngorm로 샘플 어플리 )에서는 실제로 Cairngorm Framework를 사용하여 애플리케이션 서버의 검색·등록·갱신·삭제를 실시하는 간단한 샘플 애플리케이션을 중간정도 작성했습니다. 최종회가 되는 이번 회에는 샘플 애플리케이션의 나머지의 부분(서버 사이드 제휴 부분)을 작성하여 샘플 애플리케이션을 완성시키도록 하겠습니다.

BusinessDelegate의 작성

 그러면, 서버 사이드 제휴 부분을 작성해보도록 하겠습니다. 우선, BusinessDelegate을 작성합니다. 아래의 리스트는 샘플 애플리케이션의 BusinessDelegate인 SampleappBusinessDelegate.as입니다. 이것을 cairngorm-sample/WebContent/sample/business에 작성합니다.

리스트 1 SampleappBusinessDelegate.as

import org.nevis.cairngorm.business.Responder;
import org.nevis.cairngorm.business.ServiceLocator;
import sample.vo.APServerVO;
import mx.utils.Delegate;

class sample.business.SampleappBusinessDelegate {

     private var service : Object;

     private var responder : Responder;

     public function SampleappBusinessDelegate
(responder : Responder)
 {(본래는 1행)
         this.service = ServiceLocator.getInstance().
getService("sampleAppService")
;(본래는 1행)
         this.responder = responder;
     }

     public function searchAPServer(searchCond : String) : Void {
         var call = service.search(searchCond);
         call.resultHandler = Delegate.create(responder, responder.onResult);
         call.faultHandler = Delegate.create(responder, responder.onFault);
     }

     public function registAPServer(apServerVO : APServerVO) : Void {
         var call = service.regist(apServerVO);
         call.resultHandler = Delegate.create(responder, responder.onResult);
         call.faultHandler = Delegate.create(responder, responder.onFault);
     }

     public function updateAPServer(apServerVO : APServerVO) : Void {
         var call = service.update(apServerVO);

         call.resultHandler = Delegate.create(responder, responder.onResult);
         call.faultHandler = Delegate.create(responder, responder.onFault);
     }

     public function removeAPServer(selectedNo : String) : Void {
         var call = service.remove(selectedNo);

         call.resultHandler = Delegate.create(responder, responder.onResult);
         call.faultHandler = Delegate.create(responder, responder.onFault);
     }
}

 BusinessDelegate는 특별히 계승해야 할 부모 클래스나 구현해야 할 인터페이스는 없습니다. BusinessDelegate에서는 서버 사이드의 서비스에 액세스하는 처리를 기술합니다.

 여기에서는 각 Command에 대응하는 search(검색), regist(등록), update(갱신), remove(삭제)의 4개의 메소드를 구현하였습니다. 각각의 메소드 중에는 ServiceLocator로부터 취득한 「sampleAppService」라는 이름의 서비스(이 샘플에서는 Remote Object)의 메소드를 호출하였습니다. 서비스의 취득은 constructor 내에서 실시하고 있으며 취득한 서비스는 인스턴스 변수로 유지하고 있습니다.

 또, Flex의 클래스 라이브러리로 제공되는 Delegate 클래스의 create 메소드를 사용하여 서버 사이드의 서비스 호출이 성공했을 때와 실패했을 때의 핸들링을 Command에 위양시켰습니다.

 Command의 인스턴스는, constructor의 인수에게 건네지므로 서비스와 같이 인스턴스 변수로 유지하고 있습니다. Command의 인스턴스는 Responder 인터페이스에서 받았음을 주의하도록 합니다.




2. Services의 작성
2007/01/31  

Services의 작성

 다음으로 Services를 작성합니다. 아래의 리스트는 샘플 애플리케이션의 Services인 SampleappServices.mxml입니다. 이것을 cairngorm-sample/WebContent/sample/business에 작성합니다.

리스트 2 SampleappServices.mxml

<?xml version="1.0" encoding="utf-8"?>

<cairngorm:ServiceLocator xmlns:mx="http://www.macromedia.com
/2003/mxml"(본래는 1행)  
                           xmlns:cairngorm="http://www.
iterationtwo.com/cairngorm">(본래는 1행)

   <mx:RemoteObject id="sampleAppService" named="
sampleAppService"
(본래는 1행)
                    protocol="http"
                    showBusyCursor="true"
                    result="event.call.resultHandler(event)"
                    fault="event.call.faultHandler(event)">
   </mx:RemoteObject>


</cairngorm:ServiceLocator>

 이번 회에는 서버 사이드의 서비스에 Remote Object를 사용하므로 로그인 샘플과 같이 <mx:RemoteObject>태그를 사용하여 서비스를 정의하였습니다. id 속성은 ServiceLocator의 getService 메소드로 서비스를 취득할 때에 지정하는 문자열입니다. named 속성은 WEB-INF/flex/flex-config.xml에 정의하는 서비스명입니다. 여기에서는 id 속성과 named 속성 값을 같도록 하였는데 이 부분은 다른 값이어도 상관이 없습니다.

 또한, 대응하는 flex-config.xml는 아래와 같이 됩니다. <object>태그 내의 <source>태그에는 Remote Object로서 작성하는 Java 클래스의 완전 수식명을, <type>태그에는 stateless-class나 stateful-class 중 하나를 지정합니다.

리스트 3 flex-config.xml

&<?xml version="1.0" encoding="UTF-8"?>
<flex-config xmlns="http://www.macromedia.com/2003/flex-config">

   (생략)

   <remote-objects>
     <whitelist>

   (생략)

       <named>
         <object name=" sampleAppService ">
           <source>sample.service.SampleappService</source>
           <type>stateful-class</type>
         </object>

       </named>

     </whitelist>
   </remote-objects>

   (생략)

</flex-config>

Stateless와 Stateful

Remote Object에는 클라이언트에게서 요청이 올 때마다 인스턴스를 생성/파기하는 Stateless인 것과 한 번 인스턴스를 생성하면 그것을 보관 유지해 두고 이후에 동일 클라이언트로부터의 요청이 있을 때 그 인스턴스가 대응하는 Stateful인 것 2 종류가 있습니다.

이번 샘플에서는 서비스인 Java 클래스의 인스턴스 변수로 유지하는 Map 오브젝트에 대해서 애플리케이션 서버의 추가나 갱신, 삭제 등을 실시하기 위해 매회 인스턴스를 생성해 버리면 인스턴스 변수도 초기화되어 버려 등록이나 갱신, 삭제 등이 반영되지 않게 되어 버립니다. 그 때문에 <type>에는 stateful-class를 지정하였습니다.



3. 서비스(Remote Object)의 작성
2007/01/31  

서비스(Remote Object)의 작성

 계속해서 서버 사이드의 서비스(Remote Object)를 작성합니다. 아래의 리스트는 샘플 애플리케이션의 Remote Object인 SampleappService.java입니다. 이것을 cairngorm-sample/src/sample/service에 작성합니다.

리스트 4 SampleappService.java

package sample.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import sample.vo.APServerVO;

public class SampleappService {

     /** 애플리케이션 서버를 보관 유지하는 Map(DB의 대신 ) */
     private Map _apServerMap;

     /**
      * constructor   
      */
     public SampleappService() {
         // 초기 데이터의 준비
         _apServerMap = new LinkedHashMap();

         Map apServer = new HashMap();
         apServer.put("productCode", "101");
         apServer.put("productName", "Tomcat");
         apServer.put("vendor", "Apache Software Foundation");
         apServer.put("price", "0");
         apServer.put("note", "EJB 컨테이너 없는 오픈 소스") ;
         _apServerMap.put("101", apServer);

         apServer = new HashMap();
         apServer.put("productCode", "102");
         apServer.put("productName", "WebLogic Server");
         apServer.put("vendor", "BEA");
         apServer.put("price", "2000000");
         apServer.put("note", "CPU가 듀얼코어인 경우 1.5배의 금액") ;
         _apServerMap.put("102", apServer);

         apServer = new HashMap();
         apServer.put("productCode", "103");
         apServer.put("productName", "WebSphere Application Server");
         apServer.put("vendor", "IBM");
         apServer.put("price", "2500000");
         apServer.put("note", "1년간 지원 포함");
         _apServerMap.put("103", apServer);
     }

     /**
      * 애플리케이션 서버의 검색을 실시합니다.
      */
     public Collection search(String searchCond) {

         // 검색 조건(제품 코드)이 없는 경우 모든 건을 돌려준다
         if (searchCond == null || searchCond.trim().length() == 0) {
             return _apServerMap.values();
         }

         // 검색 조건(제품 코드)에 해당하는 AP서버를 Map에서 취득
         Map targetObj = (Map) _apServerMap.get(searchCond);
         if (targetObj != null) {
             List list = new ArrayList();
             list.add(targetObj);
             return list;
         }

         return null;
     }

     /**
      * 애플리케이션 서버의 등록을 실시합니다.
      */
     public void regist(APServerVO apServerVO) {
         Map apServer = new HashMap();
         apServer.put("productCode", apServerVO.getProductCode());
         apServer.put("productName", apServerVO.getProductName());
         apServer.put("vendor", apServerVO.getVendor());
         apServer.put("price", apServerVO.getPrice());
         apServer.put("note", apServerVO.getNote());
         _apServerMap.put(apServerVO.getProductCode(),apServer);
     }

     /**
      * 애플리케이션 서버의 갱신을 실시합니다.
      */
     public void update(APServerVO apServerVO) {
         regist(apServerVO);
     }

     /**
      * 애플리케이션 서버의 삭제를 실시합니다.
      */
     public void remove(String productCode) {
         _apServerMap.remove(productCode);
     }
}

  Remote Object는 특별히 계승해야 할 부모 클래스나 구현해야 할 인터페이스는 없으며 POJO로서 작성할 수 있습니다. 여기에서는 BusinessDelegate에서 불려나가는 search(검색), regist(등록), update(갱신), remove(삭제)의 4개의 메소드를 구현하고 있습니다. 이번 회에는 샘플로서 구현을 간략화하기 때문에 실제로 데이터베이스에의 액세스는 실시하지 않으며 인스턴스 변수의 Map에 대해서 검색·등록·갱신·삭제를 실시하고 있습니다.

 인스턴스 변수의 Map은 constructor에서 초기화되어 “Tomcat” “WebLogic Server” “WebSphere Application Server”의 3개의 애플리케이션 서버를 초기 데이터로서 보관 유지하고 있습니다. 등록된 차례를 고려하여 데이터 그리드에 나타내기 위해서 Map의 구현 클래스에는 LinkedHashMap를 사용하고 있습니다.

 1개의 애플리케이션 서버는 제품 코드, 제품명, 벤더, 가격, 비고라는 5개의 키를 가진 1개의 Map으로 나타내었습니다. 이 Map이 인스턴스 변수의 Map에 제품 코드를 키로 하여 보관 유지되고 있습니다. 이 Map은 키의 차례를 고려할 필요가 없기 때문에 Map의 구현 클래스에는 HashMap를 사용합니다(가격, 비고에 관해서는 실제 제품의 것이 아니라 샘플용의 값입니다).

 Map 안에 Map이 들어가는 상자 구조로 되어 있기 때문에 「그런 것은 하지 않고 List 안에 Map을 넣으면 되는 것 아닌가. 그러면, LinkedHashMap은 사용하지 않아도 등록된 차례가 고려될 것이다」라고 생각하는 경우가 있을지도 모릅니다. 확실히 List 안에 Map을 넣어 나가는 방법이 알기 쉬운 것이겠지만 이것이라면 검색할 때에 List 안의 Map들을 모든 건에 대해 주사해 나가야 합니다. List의 요소가 적은 경우에는 이것으로도 괜찮을지 모르지만 List의 요소가 많아짐에 따라서 검색에 걸리는 시간은 자꾸 증가하게 됩니다. 갱신이나 삭제를 실시할 때도 우선 갱신·삭제 대상을 특정하기 위해서 검색을 실시하므로 갱신이나 삭제에도 시간이 걸리게 됩니다.

 그러나, List가 아닌 Map을 사용하면, 키로 액세스할 수 있기 때문에 검색이 빨라집니다. 이것은 데이터베이스에서의 인덱스를 사용한 검색에 해당합니다. 데이터베이스에서도 인덱스를 사용하지 않는 검색은 느려지지만 List를 사용하는 것도 그것과 같은 것입니다. 그러한 이유에서 애플리케이션 서버를 보관 유지하는 오브젝트는 List가 아니라 Map으로 합니다.




4. 서버 사이드와의 제휴
2007/01/31  

서버 사이드와의 제휴

 이번에는 서버 사이드의 서비스를 Remote Object로서 구현하였는데, 서버 사이드의 서비스를 반드시 Remote Object로 구현해야 하는 것은 아닙니다. Flex에서는 서버 사이드에 제휴하는 방법으로서 Remote Object 외에 HTTP Service와 Web Service가 있습니다(아래 그림 참조).

그림 1 서버 사이드와의 제휴 방법

  Remote Object는 AMF(Action Message Format)라는 Flash 독자적인 방식으로 서버와 통신합니다. AMF의 특징으로 바이너리 포맷이라는 점 때문에 오브젝트의 내용을 XML 등의 텍스트로 변환하지 않고 오브젝트끼리 그대로 교환할 수 있다는 점이 있습니다. Flex에는 전회에서 설명한 것처럼 Action Script 오브젝트와 Java 오브젝트를 자동적으로 상호 변환하는 구조가 갖춰져 있습니다.

 HTTP Service는 HTTP의 GET 리퀘스트나 POST 리퀘스트를 사용하여 서버와 통신합니다. HTTP는 텍스트 베이스의 프로토콜이기 때문에 오브젝트를 그대로 파라미터로서 송신할 수 없습니다. 리퀘스트 파라미터로 분해해서 보내거나 XML 데이터로 변환한 후에 서버에 송신할 필요가 있습니다. Struts 등의 기존 Web 애플리케이션에 Flex를 병용하는 경우 등에는 많이 사용할 것이라 생각합니다.

 Web Service는 SOAP이라는 프로토콜을 사용하여 서버와 통신합니다. SOAP은 XML 베이스의 프로토콜인데 WSDL에 서비스의 메소드나 데이터형을 정의해 둠으로써 마치 오브젝트끼리 교환하는 것처럼 취급할 수 있습니다. 그러한 의미에서는 Remote Object에 가깝다고 할 수 있습니다. 그러나, Web 서비스를 실행하기 위해서는 별도로 Web 서비스의 엔진(Apache Axis 등)이 필요하며 구현하려면 조금 임계가 높은 부분이 있습니다. SOA의 보급 등에서 Web 서비스화가 진행되면 사용할 부분이 많아진다고 생각합니다.




5. 샘플 애플리케이션의 동작 확인
2007/01/31  

샘플 애플리케이션의 동작 확인

 그러면, 샘플 애플리케이션을 움직여 봅시다. 움직이는 순서는 로그인 샘플 때의 순서와 같습니다. 우선, 패키지 익스플로러 상의 cairngorm-sample 프로젝트를 오른쪽 클릭하여 [실행]→ [Run On Server]를 선택합니다. 그러면, 아래와 같은 다이얼로그가 표시되므로 개발 환경 구축 시에 설정한 Tomcat를 선택하고 다음 버튼을 누릅니다.

화면 1 서버의 선택

 그러면, 아래와 같은 다이얼로그가 표시되므로 오른쪽의 Configured projects 란에 cairngorm-sample 프로젝트가 추가되어 있음을 확인합니다. 확인 종료 버튼을 누릅니다.

화면 2 프로젝트의 추가

 아래와 같이 Servers 뷰에 cairngorm-sample 프로젝트가 더해지고 Tomcat이 가동됩니다. Status가 Started되면 가동은 완료된 것이므로 브라우저에서 아래의 URL에 액세스하시기 바랍니다.

http://localhost:8080/cairngorm-sample/sample/view/APServerList.mxml

화면 3 Tomcat의 가동을 확인

 검색 조건(제품 코드)에 아무것도 입력하지 않고 검색 버튼을 누르면 애플리케이션 서버의 모든 건을 돌려주게 되어 있으므로 아래와 같이 3건의 애플리케이션 서버가 데이터 그리드에 표시될 것입니다. 검색 이외에도 구현한 등록, 갱신, 삭제의 처리도 시험해 보시기 바랍니다.

화면 4 검색 조건없이 검색을 실행했을 때의 화면

정리

 이상으로 샘플 애플리케이션의 작성은 끝나게 됩니다. 이번 회에는 샘플로서 데이터베이스에의 액세스를 실시하지 않거나 예외 처리 혹은 화면 회전의 상세한 제어를 생략하기도 했습니다. 애플리케이션으로서는 아직 부족한 부분이 있으므로 여유가 있는 분이라면 부족한 부분의 처리를 구현해 보셨으면 합니다.

 이번 연재는 Flex 1.5 베이스관련 연재였으므로 , 독자 여러분들이 이번 연재를 참고로 하여 새로운 Flex 2에서 Cairngorm 2를 사용하셨으면 합니다.

 본 연재는 이번가 최종회였습니다. 정말로 감사합니다.