출처: 마이크로소프트웨어 7월 기사
 

웹 애플리케이션 개발자들에게 있어 데스크탑 애플리케이션에서나 볼 수 있는 풍부한 인터페이스는 항상 부러움의 대상이다. 액티브X, 플래시, 자바 애플릿 등을 이용하여 HTTP와 HTML의 한계를 넘어선 유저 인터페이스를 구현할 수 있지만 플랫폼의 제한, 플러그인 형식으로 인한 설치문제 등이 있다. 하지만 여기서 소개할 AJAX는 전혀 새로운 기술이 아닌 지금까지 웹에서 널리 쓰여 왔던 자바스크립트, DHTML 등의 기존 기술들을 모아 풍부한 유저 인터페이스를 구현할 수 있도록 정립한 기술 집합체로 최근 관심이 모아지고 있다.

HTTP는 네트워크에서 발생하는 모든 웹 요청과 응답을 처리할 수 있는 웹 표준 프로토콜로 완전히 자리 잡았다. 하지만 HTTP는 비연결성 프로토콜로 기본적으로 요청과 요청 사이에 연관성이 없고 웹 서버에 보낸 요청에 대한 응답에 대해서 각각의 웹 문서를 매번 다시 렌더링하여 리프레시해야 하는 단점이 있다. 로딩이 끝나 렌더링이 끝난 웹 페이지를 DHTML을 이용하여 동적으로 레이아웃이나 스타일을 재구성할 수도 있지만 재구성에 필요한 데이터는 사용자의 입력 또는 페이지를 로딩할 당시 서버에서 모든 데이터를 미리 읽어와야만 한다. 하지만 사용자가 어떤 데이터를 입력할지 모르는 상황이고 그렇다고 해서 모든 경우의 수에 해당하는 데이터를 브라우저로 로딩시킬 수도 없다.
결국 웹 애플리케이션이 클라이언트와 상호작용하기 위해서는 웹 서버와의 통신이 불가피하고 웹 서버와의 통신은 매번 웹 페이지가 새롭게 리프레시된다는 것을 의미한다. 클라이언트와의 상호작용 하나 하나에 대해 웹 페이지가 매번 리프레시된다는 것은 응답성(responsiveness), 사용성(usability)의 저하를 의미한다. 웹 애플리케이션 개발자의 입장에서는 로컬PC에서 작동하는 데스크탑 애플리케이션의 빠른 응답성과 편리한 사용성이 부럽지 않을 수가 없다. 이러한 핸디캡을 극복하기 위해 이미 웹에서는 액티브X, 자바 애플릿, 플래시 등의 기술이 사용되고 있다. 이들 기술들은 간단한 브라우저 플러그인으로 작동하며 HTML만으로는 구현할 수 없는 인터랙티브한 웹 페이지를 표현할 수 있도록 해주어 데스크탑 애플리케이션 못지않은 사용성과 응답성을 보여준다.
최근 들어 이러한 플러그인들의 도움없이 브라우저 자체만 가지고 인터랙티브하고 응답성, 사용성을 높일 수 있는 AJAX(Asynchro nous Javascript + XML)가 주목을 받고 있다.

이미 AJAX는 구글 맵(http://maps.google.com), Orkut(http://www.orkut.com), Google Suggest(http://www.google.com/webhp?complete =1&hl=en), Gmail(http://www.gmail.com), A9(http:// a9.com), Flickr(http://www.flickr.com) 등에서 선보인 바 있으며 AJAX를 사용하고 있는 웹 사이트가 늘어나고 있는 추세이다.
구글 맵에서는 웹 페이지를 스크롤하거나 확대, 축소를 하게 되면 현재 페이지에 없는 부분의 지도 이미지를 페이지 리로딩없이 읽어서 변화된 부분만 채워준다. Orkut에서는 친구들에 대한 평점을 하트, 또는 별, 얼음의 개수로 표현할 수 있는데, 클릭하는 즉시 페이지 리로딩없이 바로 친구들에 대한 평점이 반영된다. Gmail에서는 메일에 별마크 붙이기를 해보면 페이지를 리로딩하지 않고 별을 붙이거나 없애거나 할 수 있고, 메일을 보낼 때 주소록에 등록된 사람의 이름 또는 주소로 데스크탑 웹메일 클라이언트에서나 볼 수 있는 자동 완성도 볼 수 있다. Flickr에서는 그림 제목이나 설명 부분을 클릭하면 바로 편집상태로 변화되고 새로운 제목이나 설명을 입력하고 저장 버튼을 누르면 바로 변경이 된다. 이 모든 게 웹페이지 전체를 리로딩하지 않고 일부만을 비동기적으로 변화시키는 AJAX를 이용한 것이다.

 


AJAX란?

AJAX는 독립적이고 구체적인 하나의 특정 기술을 지칭하는 것이 아니다. 웹에 이미 존재하고 있는 DHTML, CSS, XML, XMLHttpRequest등의 기술들을 합친 새로운 기술 또는 그 기술들을 이용하여 보여줄 수 있는 이용형태를 나타낸다.

 AJAX의 핵심을 이루는 XMLHttpRequest는 인터넷 익스플로러(이하 IE)5.0에서부터 이미 액티브X오브젝트 형태로 제공되기 시작했고 그 후 모질라(Mozilla) 1.0에서부터 윈도우 오브젝트 형태로 XMLHttpRequest 객체가 지원되기 시작하였다. 그 이외에 넷스케이프 7, 맥의 사파리(Safari) 1.2, 오페라(Opera) 7.6에서부터, 그리고 모든 파이어폭스(firefox) 버전에서 지원하고 있다. XMLHttpRequest가 IE에서 액트브X 오브젝트로 다루고 있기 때문에 IE와 다른 브라우저들 간의 XMLHttpRequest 객체를 가져오는 방법이 다르고 브라우저 간에 인터페이스나 구현의 차이로 인해 미묘한 차이점을 보이는 문제가 아직 존재한다. 

 

AJAX를 사용하는 목적

AJAX를 사용하는 가장 큰 이유는 웹에서 액티브X, 플래시 또는 자바 애플릿 등에 의존했던 사용성, 접근성, 응답성의 향상이다. 네트워크 인프라가 발전하여 네트워크 속도가 아무리 발라진다고 하더라도 새로운 컨텐츠를 보여주기 위해서 웹 페이지 전체가 매번 리로딩되는 것은 사용성, 응답성의 한계로 이어질 수밖에는 없다. 웹 페이지의 전체 레이아웃이 바뀐다면 웹페이지 전체가 다시 그려지는 것이 불가피 하겠지만(사실 DOM 인터페이스를 이용하여 HTML문서의 전체 구조를 바꿈으로서 레이아웃을 변경할 수는 있지만 효용성은 없을 것이다), 일부 레이아웃 변경 또는 레이아웃은 유지한 채 내용만 변경되는 것이라면 웹 페이지  전부를 다시 리로딩한다는 것이 불필요하게 느껴질 수 있다. 또 모든 액션이 요청을 보내고 응답이 오기를 기다리고 응담이 온 후에 다른 액션 또는 요청을 보내는 동기적인 사용 행태도 기존 웹 어플리게이션의 한계로 느껴질 수 있다.

 

AJAX는 무엇이 다른가?

AJAX의 초점은 앞에서 안급한 기존 웹 어플리케이션의 비효율성, 동기적인 액선의 단점을 해결할 수 있는 웹 페이지의 부분적인 변화, 적용과 비동기성에 있다. 하지만 굳이 AJAX만이 대안은 아니다. 액티브X, 자바 애플릿, 플래시 등이 그 대안이 될 수 있다. 하지만 그러한 기술들 대신에 AJAX가 가질 수 있는 경쟁력을 얘기하자면 플러그인을 사용하지 않고 브라우저만으로 바로 이용할 수 있다는 것과 새로운 것을 배우지 않고 DHTML, 자바스크립트, CSS등의 기존 웹 기술들을 그대로 이용할 수 있다는 것이다. 이러한 장점들은 지금도 널리 사용되고 있는 액티브X, 자바 애플릿, 플래시 등의 기술에 대한 대안으로 AJAX에 관심을 가져볼만한 충분한 이유를 준다.

 

AJAX 어플리케이션 이용 행태

기존 웹 애플리케이션 이용 행태에서는 사용자가 서버 측에 요청을 보내면 서버에서 응답이 클라이언트로 모두 전송되고 관련 리소스가 모두 로딩된 이후에 또 다른 행동을 취할 수가 있었다.

 하지만 AJAX 어플리케이션에서는 서버 측에 보내는 요청이 비동기적으로 이루어지기 때문에 요청을 보낸 이후에도 응답이 완료되기를 기다리지 않고 또 다른 요청을 보내거나 그 이외에 액션을 취할 수 있다.

 

AJAX의 구성 요소들

XMLHttpRequest

XMLHttpRequest는 웹 서버와 통신하기 위한 AJAX의 핵심 컴포넌트이다. 이미 IE에서는 5.0에서부터 액트브X 오브젝트 형태로 제공되고 있고 그 이외의 브라우저에서는 XMLHttpRequest라는 윈도우 객체에 속해 있는 객체의 형태로 제공된다. AJAX를 이용하여 스크립트를 작성할 때 가장 큰 차이를 보이는 부분이 XMLHttpRequest 객체를 가져오는 부분인데, 브라우저에 따라 차이를 보인다. 최근에 발표된 오페라 8.0에서 Gmail을 지원한다고 프로모션 하고 있는데, 이는 Gmail에서 AJAX를 사용할 수 있도록 XMLHttpRequest를 지원하기 시작했다는 의미이다.



 XMLHttpRequest는 아직 W3C(
http://www.w3c.org) 표준이 아니지만 W3C에는 XMLHttpRequest와 비슷한 DOM Level 3의 Load and Save specification이 있다. 아직 DOM Level3의 Load and Save가 구현된 브라우저는 없지만 다이나믹하게 XML문서를 읽어내서 DOM에 적용하고 DOM구조를 XML로 serialize한다는 개념은 다소 비슷한 부분이 있다.

 

XMLHttpRequest 객체 얻어오기

XMLHttpRequest를 얻어오는 방법은 IE와 그 이외의 브라우저가 서로 다르다.

 

* 인터넷 익스플로러

var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")

 

* 그 이외의 브라우저

 var xmlhttp = new XMLHttpRequest()

 

 좀 더 일반적인 방법으로는 다음과 같이 XMLHttpRequest 객체를 얻어올 수 있다.

 var xmlhttp = false

 if(window.XMLHttpRequest){

   xmlhttp = new XMLHttpRequest()

 } else {

   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")

 }

 

XMLHttpRequest의 이용

AJAX에 있어서 가장 매력적인 부분은 비동기성이다. XMLHttpRequest는 동기적, 비동기적 등 두가지 방법으로 이용할 수 있는데, AJAX에서는 비동기적인 방법을 주로 이용하게 된다. 비동기 모드로 XMLHttpRequest를 사용할 경우 send()가 호출된 후 웹 서버로부터 응답이 오기를 기다리며 브라우저가 멈춰있지 않고 웹 서버로부터 결과를 모두 받아낸 이후 콜백 핸들러에 정의된 코드가 실행되기 때문에 사용자는 계속 브라우저를 이용할 수 있게 된다.

 

 * XMLHttpRequest의 동기적 이용 예제

 xmlhttp.open("GET", "test.html", false);

 xmlhttp.send(null);

 if (xmlhttp.status == 200){

      alert(xmlhttp.responseText);

 }

 

 * XMLHttpRequest의 비동기적 이용 예제

 xmlhttp.open("GET", "test.html", true);

 xmlhttp.onreadystatechange = function() {

    if (xmlhttp.readyState == 4) {

       if (xmlhttp.status == 200) {

            alert(xmlhttp.responseText);

       }

     }

    }

  xmlhttp.send(null);

 

 XMLHttpRequest를 동기적으로 이용할 때에는 XMLHttpRequest의 send()를 호출한 뒤 response의 status code를 체크하고 responseText에서 결과를 꺼내오면 되므로 자바에서 java.net.HttpURLConnection을 사용하는 방식과 비슷하다. 동기적으로 XMLHttpRequest를 이용할 때는 XMLHttpRequest의 open() 메쏘드의 3번째 파라메터 값을 false로 줘야 한다. open()의 3번째 파라메터는 비동기적으로 이용할 것인지를 나타내는 boolean 값이 들어가고 default로 true이며 생략 가능하다.

 XMLHttpRequest를 비동기적으로 이용할 때에는 send()를 호출하기 전에 XMLHttpRequest의 onreadystatechange 프로퍼티에 XMLHttpRequest의 상태가 변할 때마다 실행될 핸들러를 function reference 형태로 줘야 한다. XMLHttpRequest의 상태는 <표 2>와 같이 5개의 상태를 가지지만 보통 가장 마지막 상태인 완료 상태를 체크해서 처리해주면 된다.

 

보안 이슈

XMLHttpRequest에서의 보안은 자바 애플릿이나 자바스크립트에서 흔히 사용하는 HTML 문서의 도메인(domain)을 이용한 보안 방법과 같다. 즉 XMLHttpRequest가 작동하는 HTML문서는 해당 문서의 doc-ument object의 도메인 프로퍼티와 같은 도메인의 서버에만 요청을 보낼 수 있다.

 

DOM

웹 페이지를 동적으로 재구성하기 위해서 빼놓을 수 없는 것이 DOM(null Object Model)이다. DOM은 일반적인 문서 구조를 플랫폼·언어 독립적으로 접근할 수 있는 인터페이스를 제공하는데, XML 문서를 구조적으로 분석하기 위해서 주로 사용된다. 거의 모든 브라우저가 DOM을 지원하기 때문에 HTML의 각 요소를 DOM인터페이스를 이용하여 접근할 수 있고 변경할 수 있다. HTML 태그로 표현되는 각 요소들, 즉 DOM 객체들을 변경할 수 있다는 얘기와 같다. 때문에 DOM은 DHTML에서 빼놓을 수 없는 요소이고 AJAX에서 서버와 통신한 결과를 반영하기 위해 필요하다.

 

DHTML

AJAX에서 서버와 통신한 결과를 화면에 다이나믹하게 적용하기 위해서 여러 가지 방법이 사용된다. 그 중 전통적이고 쉽게 이용할 수 있는 방법이 DHTML을 이용하는 방법이다. DHTML은 CSS, 자바스크립트, DOM 등을 이용해 HTML 문서의 레이아웃, 스타일, 컨텐츠를 바꿀 수 있는 기술이므로 AJAX를 이용해서 얻어낸 새로운 결과를 HTML문서에 적용시키지 알맞다.

 

XMLHttpRequest를 이용해 받은 결과 화면에 적용하기

XMLHttpRequest를 이용해서 서버와 통신 후 결과는 XMLHttpRequest의 responseHTML 프로퍼티에 저장된다. 통신 결과가 평범한 HTML이고 이 결과를 화면에 바로 적용하는 것이라면 DOM인터페이스를 사용하여 적용하고 싶은 HTML 요소를 찾은 뒤 innerHTML 프로퍼티 responseHTML의 값을 복사해주는 방식으로 결과를 적용하면 된다. 다음 예제 코드는 HTML 결과물(responseText)을 ID가 "result"인 HTML 요소에 적용하는 것이다.

...

xmlhttp.onreadystatechange = function() {

  if (xmlhttp.readyState == 4) {

     if (xmlhttp.status == 200){

        doc-ument.getElementById("result").innerHTML = xmlhttp.responseText;

     }

  }

 }

 xmlhttp.send(null);

...

 

XML.XSLT

XML은 서버와 데이터를 주고받기 위해 사용되고 XSLT는 서버로부터 받은 데이터를 HTML로 변환하기 위해 사용된다. XML 형태로 받은 데이터를 XSLT를 이용해 HTML로 변환한 뒤 그 결과를 원하는 위치에 삽입해 반영할 수 있다. XMLHttpRequest를 이용해서 웹 서버와의 통신 결과를 responseText로 받을 수도 있고 responseXML로 받을 수도 있다. responseText는 웹 서버와의 통신 결과를 문자열 그대로 저장하고 있지만 responseXML은 응답이 XML 문서인 경우에만 파싱하여 DOM의 XMLnull 객체로 가지고 있게 된다. 응답이 XML인지는 응답 헤더인 Content-Type이 text/xml인지 아닌지로 판단하고, 유효한 XML이 아닌 경우에는 XMLnull 객체의 프로퍼티나 메쏘드에 접근할 때 parse error를 낸다.

 

자바스크립트

자바스크립트는 앞에 제시된 모든 기술들을 사용하기 위한 프로그래밍 언어로 사용된다. 브라우저에서 사용되는 스크립트 언어라면 무엇이든 상관없다. IE에서만 작동하게 하려면 vbscript를 대신 사용해도 좋다. 서버와 통신을 위해 생성한 XMLHttpRequest 객체를 이용해서 통신하고 DOM 인터페이스를 이용하여 현재 페이지를 재구성 할 수 있도록 하는 역할을 한다.

 

AJAX 엔진

AJAX는 구체적이고 독립적인 기술이 아니고 여러 웹 관련 기술들을 통합적으로 이용하는 아키텍처 또는 스타일이고 아직 구체적인 스펙이 정해진 것은 아니다. AJAX는 XMLHttpRequest를 이용하여 서버와 비동기적인 통신을 하고 그 결과를 다이나믹하게 화면에 반영시켜주는 것이지만 아직은 이와 같은 어플리케이션을 손수 모두 제작해야한다. AJAX 엔진이란 이와 같은 AJAX의 행태를 구체화시킨 것으로 보통 다음과 같은 역할을 하는 자바스크립트 라이브러리를 말한다.

 

웹 서버와 데이터 호환성 제공

일반 HTML이 아닌 HTML에 반영되기 위한 데이터가 AJAX를 이용한 웹 서버와의 통신 내용이 된다. 때문에 웹 서버 애플리케이션에서 사용하는 데이터 타입과 브라우저에서 스크립트가 사용하기 위한 데이터 타입이 통일되어야할 필요가 있는데 XML이나 JSON(http://www.json.org) 또는 DWR(http://www.getahead.ltd.uk/dwr/)등을 사용할 수 있다.

 

XMLHttpRequest를 사용하기 위한 통일된 인터페이스 제공

XMLHttpRequest가 브라우저에 따라 정체성도 다르고 스펙 또한 W3C의 표준이 아닌 상태이다. 때문에 XMLHttpRequest를 다루기 위한 통일된 인터페이스가 필요하다.

 

사용자 입력 및 웹 서버와의 통신 결과 반영

현재 페이지를 유지한 상태에서 사용자 입력에 대한 결과를 반영하려면 DHTML을 이용해 반영하면 되지만 브라우저에 따라 조금씩 다른 DOM 객체 이용법과 그것들을 적용하기 위한 수많은 자바스크립트 코드를 생각하면 통일성 있게 제공해주는 라이브러리가 필요할 것이다.

 

AJAX by example

iframe을 이용하지 않고 input form을 통해서 외부 HTML 문서의 이름을 입력받아서 해당 HTML 문서를 화면의 일부에 포함시키는 예제를 생각해보자. 화면의 레이아웃은 <그림 4>와 같다.

 ajax_exam.html은 AJAX를 이용하여 외부 HTML 파일을 현재 HTML문서에 포함시킬 수 있는 스크립트를 포함하고 있다. 예제로 first.html과 second.html이 있는데 각각 다른 문자열을 가지고 있는 평범한 텍스트 파일이다. 폼의 input 영역에 파일 이름을 입력하게 되면 폼의 onsubmit 이벤트를 받아 include() 함수를 호출하게 된다. include() 함수가 실행되는 주요 흐름은 <표3>과 같다.

 include 함수는 무조건 false를 반환하므로 include() 함수가 실행될 뿐 폼이 서버에 전달되지는 않는다. 때문에 현재 HTML페이지는 리플레시되지 않고 현재 상태를 계속 유지한 체 HTTP 요청을 보내 특정 부분만을 변화시킬 수 있는 것이다.

 

<script language="javascript">

  function getXmlHttpRequest() {

     var xmlhttp = false;

     

     if (window.XMLHttpRequest) {

        xmlhttp = new XMLHttpRequest();

     } else {

        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

     }

     return xmlhttp;

  }
  function include() {

     var xmlhttp = getXmlHttpRequest();

     var url = doc-ument.ajax_form.url.value;

     if (url) {

        var originalMessage = doc-ument.getElementById("result").innerHTML;

        doc-ument.getElementById("result").innerHTML = "Loading";

        xmlhttp.open("GET", url, true);

        xmlhttp.onreadystatechange = function() {

           if (xmlhttp.readyState == 4) {

              if (xmlhttp.status == 200) {

                 doc-ument.getElementById("result").innerHTML = xmlhttp.responseText;

              } else {

                 alert("Error while loading " + url + ",("

                            + xmlhttp.status + " " + xmlhttp.statusText + ")");

                 doc-ument.getElementById("result").innerHTML = originalMessage;

               }

            }

          }

          xmlhttp.send(null);

       }

       return false;

    }

 </script>

 <form name="ajax_form" on-submit="return include();">

     <input type="text" name="url" size="50">

     <input type="submit" value="include">

     <input type="reset" value="clear">

  </form>

  <fieldset>

      <legend>Result</legend>

      <div id="result" style="height:20px;">

            Included here.

       </div>

   </fieldset>

 

변화의 여지는 많다

AJAX가 기존 기술들의 응용만으로 웹 어플리케이션에 풍부한 유저 인터페이스를 제공하는데 비해 견고하게 정립된 독립적인 기술이 아니고 XMLHttpRequest를 중심으로 한 여러 기술들을 이용하는 행태이고 확실한 표준이 아니기 때문에 변화의 여지는 많다. 또 손쉽게 구현할 수 있어서 지나치게 사용할 우려가 있으므로 신중하게 필요한 곳에 사용해야 한다. 다음 시간에는 AJAX를 이용하여 서버와 통신하는 구체적인 방법을 알아보고 관련된 프로토콜, 라이브러리를 소개하고자 한다. 끝

 


 

 

프로그래밍 세계의 주도권 경쟁이라...

 

전산실의 냉장고 만한 메인프레임의 시대에서 PC 가격의 하락 및 성능의 향상으로 1인 1PC 시대가 도래하였고 주도권은 C/S 프로그램이 가져가 버렸다.

 

하지만 인터넷의 시대의 도래로 말미암아 C/S 의 주도권은 다시금 서버쪽 프로그램으로 옮겨간 것이다. C/S 에 가장 적합했고 전 세계적으로도 가장 많은 개발자가 사용했던 언어는 비주얼 베이직이었다. 하지만 시간이 지남에 따라 그 수는 점점 줄고 있고 자바 or 닷넷의 개발자 수는 꾸준히 증가하고 있는 실정이다. 이것은 무엇을 의미하는가 하면 VB 개발자들의 상당수가 자바 개발자로 전환했다는 것을 말한다. 또한 미국의 프로그래머 연봉을 보면 개발 경력이 같더라도 자바 개발자 보다는 희귀성 측면에서 VB 나 VC++ 개발자가 좀 더 높다.

 

웹은 계속 진화하고 있다. 하드웨어어의 발전속도는 훨씬 빠르다. 개인화 PC 의 성능은 이제 서버의 성능과 비교해도 크게 손색이 없을 듯하다. 이제는 비지니스 로직을 서버 프로그램에 의존했던 방식에서 벗어나 브라우저의 역량이 점점 중요해 지는 시점이 아닌가 싶다. 리치 클라이언트의 등장이다. 아직까지는 속도가 많이 느려서 불편한 점이 없지 않아 있지만...앞으로 프로그래밍의 주도권은 어떻게 전개될까? 스크립트에 가장 적합한 언어들(php, perl, ruby, common lisp)이 점점 주목을 받고 있는 걸 어떻게 봐야 할까?

 

4장까지 ajax 에 대한 기본적인 특징들을 예제를 통해서 살펴보았다. Ajax 로 개발한다는 것은 자바스크립트를 많이 사용한다는 의미이다. 따라서 이번 5장에서는 ajax 어플리케이션을 좀 더 쉽게 개발할 수 있는 몇가지 툴을 소개하기로 하겠다. 첫번째로 자바스크립트 코드의 다큐먼트를 생성해 주는 오픈소스 설치 및 사용법에 대해서 알아보자.

 

자바스크립트의 가장 큰 단점 중의 하나는 가독성이 정말 떨어진다는 것이다. 첨부터 다시 코딩하는 것이 오히려 나을 정도로 자바스크립트는 코드를 읽는데 난해한 언어 중 하나이다. 따라서 자스크립트 소스를 보고 기능을 판단하기 보다는 주석을 보고 사용법을 익히는 것이 다른 개발자에 대한 배려라고 할 수 있겠다. 자바 언어에는 javadoc 이라는 명령어를 이용해서 주석을 html 로 작성할 수 있다. 지금은 대부분의 통합 IDE 툴에서 메뉴로 제공하고 있다. 이와 같은 기능을 구현해 놓은 JSDoc(http://jsdoc.sourceforge.net/) 이라는 Perl 로 제작된 오픈소스가 있다. 리눅서에게는 문제가 안되겠지만 윈도우 사용자들은 Perl runtime environment 를 설치해야 사용할 수 있다.

 

JSdoc 을 실행해 보기 위해서는 우선 아래와 같은 과정을 거쳐야 한다.

1. ActivePerl 설치

2. HTML::Template Perl module 설치하기

3. jsDoc 다운받기

 

 

1. ActivePerl 설치하기

오픈소스인 JSDoc 은 Perl 로 만들어 졌기 때문에 Perl 실행환경인 ActivePerl 을 설치해야 한다. 다음 url 에서 윈도우 용 AS Package 를 다운 받는다. http://www.activeperl.com/Products/Download/Download.plex?id=ActivePerl

 

ActivePerl-5.6.1.638-MSWin32-x86.zip 파일을 다운 받고 압축을 푼 후 install.bat 파일을 실행한 후 적당한 입력 파라미터를 입력하면 아래와 같이 설치가 마무리 된다.

 

 

Welcome to ActivePerl.

    This installer can install ActivePerl in any location of your choice.
    You do not need Administrator privileges.  However, please make sure
    that you have write access to this location.

Enter top level directory for install [c:\Perl]: c:\Perl

    The typical ActivePerl software installation requires 35 megabytes.
    Please make sure enough free space is available before continuing.

    ActivePerl 638 will be installed into 'c:\Perl'
Proceed? [y] y

    If you have a development environment (e.g. Visual Studio) that you
    wish to use with Perl, you should ensure that your environment (e.g.
    %LIB% and %INCLUDE%) is set before installing, for example, by running
    vcvars32.bat first.
Proceed? [y] y

Create shortcuts to the HTML documentation? [y] y

Add the Perl/bin directory to the PATH? [y] y

    Copying files...
2455개 파일이 복사되었습니다.
    Finished copying files...
Configuring Perl installation at c:\Perl
Translating C:\p4view\Apps\ActivePerl\MSI\data\ActivePerl\Perl to c:\Perl
editing c:\Perl/site/lib/ppm.xml
editing c:\Perl/site/lib/ppm-conf/Archive-Tar.pkg
editing c:\Perl/site/lib/ppm-conf/Compress-Zlib.pkg
editing c:\Perl/site/lib/ppm-conf/Data-Dump.pkg
editing c:\Perl/site/lib/ppm-conf/Digest-HMAC.pkg
editing c:\Perl/site/lib/ppm-conf/Digest-MD2.pkg
editing c:\Perl/site/lib/ppm-conf/Digest-MD4.pkg

editing c:\Perl/site/lib/ppm-conf/Digest-SHA1.pkg
editing c:\Perl/site/lib/ppm-conf/Digest.pkg
editing c:\Perl/site/lib/ppm-conf/File-CounterFile.pkg
editing c:\Perl/site/lib/ppm-conf/Font-AFM.pkg
editing c:\Perl/site/lib/ppm-conf/HTML-Parser.pkg
editing c:\Perl/site/lib/ppm-conf/HTML-Tagset.pkg
editing c:\Perl/site/lib/ppm-conf/HTML-Tree.pkg
editing c:\Perl/site/lib/ppm-conf/IO-Zlib.pkg
editing c:\Perl/site/lib/ppm-conf/libnet.pkg
editing c:\Perl/site/lib/ppm-conf/libwin32.pkg
editing c:\Perl/site/lib/ppm-conf/libwww-perl.pkg
editing c:\Perl/site/lib/ppm-conf/MD5.pkg
editing c:\Perl/site/lib/ppm-conf/MIME-Base64.pkg
editing c:\Perl/site/lib/ppm-conf/PPM-Agent-Perl.pkg
editing c:\Perl/site/lib/ppm-conf/PPM.pkg
editing c:\Perl/site/lib/ppm-conf/ppminst.bat
editing c:\Perl/site/lib/ppm-conf/SOAP-Lite.pkg
editing c:\Perl/site/lib/ppm-conf/Storable.pkg
editing c:\Perl/site/lib/ppm-conf/Test-Simple.pkg
editing c:\Perl/site/lib/ppm-conf/Tk.pkg
editing c:\Perl/site/lib/ppm-conf/URI.pkg
editing c:\Perl/site/lib/ppm-conf/XML-Parser.pkg
editing c:\Perl/site/lib/ppm-conf/XML-Simple.pkg
editing c:\Perl/site/lib/auto/XML/Simple/.packlist
editing c:\Perl/site/lib/auto/XML/Parser/.packlist
editing c:\Perl/site/lib/auto/Win32/.packlist
editing c:\Perl/site/lib/auto/Win32/AuthenticateUser/.packlist
editing c:\Perl/site/lib/auto/URI/.packlist
editing c:\Perl/site/lib/auto/Unicode/String/.packlist
editing c:\Perl/site/lib/auto/Tk/.packlist
editing c:\Perl/site/lib/auto/Text/Autoformat/.packlist
editing c:\Perl/site/lib/auto/Test/Simple/.packlist
editing c:\Perl/site/lib/auto/Storable/.packlist
editing c:\Perl/site/lib/auto/SOAP/Lite/.packlist
editing c:\Perl/site/lib/auto/PPM-Agent-Perl/.packlist
editing c:\Perl/site/lib/auto/PPM/.packlist
editing c:\Perl/site/lib/auto/PPM/Shell/.packlist
editing c:\Perl/site/lib/auto/Net/.packlist
editing c:\Perl/site/lib/auto/MIME/Base64/.packlist
editing c:\Perl/site/lib/auto/MD5/.packlist
editing c:\Perl/site/lib/auto/libwww-perl/.packlist
editing c:\Perl/site/lib/auto/IO/Zlib/.packlist
editing c:\Perl/site/lib/auto/HTML-Tree/.packlist
editing c:\Perl/site/lib/auto/HTML/Tagset/.packlist
editing c:\Perl/site/lib/auto/HTML/Parser/.packlist
editing c:\Perl/site/lib/auto/Font/AFM/.packlist
editing c:\Perl/site/lib/auto/File/CounterFile/.packlist
editing c:\Perl/site/lib/auto/Digest/.packlist
editing c:\Perl/site/lib/auto/Digest/SHA1/.packlist
editing c:\Perl/site/lib/auto/Digest/MD5/.packlist
editing c:\Perl/site/lib/auto/Digest/MD4/.packlist
editing c:\Perl/site/lib/auto/Digest/MD2/.packlist
editing c:\Perl/site/lib/auto/Digest/HMAC/.packlist
editing c:\Perl/site/lib/auto/Data/Dump/.packlist
editing c:\Perl/site/lib/auto/Compress/Zlib/.packlist
editing c:\Perl/site/lib/auto/Archive/Zip/.packlist
editing c:\Perl/site/lib/auto/Archive/Tar/.packlist
editing c:\Perl/site/lib/auto/ActiveState/Rx/.packlist
editing c:\Perl/site/lib/auto/ActiveState/RelocateTree/.packlist
editing c:\Perl/site/lib/auto/ActivePerl/DocTools/.packlist
editing c:\Perl/lib/.packlist
editing c:\Perl/lib/Config.pm
editing c:\Perl/lib/perllocal.pod
editing c:\Perl/bin/c2ph.bat
editing c:\Perl/bin/find2perl.bat
editing c:\Perl/bin/h2ph.bat
editing c:\Perl/bin/h2xs.bat
editing c:\Perl/bin/perlbug.bat
editing c:\Perl/bin/perlcc.bat
editing c:\Perl/bin/perldoc.bat
editing c:\Perl/bin/pl2pm.bat
editing c:\Perl/bin/pod2html.bat
editing c:\Perl/bin/pod2latex.bat
editing c:\Perl/bin/pod2man.bat
editing c:\Perl/bin/pod2text.bat
editing c:\Perl/bin/ppm3-bin.cfg
editing c:\Perl/bin/pstruct.bat
editing c:\Perl/bin/s2p.bat
editing c:\Perl/bin/splain.bat
cleaning out backups

Configuring c:\Perl/lib/Config.pm for use in c:\Perl...

Configuring Perl ...

Configuring PPM for use in c:\Perl...

Build directory is now C:\DOCUME~1\JYJ\LOCALS~1\Temp.

If you are behind a firewall, you may need to set the following
environment variables so that PPM will operate properly:

    set HTTP_proxy=address:port         [e.g. 192.0.0.1:8080]
    set HTTP_proxy_user=username
    set HTTP_proxy_pass=password
    set HTTP_proxy_agent=agent          [e.g. "Mozilla/5.0"]


Building HTML documentation, please wait...


This simplified installation program currently does *not*:

    o set up MSWin32 file associations
    o configure Perl for use with a Web Server

Refer to your Operating System and/or Web Server documentation for
details on how to to perform these modifications.

Thank you for installing ActivePerl!

 

윈도우에 Perl 설치가 끝나면 C:\Perl\bin 디렉토리를 패스에 걸어주자. 설치된 펄을 uninstall 하는 것은 아주 간단하다. 설치한 C:\Perl 디렉토리를 통째로 삭제하면 된다.

 

 

2. HTML::Template Perl module 설치하기

템플릿 펄 모듈은 아주 쉽게 설치된다.

 

C:\>ppm
PPM interactive shell (2.2.0) - type 'help' for available commands.
PPM> install HTML-Template
Install package 'HTML-Template?' (y/N): y
Installing package 'HTML-Template'...
Downloading http://ppm.activestate.com/PPMPackages/5.6/MSWin32-x86-multi-thread/
HTML-Template-2.7.tar.gz ...
Installing C:\Perl\html\site\lib\HTML\Template.html
Installing C:\Perl\site\lib\HTML\Template.pm
PPM> quit
Quit!

C:\>

 

3. jsDoc 다운받기

jsDoc 은 http://sourceforge.net/projects/jsdoc 에서 다운받으면 된다.

tar 형식의 파일을 풀면 jsdoc.pl 이라는 펄로 작성된 파일이 존재할 것이다.

 

이제 자바스크립트용 html 온라인 다큐먼트를 만들 준비를 모두 마쳤다. 다큐먼트를 만들 자바스크립트 파일(*.js) 을 jsDoc 이 설치된 디렉토리로 복사한 후 perl jsdoc.pl xxx.js 명령어를 입력하면 다큐먼트가 js_docs_out 라는 이름의 디렉토리 안에 생성된다. xxx.js 파일은 다큐먼트를 생성할 자바스크립트 파일의 이름이다.

 

 

4. jsDoc 샘플 실행하기

아래와 같은 명령어를 실행하면 html 다큐먼트가 생성된다.

 

E:\etc\JSdoc>perl jsdoc.pl jsDocExample.js
Loading sources from jsDocExample.js
Completed generating documentation

 

테스트용 jsDocExample.js 파일은 아래와 같다.

/**
 * @fileoverview This file is an example of how JSDoc can be used to document
 * JavaScript.
 *
 * @author Ryan Asleson
 * @version 1.0
 */

/**
 * Construct a new Person class.
 * @class This class represents an instance of a Person.
 * @constructor
 * @param {String} name The name of the Person.
 * @return A new instance of a Person.
*/
function Person(name) {
    /**
     * The Person's name
     * @type String
    */
    this.name = name;

    /**
     * Return the Person's name. This function is assigned in the class
     * constructor rather than using the prototype keyword.
     * @returns The Person's name
     * @type String
    */
    this.getName = function() {
        return name;
    }
}


/**
 * Construct a new Employee class.
 * @extends Person
 * @class This class represents an instance of an Employee.
 * @constructor
 * @return A new instance of a Person.
*/
function Employee(name, title, salary) {
    this.name = name;

    /**
     * The Employee's title
     * @type String
    */
    this.title = title;

    /**
     * The Employee's salary
     * @type int
    */
    this.salary = salary;
}

/* Employee extends Person */
Employee.prototype = new Person();

/**
 * An example of function assignment using the prototype keyword.
 * This method returns a String representation of the Employee's data.
 * @returns The Employee's name, title, and salary
 * @type String
*/
Employee.prototype.getDescription = function() {
    return this.name + " - "
        + this.title + " - "
        + "$" + this.salary;
}

 

<jsDocExample.js 의 전체 소스 코드>

 

위 jsDocExample.js 파일을 jsDoc 을 사용하여 다큐먼트를 생성한 결과는 아래와 같다.

 

 

 

출처 : jinoxst님의 블로그

'WEB2.0 > ajax' 카테고리의 다른 글

Prototype.js 가이드 01  (0) 2006.05.10
풍부한 웹 UI 구현을 위한 기술 집합체 _AJAX  (0) 2006.05.08
자동완성기능 구현하기  (0) 2006.04.12
웹서비스 접근하기  (0) 2006.04.12
툴팁 구현하기  (0) 2006.04.12
 

자동완성기능 구현하기

WEB2.0/ajax | 2006. 4. 12. 15:05
Posted by 시반

<Google Suggestion 의 ajax 를 활용한 자동완성 기능>

 

 

위 그림과 같은 자동완성 기능은 Google Suggest 에서 가장 먼저 구현하였다. 그 후 국내에는 네이버를 필두로 지금은 거의 모든 포탈업체의 검색창에는 마치 유행처럼 위와 같은 기능이 구현되어 있다.

 

 

 

<네이버의 자동완성 기능>


 

 
<다음의 자동완성 기능>
 
 
필자는 2005년 4월경 ajax 대신에 iframe 을 사용하여 자동완성 기능을 구현한 적이 있는데, 검색된 결과를 보여주는 창을 하나의 파일로 따로 만들어서 사용했었고 속도도 다소 느렸던 것으로 기억한다.
 
그럼 ajax 를 이용하여 단순한 자동완성 기능 예제를 살펴보자.
 
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>Ajax Auto Complete</title>
    <style type="text/css">
    .mouseOut {
    background: #708090;
    color: #FFFAFA;
    }
    .mouseOver {
    background: #FFFAFA;
    color: #000000;
    }
    </style>
    <script type="text/javascript">
        var xmlHttp;
        var completeDiv;
        var inputField;
        var nameTable;
        var nameTableBody;
        function createXMLHttpRequest() {
            if (window.ActiveXObject) {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else if (window.XMLHttpRequest) {
                xmlHttp = new XMLHttpRequest();    
            }
        }
        function initVars() {
            inputField = document.getElementById("names");
            nameTable = document.getElementById("name_table");
            completeDiv = document.getElementById("popup");
            nameTableBody = document.getElementById("name_table_body");
        }
        function findNames() {
            initVars();
            if (inputField.value.length > 0) {
                createXMLHttpRequest();
                var url = "AutoCompleteServlet?names=" + escape(inputField.value);
                xmlHttp.open("GET", url, true);
                xmlHttp.onreadystatechange = callback;
                xmlHttp.send(null);
            } else {
                clearNames();
            }
        }
        function callback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    setNames(xmlHttp.responseXML.getElementsByTagName("name"));
                } else if (xmlHttp.status == 204){//데이터가 존재하지 않을 경우
                    clearNames();
                }
            }
        }
       
        function setNames(the_names) {
            clearNames();
            var size = the_names.length;
            setOffsets();
            var row, cell, txtNode;
            for (var i = 0; i < size; i++) {
                var nextNode = the_names[i].firstChild.data;
                row = document.createElement("tr");
                cell = document.createElement("td");
               
                cell.onmouseout = function() {this.className='mouseOver';};
                cell.onmouseover = function() {this.className='mouseOut';};
                cell.setAttribute("bgcolor", "#FFFAFA");
                cell.setAttribute("border", "0");
                cell.onclick = function() { populateName(this);};                    
                txtNode = document.createTextNode(nextNode);
                cell.appendChild(txtNode);
                row.appendChild(cell);
                nameTableBody.appendChild(row);
            }
        }
        function setOffsets() {
            var end = inputField.offsetWidth;
            var left = calculateOffsetLeft(inputField);
            var top = calculateOffsetTop(inputField) + inputField.offsetHeight;
            completeDiv.style.border = "black 1px solid";
            completeDiv.style.left = left + "px";
            completeDiv.style.top = top + "px";
            nameTable.style.width = end + "px";
        }
       
        function calculateOffsetLeft(field) {
          return calculateOffset(field, "offsetLeft");
        }
        function calculateOffsetTop(field) {
          return calculateOffset(field, "offsetTop");
        }
        function calculateOffset(field, attr) {
          var offset = 0;
          while(field) {
            offset += field[attr];
            field = field.offsetParent;
          }
          return offset;
        }
        function populateName(cell) {
            inputField.value = cell.firstChild.nodeValue;
            clearNames();
        }
        function clearNames() {
            var ind = nameTableBody.childNodes.length;
            for (var i = ind - 1; i >= 0 ; i--) {
                 nameTableBody.removeChild(nameTableBody.childNodes[i]);
            }
            completeDiv.style.border = "none";
        }
    </script>
  </head>
  <body>
    <h1>Ajax Auto Complete Example</h1>
    Names: <input type="text" size="20" id="names" onkeyup="findNames();" style="height:20;"/>
    <div style="position:absolute;" id="popup">
        <table id="name_table" bgcolor="#FFFAFA" border="0" cellspacing="0" cellpadding="0"/>           
            <tbody id="name_table_body"></tbody>
        </table>
    </div>
  </body>
</html>
<autoComplete.html 의 전체 소스 코드>
 
 
package ajaxbook.chap4;
import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.*;
import javax.servlet.http.*;
/**
 *
 * @author nate
 * @version
 */
public class AutoCompleteServlet extends HttpServlet {
    private List names = new ArrayList();
    public void init(ServletConfig config) throws ServletException {
        names.add("Abe");
        names.add("Abel");
        names.add("Abigail");
        names.add("Abner");
        names.add("Abraham");
        names.add("Marcus");
        names.add("Marcy");
        names.add("Marge");
        names.add("Marie");
    }
   
    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String prefix = request.getParameter("names");
        NameService service = NameService.getInstance(names);
        List matching = service.findNames(prefix);
        if (matching.size() > 0) {
            PrintWriter out = response.getWriter();
            response.setContentType("text/xml");
            response.setHeader("Cache-Control", "no-cache");
            out.println("<response>");
            Iterator iter = matching.iterator();
            while(iter.hasNext()) {
                String name = (String) iter.next();
                out.println("<name>" + name + "</name>");
            }
            out.println("</response>");
            matching = null;
            service = null;
            out.close();
        } else {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
            //response.flushBuffer();
        }
    }
   
    /** Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doGet(request, response);
    }
       
    /** Returns a short description of the servlet.
     */
    public String getServletInfo() {
        return "Short description";
    }
}
 
<AutoCompleteServlet.java 의 전체 소스 코드>
 
 
 
package ajaxbook.chap4;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
 *
 * @author nate
 */
public class NameService {
    private List names;
   
    /** Creates a new instance of NameService */
    private NameService(List list_of_names) {
        this.names = list_of_names;
    }
   
    public static NameService getInstance(List list_of_names) {
        return new NameService(list_of_names);
    }
   
    public List findNames(String prefix) {
        String prefix_upper = prefix.toUpperCase();
        List matches = new ArrayList();
        Iterator iter = names.iterator();
        while(iter.hasNext()) {
            String name = (String) iter.next();
            String name_upper_case = name.toUpperCase();
            if(name_upper_case.startsWith(prefix_upper)){       
                boolean result = matches.add(name);
            }
        }
        return matches;
    }
   
}
 
<NameService.java 의 전체 소스 코드>

 

 

우선 실행결과 화면을 보고 설명을 이어가겠다.

 

 

위 그림의 검색어 입력창에 a 라는 문자를 입력하면 매칭되는 문자열의 리스트를 보여주는 그림이다.

 

입력창에 키워드를 입력하면 이벤트가 발생하고 XHR 객체가 문자를 GET/비동기 방식으로 서버로 보낸다. 서버에서는 요청한 검색어와 매칭되는 결과를 XML 로 만들어서 클라이언트로 보내고, XHR 객체가 응답 XML 을 파싱해서 결과를 화면에 보여주는 흐름이다.

 

        function callback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    setNames(xmlHttp.responseXML.getElementsByTagName("name"));
                } else if (xmlHttp.status == 204){//데이터가 존재하지 않을 경우
                    clearNames();
                }
            }
        }

 

위 메소드에는 XHR 객체의 status 코드가 204 일 경우를 처리하고 있다. 즉, 검색 결과가 없을 경우에 결과창을 지우기 위한 것이다.

위 예제에서 눈여겨 봐야 할 부분은 XML 에서 데이터를 분석해서 동적으로 결과 화면을 만드는 부분일 것이다. DOM 객체의 속성 및 메소드를 많이 접해 봐야 한다.

 

       function setNames(the_names) {
            clearNames();
            var size = the_names.length;
            setOffsets();

            var row, cell, txtNode;
            for (var i = 0; i < size; i++) {
                var nextNode = the_names[i].firstChild.data;
                row = document.createElement("tr");
                cell = document.createElement("td");
               
                cell.onmouseout = function() {this.className='mouseOver';};
                cell.onmouseover = function() {this.className='mouseOut';};
                cell.setAttribute("bgcolor", "#FFFAFA");
                cell.setAttribute("border", "0");
                cell.onclick = function() { populateName(this);};                    

                txtNode = document.createTextNode(nextNode);
                cell.appendChild(txtNode);
                row.appendChild(cell);
                nameTableBody.appendChild(row);
            }
        }

 

위 메소드는 검색결과의 한 행을 만들어 테이블의 <tr></tr> 요소를 동적으로 생성해 주는 부분이다. cell.onmouseout 및 onclick 메소드 생성할때 setAttribute() 를 사용하지 않은 이유는 IE 가 지원하지 않기 때문이다. cross-browser, 즉 모든 브라우저에서 위 샘플 실행을 보장 받기 위해서는 코딩을 위처럼 해 줘야 한다.

 

=======================================================================================

 

위 예제는 한글 테스트가 불가능하기 때문에 이 기회에 Ajax 의 한글문제에 대해서 생각해 보자. GET 방식이든 POST 방식이든 파라미터가 서버로 전달될때는 해당 페이지에 설정된 charset 으로 인코딩 된다. 영문이나 숫자는 문제가 안되지만 한글과 같은 유니코드 문자는 적절한 charset 으로 디코딩해주지 않으면 ??? 혹은 이상한 문자로 출력될 것이다.

 

Ajax 의 경우 한글 인코딩 문제는 다음과 같이 두 부분으로 나누어서 생각해 볼 수 있다.

 

첫번째는 브라우저의 XHR 객체에서 한글 파라미터를 인코딩하고 서버에서 디코딩해서 처리하는 경우이다. 물론 같은 charset 으로 처리 해야 된다. 이부분은 개발자가 충분히 charset을 변경할 수 있으므로 유연하게 대처할 수 있다.

 

두번째는 서버에서 인코딩하고 브라우저의 XHR 객체에서 한글 데이터를 디코딩하는 경우이다. 서버에서 인코딩하는 경우는 개발자가 얼마든지 바꿀 수 있지만 브라우저에서 Ajax 가 데이터를 디코딩할 경우의 charset 은 UTF-8 로 정해져 있는것 같다. 다른 charset 으로 변경할 수 있는 방법이 존재하는지는 확실하지 않지만 여러방면으로 테스트해 본 결과 UTF-8로 지정되어 있는것 같다. 따라서 서버에서 인코딩하는 부분 뿐만아니라 첫번째의 경우도 모두 charset 을 UTF-8 으로 인코딩/디코딩하는 방법이 제일 간단할 것이다.

 

1. 브라우저에서 charset 을 UTF-8 으로 설정하기

먼저 autoComplete.html 의 소스에서는 charset 을 UTF-8 로 인코딩 해주는 부분만 수정해주면 될 것이다.

var url = "AutoCompleteServlet?names=" + escape(inputField.value);

 

위 코드에서 escape 메소드는 파라미터를 유니코드방식으로 인코딩하므로 이 메소드 대신에 UTF-8 방식으로 인코딩해주는 자바스크립트 메소드인 encodeURI 혹은 encodeURIComponent 으로 아래와 같이 수정해 준다.

 

var url = "AutoCompleteServlet?names=" + encodeURI(inputField.value);

 

위 예제는 GET 방식으로 작성되었으나 POST 방식의 경우도 같은 방법으로 charset 을 설정해주면 된다. 아래 코드는 POST 방식의 findNames 메소드이다.

function findNames() {
        initVars();
        if (inputField.value.length > 0) {
                createXMLHttpRequest();
                var url = "AutoCompleteServlet";
                xmlHttp.open("POST", url, true);
                xmlHttp.onreadystatechange = callback;
                xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                xmlHttp.send("names=" + encodeURI(inputField.value));
                //encodeURI 대신에 encodeURIComponent 를 사용해도 결과는 동일하다.
        } else {
                clearNames();
        }
 }

 

2. 서버에서 charset 을 UTF-8 로 설정하기

다음은 AutoCompleteServlet 서브릿 소스에서 수정할 부분을 살펴보자.

protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String prefix = request.getParameter("names");

.

.

.

 

위의 doGet 메소드에 charset 을 UTF-8 으로 설정해주는 코드를 추가해준다. 그러면 브라우저에 UTF-8 방식으로 인코딩된 한글 파라미터는 같은 charset 으로 디코딩 될 것이다.

 

protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        String prefix = request.getParameter("names");

.

.

.

 

다음은 서버에서 브라우저로 응답을 보내기 전에 charset 을 UTF-8 로 바꿔줘야 한다.

response.setContentType("text/xml");

 

위 소스 코드를 아래와 같이 수정해 준다.

response.setContentType("text/xml;charset=UTF-8");

 

마지막으로 DB 조회결과를 가상으로 꾸미기 위하여 한글 데이터를 추가해 준다.

public void init(ServletConfig config) throws ServletException {
        names.add("Abe");
        names.add("Abel");
        names.add("Abigail");
        names.add("Abner");
        names.add("Abraham");
        names.add("Marcus");
        names.add("Marcy");
        names.add("Marge");
        names.add("Marie");
        names.add("스타크");
        names.add("스타크래프트");
        names.add("스타크래프트 치트키");
        names.add("스타크래프트 다운로드");
    }

 

수정이 끝났으면 예제를 다시 실행해 보자.

 

 

 
 

출처 : jinoxst님의 블로그

 

웹서비스 접근하기

WEB2.0/ajax | 2006. 4. 12. 15:00
Posted by 시반

본인이 생각하는 웹서비스는 이기종간의 통신을 위한 몸부림? 이라고 생각한다.  웹서비스는 ajax 와 마찬가지로 현재 진행형이다. 이미 도입하여 이용하고 있는 곳도 있고 계속해서 버젼을 올리며 칼날을 세우고 있는 곳도 있다.

웹서비스는 어떤 언어로 구현됬는지에 상관없이 그 서비스를 사용할 수 있도록 구현해야 하는 기술로써, 이런 통신이 가능토록 하기 위한 가장 기본적인 초석은 HTTP 통신 프로토콜을 사용하고 있다는 것과 이기종간의 데이터 포맷을 고려하여 텍스트 기반의 XML 을 사용한다는 것이다.

Ajax 를 이용해서 SOAP 프로토콜을 기반으로 작성된 웹서비스에 접근할 수 있을까? 가능하지만 사실 어렵다. SOAP 프로토콜은 실제 XML 객체를 파라미터로 사용하고 있기 때문에 지금까지 우리가 해온 방식인 스트링 조합가지고는 상당히 힘들다. 왜냐하면 스트링을 조합하는 과정에서 에러가 날 수있는 확률이 매우 높기 때문에 좋은 방법은 아닐 것이다. 또한 SOAP 은 GET 방식이 아닌 POST 방식을 주로 사용해야 한다. 따라서 좀 더 쉬운 방법으로 SOAP request 을 위한 정적 XML 템플릿을 XMLHttpRequest  가 로드한 후에 DOM 속성과 메소드를 이용하여 XML 데이터를 조작하는 것이 훨씬 수월할 것이다.

SOAP 을 이용하는 것 말고 좀 더 쉬운 방법이 존재한다. Representational State Transfer(REST) 은 웹서비스를 아주 쉽게 이용할 수 있는 서비스 이다. REST 는 SOAP 과 비교하자면 20%의 노력으로 SOAP 의 80% 에 해당하는 이득을 취할 수 있다고 한다. Ajax 는 REST 기반 웹서비스에 아주 적합하다. XHR 객체를 이용해서 REST 웹서비스에 비동기로 접근하고 그 결과는 XML 로 받아서 브라우저에서 파싱하여 사용하면 끝이다. 어떤가? 우리가 지금까지 해오던 방식과 아주 흡사하지 않은가?

여기서 한가지 더 짚고 가야 할 것은 XHR 객체의 보안 문제이다. 이 문제는 AJAX 강의 2장 - XMLHttpRequest 오브젝트 사용하기에 서 언급하였다. XHR 객체가 웹서비스를 사용한다는 것은 자기가 속해 있는 도메인 밖에 있는 서버의 리소스 url 에 접근한다는 의미이므로 보안 문제는 반드시 발생하게 되어 있다. 따라서 이 문제를 해결하기 위해서는 게이트웨이 기능을 하는 프로그램을 추가로 개발해야 한다. 표현이 게이트웨이라 그럴듯해 보이지만 코딩은 굉장히 간단하다. 게이트웨이 기능은 네트웍 프로그램으로 서버쪽 프로그램에서 구현할 것이다.

이번장의 샘플 프로그램은 야후에 구현되어 있는 REST 웹서비스에 접근하여 그 결과를 보여주는 프로그램이다.

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Simple XMLHttpRequest</title>
   
<script type="text/javascript">
var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function startRequest() {
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", "simpleResponse.xml", true);
    xmlHttp.send(null);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            alert("The server replied with: " + xmlHttp.responseText);
        }
    }
}
</script>
</head>

<body>
    <form action="#">
        <input type="button" value="Start Basic Asynchronous Request" onclick="startRequest();"/>
    </form>
</body>
</html>


 <yahooSearch.html 의 전체 소스 코드>


 

package ajaxbook.chap4;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.servlet.*;
import javax.servlet.http.*;

public class YahooSearchGatewayServlet extends HttpServlet {
    private static final String YAHOO_SEARCH_URL =
        "http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=thunderboltsoftware"
        + "&type=all";

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

        String url = YAHOO_SEARCH_URL + "&" + request.getQueryString();

        HttpURLConnection con = (HttpURLConnection)new URL(url).openConnection();
        con.setDoInput(true);
        con.setDoOutput(true);

        con.setRequestMethod("GET");

        //Send back the response to the browser
        response.setStatus(con.getResponseCode());
        response.setContentType("text/xml");

        BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String input = null;
        OutputStream responseOutput = response.getOutputStream();

        while((input = reader.readLine()) != null) {
            responseOutput.write(input.getBytes());
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }
}


<YahooSearchGatewayServlet.java 의 전체 소스 코드>

코드가 너무 단순한 것도 있지만 지금까지 해오던 방식 그대로이기때문에 자세한 설명은 생략하겠다. 서버쪽에 위치한 게이트웨이 프로그램은 자바를 이용해서 야후가 제공하는 REST 웹서비스 url 에 네트웍으로 접근하여 그 결과를 얻어오는 프로그램으로 굳이 자바를 사용하지 않더라도 다른 언어로 충분히 작성할 수 있을 것이다.

 

실핼 결과를 살펴보자.


 

위 그림은 yahooSearch.html 의 실행화면이다. 검색할 단어를 입력하고 검색결과 갯수를 10으로 지정한 후 Submit 버튼을 눌러보자.

 

위 그림은 ajax 라는 검색어를 10개의 검색결과로 지정해서 얻은 결과이다. 실제 야후 웹싸이트에 가서 ajax 라는 키워드로 검색해 보자.

 

 위 화면은 같은 ajax 키워드로 검색한 실제 결과이다. 샘플 예제와 순서가 조금 다를뿐 제목과 내용, 그리고 링크까지 같은 결과를 보여주고 있음을 확인 할 수 있다.

 

출처 : jinoxst님의 블로그

 

툴팁 구현하기

WEB2.0/ajax | 2006. 4. 12. 14:12
Posted by 시반

웹 페이지에서 툴팁이란 어떤 것일까?

 

아래 그림을 보자. 마우스를 해당 이미지에 갖다 대면 그 그림에 대한 세부 설명이 나오는 것을 봤을 것이다. 이런 기능을 ajax 을 이용해서 구현할 수 있다.

 


 
 
 
보잘 것 없지만 기능을 단순화 시킨 예제를 살펴보자.
 
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>Ajax Tooltip</title>
    <script type="text/javascript">       
        var xmlHttp;
        var dataDiv;
        var dataTable;
        var dataTableBody;
        var offsetEl;
        function createXMLHttpRequest() {
            if (window.ActiveXObject) {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else if (window.XMLHttpRequest) {
                xmlHttp = new XMLHttpRequest();               
            }
        }
        function initVars() {
            dataTableBody = document.getElementById("courseDataBody");           
            dataTable = document.getElementById("courseData");
            dataDiv = document.getElementById("popup");
        }
        function getCourseData(element) {
            initVars();           
            createXMLHttpRequest();
            offsetEl = element;
            var url = "ToolTipServlet?key=" + escape(element.id);
           
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = callback;
            xmlHttp.send(null);
        }
        function callback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    setData(xmlHttp.responseXML);
                }
            }
        }
       
        function setData(courseData) {           
            clearData();
            setOffsets();
            var length = courseData.getElementsByTagName("length")[0].firstChild.data;
            var par = courseData.getElementsByTagName("par")[0].firstChild.data;
           
            var row, row2;
            var parData = "Par: " + par
            var lengthData = "Length: " + length;
           
            row = createRow(parData);           
            row2 = createRow(lengthData);
           
            dataTableBody.appendChild(row);
            dataTableBody.appendChild(row2);
        }
        function createRow(data) {           
            var row, cell, txtNode;
            row = document.createElement("tr");
            cell = document.createElement("td");
            cell.setAttribute("bgcolor", "#FFFAFA");
            cell.setAttribute("border", "0");                          
            txtNode = document.createTextNode(data);
            cell.appendChild(txtNode);
            row.appendChild(cell);
          
            return row; 
        }
       
        function setOffsets() {
            var end = offsetEl.offsetWidth;
            var top = calculateOffsetTop(offsetEl);
            dataDiv.style.border = "black 1px solid";
            dataDiv.style.left = end + 15 + "px";
            dataDiv.style.top = top + "px";
        }
       
        function calculateOffsetTop(field) {
          return calculateOffset(field, "offsetTop");
        }
        function calculateOffset(field, attr) {
          var offset = 0;
          while(field) {
            offset += field[attr];
            field = field.offsetParent;
          }
          return offset;
        }
        function clearData() {           
            var ind = dataTableBody.childNodes.length;           
            for (var i = ind - 1; i >= 0 ; i--) {
                dataTableBody.removeChild(dataTableBody.childNodes[i]);      
            }
            dataDiv.style.border = "none";
        }       
    </script>
  </head>
  <body>
    <h1>Ajax Tooltip Example</h1>
    <h3>Golf Courses</h3>
    <table id="courses" bgcolor="#FFFAFA" border="1" cellspacing="0" cellpadding="2"/>
        <tbody>
            <tr><td id="1" onmouseover="getCourseData(this);" onmouseout="clearData();">Augusta National</td></tr>
            <tr><td id="2" onmouseover="getCourseData(this);" onmouseout="clearData();">Pinehurst No. 2</td></tr>
            <tr><td id="3" onmouseover="getCourseData(this);" onmouseout="clearData();">St. Andrews Links</td></tr>
            <tr><td id="4" onmouseover="getCourseData(this);" onmouseout="clearData();">Baltusrol Golf Club</td></tr>
        </tbody>       
    </table>
    <div style="position:absolute;" id="popup">
        <table id="courseData" bgcolor="#FFFAFA" border="0" cellspacing="2" cellpadding="2"/>
            <tbody id="courseDataBody"></tbody>
        </table>
    </div>
  </body>
</html>
<toolTip.html 의 전체 소스>
 
 
package ajaxbook.chap4;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.*;
import javax.servlet.http.*;
/**
 *
 * @author nate
 * @version
 */
public class ToolTipServlet extends HttpServlet {
   
    private Map courses = new HashMap();
    public void init(ServletConfig config) throws ServletException {
        CourseData augusta = new CourseData(72, 7290);
        CourseData pinehurst = new CourseData(70, 7214);
        CourseData standrews = new CourseData(72, 6566);
        CourseData baltusrol = new CourseData(70, 7392);
        courses.put(new Integer(1), augusta);
        courses.put(new Integer(2), pinehurst);
        courses.put(new Integer(3), standrews);
        courses.put(new Integer(4), baltusrol);
    }
   
    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        Integer key = Integer.valueOf(request.getParameter("key"));
        CourseData data = (CourseData) courses.get(key);
       
        PrintWriter out = response.getWriter();
        response.setContentType("text/xml");
        response.setHeader("Cache-Control", "no-cache");
        out.println("<response>");
        out.println("<par>" + data.getPar() + "</par>");
        out.println("<length>" + data.getLength() + "</length>");
        out.println("</response>");
        out.close();
    }
   
    /** Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doGet(request, response);
    }
       
    /** Returns a short description of the servlet.
     */
    public String getServletInfo() {
        return "Short description";
    }
   
    private class CourseData {
        private int par;
        private int length;
       
        public CourseData(int par, int length) {
            this.par = par;
            this.length = length;
        }
       
        public int getPar() {
            return this.par;
        }
       
        public int getLength() {
            return this.length;
        }
    }
}
 
<ToolTipServlet.java 의 전체 소스 코드>
 
 
 
이전 예제와 다른 점은 옵셋을 구현한 코드이다.
 
function calculateOffset(field, attr) {
    var offset = 0;
    while(field) {
        offset += field[attr];
        field = field.offsetParent;
    }
    return offset;
}
 
이 코드는 브라우저 마다 작동을 안할 수 있지만, 테스트 해본 결과 IE 와 firefox 에서는 잘 실행되었다. 위 예제를 실행해 본 결과는 아래와 같다.
 
 

 

출처 : jinoxst님의 블로그

'WEB2.0 > ajax' 카테고리의 다른 글

자동완성기능 구현하기  (0) 2006.04.12
웹서비스 접근하기  (0) 2006.04.12
progress bar 기능 구현하기  (0) 2006.04.11
Auto Refresh 기능 구현하기  (0) 2006.04.11
동적으로 리스트박스 구성하기  (0) 2006.04.11
 

progress bar 기능 구현하기

WEB2.0/ajax | 2006. 4. 11. 17:32
Posted by 시반

웹 프로그램의 경우 요청에 대한 응답이 제대로 오지 않거나 너무 많은 시간이 걸린다면 사용자는 불평을 하면서 다른 싸이트로 가버리고 다시는 그 싸이트를 이용하지 않을 것이다. 혹은 금전적인 문제가 걸려있다면 서비스 콜센터로 항의 전화를 할 것이다.

 

2년전 국내 모 은행 인터넷 뱅킹을 개발하면서 로그인 기능을 구현해 놓을 것을 살펴보고 놀라지 않을 수 없었다. 로그인을 하는데 자그마치 30초가 걸렸다. 웹 프로그램에서 30초라...어떻게 생각하는가? 당연히 설계가 잘못됬다. 고객에게 이런 불편을 초래하는 싸이트라면 경쟁 인터넷뱅킹 은행에게 모든 고객을 빼앗길 것이다.

 

 몇년전의 통계수치라 지금은 기억이 잘 나지 않지만, 인터넷 이용자가 응답정보를 기다리다 인내심을 더이상 참지 못하고 다른 싸이트로 가버리는 시간은 7~8초 였던 것으로 기억한다. 하지만 지금은 좀더 짧아지지 않았을까 생각한다.

 

30초는 정말 말도 안되는 시간이지만 5~6초정도의 시간이면 사용자에게 현재 서버의 작업 진행상태를 표시해주면 사용자는 안도감을 느끼면서 좀 더 기다려줄 확률은 높아질 수 있다. 이것은 일종의 서비스 차원의 아이디어일뿐이지, 사실 서버의 프로그램을 수정해서 최대한 프로세스를 단축하는 것이 가장 좋은 해결책이라 하겠다.

 

로그인 하는데 30초 걸렸던 그 프로젝트에서는 할 수 없이 서버의 상태바를 이미지로 붙여 놓았다. 이런 이미지는 사실 의미가 없는것이 현재 서버의 상태를 실시간으로 보여주는 것이 아니라 반복적인 이미지로 하여금 고객을 짜증나게 할 수도 있다. 실제 서버의 작업 진척도와 현재 브라우저에서 보여주는 정보가 일치해야 진정한 상태바 프로그램이라 할 수 있겠다.

 

상태바 프로그램을 위해서는 자원의 소모라는 손해를 감수해야만 한다. 서버의 비지니스 로직이 현재 얼마나 진행됬는지 별도로 체크해야 하는 코딩이 추가되어야 한다. 가뜩이나 시간이 오래걸리는 작업인데 그 작업이 얼마나 진행됬는지 확인하는 로직이 추가된다면 자원의 소모가 그만큼 더 많아지게 되고 시간도 더 지연되는 것이다. 장점이 있다면 단점이 있기 마련이다. 장점을 더욱 부각시키고 단점을 줄이는 방향으로 생각을 전환하자.

 

이런 복잡한 문제에서 벗어나 지금은 상태바에 대한 구현 자체가 목적이므로 단순한 예제를 통해서 살펴보기로 하자.


 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title>Ajax Progress Bar</title>
    <script type="text/javascript">
        var xmlHttp;
        var key;
        var bar_color = 'gray';
        var span_id = "block";
        var clear = "&nbsp;&nbsp;&nbsp;"

        function createXMLHttpRequest() {
            if (window.ActiveXObject) {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else if (window.XMLHttpRequest) {
                xmlHttp = new XMLHttpRequest();               
            }
        }

        function go() {
            createXMLHttpRequest();
            checkDiv();
            var url = "ProgressBarServlet?task=create";
            var button = document.getElementById("go");
            button.disabled = true;
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = goCallback;
            xmlHttp.send(null);
        }

        function goCallback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    setTimeout("pollServer()", 1000);
                }
            }
        }
       
        function pollServer() {
            createXMLHttpRequest();
            var url = "ProgressBarServlet?task=poll";
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = pollCallback;
            xmlHttp.send(null);
        }
       
        function pollCallback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    var percent_complete = xmlHttp.responseXML.getElementsByTagName("percent")[0].firstChild.data;
                   
                    var index = processResult(percent_complete);
                    for (var i = 1; i <= index; i++) {
                        var elem = document.getElementById("block" + i);
                        elem.innerHTML = clear;

                        elem.style.backgroundColor = bar_color;
                        var next_cell = i + 1;
                        if (next_cell > index && next_cell <= 9) {
                            document.getElementById("block" + next_cell).innerHTML = percent_complete + "%";
                        }
                    }
                    if (index < 9) {
                        setTimeout("pollServer()", 1000);
                    } else {
                        document.getElementById("complete").innerHTML = "Complete!";
                        document.getElementById("go").disabled = false;
                    }
                }
            }
        }
       
        function processResult(percent_complete) {
            var ind;
            if (percent_complete.length == 1) {
                ind = 1;
            } else if (percent_complete.length == 2) {
                ind = percent_complete.substring(0, 1);
            } else {
                ind = 9;
            }
            return ind;
        }

        function checkDiv() {
            var progress_bar = document.getElementById("progressBar");
            if (progress_bar.style.visibility == "visible") {
                clearBar();
                document.getElementById("complete").innerHTML = "";
            } else {
                progress_bar.style.visibility = "visible"
            }
        }
       
        function clearBar() {
            for (var i = 1; i < 10; i++) {
                var elem = document.getElementById("block" + i);
                elem.innerHTML = clear;
                elem.style.backgroundColor = "white";
            }
        }
    </script>
  </head>
  <body>
    <h1>Ajax Progress Bar Example</h1>
    Launch long-running process: <input type="button" value="Launch" id="go" onclick="go();"/>
    <p>
    <table align="center">
        <tbody>
            <tr><td>
                <div id="progressBar" style="padding:2px;border:solid black 2px;visibility:hidden">
                    <span id="block1">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block2">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block3">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block4">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block5">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block6">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block7">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block8">&nbsp;&nbsp;&nbsp;</span>
                    <span id="block9">&nbsp;&nbsp;&nbsp;</span>
                </div>
            </td></tr>
            <tr><td align="center" id="complete"></td></tr>
        </tbody>
    </table>
  </body>
</html>

<progressBar.html 의 전체 소스 코드>



package ajaxbook.chap4;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

/**
 *
 * @author nate
 * @version
 */
public class ProgressBarServlet extends HttpServlet {
    private int counter = 1;
   
    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String task = request.getParameter("task");
        String res = "";
       
        if (task.equals("create")) {
            res = "<key>1</key>";
            counter = 1;
        }
        else {
            String percent = "";
            switch (counter) {
                case 1: percent = "10"; break;
                case 2: percent = "23"; break;
                case 3: percent = "35"; break;
                case 4: percent = "51"; break;
                case 5: percent = "64"; break;
                case 6: percent = "73"; break;
                case 7: percent = "89"; break;
                case 8: percent = "100"; break;
            }
            counter++;
               
            res = "<percent>" + percent + "</percent>";
        }
       
        PrintWriter out = response.getWriter();
        response.setContentType("text/xml");
        response.setHeader("Cache-Control", "no-cache");
        out.println("<response>");
        out.println(res);
        out.println("</response>");
        out.close();
    }
   
    /** Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doGet(request, response);
    }
   
    /** Returns a short description of the servlet.
     */
    public String getServletInfo() {
        return "Short description";
    }
}


<ProgressBarServlet.java 의 전체 소스 코드>

우선 실행 결과 화면을 먼저 살펴보자.

 



위 그림에서 Launch 버튼을 클릭하면 아래와 같은 상태바 결과를 확인 할 수 있다.


이런 상태바 프로그램을 작성하는데에는 ajax 가 정말 적합하다고 생각한다. 물론 자바스크립트 코드가 많아져서 이해하는데 어려움이 있을 수도 있지만 ajax 을 사용하지 않고 다른 방식으로 구현한다고 했을때 가능은 하겠지만 이때에도 역시 스크립팅 코드가 없을 수 만은 없다.

 

소스 코드는 이전예제와 별로 크게 다르지 않으므로 설명은 생략한다.

 

'WEB2.0 > ajax' 카테고리의 다른 글

웹서비스 접근하기  (0) 2006.04.12
툴팁 구현하기  (0) 2006.04.12
Auto Refresh 기능 구현하기  (0) 2006.04.11
동적으로 리스트박스 구성하기  (0) 2006.04.11
응답 헤더정보 다루기  (0) 2006.04.11
 

Auto Refresh 기능 구현하기

WEB2.0/ajax | 2006. 4. 11. 17:30
Posted by 시반

auto refresh 기능, 즉 자동으로 페이지의 일정부분을 지정한 시간마다 정보를 업데이트 해주는 기능이다.

 

우선 이런 기능을 이미 구현해 놓은 싸이트들을 살펴보자.

 

 

 

<최근 소식을 ajax 를 이용하여 소팅 기능을 구현한 싸이트(www.digg.com/spy)>

 

 

 


 

<다운로드 카운트를 ajax 를 이용하여 구현한 싸이트(www.apple.com/itunes)>

 

 

 

위 두 싸이트를 살펴보면 일정시간을 설정하여 지속적으로 해당 부분만 정보가 수정되는 것을 확인 할 수 있다. 이번 주제 역시 ajax 의 전형적인 특징으로 전체 페이지가 새로고침되지 않고 필요한 일정부분만 정보가 수정된다는데 있다.

 

단순한 예제를 통해서 auto refresh 기능에 대해서 생각해 보자

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title>Ajax Dynamic Update</title>
    <script type="text/javascript">
        var xmlHttp;

        function createXMLHttpRequest() {
            if (window.ActiveXObject) {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else if (window.XMLHttpRequest) {
                xmlHttp = new XMLHttpRequest();               
            }
        }
       
        function doStart() {
            createXMLHttpRequest();
            var url = "DynamicUpdateServlet?task=reset";
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = startCallback;
            xmlHttp.send(null);
        }

        function startCallback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {                   
                    setTimeout("pollServer()", 5000);
                    refreshTime();
                }
            }
        }
       
        function pollServer() {
            createXMLHttpRequest();
            var url = "DynamicUpdateServlet?task=foo";
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = pollCallback;
            xmlHttp.send(null);
        }
       
        function refreshTime(){
            var time_span = document.getElementById("time");
            var time_val = time_span.innerHTML;

            var int_val = parseInt(time_val);
            var new_int_val = int_val - 1;
           
            if (new_int_val > -1) {
                setTimeout("refreshTime()", 1000);
                time_span.innerHTML = new_int_val;               
            } else {
                time_span.innerHTML = 5;
            }
        }
       
        function pollCallback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {               
                    var message = xmlHttp.responseXML.getElementsByTagName("message")[0].firstChild.data;                   
                   
                    if (message != "done") {
                        var new_row = createRow(message);
                        var table = document.getElementById("dynamicUpdateArea");
                        var table_body = table.getElementsByTagName("tbody").item(0);
                        var first_row = table_body.getElementsByTagName("tr").item(1);
                        table_body.insertBefore(new_row, first_row);                       
                        setTimeout("pollServer()", 5000);
                        refreshTime();
                    }
                }
            }
        }
       
        function createRow(message) {
            var row = document.createElement("tr");
            var cell = document.createElement("td");
            var cell_data = document.createTextNode(message);
            cell.appendChild(cell_data);
            row.appendChild(cell);
            return row;
        }
    </script>
  </head>
  <body>
    <h1>Ajax Dynamic Update Example</h1>
    This page will automatically update itself:
        <input type="button" value="Launch" id="go" onclick="doStart();"/>
    <p>
    Page will refresh in <span id="time">5</span> seconds.
    <p>
    <table id="dynamicUpdateArea" align="left">
        <tbody>
            <tr id="row0"><td></td></tr>
        </tbody>
    </table>
  </body>
</html>

<dynamicUpdate.html 의 전체 소스 코드>

 

 

package ajaxbook.chap4;

import java.io.*;
import java.net.*;

import javax.servlet.*;
import javax.servlet.http.*;

/**
 *
 * @author nate
 * @version
 */
public class DynamicUpdateServlet extends HttpServlet {
    private int counter = 1;
   
    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String res = "";
        String task = request.getParameter("task");
        String message = "";       
       
        if (task.equals("reset")) {
            counter = 1;
        } else {
            switch (counter) {
                case 1: message = "Steve walks on stage"; break;
                case 2: message = "iPods rock"; break;
                case 3: message = "Steve says Macs rule"; break;
                case 4: message = "Change is coming"; break;
                case 5: message = "Yes, OS X runs on Intel - has for years"; break;
                case 6: message = "Macs will soon have Intel chips"; break;
                case 7: message = "done"; break;
            }
            counter++;
        }

        res = "<message>" + message + "</message>";
       
        PrintWriter out = response.getWriter();
        response.setContentType("text/xml");
        response.setHeader("Cache-Control", "no-cache");
        out.println("<response>");
        out.println(res);
        out.println("</response>");
        out.close();
    }
   
    /** Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doGet(request, response);
    }
   

    /** Returns a short description of the servlet.
     */
    public String getServletInfo() {
        return "Short description";
    }
}

 

<DynamicUpdateServlet 의 전체 소스 코드>

 

 

이번 예제를 실행결과 화면을 먼저 보고 설명하는 것이 좋을 듯 싶다.

 

 


 

위 그림은 dynamicUpdate.html 화면인데 Launch 버튼을 클릭하면 아래 화면의 일정부분이 refresh 될 것이다.

 

 


 

사실 seconds 가 5, 4, 3, 2, 1 으로 값이 줄어드는 것은 소스를 보면 알겠지만 자바스크립트로 처리한 것이고 중요하게 볼것은 그 아래에 있는 문자열이다. 5초가 지날때마다 브라우저는 서버에 비동기적으로 접속해 관련 데이터를 추출하여 추가해주는 부분이다.

 

소스는 그리 길지는 않지만 dynamicUpdate.html 소스는 스크립팅 처리가 좀 들어가 있다. 하지만 지금까지의 강의 내용을 잘 읽어 봤다면 처음나오는 코드나 이해가지 않는 곳은 없으리라 생각한다

 

출처 : jinoxst님의 블로그

'WEB2.0 > ajax' 카테고리의 다른 글

툴팁 구현하기  (0) 2006.04.12
progress bar 기능 구현하기  (0) 2006.04.11
동적으로 리스트박스 구성하기  (0) 2006.04.11
응답 헤더정보 다루기  (0) 2006.04.11
폼 입력값 검증하기  (0) 2006.04.11
 

동적으로 리스트박스 구성하기

WEB2.0/ajax | 2006. 4. 11. 17:27
Posted by 시반

전통적으로 웹 프로그램밍은 사용자 입력에 의존한 step by step 어플리케이션에 매우 적합한 방식이다. 그 만큼 이전 페이지와 이후 페이지 사이에서의 사용자 입력이 중요하다. 어떻게 보면 이런 step by step 방식의 페이지 구성은 웹이 아닌 다른 프로그램과 비교해봤을때 어떻게 보면 단순하고 멍청해보이기까지 한다. AJAX 가 도입되면서 전통적인 웹 프로그램에 조금씩 수정이 가해지고 있는데 이번장에서는 지금까지 배운 내용만을 토대로 두개의 select box 값이 파라미터가 되어 매칭되는 결과를 동적으로 보여주는 예제를 소개하기로 한다.

 

사실 AJAX 를 사용하지 않더라도 이런 효과는 이전에도 가능했다. select box 의 value 값과 매칭결과를 한꺼번에 받아 hidden 요소나 혹은 다른 방식으로 웹페이지에 저장했다가 사용자의 입력에 따라 그 부분만 취사선택해서 보여주면 된다. 하지만 이경우 데이타가 많아지면 페이지 로딩시간이 지연되고 사용자가 급증할수록 서버에 무리가 갈 것이다. 그리고 일반적으로 클라이언트 보다는 서버 머신의 성능이 월등하므로 많은 데이터를 서버로부터 한꺼번에 받아서 브라우저에서 스크립팅언어로 데이터를 분석해서 보여주는 것은 역시 비효율적이다.

 

따라서 이번에는 사용자의 입력이 있을때마다 비동기 방식으로 서버에 접속해 그 결과를 바로 알 수 있는 예제를 살펴보자. AJAX 의 가장 큰 장점 중의 하나는 역시 전체 페이지 로딩이 필요 없다는 것이다. 물론 전체 페이지 로딩이 필요한 경우도 있지만, 그렇지 않은 경우에도 전체 페이지가 로딩된다는 것은 결국 서버와 클라이언트 모두 불필요한 작업을 계속 해야 된다는 것을 의미한다. 서버쪽 비지니스로직 프로그램을 해본 개발자는 잘 알 것이다. 서버의 성능을 판단하는 지표중에 TPS 라고 있다. 프로젝트를 진행하면서 구현이 끝나면 전체 성능 및 로드 테스트를 진행하는데 1 TPS 올리는 것이 얼마나 어려운지 안 해본 사람은 모를 것이다. 자원을 좀더 효율적으로 사용해보자. 그리고 좀 더 스마트하게 웹기술을 적용하게 보자.

 

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Dynamically Filling Lists</title>

<script type="text/javascript">
var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function refreshModelList() {
    var make = document.getElementById("make").value;
    var modelYear = document.getElementById("modelYear").value;

    if(make == "" || modelYear == "") {
        clearModelsList();
        return;
    }
   
    var url = "RefreshModelList?"
        + createQueryString(make, modelYear) + "&ts=" + new Date().getTime();
       
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", url, true);
    xmlHttp.send(null);
}

function createQueryString(make, modelYear) {
    var queryString = "make=" + make + "&modelYear=" + modelYear;
    return queryString;
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            updateModelsList();
        }
    }
}

function updateModelsList() {
    clearModelsList();
   
    var models = document.getElementById("models");
    var results = xmlHttp.responseXML.getElementsByTagName("model");
    var option = null;
    for(var i = 0; i < results.length; i++) {
        option = document.createElement("option");
        option.appendChild(document.createTextNode(results[i].firstChild.nodeValue));
        models.appendChild(option);
    }
}

function clearModelsList() {
    var models = document.getElementById("models");
    while(models.childNodes.length > 0) {
        models.removeChild(models.childNodes[0]);
    }
}
</script>
</head>

<body>
  <h1>Select Model Year and Make</h1>
 
  <form action="#">
    <span style="font-weight:bold;">Model Year:</span>
    <select id="modelYear" onchange="refreshModelList();">
        <option value="">Select One</option>
        <option value="2006">2006</option>
        <option value="1995">1995</option>
        <option value="1985">1985</option>
        <option value="1970">1970</option>
    </select>
   
    <br/><br/>
    <span style="font-weight:bold;">Make:</span>
    <select id="make" onchange="refreshModelList();">
        <option value="">Select One</option>
        <option value="Chevrolet">Chevrolet</option>
        <option value="Dodge">Dodge</option>
        <option value="Pontiac">Pontiac</option>
    </select>
   
    <br/><br/>
    <span style="font-weight:bold;">Models:</span>
    <br/>
    <select id="models" size="6" style="width:300px;">
   
    </select>
 
  </form>

</body>
</html>

<dynamicLists.html 의 전체 소스>

 

 

 

package ajaxbook.chap4;

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.*;
import javax.servlet.http.*;

public class RefreshModelListServlet extends HttpServlet {

    private static List availableModels = new ArrayList();
   
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
       
        int modelYear = Integer.parseInt(request.getParameter("modelYear"));
        String make = request.getParameter("make");
       
        StringBuffer results = new StringBuffer("<models>");
        MakeModelYear availableModel = null;
        for(Iterator it = availableModels.iterator(); it.hasNext();) {
            availableModel = (MakeModelYear)it.next();
            if(availableModel.modelYear == modelYear) {
                if(availableModel.make.equals(make)) {
                    results.append("<model>");
                    results.append(availableModel.model);
                    results.append("</model>");
                }
            }
        }
        results.append("</models>");
       
        response.setContentType("text/xml");
        response.getWriter().write(results.toString());
    }
   
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }
   
    public void init() throws ServletException {
        availableModels.add(new MakeModelYear(2006, "Dodge", "Charger"));
        availableModels.add(new MakeModelYear(2006, "Dodge", "Magnum"));
        availableModels.add(new MakeModelYear(2006, "Dodge", "Ram"));
        availableModels.add(new MakeModelYear(2006, "Dodge", "Viper"));
        availableModels.add(new MakeModelYear(1995, "Dodge", "Avenger"));
        availableModels.add(new MakeModelYear(1995, "Dodge", "Intrepid"));
        availableModels.add(new MakeModelYear(1995, "Dodge", "Neon"));
        availableModels.add(new MakeModelYear(1995, "Dodge", "Spirit"));
        availableModels.add(new MakeModelYear(1985, "Dodge", "Aries"));
        availableModels.add(new MakeModelYear(1985, "Dodge", "Daytona"));
        availableModels.add(new MakeModelYear(1985, "Dodge", "Diplomat"));
        availableModels.add(new MakeModelYear(1985, "Dodge", "Omni"));
        availableModels.add(new MakeModelYear(1970, "Dodge", "Challenger"));
        availableModels.add(new MakeModelYear(1970, "Dodge", "Charger"));
        availableModels.add(new MakeModelYear(1970, "Dodge", "Coronet"));
        availableModels.add(new MakeModelYear(1970, "Dodge", "Dart"));

        availableModels.add(new MakeModelYear(2006, "Chevrolet", "Colorado"));
        availableModels.add(new MakeModelYear(2006, "Chevrolet", "Corvette"));
        availableModels.add(new MakeModelYear(2006, "Chevrolet", "Equinox"));
        availableModels.add(new MakeModelYear(2006, "Chevrolet", "Monte Carlo"));
        availableModels.add(new MakeModelYear(1995, "Chevrolet", "Beretta"));
        availableModels.add(new MakeModelYear(1995, "Chevrolet", "Camaro"));
        availableModels.add(new MakeModelYear(1995, "Chevrolet", "Cavalier"));
        availableModels.add(new MakeModelYear(1995, "Chevrolet", "Lumina"));
        availableModels.add(new MakeModelYear(1985, "Chevrolet", "Cavalier"));
        availableModels.add(new MakeModelYear(1985, "Chevrolet", "Chevette"));
        availableModels.add(new MakeModelYear(1985, "Chevrolet", "Celebrity"));
        availableModels.add(new MakeModelYear(1985, "Chevrolet", "Citation II"));
        availableModels.add(new MakeModelYear(1970, "Chevrolet", "Bel Air"));
        availableModels.add(new MakeModelYear(1970, "Chevrolet", "Caprice"));
        availableModels.add(new MakeModelYear(1970, "Chevrolet", "Chevelle"));
        availableModels.add(new MakeModelYear(1970, "Chevrolet", "Monte Carlo"));

        availableModels.add(new MakeModelYear(2006, "Pontiac", "G6"));
        availableModels.add(new MakeModelYear(2006, "Pontiac", "Grand Prix"));
        availableModels.add(new MakeModelYear(2006, "Pontiac", "Solstice"));
        availableModels.add(new MakeModelYear(2006, "Pontiac", "Vibe"));
        availableModels.add(new MakeModelYear(1995, "Pontiac", "Bonneville"));
        availableModels.add(new MakeModelYear(1995, "Pontiac", "Grand Am"));
        availableModels.add(new MakeModelYear(1995, "Pontiac", "Grand Prix"));
        availableModels.add(new MakeModelYear(1995, "Pontiac", "Firebird"));
        availableModels.add(new MakeModelYear(1985, "Pontiac", "6000"));
        availableModels.add(new MakeModelYear(1985, "Pontiac", "Fiero"));
        availableModels.add(new MakeModelYear(1985, "Pontiac", "Grand Prix"));
        availableModels.add(new MakeModelYear(1985, "Pontiac", "Parisienne"));
        availableModels.add(new MakeModelYear(1970, "Pontiac", "Catalina"));
        availableModels.add(new MakeModelYear(1970, "Pontiac", "GTO"));
        availableModels.add(new MakeModelYear(1970, "Pontiac", "LeMans"));
        availableModels.add(new MakeModelYear(1970, "Pontiac", "Tempest"));
    }

    private static class MakeModelYear {
        private int modelYear;
        private String make;
        private String model;
       
        public MakeModelYear(int modelYear, String make, String model) {
            this.modelYear = modelYear;
            this.make = make;
            this.model = model;
        }
    }
}

<RefreshModelListServlet.java 의 전체 소스>

 

 

사실 위 샘플예제 또한 솔직히 추가적인 설명은 하지 않겠다. 만일 자바를 모르는 사람이라면 서버쪽 프로그램의 코드 하나하나를 이해하려고 할 필요는 없을 듯 싶다. 핵심은 ajax 이다. 이번 예제의 서버 프로그램이 길어지게 된 이유는, 사실 비지니스 로직에서는 DB 데이터를 분석하여 매칭되는 결과를 브라우저로 보내줘야 하지만 그렇게 되면 서버 프로그램이 더 복잡해지므로 샘플의 단순화를 위해 좀 무식하게 작성하였다.

 

 

 

 

<연도와 제조회사 선택여부에 따라서 맨 아래 모델 종류가 동적으로 바뀌는 화면>

 

 

출처 : jinoxst님의 블로그

'WEB2.0 > ajax' 카테고리의 다른 글

progress bar 기능 구현하기  (0) 2006.04.11
Auto Refresh 기능 구현하기  (0) 2006.04.11
응답 헤더정보 다루기  (0) 2006.04.11
폼 입력값 검증하기  (0) 2006.04.11
서버와 통신하기(요청/응답 처리)  (0) 2006.04.11
 

응답 헤더정보 다루기

WEB2.0/ajax | 2006. 4. 11. 17:23
Posted by 시반

웹 프로그래밍에서는 브라우저의 요청에 응답을 해야 한다. 정상적인 응답이든 비정상이든 브라우저는 그 결과를 표시한다. 이번 주제는 AJAX 를 이용하여 서버의 상태만을 확인해 볼 수 있는 방법을 제시하고자 한다. 서버의 상태를 확인하기 위해서는 특정 리소스 url 로 요청을 보내면 된다. 하지만 서버는 응답정보를 브라우저에 보내게 되는데, 이럴경우에 응답정보의 모든 부분이 필요하지는 않는다. 단지 헤더정보만 얻을 수 있으면 서버의 상태를 파악할 수 있다.

 

지금까지 XMLHttpRequest 객체의 open(method, url, asynch) 메소드의 method 에는 GET 및 POST 만을 사용했었다. 하지만 HEAD 을 사용하면, 즉 브라우저가 HEAD 요청을 보내면, HEAD 요청을 받은 서버는 응답을 보낼때 body 의 내용은 빼버리고 헤더 정보만 채워서 보내게 된다. 따라서 오고가는 정보의 양이 극히 줄기때문에 브라우저에서 시간간격으로 서버의 상태를 점검하는데 아주 유용하게 사용할 수 있다.

 

이번 주제의 예제를 살펴보자.

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Reading Response Headers</title>

<script type="text/javascript">
var xmlHttp;
var requestType = "";

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function doHeadRequest(request, url) {
    requestType = request;
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("HEAD", url, true);
    xmlHttp.send(null);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(requestType == "allResponseHeaders") {
            getAllResponseHeaders();
        }
        else if(requestType == "lastModified") {
            getLastModified();
        }
        else if(requestType == "isResourceAvailable") {
            getIsResourceAvailable();
        }
    }
}

function getAllResponseHeaders() {
    alert(xmlHttp.getAllResponseHeaders());
}

function getLastModified() {
    alert("Last Modified: " + xmlHttp.getResponseHeader("Last-Modified"));
}

function getIsResourceAvailable() {
    if(xmlHttp.status == 200) {
        alert("Successful response");
    }
    else if(xmlHttp.status == 404) {
        alert("Resource is unavailable");
    }
    else {
        alert("Unexpected response status: " + xmlHttp.status);
    }
}

</script>
</head>

<body>
  <h1>Reading Response Headers</h1>
 
  <a href="javascript:doHeadRequest('allResponseHeaders', 'readingResponseHeaders.xml');">Read All Response Headers</a>
 
  <br/>
  <a href="javascript:doHeadRequest('lastModified', 'readingResponseHeaders.xml');">Get Last Modified Date</a>
 
  <br/>
  <a href="javascript:doHeadRequest('isResourceAvailable', 'readingResponseHeaders.xml');">Read Available Resource</a>
 
  <br/>
  <a href="javascript:doHeadRequest('isResourceAvailable', 'not-available.xml');">Read Unavailable Resource</a>

</body>
</html>

<readingResponseHeaders.html 의 전체 소스>

 

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

<readingResponseHeaders>

 

</readingResponseHeaders>

 

<readingResponseHeaders.xml 의 전체 소스>

 

 

지금까지 GET 혹은 POST 방식만을 사용해 예제를 작성했던 생각을 떠올려 볼때 이번 예제의 유일한 차이점은 xmlHttp.open("HEAD", url, true); 이다. HEAD 는 서버에 요청을 보낼때 HEAD 요청을 보내는 것으로 요청을 받은 서버는 body 의 정보를 아무것도 채우지 않고 단지 헤더 정보만을 넣어서 브라우저로 보내게 되다.

 

의심이 간다면 readingResponseHeaders.xml 파일에 내용을 채운후 아래와 같이 코드를 수정해서 실행해보자.

 

function getAllResponseHeaders() {
    alert(xmlHttp.getAllResponseHeaders());
    alert(xmlHttp.responseText);
    alert(xmlHttp.responseXML.getElementsByTagName("readingResponseHeaders")[0].firstChild.nodeValue);
}

 

첫번째 alert() 만 헤더정보를 표시하고 두번째, 세번째는 아무런 아무런 값도 없을 것이다.(open 메소드의 HEAD 를 GET 으로 수정한 후 다시 실행해 보면 두번째, 세번째 모두 값을 출력할 것이다.)

 

따라서 서버의 상태만을 점검하는 것이라면 HEAD 요청을 사용하는 것이 브라우저와 서버간 오고가는 데이터의 양이 매우 적어지기 때문에 서버에 부하를 최소화 하는 방식이라고 하겠다.

 

 


<서버의 응답정보 중 헤더정보만을 표시한 그림>

 

 


 

<서버의 헤더정보중 Last-Modified 항목에 대한 값>

 

 


 
<XHR 객체의 state 상태가 200 일 경우>
 
 
참고로 XHR 객체의 state 의 값이 200 이면 정상적인 서버 응답종료를 의미한다. 404는 해당 서버 리소스 url 이 존재하지 않음을 의미한다. 500이면 서버가 해당 요청을 수행하다가 내부적으로 에러가 발생했음을 의미한다.
 
 

 


 

<XHR 객체의 state 상태가 404 일 경우>

3장에서는 AJAX 의 여러가지 기본적인 특성에 대해서 공부하였다. 또한 아주 간단한 예제를 통해서 그 특징들이 어떻게 적용되는지도 살펴보았다. 이번 4장에서는 좀더 발전하여 실제적으로 쓰임새 측면에서 AJAX 를 다루어 보기로 한다.

 

실용적인 측면에서 AJAX 를 어떻게 활용할 수 있을지 살펴보자. 첫번째로 사용자가 브라우저의 입력폼에 값을 입력했을때 그 값을 검증하는 기능을 구현해볼 것이다. 이번 예제에서는 간단하게 날짜를 입력했을때, 그 값을 맞게 입력했는지 검증하는 로직을 AJAX 로 적용해 볼것이다. 여러 프로젝트를 수행한 결과 이런 폼 입력값 검증작업의 경우 간단한 것은 자바스크립트를 사용하면 아주 쉽게 끝나버린다. 오히려 서버의 로직을 거치지 않으므로 서버에 부담도 없다. 하지만 서버의 DB 나 혹은 XML 에 담겨진 데이타와 비교 혹은 검증을해야만 하는 경우엔 어쩔 수 없이 서버의 비지니스 로직을 거쳐야 한다. 하지만 이럴경우 코아 비지니스 로직을 수행하기 전에 입력폼의 값을 검증하는 로직을 추가로 코딩해야 해야만 한다. 또한 입력값이 잘못 되었다면 어떤입력값이 어떻게 잘 못되었다고 친절하게 알려주는 새로운 페이지로 이동해야 한다거나, 쉽게는 팝업처리를 할 수도 있다. 이렇게 되면 사용자는 또다시 처음부터 다시 입력을 해야하는 고생을 해야 한다.

 

이럴경우 AJAX 를 이용한 폼 입력값 검증작업을 해주면 비지니스 로직을 개발할때는 코어 로직만 작성하면 되고, 고객 입장에서는 입력한 값이 어디가 어떻게 잘못된건지 바로 알수 있기때문에 아주 유용한 방법이 될 것이다.

 

그럼 소스를 살펴보자. 코드가 길어지는 것을 방지하기 위해 되도록 소스는 단순하게 짜여졌다.

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title>Using Ajax for validation</title>
 
    <script type="text/javascript">
        var xmlHttp;

        function createXMLHttpRequest() {
            if (window.ActiveXObject) {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else if (window.XMLHttpRequest) {
                xmlHttp = new XMLHttpRequest();               
            }
        }

        function validate() {
            createXMLHttpRequest();
            var date = document.getElementById("birthDate");
            var url = "ValidationServlet?birthDate=" + escape(date.value);
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = callback;
            xmlHttp.send(null);
        }

        function callback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    //var mes = xmlHttp.responseXML.getElementsByTagName("message")[0].firstChild.data;
     var mes = xmlHttp.responseXML.getElementsByTagName("message")[0].firstChild.data;
                    var val = xmlHttp.responseXML.getElementsByTagName("passed")[0].firstChild.data;
                    setMessage(mes, val);
                }
            }
        }
       
        function setMessage(message, isValid) {           
            var messageArea = document.getElementById("dateMessage");
            var fontColor = "red";
           
            if (isValid == "true") {
                fontColor = "green";               
            }
            messageArea.innerHTML = "<font color=" + fontColor + ">" + message + " </font>";
        }

    </script>
  </head>
  <body>
    <h1>Ajax Validation Example</h1>
    Birth date: <input type="text" size="10" id="birthDate" onchange="validate();"/>
    <div id="dateMessage"></div>
  </body>
</html>


<validateion.html 전체 소스 코드>

 

 

package ajaxbook.chap4;

import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import javax.servlet.*;
import javax.servlet.http.*;

public class ValidationServlet extends HttpServlet {   
   
    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        PrintWriter out = response.getWriter();
       
        boolean passed = validateDate(request.getParameter("birthDate"));
        response.setContentType("text/xml");
        response.setHeader("Cache-Control", "no-cache");
        String message = "You have entered an invalid date.";
       
        if (passed) {
            message = "You have entered a valid date.";
        }
        out.println("<response>");
        out.println("<passed>" + Boolean.toString(passed) + "</passed>");
        out.println("<message>" + message + "</message>");
        out.println("</response>");
        out.close();
     }
   
    /**
     * Checks to see whether the argument is a valid date.
     * A null date is considered invalid. This method
     * used the default data formatter and lenient
     * parsing.
     *
     * @param date a String representing the date to check
     * @return message a String represnting the outcome of the check
     */
    private boolean validateDate(String date) {
       
        boolean isValid = true;
        if(date != null) {
            SimpleDateFormat formatter= new SimpleDateFormat("MM/dd/yyyy");
            try {
                formatter.parse(date);
            } catch (ParseException pe) {
                System.out.println(pe.toString());
                isValid = false;
            }
        } else {
            isValid = false;
        }
        return isValid;
    }
}

<ValidationServlet.java 의 전체 소스 코드>

 

 

이번 예제는 사실 설명이 불필요할 정도로 매우 간단하다. 브라우저에서는 날짜를 입력받는 필드만 있다. 날짜를 입력하고 난후 포커스가 빠져나가면 입력했던 날짜는 서버 프로그램으로 전송되고 입력된 값이 날짜 형식에 적절한지 판단해서 다시 브라우저로 보내주는 형식이다.

 

 


 

<날짜를 잘못 입력했을 경우>

 

 


 

<날짜 형식으로 입력했을 경우>

 

 

출처 : jinoxst님의 블로그

'WEB2.0 > ajax' 카테고리의 다른 글

Auto Refresh 기능 구현하기  (0) 2006.04.11
동적으로 리스트박스 구성하기  (0) 2006.04.11
폼 입력값 검증하기  (0) 2006.04.11
서버와 통신하기(요청/응답 처리)  (0) 2006.04.11
XMLHttpRequest 오브젝트 사용하기  (0) 2006.04.11
 

폼 입력값 검증하기

WEB2.0/ajax | 2006. 4. 11. 17:21
Posted by 시반

3장에서는 AJAX 의 여러가지 기본적인 특성에 대해서 공부하였다. 또한 아주 간단한 예제를 통해서 그 특징들이 어떻게 적용되는지도 살펴보았다. 이번 4장에서는 좀더 발전하여 실제적으로 쓰임새 측면에서 AJAX 를 다루어 보기로 한다.

 

실용적인 측면에서 AJAX 를 어떻게 활용할 수 있을지 살펴보자. 첫번째로 사용자가 브라우저의 입력폼에 값을 입력했을때 그 값을 검증하는 기능을 구현해볼 것이다. 이번 예제에서는 간단하게 날짜를 입력했을때, 그 값을 맞게 입력했는지 검증하는 로직을 AJAX 로 적용해 볼것이다. 여러 프로젝트를 수행한 결과 이런 폼 입력값 검증작업의 경우 간단한 것은 자바스크립트를 사용하면 아주 쉽게 끝나버린다. 오히려 서버의 로직을 거치지 않으므로 서버에 부담도 없다. 하지만 서버의 DB 나 혹은 XML 에 담겨진 데이타와 비교 혹은 검증을해야만 하는 경우엔 어쩔 수 없이 서버의 비지니스 로직을 거쳐야 한다. 하지만 이럴경우 코아 비지니스 로직을 수행하기 전에 입력폼의 값을 검증하는 로직을 추가로 코딩해야 해야만 한다. 또한 입력값이 잘못 되었다면 어떤입력값이 어떻게 잘 못되었다고 친절하게 알려주는 새로운 페이지로 이동해야 한다거나, 쉽게는 팝업처리를 할 수도 있다. 이렇게 되면 사용자는 또다시 처음부터 다시 입력을 해야하는 고생을 해야 한다.

 

이럴경우 AJAX 를 이용한 폼 입력값 검증작업을 해주면 비지니스 로직을 개발할때는 코어 로직만 작성하면 되고, 고객 입장에서는 입력한 값이 어디가 어떻게 잘못된건지 바로 알수 있기때문에 아주 유용한 방법이 될 것이다.

 

그럼 소스를 살펴보자. 코드가 길어지는 것을 방지하기 위해 되도록 소스는 단순하게 짜여졌다.

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title>Using Ajax for validation</title>
 
    <script type="text/javascript">
        var xmlHttp;

        function createXMLHttpRequest() {
            if (window.ActiveXObject) {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else if (window.XMLHttpRequest) {
                xmlHttp = new XMLHttpRequest();               
            }
        }

        function validate() {
            createXMLHttpRequest();
            var date = document.getElementById("birthDate");
            var url = "ValidationServlet?birthDate=" + escape(date.value);
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = callback;
            xmlHttp.send(null);
        }

        function callback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    //var mes = xmlHttp.responseXML.getElementsByTagName("message")[0].firstChild.data;
     var mes = xmlHttp.responseXML.getElementsByTagName("message")[0].firstChild.data;
                    var val = xmlHttp.responseXML.getElementsByTagName("passed")[0].firstChild.data;
                    setMessage(mes, val);
                }
            }
        }
       
        function setMessage(message, isValid) {           
            var messageArea = document.getElementById("dateMessage");
            var fontColor = "red";
           
            if (isValid == "true") {
                fontColor = "green";               
            }
            messageArea.innerHTML = "<font color=" + fontColor + ">" + message + " </font>";
        }

    </script>
  </head>
  <body>
    <h1>Ajax Validation Example</h1>
    Birth date: <input type="text" size="10" id="birthDate" onchange="validate();"/>
    <div id="dateMessage"></div>
  </body>
</html>


<validateion.html 전체 소스 코드>

 

 

package ajaxbook.chap4;

import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import javax.servlet.*;
import javax.servlet.http.*;

public class ValidationServlet extends HttpServlet {   
   
    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        PrintWriter out = response.getWriter();
       
        boolean passed = validateDate(request.getParameter("birthDate"));
        response.setContentType("text/xml");
        response.setHeader("Cache-Control", "no-cache");
        String message = "You have entered an invalid date.";
       
        if (passed) {
            message = "You have entered a valid date.";
        }
        out.println("<response>");
        out.println("<passed>" + Boolean.toString(passed) + "</passed>");
        out.println("<message>" + message + "</message>");
        out.println("</response>");
        out.close();
     }
   
    /**
     * Checks to see whether the argument is a valid date.
     * A null date is considered invalid. This method
     * used the default data formatter and lenient
     * parsing.
     *
     * @param date a String representing the date to check
     * @return message a String represnting the outcome of the check
     */
    private boolean validateDate(String date) {
       
        boolean isValid = true;
        if(date != null) {
            SimpleDateFormat formatter= new SimpleDateFormat("MM/dd/yyyy");
            try {
                formatter.parse(date);
            } catch (ParseException pe) {
                System.out.println(pe.toString());
                isValid = false;
            }
        } else {
            isValid = false;
        }
        return isValid;
    }
}

<ValidationServlet.java 의 전체 소스 코드>

 

 

이번 예제는 사실 설명이 불필요할 정도로 매우 간단하다. 브라우저에서는 날짜를 입력받는 필드만 있다. 날짜를 입력하고 난후 포커스가 빠져나가면 입력했던 날짜는 서버 프로그램으로 전송되고 입력된 값이 날짜 형식에 적절한지 판단해서 다시 브라우저로 보내주는 형식이다.

 

 


<날짜를 잘못 입력했을 경우>

 

 


<날짜 형식으로 입력했을 경우>

 

 

출처 : jinoxst님의 블로그

 
블로그 이미지

시반

시반(詩伴)이란 함께 시를 짓는 벗이란 뜻을 가지고 있습니다. 함께 나눌수 있는 그런 공간이길 바라며...

카테고리

분류 전체보기 (233)
개발 이야기 (73)
WEB2.0 (57)
DB2 (24)
MySQL (6)
오라클 (26)
기타 (44)
취미 (0)
잡담 (2)