본문 바로가기

Java/Source_book

2010.07.25 AJAX 최종 실습 예제

[2010.07.25 AJAX 최종 실습 예제]

 

 

1. Index.html에서 사용자는 '상품목록조회' Link를 선택하여 서버에 정보를 요구한다.

2. 넘겨받은 Front Controller Servlet은 이를 자신이 직접 처리하지 않고, Service 역할을 하는 클래스에 위임한다.(MVC2 Pattern 적용)

3. Controller로부터 위임받은 일이 DB와 관련된 일이므로 Service는 다시 DAO(DataAccessObject) 클래스에 위임한다.

4. vice로부터 위임받은 일을 DAO는 iBATIS 프레임워크를 사용하여 DB에 해당 정보를 조회 후, 자신을 호출한 Service 클래스에 결과값을 return한다.

5. Service는 다시 자신을 호출한 Controller에게 결과값을 반환한다.

6. Controller는 넘겨받은 값을 다시 사용자에게 넘겨주기 위해 RequestDispatcher를 사용하여 결과값을 "category_list-json.jsp"에 Forward 한다.

 

이를 구현하기 위한 파일 구조는 아래와 같다.

ETC.

Servlet은 web.xml에서 /categoryController로 Mapping되어 있다.

<servlet>

<servlet-name>category</servlet-name>

<servlet-class>product.controller.CategoryController</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>category</servlet-name>

<url-pattern>/categoryController</url-pattern>

</servlet-mapping>

 

조회할 상품Table이 DB에 생성되어 있어야 한다.

 

CREATE TABLE FIRST_CATEGORY(

FIRST_CATEGORY_ID VARCHAR2(20) PRIMARY KEY,

FIRST_CATEGORY_NAME VARCHAR2(100) NOT NULL

);

 

CREATE TABLE SECOND_CATEGORY(

SECOND_CATEGORY_ID VARCHAR2(20) PRIMARY KEY,

SECOND_CATEGORY_NAME VARCHAR2(100) NOT NULL,

FIRST_CATEGORY_ID VARCHAR2(20),

CONSTRAINT FIRST_FK_SECOND FOREIGN KEY (FIRST_CATEGORY_ID) REFERENCES FIRST_CATEGORY(FIRST_CATEGORY_ID)

);

 

CREATE TABLE THIRD_CATEGORY(

THIRD_CATEGORY_ID VARCHAR2(20) PRIMARY KEY,

THIRD_CATEGORY_NAME VARCHAR2(100) NOT NULL,

SECOND_CATEGORY_ID VARCHAR2(20)

);

ALTER TABLE THIRD_CATEGORY ADD CONSTRAINT SECOND_FK_THIRD

FOREIGN KEY (SECOND_CATEGORY_ID) REFERENCES SECOND_CATEGORY(SECOND_CATEGORY_ID);

 

CREATE TABLE PRODUCT(

PRODUCT_ID VARCHAR2(50) PRIMARY KEY,

PRODUCT_NAME VARCHAR2(100) NOT NULL,

PRODUCT_PRICE NUMBER(8) NOT NULL,

PRODUCT_MAKER VARCHAR2(50) NOT NULL,

PRODUCT_INFO VARCHAR2(1000) NOT NULL,

THIRD_CATEGORY_ID VARCHAR2(20) NOT NULL,

CONSTRAINT PRODUCT_FK_THIRD_CATEGORY FOREIGN KEY (THIRD_CATEGORY_ID) REFERENCES THIRD_CATEGORY(THIRD_CATEGORY_ID)

);

 


 

 1. Index.html에서 살펴보기

<a href="categoryController?command=first_category_json">상품 목록 조회 JSON</a>

위 Link 태그에서 보듯이 index.html은 front controller 역할을 하는 CategoryController에게 parameter Name 'command'에 값을 'first_category_json'이라는 값을 가지고 넘어간다.

2.
CategoryController에서는 Client로부터 요청받은 작업이 무엇인지 알기 위해서 command값을 분석하고 이에 맞는 Logic을 Service 클래스가 수행하도록 지시한다. (이 때 Singleton 처리 위해 init()에서 해당 객체를 미리 호출하여 사용한다.)

public void init(){

categoryService = CategoryService.getInstance();

productService = ProductService.getInstance();

}

 

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

request.setCharacterEncoding("euc-kr");

String command = request.getParameter("command");

if(command.equals("first_category_json")){

getFirstCategoryJSON(request,response);

}else if(command.equals("second_category_json")){

getSecondCategoryJSON(request, response);

}else if(command.equals("third_category_json")){

getThirdCategoryJSON(request, response);

}else if(command.equals("get_product_list_by_third_category_id_JSON")){

getProductListByCategoryIdJSON(request, response);

}

}

 

public void getFirstCategoryJSON(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

List firstList = null;

 

try {

firstList = categoryService.getAllFirstCategory();

request.setAttribute("firstList", firstList);

forward(request, response, "category_list_json.jsp", false);

 

} catch (SQLException e) {

//error 처리

e.printStackTrace();

  }

}

 

 
3. Service에서는 객체 생성을 위한 singleton 처리 및 DAO에 작업처리를 요구한다. 또한 생성자에서 DAO 객체를 받아온다.

private static CategoryService service;

private CategoryDAO dao;

private CategoryService(){

dao = CategoryDAO.getInstance();

}

public static CategoryService getInstance(){

if(service==null)service = new CategoryService();

return service;

}

 

public List getAllFirstCategory() throws SQLException{

List list = dao.selectAllFirstCategory();

return list;

}

 4. DAO에서는 iBATIS를 연동하여 DB에서 해당 Query의 결과값을 받아와 다시 service와 Controller에게 순서대로 넘겨준다.

public List selectAllFirstCategory()throws SQLException{

List list = sqlMap.queryForList("category.selectAllFirstCategory");

return list;

}

 5. Controller에서는 이 값을 requestScope에 "firstList"라는 이름으로 저장한다.

firstList = categoryService.getAllFirstCategory();

request.setAttribute("firstList", firstList);

 
6. 그리고 forward(request, response, "category_list_json.jsp", false); 명령으로 해당 페이지로 값을 넘긴다.

이로서 Server Layer에서의 작업은 끝나고 넘겨받은 값을 Client단의 JSP 페이지에서 출력하기 위해 AJAJ(Asyncronous Javascript And JSON)을 통해 처리하도록 한다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

 

대분류 : <select name="first_category" id="first_category" onChange="getSecond()">

<OPTION VALUE='DEFAULT'>대분류</OPTION>

<!--JSTL 이용해 requestScope 속성으로 등록된 대분류 데이터를 가져와 option 태그부분을 동적으로 생성한다. -->

<!-- Map객체 형태로 넘어와 있음. -->

  <c:forEach var="firstList" items="${requestScope.firstList}">

   <option value="${firstList.FIRST_CATEGORY_ID}">${firstList.FIRST_CATEGORY_NAME }    </option>

</c:forEach>

</select>

 

var xhr;

//XMLHttpRequest 객체 생성하여 xhr 할당

function createXMLHttpRequest(){//추후 사용하게될 함수들에서 공통적으로 필요한 객체이므로 함수로 따로 빼서 호출!

if(window.ActiveXObject){

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

}else{

  xhr=new XMLHttpRequest();//IE8 버전에서는 객체도 사용가능

}

 

위 과정을 통해 DB에서 1차 대분류값은 가져오게 된다.

 

이제 중분류를 가져오도록 해야 한다. 중분류를 가져오는 방법은 대분류에서 그림과 같이 특정 값이 선택되었을 때, 그 선택된 값을 가지고 DB에 다시 Query를 날리는 방식이다.

 

  1. 우선 대분류에서 특정 값이 선택되었을 때 서버단으로 그 값을 보내고 그 값을 기반으로 DB에서 서버단에서 넘어온 값을 사용자에게 보여줄 UI를 설정한다.

먼저 중분류가 표시될 UI를 SELECT 태그로 생성한다.

중분류 : <SELECT id="second_category" onChange="getThird()"><OPTION VALUE='DEFAULT'>중분류</OPTION></SELECT>

 

그리고 첫번째 대분류값이 변경되었을 때, 실행될 이벤트의 함수를 만든다.(이미 위에서 대분류 UI를 만들 때 onChange 이벤트핸들러에서 getSecond()를 선언하였다.

 

function getSecond(){

//사용자가 선택한 첫번째 카테고리(="대분류") 변수에 저장한다.

var fir=document.getElementById("first_category");

var firstCategory=fir.options[fir.selectedIndex].value;

 

//createXMLHttpRequest 객체 생성

createXMLHttpRequest();

//CallBack 함수 지정.

xhr.onreadystatechange=getSecondCategory;

//Open & send

xhr.open("GET","categoryController?command=second_category_json&firstCategory="+firstCategory, false);

 xhr.send(null);

 }

 

 function getSecondCategory(){

 if(xhr.readyState==4){

     if(xhr.status==200){

          var secondCagegory = xhr.responseText;

//(문자열로 넘어온)받아온 다시 JSON 값으로 변경해야 한다.

          var jsonData = eval("("+secondCagegory+")");

 //중분류 select 객체

          var sel=document.getElementById("second_category");

          for(i=0; i<jsonData.secondCategory.length; i++){

               sel[i+1]= new Option(jsonData.secondCategory[i].SECOND_CATEGORY_NAME,jsonData.secondCategory[i].SECOND_CATEGORY_ID);

          }

     }

}

}


 

 2. Open()에서 "categoryController?command=second_category_json&firstCategory="+firstCategory" 으로 Controller에게 넘어온 것을 이제 Controller가 다시 분석하여 해당 Logic들을 처리한다.

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

request.setCharacterEncoding("euc-kr");

String command = request.getParameter("command");

 

if(command.equals("first_category_json")){

getFirstCategoryJSON(request,response);

}else if(command.equals("second_category_json")){

getSecondCategoryJSON(request, response);

 

 

 

public void getSecondCategoryJSON(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

response.setContentType("text/html;charset=euc-kr");

response.setHeader("Cache-Control", "no-cache");

PrintWriter out = response.getWriter();

String firstCategory = request.getParameter("firstCategory");

 

try {

String retValue = categoryService.getSecondCategoryJSON(firstCategory);

out.println(retValue);

out.flush();

out.close();

} catch (SQLException e) {//error 처리로직

e.printStackTrace();

}

}

 
3. Service 클래스

public String getSecondCategoryJSON(String firstCategory) throws SQLException{

JSONObject root = new JSONObject();

List list = dao.selectSecondCategoryByFirstCategoryId(firstCategory);

//list : [{SECOND_CATEGORY_NAME=TV, SECOND_CATEGORY_ID=S-4}, {SECOND_CATEGORY_NAME=냉장고, SECOND_CATEGORY_ID=S-5}, {SECOND_CATEGORY_NAME=세탁기, SECOND_CATEGORY_ID=S-6}]

 //JSONObject 완성한다.

 root.put("secondCategory", list);

 

return root.toString();//JSON 문자열 형태로 만들되, 이를 문자열(String) 전송한다. 이를 javascript eval() 통해 JSON 으로 다시 바꾸게 된다.

}


 4. DAO 클래스

public List selectSecondCategoryByFirstCategoryId(String firstCategory) throws SQLException{

List list = sqlMap.queryForList("category.selectSecondCategoryByFirstCategoryId", firstCategory);

return list;

}

 

이와 동일한 방법으로 소분류 역시 얻어오면 된다.

이제 마지막 소분류에서 해당 상품을 선택했을 때 아래와 같이 DB에서 상세정보를 가져와

, Table로 출력한다.

  1. 소분류에서 특정 값이 선택되었을 때(=onChange) 발생할 이벤트로서 getProductList() 이라는 이름으로 지정하였다.

<table border="1"> // Table 틀을 생성.

<thead id="thead"></thead>

<tbody id="tbody"></tbody>

</table>

 

//상품 리스트 조회 요청

//Controller client 선택한 소분류 아이디를 넘긴다.

function getProductList(){

createXMLHttpRequest();

var cmd = "get_product_list_by_third_category_id_JSON";

var tsel = document.getElementById("third_category");

var value = tsel.options[tsel.selectedIndex].value;

var url =

"categoryController?command="+cmd+"&thirdCategory="+value;

xhr.onreadystatechange=getProductListByThirdCategory;

xhr.open("GET",url, false);

xhr.send(null);

}

 

 

//상품 리스트 응답 데이터 처리

//controller 보낸 Data 가지고 상품 리스트 Table 만든다.

function getProductListByThirdCategory(){

if(xhr.readyState==4){

if(xhr.status==200){

var txt = xhr.responseText;

var jsonData = eval("("+txt+")");

//thead

var thead = document.getElementById("thead");

var tr = document.createElement("tr");

var td1 = document.createElement("td");//id

var td2 = document.createElement("td");//name

var td3 = document.createElement("td");//maker

var td4 = document.createElement("td");//price

td1.innerHTML = "제품 ID";

td2.innerHTML = "제품명";

td3.innerHTML = "제조사";

td4.innerHTML = "제품가격";

//td->tr

tr.appendChild(td1);

tr.appendChild(td2);

tr.appendChild(td3);

tr.appendChild(td4);

//tr->thead 붙이기 전에 삭제

while(thead.firstChild){

thead.removeChild(thead.firstChild);

}

thead.appendChild(tr);

//tbody

var tbody = document.getElementById("tbody");

//tr->tbody 붙이기 전에 삭제

while(tbody.firstChild){

tbody.removeChild(tbody.firstChild);

}

for(i = 0; i<jsonData.ITEM_LIST.length;i++){

thead = document.getElementById("thead");

tr = document.createElement("tr");

td1 = document.createElement("td");//id

td2 = document.createElement("td");//name

td3 = document.createElement("td");//maker

td4 = document.createElement("td");//price

 

td1.innerHTML = jsonData.ITEM_LIST[i].id;

td2.innerHTML = jsonData.ITEM_LIST[i].name;

td3.innerHTML = jsonData.ITEM_LIST[i].maker;

td4.innerHTML = jsonData.ITEM_LIST[i].price;

tr.appendChild(td1);

tr.appendChild(td2);

tr.appendChild(td3);

tr.appendChild(td4);

tbody.appendChild(tr);

}

}

}

}


 2. Controller에서 해당 넘어온 command 값과 사용자가 선택한 소분류의 값을 받아 처리한다.

}else if(command.equals("get_product_list_by_third_category_id_JSON")){

getProductListByCategoryIdJSON(request, response);

}

 

public void getProductListByCategoryIdJSON(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

response.setContentType("text/html;charset=euc-kr");

response.setHeader("Cache-Control", "no-cache");

PrintWriter out = response.getWriter();

String thirdCategory = request.getParameter("thirdCategory");

 

try {

String retValue = productService.getProductInfoByCategoryIdJSON(thirdCategory);

out.println(retValue);

out.flush();

out.close();

} catch (SQLException e) {

//error 처리

e.printStackTrace();

}

}

 3. Service 클래스

/** 소분류를 통해 상품 리스트를 조회 처리하는 Business Service 메소드

* 결과를 JSON 객체형태로 작성한다.*/

 

public String getProductInfoByCategoryIdJSON(String categoryId) throws SQLException{

List list = dao.selectProductInfoByCategory(categoryId);

JSONObject jobj = new JSONObject();//상품 목록 전체를 담을 JSONObject

JSONArray jList = new JSONArray(); // 상품 정보를 담을 JSONArray

for(Object obj : list){

ProductVO pvo = (ProductVO)obj;

JSONObject o = new JSONObject();

 o.put("id", pvo.getProductId());

o.put("name", pvo.getProductName());

o.put("maker", pvo.getProductMaker());

o.put("price", pvo.getProductPrice());

jList.add(o);

}

jobj.put("ITEM_LIST", jList);

System.out.println(jobj.toString());

return jobj.toString();

 

}


 4. DAO 클래스

public List selectProductInfoByCategory(String categoryId) throws SQLException{

    List list = sqlMap.queryForList("product.selectProductInfoByCategory", categoryId);

    return list;

}