230217 SpringBoot 카테고리 처리-1
카테고리 처리
단순한 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가 그려지지 않는다.
ctrl+h를 누르면
상세찾기가 가능하다.
가령, 특정메서드가 어디서 사용되고 있는지 등을 확인하는게 가능.
Show view-search에서 확인.