RadarURL

웹 프로그래밍
2007.05.08 20:22

AJAX 자동완성 기능 구현하기

조회 수 7004 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄



<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, 즉 모든 브라우저에서 위 샘플 실행을 보장 받기 위해서는 코딩을 위처럼 해 줘야 한다.



이것으로 4장의 강의는 모두 마친다.



이번장의 예제는 AJAX 강의 4-1장 - 폼 입력값 검증 하기 에서 다운 받아 테스트해 볼 수 있다.




-------------------------- 한글 패치 추가(2006.02.23) ---------------------





위 예제는 한글 테스트가 불가능하기 때문에 이 기회에 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("스타크래프트 다운로드");
  }



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








위 수정된 샘플은 다운받아 테스트해 볼 수 있다.

출처 : http://leedh.net/1036
?

공부 게시판

공부에 도움되는 글을 올려주세요.

List of Articles
번호 분류 제목 글쓴이 날짜 조회 수
공지 [공지] 공부 게시판 입니다. 처누 2003.08.18 928063
226 웹 프로그래밍 JSP에서 URL, IP 알아내기 JaeSoo 2007.06.24 6769
225 웹 프로그래밍 PEAR로 메일을 보내는 3가지 방법 JaeSoo 2007.06.08 4744
224 웹 프로그래밍 mail()함수또는 smtp로 메일 보내실때 인코딩 방법 JaeSoo 2007.06.08 5062
223 소프트웨어 나름대로 쓸만한 프로그램 JaeSoo 2007.06.08 5100
222 유닉스/리눅스 Tomcat을 버젼별로 실행하기 JaeSoo 2007.06.08 8013
221 네트워크 smtp를 이용하여 mail 보내기 (pear 사용) JaeSoo 2007.06.07 5288
220 웹 프로그래밍 제로보드에서 일반문서에 레벨별 권한을 주는 방법 한가지입니다. JaeSoo 2007.06.07 5016
219 웹 프로그래밍 제로보드에서 회원가입 및 정보수정후 페이지 리로드관련 (팝업아닐경우) JaeSoo 2007.06.07 5083
218 네트워크 메일 발송시 리턴메일 메시지 JaeSoo 2007.06.05 5904
217 웹 프로그래밍 제로보드 작성자 레벨 변수 와 view.php JaeSoo 2007.06.04 4854
216 웹 프로그래밍 제로보드 광고,로봇글 제한하기[이미지 코드사용] file JaeSoo 2007.06.01 5456
215 응용 프로그래밍 객체 직렬화(Serializable) : Java JaeSoo 2007.05.21 6864
214 웹 프로그래밍 XMLHttpRequest 메서드 및 속성 정리 JaeSoo 2007.05.10 4600
213 웹 프로그래밍 XMLHttpRequest 오브젝트 사용하기 JaeSoo 2007.05.10 4573
212 웹 프로그래밍 AJAX 관련 새로운 url 정리 JaeSoo 2007.05.10 4355
211 웹 프로그래밍 Ajax 소개 및 XMLHttpRequest 객체 사용하기 JaeSoo 2007.05.10 5619
210 웹 프로그래밍 검색어 자동완성 기능 전체 소스 및 데모 화면 file JaeSoo 2007.05.08 5943
209 웹 프로그래밍 초급용 js : form object 접근과 제어 JaeSoo 2007.05.08 5530
208 웹 프로그래밍 js 기본 문법 JaeSoo 2007.05.08 4451
» 웹 프로그래밍 AJAX 자동완성 기능 구현하기 JaeSoo 2007.05.08 7004
Board Pagination Prev 1 ... 108 109 110 111 112 113 114 115 116 117 ... 124 Next
/ 124


즐겨찾기 (가족)

JAESOO's HOMEPAGE


YOUNGAE's HOMEPAGE


장여은 홈페이지


장여희 홈페이지


장여원 홈페이지


즐겨찾기 (업무)

알리카페 홀릭

숭실대 컴퓨터 통신연구실 (서창진)

말레이시아 KL Sentral 한국인 GuestHouse


즐겨찾기 (취미)

어드민아이디

유에코 사랑회

아스가르드 좋은사람/나쁜사람

JServer.kr

제이서버 메타블로그

재수 티스토리


즐겨찾기 (강의, 커뮤니티)

재수 강의 홈페이지


한소리


VTMODE.COM


숭실대 인공지능학과


숭실대 통신연구실


베너