Java

Dynamic Faces를 사용한 Client-Side Polling

griffy 2007. 11. 27. 09:32

이 아티클의 영문 원본은
http://java.sun.com/mailers/techtips/enterprise/2007/TechTips_Oct07.html#1
에서 볼수 있습니다.

이 팁은 Java EE 5의 오픈 소스 참조 구현인 GlassFish와 오픈 소스 NetBeans IDE 5.5.1을 사용하여 작성했습니다. GlassFish는 GlassFish 커뮤니티 다운로드 페이지에서 다운로드할 수 있습니다. NetBeans IDE 5.5.1은 NetBeans 페이지에서 다운로드할 수 있습니다.

Dynamic Faces를 사용한 Client-Side Polling 팁에 대한 샘플 아카이브는 여기에서 다운로드할 수 있습니다. 아래에 있는 이 코드 및/또는 정보의 사용은 라이센스 계약 조건에 따릅니다.


Dynamic Faces를 사용한 Client-Side Polling

저자
JavaServer Faces 기술에 대한 공동 스펙 부문 리더 Roger Kitain

동적 웹 애플리케이션의 세계에서는 클라이언트와 서버가 상호작용할 수 있는 다양한 방법을 제공합니다. 서버 상의 정보가 자주 변경되는 경우에 특히 잘 맞는 방법이 두 가지 있습니다. 그 두 가지 방법은 다음과 같습니다.

  • HTTP Streaming
  • Client Polling

HTTP Streaming의 경우, 클라이언트/서버 연결을 장시간 동안 오픈 상태로 두어 데이터가 서버에서 클라이언트로 스트리밍되도록 합니다. 이 방법은 server-side push, reverse Ajax 또는 comet라고도 알려져 있습니다. 서버의 정보가 변경될 때 업데이트 정보를 클라이언트로 푸시(Push)합니다.

Client Polling에서는 서버로부터 새 정보를 획득하기 위해 브라우저가 정기적으로 XMLHttpRequest를 호출합니다. 예를 들면, 클라이언트는 새로운 정보를 얻기 위해 5초에 한 번씩 서버로 XMLHttpRequest를 전송할 수 있습니다. 이 방법은 또한 주기적 리프레시(periodic refresh)로도 알려져 있습니다.

Dynamic Faces 프레임워크는 전통적인 JavaServer Faces Technology(줄여서 JSF) 애플리케이션에 Ajax의 기능을 제공합니다. 대개 Ajax 함수 호출은 Java 개발자들에게는 생소할 수도 있는 JavaScript로 작성됩니다. Dynamic Faces를 통해 JavaScript를 거의 또는 전혀 사용하지 않고 Ajax의 기능을 JSF 애플리케이션에 추가할 수 있습니다. Dynamic Faces는 별도의 설치 과정이 필요 없는 작은 JavaScript 라이브러리를 제공하여 JSF 애플리케이션과 함께 사용할 수 있도록 해줍니다.

본 팁에서는 Dynamic Faces를 사용하여 client-side polling을 수행하는 실시간 주식 시세 조회 애플리케이션을 구축하는 방법을 제시합니다. JavaScript 코딩을 많이 할 필요가 없음을 확인할 수 있을 것입니다. 샘플 애플리케이션 코드가 수록된 패키지가 본 팁과 함께 제공됩니다. 이 팁의 코드 예제는 해당 패키지에 포함된 샘플의 소스 코드에서 따온 것입니다.

주식 시세 조회 애플리케이션

본 팁에서는 주식 시세 조회 애플리케이션을 사용하여 Dynamic Faces를 사용한 client-side polling의 예를 제시합니다.

먼저 애플리케이션에서 사용자 인터페이스(UI)를 살펴봅시다.

사용자 인터페이스

W
주식시세조회



UI는 매우 기본적인 형태입니다. 사용자는 Symbol 텍스트필드에 하나 이상의 공백으로 구분된 주식 심볼을 입력하고 Search 버튼을 클릭합니다. 그 결과, 애플리케이션에서는 사용자가 입력한 심볼에 대응하는 관련 주식 데이터 테이블을 표시합니다.

사용자가 방화벽 뒤에 있는 경우라면 Proxy Host 및 Proxy Port 필드에 프록시 정보를 입력합니다.

UI에서 가장 흥미로운 부분은 Streaming 필드일 것입니다. On 또는 Off 중에서 선택합니다. Streaming이 On으로 설정되면 클라이언트는 서버로 폴링(polling)을 수행하여 10초에 한 번씩(또는 지정된 시간 간격마다) Ajax 트랜잭션을 시작하도록 합니다.

Remote/Local 필드에서는 Local 또는 Remote를 선택할 수 있습니다. Local을 선택하면 애플리케이션은 로컬 데이터를 사용합니다. 이 값은 네트워크 연결을 사용할 수 없을 때 선택합니다. Remote를 선택하는 경우, 애플리케이션은 야후의 주식 시세 정보 서비스(Yahoo Stock Quoting service)를 호출하여 주식 데이터를 획득합니다.

결과 테이블의 크기는 사용자가 입력한 심볼의 수에 따라 다릅니다.

이제 애플리케이션에 사용된 요소들을 살펴봅시다.

구성 요소

다음 세 가지 요소가 이 애플리케이션에 사용됩니다.

  • JSP(JavaServer Pages) 페이지
  • JavaScript 파일
  • JSF Managed Bean

JSP 페이지

다음은 애플리케이션의 JSP 페이지의 일부인 home.jsp으로 관련된 부분을 보여줍니다.


   <f:view>
   <html>
   <head>
   ...
   ...
   <jsfExt:scripts/>
   <script type="text/javascript">
   ...
   ...
   include_js('javascripts/stock-faces.js');
   </script>
   </head>
   <body>
     <h:form id="form" prependId="false">
     ...
     <h:panelGrid border="1" columns="1"
        styleClass="panel-input-border">
             <h:panelGrid border="1" columns="7">
                 <h:outputText value="Symbol:"/>
                 <h:inputText id="symbol"/>
                 <h:commandButton id="search" value="Search"
                    onclick="DynaFaces.fireAjaxTransaction(
                    this, {});return false;"
               actionListener="#{bean.getStockInfo}" />
               ...
               <h:selectOneMenu id="streaming" value="Off"
                  onchange="toggleStreaming()">
               ...
             </h:panelGrid>
     </h:panelGrid> 
    
     <h:panelGrid id="stockdata" border="1" columns="8"
       styleClass="panel-data-border" rendered="false">
     ...
   </body>
   </html>
   </f:view>

다음은 위의 코드에서 주목해야 할 부분에 대한 설명입니다.

  • <jsfExt:scripts/>는 Dynamic Faces 애플리케이션에서 포함해야 하는 표준 태그입니다. Dynamic Faces JavaScript 라이브러리를 포함합니다.
  • include_js('javascripts/stock-faces.js'); 행은 애플리케이션의 JavaScript 파일인 stock-faces.js를 로드하는 유틸리티 함수입니다.
  • h:commandButton 태그에는 해당 버튼에 대한 onclick JavaScript 이벤트 핸들러가 있습니다. 이벤트 핸들러인 DynaFaces.fireAjaxTransaction에서는 버튼을 클릭하면 서버로 Ajax 요청을 전송합니다. 그리고 나서 #{bean.getStockInfo}로 지정된 actionListener가 서버에서 수행됩니다. 여기서 중요한 것은 서버에서 수행되는 모든 뷰나 JSF 구성요소 처리가 Ajax를 통해 일어난다는 점입니다.
  • "streaming" 옵션은 h:selectOneMenu 구성요소이며 onchange JavaScript 이벤트 핸들러가 있습니다.
  • id가 "stockdata"인 h:panelGrid 태그는 주식 데이터의 동적 테이블을 표시합니다. 주어진 속성이 "false"로 설정되어 있는 것은 테이블이 처음에는 표시되지 않음을 의미합니다. 그러나 리턴된 주식 데이터가 있을 경우 애플리케이션 코드에서 해당 속성을 true로 설정합니다.

JavaScript 파일

다음은 애플리케이션을 위한 JavaScript 파일 stockfaces.js입니다.

   var pollId;
  
   /** Delay between requests to the server when polling. */
   var pollDelay = 10000;
  
   /** Start polling the server */
   function start() {
       pollId = setInterval(poll, pollDelay);
   } 
  
   /** Stop polling the server */
   function stop() {
       clearInterval(pollId);
   }
  
   function poll() {
       queueEvent();
       DynaFaces.fireAjaxTransaction(null, {});
   }
  
   function queueEvent() {
       var actionEvent =
           new DynaFaces.ActionEvent("search",
           DynaFaces.PhaseId.INVOKE_APPLICATION);
       DynaFaces.queueFacesEvent(actionEvent);
       return false;
   }
  
   function toggleStreaming() {
       var menu = document.getElementById("streaming");
       var idx = menu.selectedIndex;
       var streaming = menu[idx].value;
       if (streaming == "Off") {
           stop();
       } else if (streaming == "On") {
           start();
       }
   }

avaScript 코드에서 하는 일은 다음과 같습니다.

  • 폴링 지연(polling delay) 즉, 서버로의 호출 사이의 시간 간격은 10초로 설정됩니다.
  • start() 함수는 서버 폴링을 초기화합니다.
  • stop() 함수는 서버 폴링을 중지시킵니다.
  • poll() 함수는 server-side JSF 액션 이벤트를 큐에 넣습니다. 그 후 Dynamic Faces 라이브러리를 사용하여 서버로 Ajax 요청을 시작합니다.
  • queueEvent() 함수는 Dynamic Faces 라이브러리를 사용하여 server-side JSF 액션 이벤트를 큐에 넣습니다. Ajax 요청이 서버로 들어옴에 따라 표준 JSF 라이프사이클을 처리하는 동안 액션 이벤트가 처리됩니다.
  • toggleStreaming() 함수는 "streaming" 메뉴 콘트롤의 값을 토글합니다.

JSF Managed Bean

다음은 JSF managed bean인 Bean.java의 일부이며 관련된 부분을 나타냅니다.

   /**
    * This bean has methods to retrieve stock information from
    * the Yahoo quote service.
    */
   public class Bean {
  
       private static final String SERVICE_URL =
          "http://quote.yahoo.com/d/quotes.csv";
       /**
        * Action method that is used to retrieve stock
        * information. This method uses two helper methods - one
        * to get the stock information, and the other to
        * dynamically build the "data" components for the UI.
        */
       public void getStockInfo(ActionEvent ae) {
   ...
   ...                      
          stockData = getStockData(symbols);                      
          buildUI(stockData);
   ...
    }
   
    /**
     * Helper method to get the stock data (remotely).
     */
    private String[] getStockData(String[] symbols)
        throws IOException, MalformedURLException {
        String[] data = new String[symbols.length];
        for (int i=0; i<symbols.length; i++) {
            StringBuffer sb = new StringBuffer(SERVICE_URL);
    ...
    ...
        }
        return data;
    }
   
    /**
     * Helper method to dynamically add JSF components to
     * display the data.
     */
    private void buildUI(String[] stockData) {
        FacesContext context =
            FacesContext.getCurrentInstance();
        UIForm form =
            (UIForm)context.getViewRoot().findComponent("form");
        UIPanel dataPanel =
            (UIPanel)form.findComponent("stockdata");
    ...
    ...
        // Create and add components with data values
   
        // Symbol
        ...
        dataPanel.getChildren().add(outputComponent);
       
        // Name
        ...
        dataPanel.getChildren().add(outputComponent);
       
        // Open Price (if any)
        ...
        dataPanel.getChildren().add(outputComponent);
        ...
        ...
        }
        dataPanel.setRendered(true);
    }


이 JSF Managed Bean에는 액션 메소드 getStockInfo가 있으며 이 메소드는 다음 두 가지를 수행합니다.

  • 모든 심볼에 대한 주식 데이터를 검색하기 위해 헬퍼 메소드 getStockData를 사용하여 야후 주식 시세 정보 서비스(SERVICE_URL에 정의됨)에 접근합니다.
  • 헬퍼 메소드 buildUI를 사용하여 주식 데이터로부터 JSF 구성요소들을 생성하고 해당 JSF 구성요소를 JSF 구성요소 뷰에 추가합니다. 모든 구성요소를 생성 및 추가한 후 액션 메소드는 stockdata JSF 구성요소에 대해 주어진 속성을 true로 설정합니다.

액션 메소드 getStockInfo는 Search 버튼을 누를 때 호출됩니다. 또한 Ajax poll 요청에 대한 결과로 호출되기도 합니다. 이는 각 클라이언트 poll에서 이 이벤트 핸들러와 연결된 액션 이벤트를 큐에 넣기 때문입니다. JavaScript 파일 stock-faces.js에서 queueEvent 메소드를 참조하십시오.

샘플 코드 실행하기

샘플 패키지에는 본 팁이 포함되어 있으며 팁에서 다루는 기술을 설명합니다. Servlet 2.5 API, JavaServer Pages(JSP) Technology 2.1 및 JavaServer Faces Technology 1.2를 지원하는 모든 웹 컨테이너에 대해 샘플 패키지를 전개할 수 있습니다. 다음의 지시사항은 사용자가 GlassFish를 사용한다고 가정합니다.

샘플을 설치하고 실행하려면 다음 단계를 수행합니다.

  1. GlassFish가 아직 없는 경우 GlassFish를 다운로드 및 설치합니다.
  2. 팁의 샘플 애플리케이션을 다운로드하고 압축을 풉니다. 새로 생성된 디렉토리를 <sample_install_dir>/client-poll-dfaces로 볼 때, <sample_install_dir>는 샘플 애플리케이션을 설치한 디렉토리입니다. 예를 들어, Windows 시스템에서 C:\ 에 압축을 풀었다면 새로 생성된 디렉토리는 C:\client-poll-dfaces이어야 합니다.
  3. 다음 명령을 입력하여 GlassFish를 시작합니다.
    <GF_HOME>/bin/asadmin start-domain domain1
    이 때 <GF_HOME>은 사용자가 GlassFish를 설치한 디렉토리입니다.
  4. 샘플을 설치하려면
    <sample_install_dir>/client-poll-dfaces/stock-faces.war을 <GF_HOME>/domains/domain1/autodeploy에 복사하십시오.
  5. 브라우저를 열고 다음 주소로 이동합니다. URL: http://localhost:8080/stock-faces/. 주식 시세 조회 애플리케이션 UI가 나타납니다.  
    주식 시세 조회

    주식 시세 조회
     
  6. 하나 이상의 주식 심볼을 공백으로 구분하여 입력하십시오(예: JAVA LMT IBM). 사용자가 방화벽 뒤에 있는 경우에는 Proxy Host 및 Proxy Port 필드에 해당 프록시 정보를 지정하십시오. Search 버튼을 클릭하십시오. 사용자가 입력한 심볼에 대한 주식 데이터가 테이블 형태로 표시되어 있는 것을 볼 수 있습니다.  
    주식 시세 조회

    주식 시세 조회
  7. Streaming 및 Local/Remote 설정에 대해 여러 가지 조합으로 시도해 보고 결과를 확인하십시오. Streaming이 On으로 설정된 경우 Search 버튼을 누를 필요가 없다는 것을 알 수 있습니다. Symbol 텍스트 필드에 지정한 주식 심볼이 Ajax 메커니즘을 통해 서버로 자동 전송됩니다. Local을 선택한 경우에는 이름과 가격을 시뮬레이션하므로 데이터가 Remote를 선택했을 때와 다른 결과를 보여줄 것입니다.


요약

본 팁에서는 Ajax를 JSF와 결합하여 동적인 애플리케이션을 생성하는 방법을 설명했습니다. 이 애플리케이션에서는 Dynamic Faces의 두 가지 기능을 설명했습니다.

  • fireAjaxTransaction
  • JavaScript에서의 원격 JSF 이벤트 큐잉(event queuing)

Dynamic Faces에 대한 자세한 정보는 jsf-extensions project를 참조하십시오. 또한 Ed Burns의 blog Introducing Project Dynamic Faces도 참조하십시오.