웹_프론트_백엔드/JAVA프레임윅기반_풀스택

2020.06.17

shine94 2020. 6. 17. 09:50

[AJAX와 JSON을 이용하여 다단계 Category 구현]

1. 서버단
1) common > D.java 

package common;

/*
 * DB 접속 정보, 쿼리문, 테이블명, 컬럼명 등은
 * 별도로 관리하든지
 * XML, 초기화 파라미터 등에서 관리하는 것이 좋다.
 */
public class D {
	public static final String DRIVER = "oracle.jdbc.driver.OracleDriver";  // JDBC 드라이버 클래스
	public static final String URL = "jdbc:oracle:thin:@localhost:1521:XE";  // DB 접속 URL
	public static final String USERID = "scott0316";  // DB 접속 계정 정보
	public static final String USERPW = "tiger0316";
	
	// 특정 depth 의 특정 parent 인 카테고리들 읽어오기
	public static final String SQL_CATEGORY_BY_DEPTH_N_PARENT 
			= "SELECT ca_uid, ca_name, ca_depth, ca_parent, ca_order " 
					+ "FROM test_category " 
					+ "WHERE ca_depth = ? AND ca_parent = ? "
					+ "ORDER BY ca_order ASC";

	// parent 값이 NULL 값일때
	public static final String SQL_CATEGORY_BY_DEPTH_N_PARENT_NULL 
			= "SELECT ca_uid, ca_name, ca_depth, ca_parent, ca_order " 
					+ "FROM test_category " 
					+ "WHERE ca_depth = ? AND ca_parent IS NULL "
					+ "ORDER BY ca_order ASC";
}


2) com.lec.beans

** AjaxCategoryList.java

package com.lec.beans;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;

public class AjaxCategoryList {
	
	private int count;   			// 데이터 개수
	@JsonProperty("data")
	private List<CategoryDTO> list;	// 리스트
	private String message;			// 처리 메세지
	private String status;			// 처리 결과
	
	
	// 기본생성자
	public AjaxCategoryList() {}

	
	// getter, setter
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}

	public List<CategoryDTO> getList() {
		return list;
	}
	public void setList(List<CategoryDTO> list) {
		this.list = list;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getStatus() {
		return status;
	}
	public void setStatus(String status) {
		this.status = status;
	}
	

} // end AjaxCategoryList()

 

** CategoryDTO.java 

package com.lec.beans;

public class CategoryDTO {
	private int uid;		// ca_uid
	private String name;	// ca_name
	private int depth;		// ca_depth
	private int parent;		// ca_parent
	private int order;		// ca_order
	
	// 기본생성자, 매개변수 생성자
	public CategoryDTO() {}

	public CategoryDTO(int uid, String name, int depth, int parent, int order) {
		super();
		this.uid = uid;
		this.name = name;
		this.depth = depth;
		this.parent = parent;
		this.order = order;
	}

	
	// getter, setter
	public int getUid() {
		return uid;
	}
	public void setUid(int uid) {
		this.uid = uid;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	public int getDepth() {
		return depth;
	}
	public void setDepth(int depth) {
		this.depth = depth;
	}

	public int getParent() {
		return parent;
	}
	public void setParent(int parent) {
		this.parent = parent;
	}

	public int getOrder() {
		return order;
	}
	public void setOrder(int order) {
		this.order = order;
	}
	

}


** CategoryDAO.java

package com.lec.beans;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.ArrayList;

import common.D;

public class CategoryDAO {
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;
	
	// DAO 객체가 생성될때 Connection 도 생성된다.
	public CategoryDAO() {
		try {
			Class.forName(D.DRIVER);
			conn = DriverManager.getConnection(D.URL, D.USERID, D.USERPW);
			
		} catch(Exception e) {
			e.printStackTrace();
		}		
	}
	
	// DB 자원 반납 메소드,
	public void close() throws SQLException {
		if(rs != null) rs.close();
		if(pstmt != null) pstmt.close();
		if(stmt != null) stmt.close();
		if(conn != null) conn.close();
	} // end close()
	
	
	// ------------------------------------------------------------------------
	
	
	// ResultSet --> DTO 배열로 리턴
	public CategoryDTO[] createArray(ResultSet rs) throws SQLException {
		CategoryDTO[] arr = null;
		
		ArrayList<CategoryDTO> list = new ArrayList<CategoryDTO>();
		
		while(rs.next()) {
			int uid = rs.getInt("ca_uid");
			String name = rs.getString("ca_name");
			int depth = rs.getInt("ca_depth");
			int parent = rs.getInt("ca_parent");
			int order = rs.getInt("ca_order");
			
			
			CategoryDTO dto = new CategoryDTO(uid, name, depth, parent, order);
			list.add(dto);
			
		} // end while
		
		int size = list.size();
		
		if(size == 0) return null;
		
		arr = new CategoryDTO[size];
		list.toArray(arr);  // List -> 배열
		
		return arr;
	} // end createArray()
	
	// -----------------------------------------------------------------------------
	
	// depth와 parent로 카테고리 읽기  (order 오름차순 순서로), parent 값이 있을때
	public CategoryDTO[] selectByDepthAndParent(int depth, String parent) throws SQLException {
		CategoryDTO[] arr = null;
		
		parent = parent.trim();
		
		try {
			pstmt = conn.prepareStatement(D.SQL_CATEGORY_BY_DEPTH_N_PARENT);
			pstmt.setInt(1, depth);
			pstmt.setString(2, parent);
			rs = pstmt.executeQuery();
			
			arr = createArray(rs);
		} finally {
			close();
		}		
		
		return arr;
		
	} // end selectByDepthAndParent()
	
	// 카테고리 읽기  (order 오름차순 순서로), parent "", 0, null
	public CategoryDTO[] selectByDepthAndParent(int depth) throws SQLException {
		CategoryDTO[] arr = null;
		
		try {
			// IS NULL
			pstmt = conn.prepareStatement(D.SQL_CATEGORY_BY_DEPTH_N_PARENT_NULL);
			pstmt.setInt(1, depth);
			rs = pstmt.executeQuery();
			
			arr = createArray(rs);
		} finally {
			close();
		}		
		
		return arr;
		
	} // end selectByDepthAndParent()
	
}

 

3) com.controller > AjaxController 서블릿

package com.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.command.*;

@WebServlet("*.ajax")
public class AjaxController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public AjaxController() {
        super();
    }

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

	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		doAction(request, response);
	}

	protected void doAction(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		
		request.setCharacterEncoding("utf-8");
		
		// 어떠한 커맨드? --> 어떠한 로직을 수행할 것인지
		Command command = null;
		
		// URL로부터 URI, ContentPath, Command 분리
		String uri = request.getRequestURI();
		String conPath = request.getContextPath();
		String com = uri.substring(conPath.length());
		
		switch(com) {
		case "/cate_list.ajax":		// 목록 요청
			new CateListCommand().execute(request, response);
			new AjaxCategoryListCommand().execute(request, response);
			break;
		}
		
	} // end doAction()
	
} // end Controller

 

4) com.command
** Command 인터페이스

package com.command;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface Command {
	void execute(HttpServletRequest request, HttpServletResponse response);
}


** AjaxCategoryListCommand.java

package com.command;

import java.io.IOException;
import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lec.beans.AjaxCategoryList;
import com.lec.beans.CategoryDTO;

public class AjaxCategoryListCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		CategoryDTO[] dtoArr = (CategoryDTO[]) request.getAttribute("list");
		
		AjaxCategoryList list  = new AjaxCategoryList();	// response할 자바 객체
		
		list.setStatus((String)request.getAttribute("status"));
		list.setMessage((String)request.getAttribute("message"));
		
		if(dtoArr != null) {
			list.setCount(dtoArr.length);
			list.setList(Arrays.asList(dtoArr));
		} // end if
		
		// depth 가 2이상인데 parent 가 없거나 parent가 유효하지 않은 uid인 경우
		if(list.getCount() == 0) {
			list.setMessage("0개의 데이터");
		}
		
		
		// json 매핑
		ObjectMapper mapper = new ObjectMapper();
		
		try {
			String jsonStirng = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(list);
			response.setContentType("application/json; charset=utf-8");
			response.getWriter().write(jsonStirng);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	} // end execute()

} // end Command


** CateListCommand.java

package com.command;

import java.sql.SQLException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lec.beans.CategoryDAO;
import com.lec.beans.CategoryDTO;

public class CateListCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		CategoryDAO dao = new CategoryDAO();
		CategoryDTO[] arr = null;
		
		// ajax response 에 필요한 값들
		StringBuffer message = new StringBuffer();
		String status = "FAIL";   // 기본 FAIL
		
		int depth = Integer.parseInt(request.getParameter("depth"));
		String parent = request.getParameter("parent");
		
		try {
			// 카테고리 읽기
			// parent "", 0, null
			if(parent == null || parent.equals("") || parent.equals("0")) {
				arr = dao.selectByDepthAndParent(depth);
			} else {	// parent 값이 있을때
				arr = dao.selectByDepthAndParent(depth, parent);
			}
			
			if(arr == null) {
				message.append("[리스트할 데이터가 없습니다]");
			} else {
				status = "OK";
			}
			
		} catch(SQLException e) {
			message.append("[트랜잭션 에러:" + e.getMessage()+ "]");
		} // end try
		
		request.setAttribute("status", status);
		request.setAttribute("message", message.toString());
		
		request.setAttribute("list", arr);

	} // end execute()

} // end Command

 


2. 클라이언트단 

1) index.jsp 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/CSS/common.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="${pageContext.request.contextPath }/JS/category.js"></script>

<title>다단계 카테고리 - SELECT 하기</title>
</head>
<body>
<div class="cate" id="mycate">

	<span>
		<select id="depth_first" disabled></select>
	</span>
	
	<span>
		<select id="depth_second" disabled></select>
	</span>
	
	<span>
		<select id="depth_third" disabled></select>
	</span>
	
</div>

</body>
</html>

 

2) JS > category.js 

var depth = 1;
var parent = 0;

$(document).ready(function(){
	// 첫 로딩할때 보여지기 위해
	selectLoad(depth, parent);

	// 첫번째 select 값이 선택될때 하위 선택 박스 활성화..!!
	$("#depth_first").change(function(){
		window.depth = 2;
		window.parent = $("#depth_first option:selected").val();
		
		
		if(window.parent != 0) {
			selectLoad(depth, parent);
		} else {
			$("#depth_second").attr('disabled', true);
			$("#depth_second option").remove();
			$("#depth_third").attr('disabled', true);
			$("#depth_third option").remove();
		}
	});
	
	// 두번째 select 값이 선택될때 하위 선택 박스 활성화..!!
	$("#depth_second").change(function(){
		window.depth = 3;
		window.parent = $("#depth_second option:selected").val();
		
		if(window.parent != 0) {
			selectLoad(depth, parent);
		} else {
			$("#depth_third").attr('disabled', true);
			$("#depth_third option").remove();
		}
	});

});


function selectLoad(depth, parent){
	
	$.ajax({
		url : "cate_list.ajax?depth=" + depth + "&parent=" + parent
		, type : "GET"
		, cache : false
		, success : function(data, status){
			if(status == "success"){
				selectOption(data, depth, parent);
			}			
		}
	});
	
} // end selectLoad()

function selectOption(jsonObj, depth, parent){
	result = ""; 
	
	if(jsonObj.status == "OK"){
		
		var count = jsonObj.count;
		
		var i;
		var items = jsonObj.data;   // 배열
		result="<option value='0' selected>--선택하세요--</option>\n"
		for(i = 0; i < count; i++){
			result += "<option value='" + items[i].uid + "'>" + items[i].name + "</option>\n";
		} // end for
		
		if(depth == 1) {
			$("#depth_first").attr('disabled', false);
			$("#depth_first").html(result);
		}
		if(depth == 2) {
			$("#depth_second").attr('disabled', false);
			$("#depth_second").html(result);
		}
		if(depth == 3) {
			$("#depth_third").attr('disabled', false);
			$("#depth_third").html(result);
		}
		
		return true;
	} else {
		alert(jsonObj.message);
		return false;
	}
	return false;
} // end selectOption()


3) common.css

@charset "UTF-8";

#mycate span select {
	padding: 5px 10px;
	border-radius: 5px;
}

#mycate span select:disabled {
	padding: 5px 50px;
}

 

 

3. 이번 과제를 통해 배운점
1) 수업시간에 배운 AJAX와 Jackson을 이용하여 JSON 데이터로 스스로 변환
 : 변환하는 과정에서 NullPointException 에러가 많이 발생했는데 

   디버그 과정을 통해 자바와 JSON으로 어떻게 데이터에 담아야 하는지와

   담는 과정에서 왜 NullPointException이 발생했는지 배우고 느낄 수 있었다.


2) MVC 모델 2 사용
 : 게시판 배울때만 사용했지만 카테고리라는 전혀 새로운 주제로 MVC 모델 2를 적용해봄으로써

   해당 모델을 다른 주제로 응용해보는 경험을 했다.

'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글

2020.06.19  (0) 2020.06.19
2020.06.18  (0) 2020.06.18
2020.06.16  (0) 2020.06.16
JSP Team Project(2020.05.25 ~ 2020.06.15)  (0) 2020.06.16
2020.06.10  (0) 2020.06.10