10/12/2018

스프링 쇼핑몰 만들기 #15. 상품 목록 구현

스프링 쇼핑몰 만들기
 - 깃허브 링크

#1~14까지는 주로 관리자 모드 중시였습니다. 아직 관리자 기능이 다 끝난건 아니지만, 사용자 기능과 맞물려있는 기능 위주라서 사용자 기능을 먼저 구현하겠습니다.

src/main/webapp/WEB-INF/views/include에 aside.jsp를 만들고 코드를 추가합니다.

그리고 home.jsp의 <head> 내부에 스타일(CSS)를 추가합니다.

<style>

body { margin:0; padding:0; font-family:'맑은 고딕', verdana; }
a { color:#05f; text-decoration:none; }
a:hover { text-decoration:underline; }

h1, h2, h3, h4, h5, h6 { margin:0; padding:0; }
ul, lo, li { margin:0; padding:0; list-style:none; }

/* ---------- */

div#root { width:900px; margin:0 auto; }
header#header { }
nav#nav { }
section#container { }
section#content { float:right; width:700px; }
aside#aside { float:left; width:180px; }
section#container::after { content:""; display:block; clear:both; }
footer#footer { background:#eee; padding:20px; }

/* ---------- */

header#header div#header_box { text-align:center; padding:30px 0; }
header#header div#header_box h1 { font-size:50px; }
header#header div#header_box h1 a { color:#000; }

nav#nav div#nav_box { font-size:14px; padding:10px; text-align:right; }
nav#nav div#nav_box li { display:inline-block; margin:0 10px; }
nav#nav div#nav_box li a { color:#333; }

section#container { }

aside#aside h3 { font-size:22px; margin-bottom:20px; text-align:center; }
aside#aside li { font-size:16px; text-align:center; }
aside#aside li a { color:#000; display:block; padding:10px 0; }
aside#aside li a:hover { text-decoration:none; background:#eee; }

footer#footer { margin-top:100px; border-radius:50px 50px 0 0; }
footer#footer div#footer_box { padding:0 20px; }

</style>

아직도 투박하지만, 모습보단 낫습니다.

사이드바(aside)의 메뉴에 마우스를 가져가면 배경색이 변경되어 현재 선택하고있는 메뉴를 알 수 있습니다.

탄약, 방어구, 회복제 카테고리는 1차 분류로 끝나지만, 무기 카테고리는 2차 분류가 있습니다.

aside.jsp에 무기 카테고리 <li> ~ </li> 안에 코드를 추가합니다.

주의할점은, 무기 카테고리의 링크 태그 <a> 밖에 추가해야하고, <li> ~ </li> 안에 있어야한다는 점 입니다.

그리고 스타일도 추가합니다.

aside#aside li { position:relative; }
aside#aside li:hover { background:#eee; }
aside#aside li > ul.low { display:none; position:absolute; top:0; left:180px;  }
aside#aside li:hover > ul.low { display:block; }
aside#aside li:hover > ul.low li a { background:#eee; border:1px solid #eee; }
aside#aside li:hover > ul.low li a:hover { background:#fff;}
aside#aside li > ul.low li { width:180px; }

드롭다운 메뉴 만들기를 사용했습니다.

무기 카테고리에 마우스를 가져가면 2차 분류가 옆에 생기고, 마우스를 다른곳으로 옮기면 사라집니다.

각 카테고리를 구분하는 cateCode를 참고하여 링크 주소를 작성합니다.

/shop/list 는 기본 경로이며, 뒤의 ?c=[번호1]&l=[번호2] 는 구분자입니다.

번호1은 cateCode와 같은 숫자를, 번호2는 카테고리의 레벨을 넣었습니다. 카테고리의 레벨이 높을수록(숫자가 클수록) 하위 카테고리입니다.

각 카테고리를 출력하는 쿼리를 작성하고, 테스트해봅니다.

shopMapper.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.shopMapper">
   

<select id="list" resultType="com.kubg.domain.GoodsViewVO">
select
    g.gdsNum, g.gdsName, g.cateCode, c.cateCodeRef, c.cateName, gdsPrice, gdsStock, gdsDes, gdsImg, gdsDate, g.gdsImg, g.gdsThumbImg
        from tbl_goods g
            inner join goods_category c
                on g.cateCode = c.cateCode           
            where g.cateCode = #{cateCode}
</select>

</mapper>

카테고리 이름(catenName)까지 표시할 수 있어야하기 때문에, 2개의 테이블을 조인했습니다.

DAO와 Service를 추가합니다.

ShopController.java를 생성하고 코드를 추가합니다.

package com.kubg.controller;

import java.util.List;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.kubg.domain.GoodsViewVO;
import com.kubg.service.ShopService;

@Controller
@RequestMapping("/shop/*")
public class ShopController {

private static final Logger logger = LoggerFactory.getLogger(ShopController.class);

@Inject
ShopService service;

// 카테고리별 상품 리스트
@RequestMapping(value = "/list", method = RequestMethod.GET)
public void getList(@RequestParam("c") int cateCode,
@RequestParam("l") int level, Model model) throws Exception {
logger.info("get llist");

List<GoodsViewVO> list = null;
list = service.list(cateCode);

model.addAttribute("list", list);

}
}

@RequestParam을 이용하여 URL에 있는 값을 가져와 변수에 저장하여 사용합니다.

shop 폴더를 생성한 뒤, home.jsp를 복사/붙여넣기 한 뒤 이름을 list.jsp로 변경합니다. 그리고 가장 위에 태그 라이브러리를 추가합니다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

결과가 출력될 영역에 코드를 추가합니다.

<ul>
<c:forEach items="${list}" var="list">
<li>
<div class="goodsThumb">
<img src="${list.gdsThumbImg}">
</div>
<div class="goodsName">
<a href="/shop/view?n=${list.gdsNum}">${list.gdsName}</a>
</div>
</li>
</c:forEach>
</ul>

관리자 모드에서는 테이블 태그를 이용했지만, 이번엔 목록 태그를 사용했습니다.

무기 카테고리를 클릭해봤는데.. 무기 카테고리의 하위 카테고리가 나오질 않네요.

당연하지만, 이것에 대한 처리를 하지 않았기 때문입니다.

무기 카테고리는 1차 분류와 2차 분류가 동시에 있기 때문에, 처음 사용한 쿼리만으로는 모두 출력할 수 없습니다. 예를들어 위 화면의 '돌격 소총' 카테고리 코드(cateCode)는 101번이며, 무기 카테고리의 하위 이므로 카테고리 참조 코드(cateCodeRef)는 100입니다.

그러므로 조건을 '카테고리 코드(cateCode)가 100이거나 카테고리 참조 코드(cateCodeRef)가 100이면'으로 바꾸게되면 모두 출력할 수 있습니다.

기존에 있던 쿼리의 아이디(ID)를 수정하고, 새로운 쿼리를 추가합니다.

<!-- 카테고리별 상품 리스트 : 1차 분류 -->
<select id="list_1" resultType="com.kubg.domain.GoodsViewVO">
select
    g.gdsNum, g.gdsName, g.cateCode, c.cateCodeRef, c.cateName,
    gdsPrice, gdsStock, gdsDes, gdsDate, g.gdsImg, g.gdsThumbImg
        from tbl_goods g
            inner join goods_category c
                on g.cateCode = c.cateCode           
            where g.cateCode = #{cateCode}
            or c.cateCodeRef = #{cateCodeRef}
</select>

<!-- 카테고리별 상품 리스트 : 2차 분류 -->
<select id="list_2" resultType="com.kubg.domain.GoodsViewVO">
select
    g.gdsNum, g.gdsName, g.cateCode, c.cateCodeRef, c.cateName,
    gdsPrice, gdsStock, gdsDes, gdsDate, g.gdsImg, g.gdsThumbImg
        from tbl_goods g
            inner join goods_category c
                on g.cateCode = c.cateCode           
            where g.cateCode = #{cateCode}
</select>

list_1은 1차 분류용, list_2는 2차 분류용입니다.

둘다 거의 같은 코드지만, 1차 분류에 or c.cateCodeRef = #{cateCodeRef} 하나가 추가된거라서.. 불필요한 코드가 많으니 나중에 통합시킬 필요가 있겠습니다.

DAO를 1차 분류용과 2차 분류용으로 구분합니다.

// 카테고리별 상품 리스트 : 1차 분류
@Override
public List<GoodsViewVO> list(int cateCode, int cateCodeRef) throws Exception {

HashMap<String, Object> map = new HashMap<String, Object>();

map.put("cateCode", cateCode);
map.put("cateCodeRef", cateCodeRef);

return sql.selectList(namespace + ".list_1", map);
}

// 카테고리별 상품 리스트 : 2차 분류
@Override
public List<GoodsViewVO> list(int cateCode) throws Exception {

return sql.selectList(namespace + ".list_2", cateCode);
}

2차 분류는 기존 코드와 차이점이 없습니다.

1차 분류는 카테고리 코드(cateCode)와 카테고리 참조 코드(cateCodeRef) 2개의 매개변수를 받아온뒤, 해시맵을 이용해 두 값을 하나의 맵으로 합쳐서 매퍼로 보냈습니다.

1차 분류와 2차 분류의 메서드명이 list로 똑같은데, 이 둘은 매개변수가 다르기 때문에 오버로딩할 수 있으니 에러는 발생하지 않습니다.

Service도 수정합니다.

// 카테고리별 상품 리스트
@Override
public List<GoodsViewVO> list(int cateCode, int level) throws Exception {

int cateCodeRef = 0;  // 카테고리 참조 코드. 없어도 무관

if(level == 1) {  // lavel 1 = 1차 분류.

cateCodeRef = cateCode;
return dao.list(cateCode, cateCodeRef);
// 두가지 모두 cateCode로 해도 무관

} else {  // lavel 2 = 2차 분류

return dao.list(cateCode);

}

}

DAO는 2개지만, Service는 매개변수인 level의 값을 이용해 조건문으로 분기시키면 되니 1개면 됩니다.

프로젝트를 실행하면 제대로 출력은 되는데, 모양이 영 아니네요.

<head> 태그 내부에 새로운 스타일을 추가합니다.

<style>
section#content ul li { display:inline-block; margin:10px; }
section#content div.goodsThumb img { width:200px; height:200px; }
section#content div.goodsName { padding:10px 0; text-align:center; }
section#content div.goodsName a { color:#000; }
</style>

원래 있던 스타일은 거의 공통적으로 사용되겠지만, 새로 추가한 스타일은 목록에서만 사용되기 때문에, 나중에 파일로 분류하기 용이하도록 구분해둡니다.

무기 카테고리를 클릭하면, 무기 카테고리의 하위 카테고리도 같이 나옵니다.

무기 카테고리의 하위 카테고리를 선택하면, 선택한 카테고리만 나오게됩니다.

게시물 수정
  1. 죄송하지만 깃허브에 소스좀 보여주시면안될까요?

    답글삭제
    답글
    1. 안녕하세요?
      깃허브 주소는 가장 위에 있습니다.
      그런데.. 이 게시물 시리즈(?)는 제가 도중에 날림으로 끝내서 전체적으로 완성도가 떨어집니다ㅠㅠ;

      삭제
    2. 상품쪽으로는 거의 다없는거 같던데ㅠㅠ

      삭제
    3. 깃허브에 있는 코드는, 삭제 메서드를 제외하고 동일한 코드입니다.
      찾으시는 코드가 정확히 어느 부분인가요?

      삭제
  2. 궁금한게 컨트롤러에서 list=service.list(caateCode);
    이부분이요
    list는 파라미터가 두개 아닌가요? cateCode와 level둘다 담아야하는거같은데

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

      list는 이런 형태이므로 파라미터(매개변수)는 1개만 사용합니다.

      삭제
    2. 저는 1개만 쓸 경우에 에러가 나서 level을 추가해주었습니다.
      list = shopService.list(catecode, level);

      삭제
  3. 만드는데 문제가 있어서 메일로 보냈습니다.

    답글삭제
  4. 구현하는데 오류가 좀 있어서 메일로 문의드렸어요. 답변부탁드릴게요 감사합니다!

    답글삭제