10/05/2018

스프링 쇼핑몰 만들기 #7. 카테고리 구성

스프링 쇼핑몰 만들기

상품 등록 기능을 추가하기 전에, 카테고리를 먼저 구성하겠습니다.

다양한 상품을 다룰땐 카테고리 유동적일 수 있지만, 지금 만드는 쇼핑몰은 상품 갯수가 적고 거의 변동이 없기 때문에 미리 카테고리를 구성해두면 편리합니다.

물론 카테고리 추가, 삭제, 수정 기능도 나중에 구현할 것입니다.

테이블은 미리 준비된것을 사용합니다.

이 테이블은 cateCodeRef가 cateCode를 참조하여 계층구조로 되어있는 테이블입니다.

예시를 위해 임시 데이터를 추가하고

이런식으로 구성됩니다.

cateCodeRef의 값이 없다면(null) 그 카테고리는 최상위 카테고리이며, cateCodeRef의 값이 다른 cateCode와 같다면 그 카테고리는 cateCode의 하위 카테고리입니다.

cateCodeRef는 cateCode를 참조(foreign)하기 때문에, cateCodeRef는 존재하지 않는 cateCode를 입력할 수 없습니다.

확인했다면 임시 데이터를 모두 지워줍니다.

저는 어느 게임의 아이템을 이용한 쇼핑몰을 만들 계획이며, 이에 필요한 데이터를 미리 준비해두었습니다.

# 무기(weapon)
- 돌격소총(AR) : M16등 9개
- 기관단총(SMG) : Micro UZI등 4개
- 경기관총(LMG) : DP-28등 2개
- 산탄총(SG) : S686등 3개
- 지정사수소총(DMR) : Mini-14등 6개
- 저격소총(SR) : Kar98K등 3개
- 기타(ETC) : 프라이팬 1개

# 탄(bullet)
- 5.56mm
- 7.62mm
- 9mm
- 45ACP
- 12guage
- 300magnum

# 방어구(armor)
- 모자(helmet)
- 조끼(jacket)
- 가방(backpack)

# 회복제
- 붕대(bandage)
- 구급상자(first aid kit)
- 치료킷(mad kit)
- 에너지 드링크(energy drink)
- 진통제(painkiller)

이렇게 카테고리를 구성할 예정입니다.

무기만 2단계로 되어있고, 나머지는 1단계로 끝나는 구조입니다.

위의 구조에 맞게 데이터를 추가합니다.

insert into goods_category (cateName, cateCode) values ('무기', '100');
insert into goods_category (cateName, cateCode, cateCodeRef) values ('돌격소총', '101', '100');
insert into goods_category (cateName, cateCode, cateCodeRef) values ('기관단총', '102', '100');
insert into goods_category (cateName, cateCode, cateCodeRef) values ('경기관총', '103', '100');
insert into goods_category (cateName, cateCode, cateCodeRef) values ('산탄총', '104', '100');
insert into goods_category (cateName, cateCode, cateCodeRef) values ('지정사수소총', '105', '100');
insert into goods_category (cateName, cateCode, cateCodeRef) values ('저격소총', '106', '100');
insert into goods_category (cateName, cateCode, cateCodeRef) values ('기타', '107', '100');

insert into goods_category (cateName, cateCode) values ('탄', '200');
insert into goods_category (cateName, cateCode) values ('방어구', '300');
insert into goods_category (cateName, cateCode) values ('회복제', '400');

입력된 데이터가 잘못되지 않았나 확인합니다.

직접 작업한것이므로, 커밋까지 시켜줍니다.

관리자 화면에서 '상품 등록' 링크를 클릭하면 상품 등록 페이지로 이동하게 할 것입니다.

admin 폴더 하위에 goods 폴더를 생성하고, index.jsp파일을 복사/붙여넣기 한 뒤 register.jsp로 이름을 변경한 뒤 필요한 일부 코드를 수정합니다.

컨트롤러에 상품 등록용 메서드를 추가합니다.

// 상품 등록
@RequestMapping(value = "/goods/register", method = RequestMethod.GET)
public void getGoodsRegister() throws Exception {
 logger.info("get goods register");
}

aside.jsp에 상품 등록으로 이동할 수 있는 링크를 추가합니다.

'상품 등록' 링크를 클릭하면, 상품 등록 페이지로 이동할 수 있게 되었습니다.

카테고리를 사용하기 위해 com.kubg.domain에 CategoryVO.java를 추가합니다.

매퍼에 카테고리를 추가하기 위핸 쿼리문을 작성합니다.

하지만 이 쿼리를 그대로 사용하지 않고, 좀 더 구분하기 쉬운 쿼리를 사용할것 입니다.

level을 이용한 쿼리 결과 자체에 계층을 표시했습니다.

select level, cateName, cateCode, cateCodeRef from goods_category
    start with cateCodeRef is null connect by prior cateCode = cateCodeRef;

카테고리의 계층에 맞게 level값이 커지는 구조입니다. 이 쿼리를 이용하면 level값만 확인해도 상위/하위를 구분할 수 있습니다.

adminMapper.xml을 생성하고 코드를 추가합니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kubg.mappers.adminMapper">
   
<!-- 카테고리 호출 -->
<select id="category" resultType="com.kubg.domain.CategoryVO">
 select
  level, cateName, cateCode, cateCodeRef
 from goods_category
     start with cateCodeRef is null connect by prior cateCode = cateCodeRef
</select>

</mapper>

매퍼에 level이 추가되었으므로, CategoryVO에 level을 추가합니다.

매퍼의 쿼리문을 호출하는 DAO와, DAO를 호출하는 Service를 생성합니다.

테이블을 호출만 하면 되기 때문에 간단합니다.

컨트롤러 작업을 하기 전에, JSON 라이브러리를 추가합니다.

이 라이브러리는 자바 데이터를 JSON형식으로 변환해줍니다.

POM.XML의 <dependencies> ~ </dependencies> 내부에 추가합니다.

<dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.4</version>
   
    <classifier>jdk15</classifier>
</dependency>

<classifier>jdk15</classifier> 를 추가하지 않으면 에러가 발생합니다.

이제 컨트롤러에서 상품 등록 메서드의 코드를 추가합니다.

// 상품 등록
@RequestMapping(value = "/goods/register", method = RequestMethod.GET)
public void getGoodsRegister(Model model) throws Exception {
 logger.info("get goods register");
 
 List<CategoryVO> category = null;
 category = adminService.category();
 model.addAttribute("category", JSONArray.fromObject(category));
}

CategoryVO형태의 List변수 category를 선언하고 adminService.category() 호출한 뒤 결과값을 category에 입력, JSONArray를 이용해 category를 JSON타입으로 변경한 뒤, category라는 명칭으로 모델에 추가했습니다.

이 메서드(getGoodsRegister)가 호출 될 때 모델을 jsp에 넘겨서 사용할 수 있습니다.

register.jsp에 form을 추가합니다.

<form role="form" method="post" autocomplete="off">
 
 <label>1차 분류</label>
 <select class="category1">
  <option value="">전체</option>
 </select>
 
 <label>2차 분류</label>
 <select class="category2">
  <option value="">전체</option>
 </select>
 
</form>

아직 상품 등록을 하지 않고, 카테고리만 띄울것이기 때문에 셀렉트박스 <select> 만 넣었습니다.

1차 분류는 상위 카테고리, 2차 분류는 하위 카테고리입니다.
아무것도 넣지 않았는데, 자바스크립트와 제이쿼리를 이용하여 추가할것입니다.

먼저 1차 분류에 데이터를 넣어줄 코드를 작성합니다.

<script>
// 컨트롤러에서 데이터 받기
var jsonData = JSON.parse('${category}');
console.log(jsonData);

var cate1Arr = new Array();
var cate1Obj = new Object();

// 1차 분류 셀렉트 박스에 삽입할 데이터 준비
for(var i = 0; i < jsonData.length; i++) {
 
 if(jsonData[i].level == "1") {
  cate1Obj = new Object();  //초기화
  cate1Obj.cateCode = jsonData[i].cateCode;
  cate1Obj.cateName = jsonData[i].cateName;
  cate1Arr.push(cate1Obj);
 }
}

// 1차 분류 셀렉트 박스에 데이터 삽입
var cate1Select = $("select.category1")

for(var i = 0; i < cate1Arr.length; i++) {
 cate1Select.append("<option value='" + cate1Arr[i].cateCode + "'>"
      + cate1Arr[i].cateName + "</option>"); 
}

</script>

컨트롤러에서 모델에 보낸 값인 category를 jsonData에 저장합니다. 이때 JSON.parse('${category}');가 아닌 JSON.parse("${category}"); 로, 즉 쌍따옴표를 사용하게 되면 에러가 발생하니 주의합니다.

jsondata에서 level값이 1인 경우에만 cate1Obj에 추가하고, 이 추가한 데이터를 cate1Arr에 추가합니다. 이렇게 추가한 값을 cate1Select에 추가합니다.

프로젝트를 실행하여 상품 등록 페이지로 이동해보면, 1차 분류에 데이터가 들어가있는걸 확인할 수 있습니다.

2차 분류는 아직 작업하지 않았기 때문에 아무것도 없습니다.

이제 2차 분류에 데이터를 추가하기 위한 코드를 작성합니다.

$(document).on("change", "select.category1", function(){

 var cate2Arr = new Array();
 var cate2Obj = new Object();
 
 // 2차 분류 셀렉트 박스에 삽입할 데이터 준비
 for(var i = 0; i < jsonData.length; i++) {
  
  if(jsonData[i].level == "2") {
   cate2Obj = new Object();  //초기화
   cate2Obj.cateCode = jsonData[i].cateCode;
   cate2Obj.cateName = jsonData[i].cateName;
   cate2Obj.cateCodeRef = jsonData[i].cateCodeRef;
   
   cate2Arr.push(cate2Obj);
  }
 }
 
 var cate2Select = $("select.category2");

 for(var i = 0; i < cate2Arr.length; i++) {
   cate2Select.append("<option value='" + cate2Arr[i].cateCode + "'>"
        + cate2Arr[i].cateName + "</option>");
 } 
});

가장 먼저 $(document).on("change", "select.category1", function() { }); 는 select.category1 이 변경되었다면 실행되는 함수입니다.

2차 분류이기 때문에 level이 2인 조건을 넣어두었고, 2차 분류에만 있는 참조코드명인 cateCodeRef도 있습니다. 그외엔 1차 분류와 똑같습니다.

이제 2차 분류까지 나오긴 하는데...

다른 1차 분류를 선택했을 경우, 2차 분류가 중복출력되는 문제가 있습니다. 1차 분류를 다른걸로 선택했을 때, 2차 분류의 내용을 모두 지운뒤 1차 분류에 맞는 2차 분류를 추가하는 방식으로 변경해야겠습니다.

$(document).on("change", "select.category1", function(){

 var cate2Arr = new Array();
 var cate2Obj = new Object();
 
 // 2차 분류 셀렉트 박스에 삽입할 데이터 준비
 for(var i = 0; i < jsonData.length; i++) {
  
  if(jsonData[i].level == "2") {
   cate2Obj = new Object();  //초기화
   cate2Obj.cateCode = jsonData[i].cateCode;
   cate2Obj.cateName = jsonData[i].cateName;
   cate2Obj.cateCodeRef = jsonData[i].cateCodeRef;
   
   cate2Arr.push(cate2Obj);
  }
 }
 
 var cate2Select = $("select.category2");
 
 /*
 for(var i = 0; i < cate2Arr.length; i++) {
   cate2Select.append("<option value='" + cate2Arr[i].cateCode + "'>"
        + cate2Arr[i].cateName + "</option>");
 }
 */
 
 cate2Select.children().remove();

 $("option:selected", this).each(function(){
  
  var selectVal = $(this).val();  
  cate2Select.append("<option value=''>전체</option>");
  
  for(var i = 0; i < cate2Arr.length; i++) {
   if(selectVal == cate2Arr[i].cateCodeRef) {
    cate2Select.append("<option value='" + cate2Arr[i].cateCode + "'>"
         + cate2Arr[i].cateName + "</option>");
   }
  }
  
 });
 
});

기존 데이터를 추가하는 코드는 주석 /* */ 을 걸어서 실행되지 않도록 합니다.

cate2Select.children().remove(); 를 이용해 기존에 있던 데이터를 삭제해주고 $("option:selected", this).each(function(){ }); 를 실행합니다.

변수 selectVal에 현재 선택된(:selected) 1차 분류값인 $(this).val(); 를 저장하고, 셀렉트 박스에 '전체'라고 표시될 데이터를 추가합니다.

1차 분류값인 selectVal과 cate2Arr[i].cateCodeRef를 비교하여 동일할 경우, 즉 상위 카테고리에 맞는 하위 카테고리일 경우에만 데이터를 추가합니다.

이제 1차 분류를 선택하면 그에 맞는 2차 분류가 나올것 입니다.

예상대로 1차 분류에 맞는 2차 분류가 잘 출력됩니다.

1차 분류를 변경할 경우, 그에 맞는 2차 분류가 나오게됩니다. 현재 '탄'에 대한 2차 분류가 없기 때문에 아무것도 표시되지 않습니다.

게시물 수정
  1. 안녕하세요~ 또 하다가 막혀서 질문을 좀 올려봅니다..ㅠㅠㅠ
    1차 분류에서 선택을 하면 level2에 해당하는 2차 분류 항목들이 2차 리스트상자에 모두다 나오고 있습니다... 어떤게 잘못된건지 도통 모르겠습니다..
    console.log로 값을 받아봤습니다만... level2 값만 확인이 되고 1차 분류가 선택이 되었을때의 값을 확인하질 못하겠습니다..ㅠㅠ

    보고 제가 작성한 코드는 이렇습니다...
    억 javascript는 댓글에 작성이 되질 않는군요 ㅠㅠㅠ 이메일로 보내도 괜찮을런지요..ㅠㅠ

    답글삭제
    답글
    1. 안녕하세요?

      메일을 확인하고 답장을 드렸습니다.

      HTML태그를 입력하면, 말그대로 태그로 인식됩니다. HTML 태그 특수문자 변환에서 특수문자를 변환 시킨 후 입력하시면 됩니다.

      삭제
  2. 아아아...멍청하게 본문 내용을 다르게 이해 하고 질문을 드렸네요..ㅠㅠ
    이제야 확인하고 적용해서 정상 출력했습니다...
    감사합니다...!!

    답글삭제
  3. 익명3/14/2019

    안녕하세요. 블로그글 바탕으로 조금씩 바꿔가면서 하고 있는데, 데이터 흐름이 // 1차 분류 셀렉트 박스에 데이터 삽입 이후부터 들어가질 않네요. 혹시 왜 그런건지 알 수 있을까요?

    답글삭제
  4. // 1차 분류 셀렉트 박스에 데이터 삽입
    var cate1Select = $("select.category1");

    이 부분에서 막히는데 이 부분에서 select.category1의 값은 어디서 받아오는걸까요?
    저 부분에서 값을 받아오지못해 저 부분부터 읽히지가 않아 카테고리 출력이 안되는 것 같은데...

    답글삭제
    답글
    1. 안녕하세요?

      var cate1Select = $("select.category1");select.category1값을 cate1Select에 넣는다는 의미가 아니고... 해석의 차이가 있으나 $("select.category1") 대신 cate1Select를 사용하겠다는 의미입니다. 그리고 select.catrgory1은 데이터를 넣어서 사용될 요소이기 때문에 값 자체가 없는게 맞습니다.

      그러므로 cate1Select.append( [ 내용 ] ); 같은 경우 $("select.category1").apped( [ 내용 ] );으로 사용하는것과 마찬가지입니다.

      select.category1에 값을 넣는 작업은 자바스크립트의 for문을 이용하여 넣습니다.

      하지만 var cate1Select = $("select.category1");에서 에러가 발생한다면, 제이쿼리를 임포트하지 않았거나 저와 다른 클래스명을 사용하고 있는건 아닌지 확인해주세요.

      똑같은데도 에러가 발생한다면... 메일이나 댓글로 코드를 보내주시면 확인해보겠습니다.

      삭제
    2. 부트스트렙이나 jquery 사용하면서 무언가 바뀐거 같아요
      저도 한참 검색해서
      body 태그 사이에

      추가 하니 작동되더군요

      삭제
    3. 스크립트 문이 작동이 안되네요

      https://javaoop.tistory.com/30

      여기보세요 ^^

      삭제
    4. 안녕하세요? 방문해주셔서 감사합니다.

      제이쿼리를 임포트하지 않았다는건 생각못했네요;
      제이쿼리같은 라이브러리는 body보다는 head내부에 넣는것을 권장드립니다.

      삭제
  5. 작성자가 댓글을 삭제했습니다.

    답글삭제
  6. 안녕하세요 소중한 글 잘 읽으면서 공부하고 있습니다. 제가 이번에 카테고리분류를 만들면서 카테고리를 선택하지 않으면 글을 작성하지 못하게 자바스크립트로 막으려고 하고 있는데요.
    cate2Select.append("<option id = cateCode2 value='" + selectVal + "'>전체</option>");

    for(var i = 0; i < cate2Arr.length; i++) {
    if(selectVal == cate2Arr[i].cateCodeRef) {
    cate2Select.append("<option id = cateCode2 value='" + cate2Arr[i].cateCode + "'>"
    + cate2Arr[i].cateName + "</option>");
    이런식으로 옵션에 id 를 지정해주고
    var cateCode = $("#cateCode2").val();
    if(cateCode != '' || cateCode == 100 || cateCode == 200 || cateCode == 300){
    alert('분류를 선택해 주세요');
    return false;
    }
    온서브밋으로 자바스크립트로 보내서 막으려고 하는데요. 이게 카테고리2를 선택을 해도 막아버리네요. 좋은 방법없을까요?

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      말씀하시는게 '카테고리를 선택하지 않으면 게시물 작성을 막는다'이지요?
      그렇다면 조건은 1차와 2차 분류(카테고리)에 값(value)가 있어야한다가 되겠네요.

      이 작업을 하기에 앞서서, 2차 분류가 없는 1차 분류.. 게시물을 기준으로 하면 '탄' 또는 '방어구'를 선택하였을 때, 2차 분류에 1차 분류 값을 전달해주는 코드가 있어야합니다.

      2차 분류가 없다면, 선택한 1차 분류의 값(value)를 2차 분류의 '전체'에 넣는다 가 되는것인데.. 게시물을 기준으로 하면 cate2Select.append("<option value=''>전체</option>");를 조건문으로 감싸서 cate2Arr 의 길이가 0 이라면, '전체'에 1차 분류의 값(value)를 넣어주는 방식으로 하시면 됩니다.

      그후 스크립트의 가장 아랫부분, 즉 카테고리 선택에 관련된 스크립트가 모두 끝난 이후에 2차 분류의 값(value)이 없다면 특정 동작(에디터나 완료 버튼을 비활성화하는 등)을 실행하는 코드를 넣으면 되겠습니다.

      혹시 진행이 잘 안되신다면 다시 답글 남겨주세요.

      삭제
    2. 와 답변해주셔서 정말로 감사합니다. '카테고리를 선택하지 않으면 게시물 작성을 막는다'가 제가 하고 싶은게 정확히 맞습니다. 그런데 저는 모든 카테고리를 무조건 2차분류까지 있게 만들었습니다. 1차분류로만 분류되는 품목은 없구요. 그래서 무조건 2차분류를 선택하지 않으면 게시물 작성을 막고 싶은데요. 일단 제가 작성한 코드는
      <script>
      $(document).ready(function(){

      // 컨트롤러에서 데이터 받기
      var jsonData = JSON.parse('${category}');
      console.log(jsonData);

      var cate1Arr = new Array();
      var cate1Obj = new Object();

      // 1차 분류 셀렉트 박스에 삽입할 데이터 준비
      for(var i = 0; i < jsonData.length; i++) {

      if(jsonData[i].level == "1") {
      cate1Obj = new Object(); //초기화
      cate1Obj.cateCode = jsonData[i].cateCode;
      cate1Obj.cateName = jsonData[i].cateName;
      cate1Arr.push(cate1Obj);
      }
      }

      // 1차 분류 셀렉트 박스에 데이터 삽입
      var cate1Select = $("select.category1")

      for(var i = 0; i < cate1Arr.length; i++) {
      cate1Select.append("<option value='" + cate1Arr[i].cateCode + "'>"
      + cate1Arr[i].cateName + "</option>");
      }


      $(document).on("change", "select.category1", function(){

      var cate2Arr = new Array();
      var cate2Obj = new Object();

      // 2차 분류 셀렉트 박스에 삽입할 데이터 준비
      for(var i = 0; i < jsonData.length; i++) {

      if(jsonData[i].level == "2") {
      cate2Obj = new Object(); //초기화
      cate2Obj.cateCode = jsonData[i].cateCode;
      cate2Obj.cateName = jsonData[i].cateName;
      cate2Obj.cateCodeRef = jsonData[i].cateCodeRef;

      cate2Arr.push(cate2Obj);
      }
      }

      var cate2Select = $("select.category2");


      cate2Select.children().remove();

      $("option:selected", this).each(function(){

      var selectVal = $(this).val();



      cate2Select.append("<option id = cateCode2 value='" + selectVal + "'>전체</option>");

      for(var i = 0; i < cate2Arr.length; i++) {
      if(selectVal == cate2Arr[i].cateCodeRef) {
      cate2Select.append("<option id = cateCode2 value='" + cate2Arr[i].cateCode + "'>"
      + cate2Arr[i].cateName + "</option>");
      }
      }
      });

      });

      })


      function check_submit(){
      var title = $('#gdsName').val();
      var price = $('#gdsPrice').val();
      var stock = $("#gdsStock").val();
      var des = $("#gdsDes").val();
      var img = $("#gdsImg").val();
      var idx = img.lastIndexOf(".")+1;
      var extension = (img.substr(idx, img.length)).toLowerCase();

      var cateCode = $("#cateCode2").val();


      if(title==''){
      alert('상품명을 입력해 주세욧!');
      return false;
      }

      if(price == ''){
      alert('가격을 입력해 주세요!');
      return false;
      }

      if(stock == ''){
      alert('수량을 입력해 주세요!');
      return false;
      }

      if(des == ''){
      alert('내용을 입력해 주세요!');
      return false;
      }

      if(img == ''){
      alert('이미지 파일을 올려주세요!');
      return false;
      }

      if(extension == "jpg" || extension == "png"){

      }
      else{
      alert('jpg 파일이나 png파일만 올려주세요!');
      return false;
      }

      /* if(cateCode != '' || cateCode == 100 || cateCode == 200 || cateCode == 300){
      alert('분류를 선택해 주세요');
      return false;
      } */

      if(cate2Arr.length==0){
      alert('분류를 선택해 주세요');
      return false;
      }


      return true;
      }
      </script>
      이렇게 했는데 2차분류를 선택하지 않아도 글이 작성됩니다 ㅠㅠ 어떻게 하면 좋을까요. 답변해주시면 정말로 감사하겠습니다.

      삭제
    3. 분류를 선택하지 않았을 경우(즉 값이 없을 경우) 입력 자체를 막아버리는 간단하지만 조잡한(?) 예시입니다.(다운로드)
      다운로드 받아서 실행해보시면 느낌(?)이 올겁니다.

      HTML코드를 안봤지만, input태그에 required속성도 사용하시기 바랍니다.

      삭제
    4. 답변해주셔서 정말 감사합니다. 그런데 1차분류를 선택하지 않으면 2차분류에도 값이 들어가지 않아서 글 작성이 막아지지만, 1차분류는 선택하고 2차분류를 선택하지 않으면 글이 작성됩니다. 1차분류를 선택하는 순간 2차분류에 값이 넘어가서 그러는 것 같습니다. 이 문제는 해결할 수 있을까요??

      삭제
    5. 확인이 좀 늦었네요.

      제가 답글로 달아둔 파일처럼 하시면 말씀하신 부분도 해결될것 같은데, 다른 구조로 되어있는것 같네요.

      기본적으로
      1차 분류를 선택하지 않은 상태라면 2차 분류는 비활성화(disabled)
      2차 분류를 선택하지 않은 상태라면 글쓰기 비활성화
      이런식으로 구성해주시고

      1차 분류를 선택했을 때, 2차 분류에 option 넣어주는 과정에서 글쓰기를 비활성화 해주시면 됩니다.

      삭제
  7. 작성자가 댓글을 삭제했습니다.

    답글삭제
  8. 안녕하세요

    객체를 json으로 변환해주는역할을 gson이나 등등 비슷한 라이브러리들로 처리 가능한가요??
    기존 jackson-bind로는 바꾸는 방법이 없는거겠죠?

    답글삭제
    답글
    1. 안녕하세요?

      최종적으로 JSON형으로 만든다면 상관없습니다.
      특별한 경우가 없는 이상... 어떤 라이브러리든 직접 제작하시던 무관하게 진행하실 수 있습니다.

      삭제
  9. 안녕하세요 혹시 질문하나만 해도될까요?? 이미지 상세페에지 같이 여러개를 나올수 있게 가능한가요?

    답글삭제
    답글
    1. 안녕하세요? 상품 정보를 여러개 같이 나오게 하신다는 말씀같은데, 물론 가능합니다.
      다만 그렇게 될 경우 스크롤이 길어지고, 스크롤이 길어지면 가독성에 영향이 있기 때문에... 도중까지만 나오게하고 한번 클릭하면 상세 페이지로 이동하도록하는걸 추천드립니다.

      삭제
  10. 5개정도만 나오게 하고싶은데
    어떻게 하는지 잘모르겠어요ㅠㅠ

    답글삭제
  11. 안녕하세요. 항상 잘 보고 공부하고있어요
    다름이 아니라 제가 db를 mysql로 하다보니 goods테이블 생성시 시퀀스를 따로 안만들고
    auto_increment를 사용하고있어서 전까지는 별문제가 없었는데
    이번에 select level, cateName, cateCode, cateCodeRef from goods_category
        start with cateCodeRef is null connect by prior cateCode = cateCodeRef;
    여기에서 문제가 생기네요 ㅠ 이것을 혹시 mysql로 어찌 바꾸면 좋을까요??

    답글삭제
  12. 안녕하세요? 방문해주셔서 감사합니다.
    tbl_goods 테이블에서 시퀀스를 사용하는 컬럼은 gdsNum이며, mySql 또는 MariaDB에서는 오라클에서 하던것과 마찬가지로 gdsNum 컬럼에 auto_increment를 사용하시면 됩니다.

    말씀하신 카테고리 테이블인 goods_category에서는 시퀀스를 사용하지 않기 때문에 auto_increment와 관련된 에러는 발생하지 않습니다.

    에러가 발생한다면, 에러 메시지를 보고 조치해야할 것 같네요.

    답글삭제
  13. mysql로db를 쓰고있습니다.
    select level, cateName, cateCode, cateCodeRef from goods_category
        start with cateCodeRef is null connect by prior cateCode = cateCodeRef;

    Syntax error : 'with' (with) is not valid input at this position이라는 오류가 나오는데 문법좀 알려주실수 있을까요?

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      mysql인 경우 start with 기능을 사용할 수 없습니다.
      mysql을 이용해 본문에 있는 쿼리결과와 동일하게 출력하기 위해서는, 레벨당 조건을 이용하고 union을 이용해 합치면 되겠습니다.

      WITH TBL AS(
      SELECT '무기' AS CATENAME , 100 AS CATECODE, NULL AS CATECODEREF FROM dual UNION ALL
      SELECT '돌격소총' AS CATENAME , 101 AS CATECODE, 100 AS CATECODEREF FROM dual UNION ALL
      SELECT '기관단총' AS CATENAME , 102 AS CATECODE, 100 AS CATECODEREF FROM dual UNION ALL
      SELECT '경기관총' AS CATENAME , 103 AS CATECODE, 100 AS CATECODEREF FROM dual UNION ALL
      SELECT '산탄총' AS CATENAME , 104 AS CATECODE, 100 AS CATECODEREF FROM dual UNION ALL
      SELECT '지정사수소총' AS CATENAME , 105 AS CATECODE, 100 AS CATECODEREF FROM dual UNION ALL
      SELECT '저격소총' AS CATENAME , 106 AS CATECODE, 100 AS CATECODEREF FROM dual UNION ALL
      SELECT '기타' AS CATENAME , 107 AS CATECODE, 100 AS CATECODEREF FROM dual UNION ALL
      SELECT '탄' AS CATENAME , 200 AS CATECODE, NULL AS CATECODEREF FROM dual UNION ALL
      SELECT '방어구' AS CATENAME , 300 AS CATECODE, NULL AS CATECODEREF FROM dual UNION ALL
      SELECT '회복제' AS CATENAME , 400 AS CATECODE, NULL AS CATECODEREF FROM dual
      )
      SELECT 1 AS LEVEL, CATENAME, CATECODE, CATECODEREF FROM TBL WHERE CATECODEREF is NULL
      UNION ALL
      SELECT 2 AS LEVEL, CATENAME, CATECODE, CATECODEREF FROM TBL WHERE CATECODEREF IS NOT null
      ORDER BY CATECODE

      삭제
    2. 작성자가 댓글을 삭제했습니다.

      삭제
  14. 익명9/24/2020

    1차만 넣어서 js파일에서 할려고 하는데 어떻게 해야 좋을까요

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      카테고리를 1차만 사용하실 경우엔... 2차 카테고리 부분을 아예 작성하지 않으시면 됩니다.

      삭제
  15. select level, cateName, cateCode, cateCodeRef from goods_category
    start with cateCodeRef is null connect by prior cateCode = cateCodeRef;

    mysql server 5.7버전인데 mysql 쿼리로 바꾸면 어떻게 되는지요..
    mysql로 바꿔서 해보고 있는데 여기서 딱 막히네요..ㅠ

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      mysql에는 오라클의 star with~기능을 제공하지 않아서 '직접'구현해야합니다.
      저는 db를 가져와서 로직상에서 해결하는 방법을 사용하기 때문에 링크로 대체하겠습니다ㅠㅠ

      MySQL에서 계층형 쿼리 문제와 그 외...
      Hierarchical queries in MySQL

      삭제
  16. MariaDB의 경우 계층형 쿼리를 사용하시려면 아래를 참고하시면 됩니다.

    WITH recursive cte AS (
    SELECT cateName, cateCode, cateCodeRef, 1 AS level
    FROM goods_category
    WHERE cateCodeRef IS null
    UNION all
    SELECT p.cateName, p.cateCode, p.cateCodeRef, 1 + LEVEL AS level
    FROM goods_category p
    INNER JOIN cte
    ON p.cateCodeRef = cte.cateCode
    )
    SELECT cateName, cateCode, cateCodeRef, LEVEL FROM cte;

    답글삭제
    답글
    1. 앗..늦었지만 감사드립니다(__)

      삭제
    2. MySQL 5.7버전에서는 WITH 사용 불가로 MySQL 8.0.28 최신버전으로 바꾸니 위에 쿼리 작동합니다.

      삭제
  17. 꾸꾸2/18/2021

    안녕하세요 항상 좋의 포스팅 감사합니다.

    읽어보던중 궁금한 점이 있었어 질문 드립니다.

    상품 등록 Controller에서 페이지를 로딩할 때 Model에 JSONArray를 이용해 category를 JSON타입으로 변경한 뒤 담아서 보냈습니다.

    그 이후 register.jsp 스크립트에서
    var jsonData = JSON.parse('${category}');
    를 통해서 다시 JSON으로 파싱을 하는데 제가 봤을때 Json 형식으로 파싱을 2번 하는것으로 보이는데 그렇게 하신 이유가 있을까요.

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      특별한 이유 없이 그냥 끄적이다가 두번 들어간걸로 추측됩니다(....)

      ㅠㅠ..

      삭제
  18. 안녕하세요! kuzuro님 글보면서 공부하고 있는 1인 입니다. 몇일 고민하다 여쭤봅니다ㅠ..

    head에 스크립트도 잘 들어있고..
    콘솔 로그에는 아래와 같이 잘 나오는데..
    (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    0: {cateCode: "100", cateCodeRef: "", cateName: "상의", level: 1}
    1: {cateCode: "101", cateCodeRef: "100", cateName: "티셔츠", level: 2}
    2: {cateCode: "102", cateCodeRef: "100", cateName: "남방", level: 2}
    3: {cateCode: "103", cateCodeRef: "100", cateName: "맨투맨", level: 2}
    4: {cateCode: "104", cateCodeRef: "100", cateName: "후드", level: 2}
    5: {cateCode: "200", cateCodeRef: "", cateName: "치마", level: 1}
    6: {cateCode: "300", cateCodeRef: "", cateName: "바지", level: 1}
    7: {cateCode: "400", cateCodeRef: "", cateName: "악세사리", level: 1}
    length: 8
    __proto__: Array(0)
    1
    register:71 {cateCode: "100", cateName: "상의"}
    1
    register:71 {cateCode: "200", cateName: "치마"}
    1
    register:71 {cateCode: "300", cateName: "바지"}
    1
    register:71 {cateCode: "400", cateName: "악세사리"}


    옵션을 선택하면 전체만 나오고 카테고리가 안나오는데... 어느 부분이 잘못된건지 알 수 가 없어서요ㅠㅠ...
    혹시나 조언 구할 수 있을까 싶어 글 남깁니다..

    답글삭제
    답글
    1. 앗 스크립트를 Body 상단쪽에서 Body 하단쪽으로 옮기니 나오네요ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ............

      삭제
    2. 익명6/19/2022

      와... 덕분에 저도 해결 잘했습니다.

      삭제
  19. 익명5/03/2021

    안녕하세요
    Controller에서 Service.cateogry()를 할 때 category()를 static으로 선언하라고 하는데 이유가 뭘까요? 코드는 똑같이 한거 같은데 잘 모르겠네요 ㅠㅠ AdiminService에서는 public List category() throws Exception;이렇게 선언한게 다 입니다.

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      아마도... 메서드는 퍼블릭이지만 클래스가 스태틱이 아니었나 추측을 해봅니다..
      ..라고 적으려고 했는데 명확한건 좀 더 찾아봐야겠네요

      삭제
  20. 안녕하세요

    관리자화면이 원래는 떳엇는데 진행도중 갑자기 404에러뜨는데 원인을 알수있을까요

    WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/admin/index] in DispatcherServlet with name 'appServlet'

    이렇게나옵니다

    답글삭제
    답글
    1. 엇 해결했습니다

      삭제
  21. 익명1/17/2023

    셀렉트 박스에 "전체"만 나오는 문제. 이거는 코드 복붙 잘못해서 { ( ; 이런거 짝이 안맞아서 생긴 문제였네요. jsp에 스크립트 오류있게 써도 실행이 잘만 돼가지고 조금 헤맸습니다.

    답글삭제