Spring Boot

230217 SpringBoot 카테고리 처리-1

주영재 2023. 2. 17. 19:34

카테고리 처리


단순한 1단 카테고리는 1:N의 처리로 단순하지만,
2단, 3단 카테고리를 가지고 있는 경우 self join이 들어간다.


카테고리 SQL학습

CREATE TABLE PRODUCT_CATEGORY(
	CATEGORY_ID INT PRIMARY KEY AUTO_INCREMENT, ##카테고리 PK
	GROUP_ID VARCHAR(10), ##카테고리 대분류 그룹
	CATEGORY_LV INT, ##1,2,3단
	CATEGORY_NM VARCHAR(100), ##대분류 중분류 소분류
	CATEGORY_DETAIL_LV INT, ##분류별 순서
	CATEGORY_DETAIL_NM VARCHAR(100), ##이름
	CATEGORY_PARENT_LV INT , ##1,2,3에 대한 부모컬럼
	CATEGORY_DETAIL_PARENT_LV INT ##분류별 순서에 대한 부모컬럼
);

색깔로 확인하면서 이해하기


카테고리 레벨이 1이면 대분류, 2이면 중분류, 3이면 소분류로 할 것
detail_lv는 분류별 순서
parent_lv는 1,2,3에 대한 부모컬럼. self join할 때 사용.
detail_parent_lv은 분류별 순서에 대한 부모컬럼

 

 

데이터를 넣고,

SELECT A1.CATEGORY_PARENT_LV,
A1.CATEGORY_DETAIL_NM AS '소분류',
A2.CATEGORY_DETAIL_NM AS '중분류',
A3.CATEGORY_DETAIL_NM AS '대분류',
CASE A1.category_parent_LV
 	        WHEN 0 THEN A1.category_detail_NM
 			WHEN 1 THEN CONCAT(A2.category_detail_NM,' > ', A1.category_detail_NM)
 			WHEN 2 THEN CONCAT(A3.category_detail_NM, ' > ', A2.category_detail_NM,' > ', A1.category_detail_NM)
 	   END as CATEGORY_NAV
FROM PRODUCT_CATEGORY A1 # 소분류
LEFT OUTER JOIN PRODUCT_CATEGORY A2 # 중분류
ON A1.CATEGORY_PARENT_LV = A2.CATEGORY_LV AND A1.CATEGORY_DETAIL_PARENT_LV = A2.CATEGORY_DETAIL_LV AND A1.GROUP_ID = A2.GROUP_ID
LEFT OUTER JOIN PRODUCT_CATEGORY A3
ON A2.CATEGORY_PARENT_LV = A3.CATEGORY_LV AND A2.CATEGORY_DETAIL_PARENT_LV = A3.CATEGORY_DETAIL_LV;

하면

 

이렇게 나온다.


Rest API를 통한 화면그리기

 

 

 

 

CategoryVO

package com.codehistory.myweb.command;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CategoryVO {
	
//	CATEGORY_ID INT PRIMARY KEY AUTO_INCREMENT,
//  GROUP_ID VARCHAR(10),
//  CATEGORY_LV INT, ##1,2,3
//  CATEGORY_NM VARCHAR(100), ##대분류중분류소분류
//  CATEGORY_DETAIL_LV INT, ##ORDER순서
//  CATEGORY_DETAIL_NM VARCHAR(100), ##이름
//  CATEGORY_PARENT_LV INT , ##1,2,3에 대한 부모컬럼
//  CATEGORY_DETAIL_PARENT_LV INT ##ORDER순서에 대한 부모컬럼
	
	private int category_id;
	private String group_id;
	private int category_lv;
	private String category_nm;
	private int category_detail_lv;
	private String category_detail_nm;
	private int category_parent_lv;
	private int category_detail_paretn_lv;
	
	
}

 

 

 

 

AjaxController.java

package com.codehistory.myweb.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.codehistory.myweb.command.CategoryVO;
import com.codehistory.myweb.product.service.ProductService;

@RestController
public class AjaxController {
	
	@Autowired
	private ProductService productService;
	
	
	//대분류 카테고리 요청
	@GetMapping("/getCategory")
	public List<CategoryVO> getCategory(){
		return productService.getCategory();
	}
	
	//중분류, 소분류 카테고리 요청
	@GetMapping("/getCategoryChild/{group_id}/{category_lv}/{category_detail_lv}")
	public List<CategoryVO> getCategoryChild(@PathVariable("group_id") String group_id,
					@PathVariable("category_lv") int category_lv,
					@PathVariable("category_detail_lv")int category_detail_lv){
		
		CategoryVO vo =CategoryVO.builder().group_id(group_id).category_lv(category_lv).category_detail_lv(category_detail_lv).build();
		return productService.getCategoryChild(vo);
	}
	
}

비동기통신을 위해 @RestController로 선언

 


인터페이스를 상속받은 서비스와 매퍼

 

 

ProductServiceImpl.java 에서

@Override
public List<CategoryVO> getCategory() {
	return productMapper.getCategory();
}

@Override
public List<CategoryVO> getCategoryChild(CategoryVO vo) {
	return productMapper.getCategoryChild(vo);
}

 

 

ProductMapper.xml에서

 <!-- 카테고리대분류 lv=1인 데이터 -->
  <select id="getCategory" resultType="CategoryVO">
  	select * from PRODUCT_CATEGORY
  	where category_lv=1
  </select>
  
  <!-- 카테고리 중,소분류 group_id, parent_lv, detail_parent_lv에 매칭 -->
  <select id="getCategoryChild" resultType="CategoryVO">
  	select * from PRODUCT_CATEGORY
  	where group_id=#{group_id}
  	 and category_parent_lv=#{category_lv}
  	 and category_detail_parent_lv=#{category_detail_lv}
  </select>

※화면상에 컬럼값이 노출되지 않게, $.ajax의 success 콜백에서 map형식으로 보내던가 하는 방식으로 해야 함. 알아볼 수 없도록 치환!
지금은 연습

 

ul-li를 success 콜백함수 내에서 그린다.

 

 

 

ProductReg.html에서

<tr>
  <th>카테고리</th>
  	<td colspan="5" >
     <!-- 카테고리값을 담는 태그 -->
     <input type="hidden" name="prod_category" value="">
     <div class="loading" style="display: none;">
     	<div class="loader" ></div>
    	 <div class="loading-overlay" ></div>
     </div>

     <div style="color:red">상품등록 완료 후에는 수정이 불가능하니 신중히 선택해주세요.</div>
     <div class="categoryListWrap">
     </div>
                                                    
     </td>
 </tr>
 
 ...
 
 
<script th:inline="javascript">
	//화면이 로드가 일어난 후에 실행(카테고리 데이터)
	$(document).ready(function(){
		$.ajax({
			url:"../getCategory",
			type:"get",
			success:function(result){
				console.log(result)
				
				var str="";
				
				str+='<ul class="categoryList" style="position: relative;" onclick="getCategory_List(event);" >';
				result.forEach((item,index)=>{
                	str+='<li><a href="#" data-set='+JSON.stringify(item)+'>'+item.category_detail_nm+'</a></li>';
				})
                str+='</ul>';
				
                $(".categoryListWrap").append(str); //자식으로 추가
			},
			error:function(err){alaert("카테고리 조회에 실패했습니다. 담당자에게 문의하세요.")}
		})
	});
</script>

jquery의 append()는 선택된 요소의 마지막에 새로운 HTML요소나 콘텐츠를 추가한다.
html함수를 사용하면, 선택된 요소의 태그들을 지우고 새롭게 작성한다.

 

 

style.js에서

/* 카테고리 */
function getCategory_List(e) {
	e.preventDefault(); //고유이벤트중지
	if( e.target.tagName != 'A') return; //태그검증
	var obj = $(e.target).data("set"); //데이터셋을 가져옴

	//토글색처리
	$(e.currentTarget).find("a").removeClass("sub_menu_select");
	$(e.target).addClass("sub_menu_select");
	//태그처리
	if(obj.category_lv == 1 || obj.category_lv == 2) {
		console.log('1lv');
		$().loading(); //로딩
		$(e.currentTarget).category_remove(); //이전 카테고리삭제

		//////////////////////////////////////////////////
		//비동기콜백에서 category_create() 호출
		//비동기호출후 category_set() 호출
		//category_create(); //다음 카테고리생성
		
		console.log(obj);
		$.ajax({
			url:"../getCategoryChild/"+obj.group_id+"/"+obj.category_lv+"/"+obj.category_detail_lv,
			type:"get",
			success:function(result){category_create(result)},
			error:function(err){alert("카테고리 조회에 실패했습니다. 관리자에게 문의해주세요")}
		})
		//////////////////////////////////////////////////
	} 
	//카테고리 키값 처리(선택한 값의 group_id, category_pk를 처리
	$(e.target).category_set();
}

//카테고리세팅
$.fn.category_set = function() {
	var category_id = this.data("set").category_id;
	var group_id = this.data("set").group_id;
	$("input[name='prod_category']").val(group_id + category_id ); //name이 prod_category인 곳에 추가
}
//이전카테고리 삭제JS
$.fn.category_remove = function() {
	while(this.next().length != 0) {
		$(this).next().remove();
	}
}
//다음카테고리 생성JS
function category_create(data) {

	//예시데이터
	/*
	var data = [
	 {category_lv: 2, group_id: 'B', category_detail_nm: '값선택', category_detail_parent_nm: '값선택'},
	 {category_lv: 2, group_id: 'B', category_detail_nm: '값선택', category_detail_parent_nm: '값선택'},
	 {category_lv: 2, group_id: 'B', category_detail_nm: '값선택', category_detail_parent_nm: '값선택'}
  	];
  	*/

	var category = "";
	category += '<ul class="categoryList" style="position: relative;" onclick="getCategory_List(event);" >';
	data.forEach(function(result, index) {
		category += '<li><a href="#" data-set='+ JSON.stringify(result) +'>'+ result.category_detail_nm +'</a></li>';
	});
	category += '</ul>';

	$(".categoryListWrap").append(category);
}

/* loading JS */
$.fn.loading = function() {
	$(".loading").css({display: "block"});
	window.setTimeout(function() {
		$(".loading").css({display: "none"});
	}, 1000);
}

jquery의 data함수는 dataset과 같은 역할.

 

style.js의 getCategory_List함수에서
category_lv가 1과 2일때만 실행하는 이유는
카테고리가 3중으로 되어 있기 때문. db를 이해하면 된다. 3일때는 추가로 불러올 것이 없으니 함수를 실행하지 않음.
대분류, 중분류 카테고리를 선택하면 이전의 내용들을 지우는 함수를 호출한다.


막 클릭하는 걸 금지하기 위해 $().loading과 css로 막혀 있다.

 

중분류, 소분류는 group_id, category_lv와 category_detail_lv에 따라 mapper에서 결정되어 나온다.

 

 

ProductReg.html에서

카테고리값을 담는 hidden태그의 value값을 주는 건
js의 $.ajax가 들어있는 if문 바깥에서.



ul, li태그가 선택되는 순간 값을 hidden태그에 담음.
제이쿼리방식으로 카테고리세팅 함수가 만들어져 있다. $.fn.함수명
함수호출은 $(매개변수).함수명

 

 

JSON.stringify로 데이터를 담아 보내는 이유는 네트워크 연결을 통해 주고받을 때 네트워크가 json을 읽지 못하기 때문.

형변환을 하지 않으면 카테고리를 계속해서 눌렀을 때 li가 그려지지 않는다.

 

카테고리 li를 누르면 script, js연결을 통해 새로운 li가 그려진다.
li는 category_lv가 1이나 2일 때만 그려진다!


ctrl+h를 누르면
상세찾기가 가능하다.
가령, 특정메서드가 어디서 사용되고 있는지 등을 확인하는게 가능.
Show view-search에서 확인.