본문 바로가기
Team Project (국비)/Team Project 메모

Spring 프레임워크로 비동기 처리 구현 하기

by Hwanii_ 2023. 9. 4.
728x90

1.

DB 내부에 상품 데이터의 이름을 포함하고 단어를,

사용자가 입력했을 경우, 필터된 해당 상품들을 클라이언트 에게 보여 주기.

 

2.

필터 검색은 비동기처리로 구현 하기.

 

비동기 처리로 구현한 이유 :

검색에 관한 기능만 필요하기 때문에, 불필요한 자원 낭비를 할 필요가 없다.

 

3.

Spring 프레임워크를 사용해서 비동기처리를 구현 했다.

 

3-2.

웹 브라우저 에서 확인 하기.

 

 

아무것도 검색값을 입력 하지 않고 검색을 하면 모든 상품이 검색 된다.

 

 

상품 이름이 포함된 단어를 입력 하면, 화면 이동 없이 필터된 상품 데이터가 검색 되는것을 확인 할 수 있다.

 

4.

M - V - C 패턴 파트별 주요 기능 코드 확인 하기.

 

[ Model ]

 

ProductDAO2

 

//	필터 검색
	static final String SQL_SELECTALL_FILTER
	= "SELECT P.PNUM,P.PNAME,P.PPRICE,P.PIMAGE,P.PCNT,P.PCATEGORY,P.PALCOHOL,P.PSWEET,P.PSOUR,P.PSPARKLE,P.PIMAGEDETAIL,"
			+ " ROUND(AVG(B.BSTAR),1) AS PSTARAVG, COUNT(B.BSTAR) AS PSTARCNT"
			+ " FROM PRODUCT P LEFT JOIN BOARD B ON P.PNUM = B.PNUM WHERE 1 = 1";

 

LEFT JOIN 을 사용한 이유 :

어떤 상품에 리뷰가 한개라도 달려 있지 않는 경우라면,

BOARD 테이블에 특정 상품에 대한 리뷰 데이터가 없게 된다.

즉, 상품의 PK가 없다는 의미가 된다.

아래의 쿼리문은,

BOARD 테이블의 FK로 사용 되고 있는 상품의 PK (P.PNUM) 하고,

PRODUCT 테이블의 PK로 사용 되고 있는 상품의 PK (P.PNUM) 을 기준으로 JOIN을 했고,

그냥 JOIN을 사용할경우, BOARD 테이블에 상품의 PK (P.PNUM) 가 존재 하지 않으면,

데이터가 NULL 이기 때문에, 해당 하지 않는 데이터는 SELECT 가 되지 않게 된다.

따라서, LEFT JOIN 을 사용하고, PRODUCT 테이블이 왼쪽에 있는 테이블 이기 때문에,

PRODUCT 테이블의 모든 행이 SELECT 결과에 포함 되도록 해서,

PRODUCT 테이블의 모든 데이터가 SELECT 되도록 작성한 쿼리문 이다.

 

집계 함수 : 

1) ROUND(number, decimals) :

number - 반올림할 숫자 또는 숫자 열 이다.

decimals - 소수점 이하 자리수를 뜻한다. 지정 하지 않으면 디폴트는 0이 된다.

1은 소수점 이하 한 자리 까지 반올림된 값을 반환 한다.

 

2) AVG(column) :

column - 평균을 계산하려는 해당 열의 이름을 작성 하면 된다.

보드 테이블의 데이터 중에서 BSTAR 열에 대해 행의 개수를 구하고, 평균을 집계 한다.

 

3) COUNT(expression) :

expression - 행의 수를 세고자 하는 열의 이름을 작성 한다.

보드 테이블의 데이터 중에서 BSTAR 열에 대한 행의 개수를 집계 한다.

 

[ View ]

 

<ul id="productFilterSearchUl">
	<!-- 검색 결과 필터 상품 출력 위치. -->
</ul>

<script type="text/javascript">
    
        $(document).ready(function () {
        	
            $("#productFilterSearchButton").click(search);
            
            $("#productFilterSearchInput").keydown(function (event) {
            	
                if (event.keyCode === 13) {										//	엔터 눌러도 상품 검색 가능 하도록 설정 하기.
                    search();
                }
                
            });

            function search() {
            	
                var searchContent = $("#productFilterSearchInput").val();

                $.ajax({
                    url: "productFilterSearch.do",
                    type: "POST",
                    data: { pName: searchContent },
                    dataType: "json",
                    success: function (jdatas) {
                        console.log("log : 11111");
                        console.log("jdatas : " + JSON.stringify(jdatas));		//	JSON.stringify() 개념 정리 하기.

                        var ul = $("#productFilterSearchUl");
                        ul.empty();

                        for (var i = 0; i < jdatas.length; i++) {
                            var productName = $("<li>").text(jdatas[i].pName);
                            ul.append(productName);
                        }

                    },
                    error: function (error) {
                        console.log('error [' + error + ']');
                    }
                    
                });
                
            }
            
        });
        
</script>

 

Ajax 메서드를 사용 해서 비동기처리를 구현 했다.

 

JSON 데이터 형식으로 사용자가 입력한 필터값을 pName 이라는 key 이름으로 보내고,

Controller 에게 받는 데이터는 JSON 데이터 형식으로 받는것으로 작성 했다.

 

JSON == JavaScript Object Notation

>> 일련의 데이터를 표현하는 데이터 형식중 하나 이다.

>> 데이터를 저장하고 교환하기 위한 경량의 데이터 형식 이다.

 

JSON 데이터 형식의 장점은 아래와 같다.

 

1. 간결하고, 가독성이 좋다.

 

JSON 데이터 형식은 텍스트 형식 (문자열 == String) 으로 이루어져 있어서,

사람 + 기계 모두 이해 하기 쉽다.

 

2. Key-Value 한 쌍 형식 이다.

 

JSON 데이터 형식은 이름-값 한 쌍 으로 데이터를 표현 한다.

Key는 문자열이고, Value는 문자열, 숫자, 불리언, 배열, 객체, .. 등 다양한 데이터 형식을 갖는다.

 

3. 계층 구조.

 

JSON 데이터 형식은 중첩된 데이터 구조를 지원 한다.

객체 안에 다른 객체나 배열을 포함 할 수 있다.

 

4. 높은 호환성.

 

JSON 데이터 형식은 프로그래밍 언어나, 플랫폼과 무관하게 사용이 가능 하다.

다양한 프로그래밍 언어에서 JSON 데이터 형식을 생성하고 파싱하는 라이브러리가 제공 된다.

 

[ Controller ]

 

package com.spring.view.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.gson.Gson;
import com.spring.biz.product.ProductService;
import com.spring.biz.product.ProductVO;

@Controller
public class ProductFilterSearchController {	//	상품 필터 검색. (비동기 처리)
	
	@Autowired
	private ProductService productService;
	
	@RequestMapping(value = "/productFilterSearch.do")
	@ResponseBody
	public String productFilterSearch(ProductVO pVO, Gson gson) {
		
		pVO.setSk("FILTER");
		List<ProductVO> pdatas = productService.selectAll(pVO);	//	사용자가 입력한 상품 이름이 포함 되어 있는 상품들.
		
		String jdatas = gson.toJson(pdatas);					//	JSON 데이터 형식으로 변환.
		
		return jdatas;
	}

}	//	ProductFilterSearchController

 

Gson 라이브러리를 사용 해서 자바의 배열리스트 데이터를 JSON 데이터로 변환 했다.

자바의 컬렉션 프레임워크 중 하나인 배열리스트 뿐만 아니라,

그외에 객체 데이터들 모두 Ajax() 메서드가 읽을 수 없는 이슈가 발생 했기 때문 이다.

 

Spring 프레임워크 에서 비동기처리를 구현 하기 위해서는,

@ResponseBody 어노테이션을 작성해줘야 한다.

 

@Controller 어노테이션이 명시 되어 있는 클래스 내부의 특정한 메서드가

반환하는 리턴값은 일반적으로 View 페이지를 찾으려고 ViewResolver 가 작동 된다.

이 ViewResolver 은 메서드가 반환한 문자열 값을 가지고 실제 파일의 경로를 찾게 된다.

 

하지만,

비동기처리를 위해 작성된 XxxController 내부의 메서드 에서는,

데이터 (값) 을 반환해줘야 한다.

 

@ResponseBody 어노테이션은 ViewResolver를 우회하고,

컨트롤러 메서드의 반환값을 직접적으로,

클라이언트에게 보내는 실제 정보가 담겨 있는 웹서버로 전송하도록 한다.

 

5.

코드 원본 확인 하기.

 

[ Model ]

 

ProductVO

package com.spring.biz.product;

import org.springframework.web.multipart.MultipartFile;

public class ProductVO {			//	상품 (주류)

    private int pNum;				//	상품 번호. (PK)
    private String pName;			//	상품 이름.
    private int pPrice;				//	상품 가격.
    private String pImage;			//	상품 이미지.
    private int pCnt; 				// 	상품 재고.
    private String pCategory; 			// 	상품_카테고리.
    private double pAlcohol; 			// 	상품_도수.
    private String pSweet; 			// 	상품_단맛.
    private String pSour; 			// 	상품_신맛.
    private String pSparkle; 			// 	상품_탄산.
    private String pImagedetail; 		// 	상품 상세 이미지.

	//	---------- 임시변수 ----------
	
	private String sk; 			//	Search Keyword.
	
	private int pStarCnt;
	private double pStarAvg;
	private int tmpCnt;
	
	//	장바구니는 세션으로 관리 => JAVA <--> View
	//	같은 상품을 장바구니에 담았을 경우, cnt를 관리하기 위한 임시 변수.
	
	private MultipartFile fileUpload1;
	private MultipartFile fileUpload2;

	static String FilePath
	= "C:\\Users\\dhfg0\\Desktop\\spring\\springWS\\0825\\src\\main\\webapp\\assets\\img\\products\\";
	
	//	getter & setter
	
	public int getpNum() {
		return pNum;
	}
	public void setpNum(int pNum) {
		this.pNum = pNum;
	}
	public String getpName() {
		return pName;
	}
	public void setpName(String pName) {
		this.pName = pName;
	}
	public int getpPrice() {
		return pPrice;
	}
	public void setpPrice(int pPrice) {
		this.pPrice = pPrice;
	}
	public String getpImage() {
		return pImage;
	}
	public void setpImage(String pImage) {
		this.pImage = pImage;
	}
	public int getpCnt() {
		return pCnt;
	}
	public void setpCnt(int pCnt) {
		this.pCnt = pCnt;
	}
	public String getpCategory() {
		return pCategory;
	}
	public void setpCategory(String pCategory) {
		this.pCategory = pCategory;
	}
	public double getpAlcohol() {
		return pAlcohol;
	}
	public void setpAlcohol(double pAlcohol) {
		this.pAlcohol = pAlcohol;
	}
	public String getpSweet() {
		return pSweet;
	}
	public void setpSweet(String pSweet) {
		this.pSweet = pSweet;
	}
	public String getpSour() {
		return pSour;
	}
	public void setpSour(String pSour) {
		this.pSour = pSour;
	}
	public String getpSparkle() {
		return pSparkle;
	}
	public void setpSparkle(String pSparkle) {
		this.pSparkle = pSparkle;
	}
	public String getpImagedetail() {
		return pImagedetail;
	}
	public void setpImagedetail(String pImagedetail) {
		this.pImagedetail = pImagedetail;
	}

	public String getSk() {
		return sk;
	}
	public void setSk(String sk) {
		this.sk = sk;
	}

	public int getpStarCnt() {
		return pStarCnt;
	}
	public void setpStarCnt(int pStarCnt) {
		this.pStarCnt = pStarCnt;
	}
	public double getpStarAvg() {
		return pStarAvg;
	}
	public void setpStarAvg(double pStarAvg) {
		this.pStarAvg = pStarAvg;
	}
	public int getTmpCnt() {
		return tmpCnt;
	}
	public void setTmpCnt(int tmpCnt) {
		this.tmpCnt = tmpCnt;
	}

	public MultipartFile getFileUpload1() {
		return fileUpload1;
	}
	public void setFileUpload1(MultipartFile fileUpload1) {
		this.fileUpload1 = fileUpload1;
	}
	public MultipartFile getFileUpload2() {
		return fileUpload2;
	}
	public void setFileUpload2(MultipartFile fileUpload2) {
		this.fileUpload2 = fileUpload2;
	}
	
	public static String getFilePath() {
		return FilePath;
	}
	public static void setFilePath(String filePath) {
		FilePath = filePath;
	}
	
	@Override
	public String toString() {
		return "ProductVO [pNum=" + pNum + ", pName=" + pName + ", pPrice=" + pPrice + ", pImage=" + pImage + ", pCnt="
				+ pCnt + ", pCategory=" + pCategory + ", pAlcohol=" + pAlcohol + ", pSweet=" + pSweet + ", pSour="
				+ pSour + ", pSparkle=" + pSparkle + ", pImagedetail=" + pImagedetail + ", sk=" + sk + ", pStarCnt="
				+ pStarCnt + ", pStarAvg=" + pStarAvg + ", tmpCnt=" + tmpCnt + ", fileUpload1=" + fileUpload1
				+ ", fileUpload2=" + fileUpload2 + "]";
	}

	@Override
	public boolean equals(Object obj) {	//	Object 타입의 매개변수 obj를 ProductVO 타입으로 다운 캐스팅 == 다형성

		ProductVO pVO = (ProductVO) obj;

		if (this.pNum == pVO.pNum) {
			return true;
		}
		return false;
	}

}	//	ProductVO

 

ProductDAO2

 

package com.spring.biz.product;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

@Repository("productDAO")
public class ProductDAO2 {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	static final String SQL_INSERT
	= "INSERT INTO PRODUCT (PNUM,PNAME,PPRICE,PIMAGE,PCNT,PCATEGORY,PALCOHOL,PSWEET,PSOUR,PSPARKLE,PIMAGEDETAIL)"
			+ " VALUES ((SELECT NVL(MAX(PNUM),0) + 1 FROM PRODUCT),?,?,?,?,?,?,?,?,?,?)";

	//	필터 검색
	static final String SQL_SELECTALL_FILTER
	= "SELECT P.PNUM,P.PNAME,P.PPRICE,P.PIMAGE,P.PCNT,P.PCATEGORY,P.PALCOHOL,P.PSWEET,P.PSOUR,P.PSPARKLE,P.PIMAGEDETAIL,"
			+ " ROUND(AVG(B.BSTAR),1) AS PSTARAVG, COUNT(B.BSTAR) AS PSTARCNT"
			+ " FROM PRODUCT P LEFT JOIN BOARD B ON P.PNUM = B.PNUM WHERE 1 = 1";

	//	전체 상품
	static final String SQL_SELECTALL_ALL
	= "SELECT PNUM, PNAME, PPRICE, PIMAGE, PCNT, PCATEGORY, PALCOHOL, PSWEET, PSOUR, PSPARKLE, PIMAGEDETAIL"
			+ " FROM PRODUCT";

	static final String SQL_SELECTONE
	= "SELECT P.PNUM,P.PNAME,P.PPRICE,P.PIMAGE,P.PCNT,P.PCATEGORY,P.PALCOHOL,P.PSWEET,P.PSOUR,P.PSPARKLE,P.PIMAGEDETAIL,"
			+ " ROUND(AVG(B.BSTAR),1) AS PSTARAVG, COUNT(B.BSTAR) AS PSTARCNT"
			+ " FROM PRODUCT P LEFT JOIN BOARD B ON P.PNUM = B.PNUM WHERE P.PNUM = ?"
			+ " GROUP BY P.PNUM,P.PNAME,P.PPRICE,P.PIMAGE,P.PCNT,P.PCATEGORY,P.PALCOHOL,P.PSWEET,P.PSOUR,P.PSPARKLE,P.PIMAGEDETAIL";

	static final String SQL_UPDATE_DETAIL
	= "UPDATE PRODUCT SET PNAME = ?, PPRICE = ?, PCNT = ?, PALCOHOL = ?, PCATEGORY = ?, PSOUR = ?, PSWEET = ?, PSPARKLE = ? WHERE PNUM = ?";

	static final String SQL_UPDATE_CNT = "UPDATE PRODUCT SET PCNT = PCNT - ? WHERE PNUM = ?";

	static final String SQL_DELETE = "DELETE FROM PRODUCT WHERE PNUM = ?";

	public boolean insert(ProductVO pVO) {

		System.out.println("log : ProductDAO2 : insert()");

		try {
			int rs
			= jdbcTemplate.update(SQL_INSERT, 
					pVO.getpName(), pVO.getpPrice(), pVO.getpImage(), pVO.getpCnt(), pVO.getpCategory(), 
					pVO.getpAlcohol(), pVO.getpSweet(), pVO.getpSour(), pVO.getpSparkle(), pVO.getpImagedetail());

			if (rs <= 0) {
				return false;
			}
		} catch (DataAccessException e) {
			return false;
		}
		return true;
	}

	public List<ProductVO> selectAll(ProductVO pVO) {

		System.out.println("log : ProductDAO2 : selectAll()");

		try {
			if (pVO.getSk().equals("FILTER")) {

				ArrayList<Object> parameters = new ArrayList<Object>();
				//	사용자가 선택한 필터 값을 저장 하기 위한 배열리스트.

				StringBuilder queryBuilder = new StringBuilder(SQL_SELECTALL_FILTER);
				//	StringBuilder 클래스는 문자열을 동적으로 다룰 수 있다.
				//	append() 메서드로 객체에 문자열을 연속적으로 추가.

				Double pAlcohol = pVO.getpAlcohol();
				//	null 값을 활용 하기 위해, 래퍼 클래스 사용.

				if (pVO.getpAlcohol() == 0.0) {
					pAlcohol = null;
				}
				//	View 에서, 도수 조건 선택 안했을 때, 디폴트 값으로 0.0이 들어오는 경우 null로 처리 하기.

				boolean validityCheckpSweet
				= pVO.getpSweet() == null || !(pVO.getpSweet().equals("약")) && !(pVO.getpSweet().equals("중")) && !(pVO.getpSweet().equals("강"));

				boolean validityCheckpSour
				= pVO.getpSour() == null || !(pVO.getpSour().equals("약")) && !(pVO.getpSour().equals("중")) && !(pVO.getpSour().equals("강"));

				boolean validityCheckpSparkle
				= pVO.getpSparkle() == null || !(pVO.getpSparkle().equals("약")) && !(pVO.getpSparkle().equals("중")) && !(pVO.getpSparkle().equals("강"));

				if (validityCheckpSweet) { pVO.setpSweet(null); }
				if (validityCheckpSour) { pVO.setpSour(null); }
				if (validityCheckpSparkle) { pVO.setpSparkle(null); }

				// 이름 조건
				if (pVO.getpName() != null) {
					queryBuilder.append(" AND PNAME LIKE '%'|| ? || '%'");
					parameters.add(pVO.getpName());
				}
				// 카테고리 조건
				if (pVO.getpCategory() != null) {
					queryBuilder.append(" AND PCATEGORY = ?");
					parameters.add(pVO.getpCategory());
				}
				// 알콜 조건
				if (pAlcohol != null) {
					queryBuilder.append(" AND PALCOHOL BETWEEN ? AND ?");	//	도수 필터에서, 한개의 도수값만 전달 받는다. 두 값 중에서 큰 값 하나만 받는 설계 이다.
					parameters.add(pAlcohol - 10.0);						//	0.0도 ~ 10.0도 필터일 경우, 10.0도만 받는다.  
					parameters.add(pAlcohol);								//	그러면, pAlcohol의 값이 0.0도 ~ 10.0도 으로 add() 된다.
				}
				// 당도 조건
				if (pVO.getpSweet() != null) {
					queryBuilder.append(" AND PSWEET = ?"); 				//	"SELECT * FROM PRODUCT WHERE 1=1" 쿼리문에,
					parameters.add(pVO.getpSweet());
				}
				// 신맛 조건
				if (pVO.getpSour() != null) {
					queryBuilder.append(" AND PSOUR = ?"); 					//	if문의 조건이 맞으면,
					parameters.add(pVO.getpSour());
				}
				// 탄산 조건
				if (pVO.getpSparkle() != null) {
					queryBuilder.append(" AND PSPARKLE = ?"); 				//	동적으로 조건에 맞는 쿼리문이 append() 된다.
					parameters.add(pVO.getpSparkle());
				}

				queryBuilder.append(" GROUP BY P.PNUM,P.PNAME,P.PPRICE,P.PIMAGE,P.PCNT,P.PCATEGORY,P.PALCOHOL,P.PSWEET,P.PSOUR,P.PSPARKLE,P.PIMAGEDETAIL");
				//	쿼리문에 집계 함수를 사용 했기 때문에, 모든 칼럼을 명시 해서 그룹화.

				System.out.println("log : " + queryBuilder.toString());

				Object[] args = new Object[parameters.size()];				//	args의 크기

				for (int i = 0; i < parameters.size(); i++) { 				// 	args 크기만큼 반복
					args[i] = parameters.get(i);							//	args[i] 에 params 배열리스트에 있는 값들을 넣기.
				}

				return jdbcTemplate.query(queryBuilder.toString(), args, new ProductRowMapper_SELECTONE_SELECTALL_FILTER());

			} else if (pVO.getSk().equals("ALL")) {

				return jdbcTemplate.query(SQL_SELECTALL_ALL, new ProductRowMapper_SELECTALL_ALL());
			}
		} catch (DataAccessException e) {
			return null;
		}
		return null;
	}	//	selectAll()

	public ProductVO selectOne(ProductVO pVO) {

		System.out.println("log : ProductDAO2 : selectOne()");

		try {
			Object[] args = {pVO.getpNum()};
			return jdbcTemplate.queryForObject(SQL_SELECTONE, args, new ProductRowMapper_SELECTONE_SELECTALL_FILTER());

		} catch (DataAccessException e) {	//	조회 결과가 없는 경우에 대한 처리.
			return null;
		}
	}

	public boolean update(ProductVO pVO) {

		System.out.println("log : ProductDAO2 : update()");

		try {
			int rs = 0;

			if (pVO.getSk().equals("UPDATEDETAIL")) {
				rs = jdbcTemplate.update(SQL_UPDATE_DETAIL, pVO.getpName(), pVO.getpPrice(), pVO.getpCnt(),
						pVO.getpAlcohol(), pVO.getpCategory(), pVO.getpSour(), pVO.getpSweet(), pVO.getpSparkle(),
						pVO.getpNum());

			} else {
				rs = jdbcTemplate.update(SQL_UPDATE_CNT, pVO.getTmpCnt(), pVO.getpNum());
			}

			if (rs <= 0) {
				return false;
			}
		} catch (DataAccessException e) {
			return false;
		}
		return true;
	}

	public boolean delete(ProductVO pVO) {	//	회원 탈퇴.

		System.out.println("log : MemberDAO2 : delete()");

		try {
			int rs = jdbcTemplate.update(SQL_DELETE, pVO.getpNum());

			if (rs <= 0) {
				return false;
			}
		} catch (DataAccessException e) {
			return false;
		}
		return true;
	}

}	//	ProductDAO2

class ProductRowMapper_SELECTONE_SELECTALL_FILTER implements RowMapper<ProductVO> {

	@Override
	public ProductVO mapRow(ResultSet rs, int rowNum) throws SQLException {

		ProductVO pdata = new ProductVO();
		try {
			pdata.setpNum(rs.getInt("PNUM"));
			pdata.setpName(rs.getString("PNAME"));
			pdata.setpPrice(rs.getInt("PPRICE"));
			pdata.setpImage(rs.getString("PIMAGE"));
			pdata.setpCnt(rs.getInt("PCNT"));
			pdata.setpCategory(rs.getString("PCATEGORY"));
			pdata.setpAlcohol(rs.getDouble("PALCOHOL"));
			pdata.setpSweet(rs.getString("PSWEET"));
			pdata.setpSour(rs.getString("PSOUR"));
			pdata.setpSparkle(rs.getString("PSPARKLE"));
			pdata.setpImagedetail(rs.getString("PIMAGEDETAIL"));
			pdata.setpStarAvg(rs.getDouble("PSTARAVG"));
			pdata.setpStarCnt(rs.getInt("PSTARCNT"));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		return pdata;
	}

}	//	ProductRowMapper_SELECTONE_SELECTALL_FILTER

class ProductRowMapper_SELECTALL_ALL implements RowMapper<ProductVO> {

	@Override
	public ProductVO mapRow(ResultSet rs, int rowNum) throws SQLException {

		ProductVO pdata = new ProductVO();
		try {
			pdata.setpNum(rs.getInt("PNUM"));
			pdata.setpName(rs.getString("PNAME"));
			pdata.setpPrice(rs.getInt("PPRICE"));
			pdata.setpImage(rs.getString("PIMAGE"));
			pdata.setpCnt(rs.getInt("PCNT"));
			pdata.setpCategory(rs.getString("PCATEGORY"));
			pdata.setpAlcohol(rs.getDouble("PALCOHOL"));
			pdata.setpSweet(rs.getString("PSWEET"));
			pdata.setpSour(rs.getString("PSOUR"));
			pdata.setpSparkle(rs.getString("PSPARKLE"));
			pdata.setpImagedetail(rs.getString("PIMAGEDETAIL"));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		return pdata;
	}

}	//	ProductRowMapper_SELECTALL_ALL

 

[ View ]

 

productFilterSearch.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html lang="ko">

<head>

<script
	src="https://code.jquery.com/jquery-3.7.0.min.js"
	integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g="
	crossorigin="anonymous">
</script>  
  
</head>

<body>

    <div id="productFilterSearchBox">
    
    	<input type="text" id="productFilterSearchInput" placeholder="상품 이름을 입력 하세요.">
    	
    	<button type="button" id="productFilterSearchButton">상품 검색</button>
    	
    	<ul id="productFilterSearchUl">
    		<!-- 검색 결과 필터 상품 출력 위치. -->
    	</ul>
    	
    	<div class="productFilterSearchClose"><a onclick="history.go(-1);">검색 취소</a></div>
    	
    </div>
    
    <script type="text/javascript">
    
        $(document).ready(function () {
        	
            $("#productFilterSearchButton").click(search);
            
            $("#productFilterSearchInput").keydown(function (event) {
            	
                if (event.keyCode === 13) {										//	엔터 눌러도 상품 검색 가능 하도록 설정 하기.
                    search();
                }
                
            });

            function search() {
            	
                var searchContent = $("#productFilterSearchInput").val();

                $.ajax({
                    url: "productFilterSearch.do",
                    type: "POST",
                    data: { pName: searchContent },
                    dataType: "json",
                    success: function (jdatas) {
                        console.log("log : 11111");
                        console.log("jdatas : " + JSON.stringify(jdatas));		//	JSON.stringify() 개념 정리 하기.

                        var ul = $("#productFilterSearchUl");
                        ul.empty();

                        for (var i = 0; i < jdatas.length; i++) {
                            var productName = $("<li>").text(jdatas[i].pName);
                            ul.append(productName);
                        }

                    },
                    error: function (error) {
                        console.log('error [' + error + ']');
                    }
                    
                });
                
            }
            
        });
        
    </script>
    
</body>

</html>

 

[ Controller ]

 

ProductFilterSearchPageController

 

package com.spring.view.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ProductFilterSearchPageController {	//	상품 필터 검색 페이지.
	
	@RequestMapping(value = "/productFilterSearchPage.do")
	public String ProductFilterSearchPage() {
		
		System.out.println("log : ProductFilterSearchPageController : ProductFilterSearchPage()");
		
		
		return "redirect:productFilterSearch.jsp";
	}

}	//	ProductFilterSearchPageController

 

ProductFilterSearchController

 

package com.spring.view.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.gson.Gson;
import com.spring.biz.product.ProductService;
import com.spring.biz.product.ProductVO;

@Controller
public class ProductFilterSearchController {	//	상품 필터 검색. (비동기 처리)
	
	@Autowired
	private ProductService productService;
	
	@RequestMapping(value = "/productFilterSearch.do")
	@ResponseBody
	public String productFilterSearch(ProductVO pVO, Gson gson) {
		
		pVO.setSk("FILTER");
		List<ProductVO> pdatas = productService.selectAll(pVO);	//	사용자가 입력한 상품 이름이 포함 되어 있는 상품들.
		
		String jdatas = gson.toJson(pdatas);					//	JSON 데이터 형식으로 변환.
		
		return jdatas;
	}

}	//	ProductFilterSearchController

 

[ Model ]

 

boardVO

 

package com.spring.biz.board;

import java.util.Date;

public class BoardVO {   	 	//	상품 리뷰

    private int bNum;    		//	리뷰 번호. (PK)
    private String mID;    		// 	회원 아이디. (FK)
    private int pNum;    		// 	상품 번호. (FK)
    private String bContent;    	// 	리뷰 내용.
    private double bStar;    		// 	회원 별점.
    private Date bDate;    		// 	리뷰 작성 날짜.
    private String mName;   		// 	회원 닉네임. (FK)

    //	---------- 임시변수 ----------

    private String sk;			//	Search Keyword.
    
    private int rnum;    		//	ROW_NUMBER() 를 사용 하기 위한 임시 변수.
    private String pName;    		//	상품 이름 (조인)
    private String pImage;    		//	상품 이미지 (조인)
    private boolean check;		//	리뷰를 내가 신고 했는지 안 했는지를 체크하는 임시 변수.
    
    //	getter & setter
    
	public int getbNum() {
		return bNum;
	}
	public void setbNum(int bNum) {
		this.bNum = bNum;
	}
	public String getmID() {
		return mID;
	}
	public void setmID(String mID) {
		this.mID = mID;
	}
	public int getpNum() {
		return pNum;
	}
	public void setpNum(int pNum) {
		this.pNum = pNum;
	}
	public String getbContent() {
		return bContent;
	}
	public void setbContent(String bContent) {
		this.bContent = bContent;
	}
	public double getbStar() {
		return bStar;
	}
	public void setbStar(double bStar) {
		this.bStar = bStar;
	}
	public Date getbDate() {
		return bDate;
	}
	public void setbDate(Date bDate) {
		this.bDate = bDate;
	}
	public String getmName() {
		return mName;
	}
	public void setmName(String mName) {
		this.mName = mName;
	}
	
	public String getSk() {
		return sk;
	}
	public void setSk(String sk) {
		this.sk = sk;
	}
	
	public int getRnum() {
		return rnum;
	}
	public void setRnum(int rnum) {
		this.rnum = rnum;
	}
	public String getpName() {
		return pName;
	}
	public void setpName(String pName) {
		this.pName = pName;
	}
	public String getpImage() {
		return pImage;
	}
	public void setpImage(String pImage) {
		this.pImage = pImage;
	}
	public boolean isCheck() {
		return check;
	}
	public void setCheck(boolean check) {
		this.check = check;
	}
	
	@Override
	public String toString() {
		return "BoardVO [bNum=" + bNum + ", mID=" + mID + ", pNum=" + pNum + ", bContent=" + bContent + ", bStar="
				+ bStar + ", bDate=" + bDate + ", mName=" + mName + ", sk=" + sk + ", rnum=" + rnum + ", pName=" + pName
				+ ", pImage=" + pImage + ", check=" + check + "]";
	}
    
}    //   BoardVO

 

boardDAO2

 

package com.spring.biz.board;

import java.sql.ResultSet;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

@Repository("boardDAO")
public class BoardDAO2 {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    static final String SQL_INSERT
            = "INSERT INTO BOARD (BNUM,MID,MNAME,PNUM,BCONTENT,BSTAR) VALUES ((SELECT NVL(MAX(BNUM),0) + 1 FROM BOARD),?,?,?,?,?)";
    
    //  보드 (리뷰) 테이블에 있는 모든 리뷰중에서, 해당 상품 (P.PNUM) 에 작성 되어 있는 리뷰를 SELECT 하기. (상품 이름이 필요해서 상품 테이블과 조인.)
    static final String SQL_SELECTALL_PRODUCT
            = "SELECT ROW_NUMBER() OVER (ORDER BY BNUM) AS RNUM, B.BNUM, B.MID, B.PNUM, B.BCONTENT, B.BSTAR, B.BDATE, B.MNAME, P.PNAME"
            + " FROM BOARD B INNER JOIN PRODUCT P ON B.PNUM = P.PNUM"
            + " WHERE P.PNUM = ?";
    
    //  보드 (리뷰) 테이블에서 있는 모든 리뷰중에서, 내가 작성한 리뷰를 SELECT 하기. (상품 이름이 필요해서 상품 테이블과 조인.)
    static final String SQL_SELECTALL_MYPAGE
            = "SELECT ROW_NUMBER() OVER (ORDER BY BNUM) AS RNUM, B.BNUM, B.MID, B.PNUM, B.BCONTENT, B.BSTAR, B.BDATE, B.MNAME, P.PNAME, P.PIMAGE"
            + " FROM BOARD B INNER JOIN PRODUCT P ON B.PNUM = P.PNUM"
            + " WHERE MID = ?";
    
    static final String SQL_SELECTONE = "SELECT * FROM BOARD WHERE BNUM = ?";
    
    static final String SQL_UPDATE = "UPDATE BOARD SET BCONTENT = ?, BSTAR = ? WHERE BNUM = ?";
    
    static final String SQL_DELETE = "DELETE FROM BOARD WHERE BNUM = ?";

    public boolean insert(BoardVO bVO) {

        System.out.println("log : BoardDAO2 : insert()");

        try {
            int rs = jdbcTemplate.update(SQL_INSERT, bVO.getmID(), bVO.getmName(), bVO.getpNum(), bVO.getbContent(), bVO.getbStar());

            if (rs <= 0) {
                return false;
            }
        } catch (DataAccessException e) {
            System.out.println("log : BoardDAO2 : insert() : catch");
            return false;
        }
        return true;
    }

    public List<BoardVO> selectAll(BoardVO bVO) {

        System.out.println("log : BoardDAO2 : selectAll()");
        
        //	sk가 다른 것들 쿼리문에 들어갈 인자 값이 다르기 때문에 sk 마다 args 를 다르게 선언.
        try {
            if (bVO.getSk().equals("PRODUCT")) {
                Object[] args = {bVO.getpNum()};
                return jdbcTemplate.query(SQL_SELECTALL_PRODUCT, args, new BoardRowMapper_SELECTALL());
            } else if (bVO.getSk().equals("MYPAGE")) {
                Object[] args = {bVO.getmID()};
                return jdbcTemplate.query(SQL_SELECTALL_MYPAGE, args, new BoardRowMapper_SELECTALL());
            }
        } catch (DataAccessException e) {
            System.out.println("log : BoardDAO2 : selectAll() : catch");
            return null;
        }
        return null;
    }

    public BoardVO selectOne(BoardVO bVO) {

        System.out.println("log : BoardDAO2 : selectOne()");

        try {
            Object[] args = {bVO.getbNum()};
            return jdbcTemplate.queryForObject(SQL_SELECTONE, args, new BoardRowMapper_SELECTONE());
        } catch (DataAccessException e) {	//	조회 결과가 없는 경우에 대한 처리.
            System.out.println("log : BoardDAO2 : selectOne() : catch");
            return null;
        }
    }

    public boolean update(BoardVO bVO) {

        System.out.println("log : BoardDAO2 : update()");

        try {
            int rs = jdbcTemplate.update(SQL_UPDATE, bVO.getbContent(), bVO.getbStar(), bVO.getbNum());

            if (rs <= 0) {
                return false;
            }
        } catch (DataAccessException e) {
            System.out.println("log : BoardDAO2 : update() : catch");
            return false;
        }
        return true;
    }

    public boolean delete(BoardVO bVO) {

        System.out.println("log : BoardDAO2 : delete()");
        try {
            int rs = jdbcTemplate.update(SQL_DELETE, bVO.getbNum());

            if (rs <= 0) {
                return false;
            }
        } catch (DataAccessException e) {
            System.out.println("log : BoardDAO2 : delete() : catch");
            return false;
        }
        return true;
    }
    
}	//	BoardDAO2

class BoardRowMapper_SELECTONE implements RowMapper<BoardVO> {

    public BoardVO mapRow(ResultSet rs, int rowNum) {
        BoardVO bdata = new BoardVO();
        try {
            bdata.setbNum(rs.getInt("BNUM"));
            bdata.setmID(rs.getString("MID"));
            bdata.setpNum(rs.getInt("PNUM"));
            bdata.setbContent(rs.getString("BCONTENT"));
            bdata.setbStar(rs.getDouble("BSTAR"));
            bdata.setbDate(rs.getDate("BDATE"));
            bdata.setmName(rs.getString("MNAME"));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return bdata;
    }
    
}	//	BoardRowMapper_SELECTONE

class BoardRowMapper_SELECTALL implements RowMapper<BoardVO> {

    public BoardVO mapRow(ResultSet rs, int rowNum) {
        BoardVO bdata = new BoardVO();
        try {
            bdata.setRnum(rs.getInt("RNUM"));
            bdata.setbNum(rs.getInt("BNUM"));
            bdata.setmID(rs.getString("MID"));
            bdata.setpNum(rs.getInt("PNUM"));
            bdata.setbContent(rs.getString("BCONTENT"));
            bdata.setbStar(rs.getDouble("BSTAR"));
            bdata.setbDate(rs.getDate("BDATE"));
            bdata.setmName(rs.getString("MNAME"));
            bdata.setpName(rs.getString("PNAME"));
            bdata.setpImage(rs.getString("PIMAGE"));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return bdata;
    }
    
}	//	BoardRowMapper_SELECTALL

 

반응형