일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 한국건설관리시스템
- 생성자오버로드
- cursor문
- exception
- 인터페이스
- 대덕인재개발원
- 컬렉션 타입
- 집합_SET
- 메소드오버로딩
- 컬렉션프레임워크
- 참조형변수
- 자바
- 자동차수리시스템
- 다형성
- 정수형타입
- 사용자예외클래스생성
- 추상메서드
- 제네릭
- 예외미루기
- Java
- 객체 비교
- EnhancedFor
- 어윈 사용법
- NestedFor
- abstract
- 예외처리
- oracle
- 오라클
- 환경설정
- GRANT VIEW
- Today
- Total
거니의 velog
(3) 도서 쇼핑몰 만들기 3 본문
4. 상품 상세 구현하기
* 이번에는 상품 상세 페이지와 상품 조회 기능을 구현해 보자.
1. 매퍼 파일인 goods.xml에 상품 상세 페이지에 표시한 상품 정보와 상품 메인 이미지 및 상세 이미지 정보를 조회하는 SQL문을 추가한다.
<!-- 상품 정보와 상품 메인 이미지 파일 정보를 조회한다. -->
<select id="selectGoodsDetail" resultMap="goodsResult" parameterType="String" >
<![CDATA[
select g.*,d.fileName from t_shopping_goods g, t_goods_detail_image d
where g.goods_id=d.goods_id
and d.filetype='main_image'
and g.goods_id=#{goods_id}
order by g.goods_id
]]>
</select>
<!-- 상품 상세 이미지 파일 정보를 조회한다. -->
<select id="selectGoodsDetailImage" resultMap="imageResult" parameterType="String" >
<![CDATA[
select * from t_goods_detail_image
where fileType!='main_image'
and goods_id=#{goods_id}
]]>
</select>
2. 상품 관련 기능을 구현한 자바 클래스를 준비한다.
* 상품 상세 이미지를 표시하면서 빠른 메뉴(퀵 메뉴라고도 함)에 최근 본 상품을 추가하여 표시한다. 빠른 메뉴에는 최대 네 개 까지의 상품을 저장할 수 있다.
* 다음은 빠른 메뉴에 최근 본 상품을 추가하고 표시하는 과정이다.
(1) 세션에 저장된 최근 본 상품을 추가하고 표시하는 과정이다.
(2) 상품 목록에 저장된 상품 개수가 네 개 미만이고 방금 본 상품이 상품 목록에 있는지 체크한다.
(3) 없으면 상품 목록에 추가한다.
(4) 다시 상품 목록을 세션에 추가한다.
(5) 화면에 상품을 표시하는 quickMenu.jsp에서는 세션의 최근 본 상품 목록을 가져와 차례대로 표시한다.
3. 이 과정을 GoodsControllerImpl 클래스에 다음과 같이 구현한다.
package com.bookshop01.goods.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
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.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.bookshop01.common.base.BaseController;
import com.bookshop01.goods.service.GoodsService;
import com.bookshop01.goods.vo.GoodsVO;
import net.sf.json.JSONObject;
@Controller("goodsController")
@RequestMapping(value = "/goods")
public class GoodsControllerImpl extends BaseController implements GoodsController {
@Autowired
private GoodsService goodsService;
@RequestMapping(value = "/goodsDetail.do", method = RequestMethod.GET)
public ModelAndView goodsDetail(@RequestParam("goods_id") String goods_id, // 조회할 상품 번호를 전달받는다.
HttpServletRequest request,
HttpServletResponse response) throws Exception {
String viewName = (String) request.getAttribute("viewName");
HttpSession session = request.getSession();
Map goodsMap = goodsService.goodsDetail(goods_id); // 상품 정보를 조회한 후 Map으로 반환한다.
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("goodsMap", goodsMap);
GoodsVO goodsVO = (GoodsVO) goodsMap.get("goodsVO");
addGoodsInQuick(goods_id, goodsVO, session); // 조회한 상품 정보를 빠른 메뉴에 표시하기 위해 전달한다.
return mav;
}
...
private void addGoodsInQuick(String goods_id, GoodsVO goodsVO, HttpSession session) {
boolean already_existed = false;
List<GoodsVO> quickGoodsList; // 최근 본 상품 저장 ArrayList
quickGoodsList = (ArrayList<GoodsVO>) session.getAttribute("quickGoodsList"); // 세션에 저장된 최근 본 상품 목록을 가져온다.
if (quickGoodsList != null) { // 최근 본 상품이 있는 경우
if (quickGoodsList.size() < 4) { // 미리본 상품 리스트에 상품개수가 네 개 이하인 경우
for (int i = 0; i < quickGoodsList.size(); i++) {
GoodsVO _goodsBean = (GoodsVO) quickGoodsList.get(i);
if (goods_id.equals(_goodsBean.getGoods_id())) {
already_existed = true;
break;
}
} // 상품 목록을 가져와 이미 존재하는 상품인지 비교한다.
if (already_existed == false) {
quickGoodsList.add(goodsVO);
} // already_existed가 false이면 상품 정보를 목록에 저장한다.
}
} else {
quickGoodsList = new ArrayList<GoodsVO>();
quickGoodsList.add(goodsVO);
}
session.setAttribute("quickGoodsList", quickGoodsList); // 최근 본 상품 목록을 세션에 저장한다.
session.setAttribute("quickGoodsListNum", quickGoodsList.size()); // 최근 본 상품 목록에 저장된 상품 개수를 세션에 저장한다.
}
}
4. GoodsServiceImpl 클래스를 다음과 같이 작성한다.
package com.bookshop01.goods.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.bookshop01.goods.dao.GoodsDAO;
import com.bookshop01.goods.vo.GoodsVO;
import com.bookshop01.goods.vo.ImageFileVO;
@Service("goodsService")
@Transactional(propagation = Propagation.REQUIRED)
public class GoodsServiceImpl implements GoodsService {
@Autowired
private GoodsDAO goodsDAO;
...
public Map goodsDetail(String _goods_id) throws Exception {
Map goodsMap = new HashMap();
GoodsVO goodsVO = goodsDAO.selectGoodsDetail(_goods_id);
goodsMap.put("goodsVO", goodsVO);
List<ImageFileVO> imageList = goodsDAO.selectGoodsDetailImage(_goods_id);
goodsMap.put("imageList", imageList); // 상품 정보와 이미지 정보를 조회한 후 HashMap에 저장한다.
return goodsMap;
}
5. 상품 상세를 화면에 표시할 JSP 파일을 준비한다.
6. goodsDetail.jsp에서는 상품 상세 페이지를 보여주는 페이지로 테이블에서 조회한 상품 정보를 표시한다. 상품 목차 등에 개행문자(\r\n)가 포함되어 있으면 웹 페이지에서는 이를 <br /> 태그로 사용해 바꿔줘야 한다. 즉, 테이블의 상품 목차를 웹 페이지에 표시하면서 개행 기능을 유지하려면 개행문자 (\r\n) 를 <br/> 태그로 대체해서 표시해야 상품 목차를 등록할 때의 형태로 웹 페이지에 표시된다.
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8" isELIgnored="false"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<c:set var="goods" value="${goodsMap.goodsVO}" />
<c:set var="imageList" value="${goodsMap.imageList }" />
<%
//치환 변수 선언합니다.
pageContext.setAttribute("crcn", "\r\n"); //개행문자
pageContext.setAttribute("crcn" , "\n"); //Ajax로 변경 시 개행 문자
pageContext.setAttribute("br", "<br/>"); //br 태그
%>
<html>
<head>
<style>
#layer {
z-index: 2;
position: absolute;
top: 0px;
left: 0px;
width: 100%;
}
#popup {
z-index: 3;
position: fixed;
text-align: center;
left: 50%;
top: 45%;
width: 300px;
height: 200px;
background-color: #ccffff;
border: 3px solid #87cb42;
}
#close {
z-index: 4;
float: right;
}
</style>
<script type="text/javascript">
function add_cart(goods_id) {
$.ajax({
type: "post",
async: false, //false인 경우 동기식으로 처리한다.
url: "${contextPath}/cart/addGoodsInCart.do",
data: {
goods_id: goods_id
},
success: function(data, textStatus) {
//alert(data);
// $('#message').append(data);
if (data.trim() == 'add_success') {
imagePopup('open', '.layer01');
} else if (data.trim() == 'already_existed') {
alert("이미 카트에 등록된 상품입니다.");
}
},
error: function(data, textStatus) {
alert("에러가 발생했습니다." + data);
},
complete: function(data, textStatus) {
//alert("작업을완료 했습니다");
}
}); //end ajax
}
function imagePopup(type) {
if (type == 'open') {
// 팝업창을 연다.
jQuery('#layer').attr('style', 'visibility:visible');
// 페이지를 가리기위한 레이어 영역의 높이를 페이지 전체의 높이와 같게 한다.
jQuery('#layer').height(jQuery(document).height());
} else if (type == 'close') {
// 팝업창을 닫는다.
jQuery('#layer').attr('style', 'visibility:hidden');
}
}
function fn_order_each_goods(goods_id, goods_title, goods_sales_price, fileName) {
var _isLogOn = document.getElementById("isLogOn");
var isLogOn = _isLogOn.value;
if (isLogOn == "false" || isLogOn == '') {
alert("로그인 후 주문이 가능합니다!!!");
}
var total_price, final_total_price;
var order_goods_qty = document.getElementById("order_goods_qty");
var formObj = document.createElement("form");
var i_goods_id = document.createElement("input");
var i_goods_title = document.createElement("input");
var i_goods_sales_price = document.createElement("input");
var i_fileName = document.createElement("input");
var i_order_goods_qty = document.createElement("input");
i_goods_id.name = "goods_id";
i_goods_title.name = "goods_title";
i_goods_sales_price.name = "goods_sales_price";
i_fileName.name = "goods_fileName";
i_order_goods_qty.name = "order_goods_qty";
i_goods_id.value = goods_id;
i_order_goods_qty.value = order_goods_qty.value;
i_goods_title.value = goods_title;
i_goods_sales_price.value = goods_sales_price;
i_fileName.value = fileName;
formObj.appendChild(i_goods_id);
formObj.appendChild(i_goods_title);
formObj.appendChild(i_goods_sales_price);
formObj.appendChild(i_fileName);
formObj.appendChild(i_order_goods_qty);
document.body.appendChild(formObj);
formObj.method = "post";
formObj.action = "${contextPath}/order/orderEachGoods.do";
formObj.submit();
}
</script>
</head>
<body>
<hgroup>
<h1>컴퓨터와 인터넷</h1>
<h2>국내외 도서 > 컴퓨터와 인터넷 > 웹 개발</h2>
<h3>${goods.goods_title }</h3>
<h4>${goods.goods_writer} 저| ${goods.goods_publisher}</h4>
</hgroup>
<div id="goods_image">
<figure>
<img alt="HTML5 & CSS3" src="${contextPath}/thumbnails.do?goods_id=${goods.goods_id}&fileName=${goods.goods_fileName}">
</figure>
</div>
<div id="detail_table">
<table>
<tbody>
<tr>
<td class="fixed">정가</td>
<td class="active"><span>
<fmt:formatNumber value="${goods.goods_price}" type="number" var="goods_price" />
${goods_price}원
</span></td>
</tr>
<tr class="dot_line">
<td class="fixed">판매가</td>
<td class="active"><span>
<fmt:formatNumber value="${goods.goods_price*0.9}" type="number" var="discounted_price" />
${discounted_price}원(10%할인)
</span></td>
</tr>
<tr>
<td class="fixed">포인트적립</td>
<td class="active">${goods.goods_point}P(10%적립)</td>
</tr>
<tr class="dot_line">
<td class="fixed">포인트 추가적립</td>
<td class="fixed">만원이상 구매시 1,000P, 5만원이상 구매시 2,000P추가적립 편의점 배송 이용시 300P 추가적립</td>
</tr>
<tr>
<td class="fixed">발행일</td>
<td class="fixed">
<c:set var="pub_date" value="${goods.goods_published_date}" />
<c:set var="arr" value="${fn:split(pub_date,' ')}" />
<c:out value="${arr[0]}" />
</td>
</tr>
<tr>
<td class="fixed">페이지 수</td>
<td class="fixed">${goods.goods_total_page}쪽</td>
</tr>
<tr class="dot_line">
<td class="fixed">ISBN</td>
<td class="fixed">${goods.goods_isbn}</td>
</tr>
<tr>
<td class="fixed">배송료</td>
<td class="fixed"><strong>무료</strong></td>
</tr>
<tr>
<td class="fixed">배송안내</td>
<td class="fixed"><strong>[당일배송]</strong> 당일배송 서비스 시작!<br> <strong>[휴일배송]</strong>
휴일에도 배송받는 Bookshop</TD>
</tr>
<tr>
<td class="fixed">도착예정일</td>
<td class="fixed">지금 주문 시 내일 도착 예정</td>
</tr>
<tr>
<td class="fixed">수량</td>
<td class="fixed">
<select style="width: 60px;" id="order_goods_qty">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</td>
</tr>
</tbody>
</table>
<ul>
<li><a class="buy" href="javascript:fn_order_each_goods('${goods.goods_id }','${goods.goods_title }','${goods.goods_sales_price}','${goods.goods_fileName}');">구매하기 </a></li>
<li><a class="cart" href="javascript:add_cart('${goods.goods_id }')">장바구니</a></li>
<li><a class="wish" href="#">위시리스트</a></li>
</ul>
</div>
<div class="clear"></div>
<!-- 내용 들어 가는 곳 -->
<div id="container">
<ul class="tabs">
<li><a href="#tab1">책소개</a></li>
<li><a href="#tab2">저자소개</a></li>
<li><a href="#tab3">책목차</a></li>
<li><a href="#tab4">출판사서평</a></li>
<li><a href="#tab5">추천사</a></li>
<li><a href="#tab6">리뷰</a></li>
</ul>
<div class="tab_container">
<div class="tab_content" id="tab1">
<h4>책소개</h4>
<p>${fn:replace(goods.goods_intro,crcn,br)}</p>
<c:forEach var="image" items="${imageList }">
<img src="${contextPath}/download.do?goods_id=${goods.goods_id}&fileName=${image.fileName}">
</c:forEach>
</div>
<div class="tab_content" id="tab2">
<h4>저자소개</h4>
<p>
<div class="writer">저자 : ${goods.goods_writer}</div>
<%-- fn:replace 함수를 이용해 저자 소개에 포함된 crcn(개행문자)을 br태그로 대체한다. --%>
<p>${fn:replace(goods.goods_writer_intro,crcn,br) }</p>
</div>
<div class="tab_content" id="tab3">
<h4>책목차</h4>
<%-- 마찬가지로 상품 목차에 포함된 crcn(개행문자)을 br 태그로 대체한다. --%>
<p>${fn:replace(goods.goods_contents_order,crcn,br)}</p>
</div>
<div class="tab_content" id="tab4">
<h4>출판사서평</h4>
<%-- 상품 목차에 포함된 crcn(개행문자)을 br 태그로 대체한다. --%>
<p>${fn:replace(goods.goods_publisher_comment ,crcn,br)}</p>
</div>
<div class="tab_content" id="tab5">
<h4>추천사</h4>
<%-- 상품 추천평에 포함된 crcn(개행문자)을 br 태그로 대체한다. --%>
<p>${fn:replace(goods.goods_recommendation,crcn,br) }</p>
</div>
<div class="tab_content" id="tab6">
<h4>리뷰</h4>
</div>
</div>
</div>
<div class="clear"></div>
<div id="layer" style="visibility: hidden">
<!-- visibility:hidden 으로 설정하여 해당 div안의 모든것들을 가려둔다. -->
<div id="popup">
<!-- 팝업창 닫기 버튼 -->
<a href="javascript:" onClick="javascript:imagePopup('close', '.layer01');"> <img src="${contextPath}/resources/image/close.png" id="close" />
</a> <br />
<font size="12" id="contents">장바구니에 담았습니다.</font><br>
<form action='${contextPath}/cart/myCartList.do'>
<input type="submit" value="장바구니 보기">
</form>
</body>
</html>
<input type="hidden" name="isLogOn" id="isLogOn" value="${isLogOn}" />
7. 이번에는 최근 본 상품 이미지를 표시하는 quickMenu.jsp를 작성한다. '최근 본 상품'은 상품 목록에서 상품 정보를 가져온 다음 첫 번째 상품 이미지만 표시하고 다른 상품 이미지는 <hidden> 태그에 저장한다(동일한 <hidden> 태그에 여러 개의 데이터 저장 시 자동으로 배열로 저장된다). 사용자가 다음을 클릭하면 <hidden> 태그의 상품 정보를 자바스크립트 함수로 전달하여 이미지를 표시한다.
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"
isELIgnored="false"
%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<script>
var array_index = 0;
var SERVER_URL = "${contextPath}/thumbnails.do";
function fn_show_next_goods() {
var img_sticky = document.getElementById("img_sticky");
var cur_goods_num = document.getElementById("cur_goods_num");
var _h_goods_id = document.frm_sticky.h_goods_id;
var _h_goods_fileName = document.frm_sticky.h_goods_fileName;
if (array_index < _h_goods_id.length - 1) // 다음 클릭 시 배열의 인덱스를 1 증가시킨다.
array_index++;
var goods_id = _h_goods_id[array_index].value;
var fileName = _h_goods_fileName[array_index].value;
img_sticky.src = SERVER_URL + "?goods_id=" + goods_id + "&fileName=" + fileName;
cur_goods_num.innerHTML = array_index + 1; // 증가된 인덱스에 대한 배열 요소의 상품 번호외 이미지 파일 이름을 가져와 표시한다.
} // 빠른 메뉴의 다음 클릭 시 <hidden> 태그에 저장된 상품 정보를 가져와 이미지를 표시한다.
function fn_show_previous_goods() {
var img_sticky = document.getElementById("img_sticky");
var cur_goods_num = document.getElementById("cur_goods_num");
var _h_goods_id = document.frm_sticky.h_goods_id;
var _h_goods_fileName = document.frm_sticky.h_goods_fileName;
if (array_index > 0)
array_index--;
var goods_id = _h_goods_id[array_index].value;
var fileName = _h_goods_fileName[array_index].value;
img_sticky.src = SERVER_URL + "?goods_id=" + goods_id + "&fileName=" + fileName;
cur_goods_num.innerHTML = array_index + 1;
}
function goodsDetail() {
var cur_goods_num = document.getElementById("cur_goods_num");
arrIdx = cur_goods_num.innerHTML - 1;
var img_sticky = document.getElementById("img_sticky");
var h_goods_id = document.frm_sticky.h_goods_id;
var len = h_goods_id.length;
if (len > 1) {
goods_id = h_goods_id[arrIdx].value;
} else {
goods_id = h_goods_id.value;
}
var formObj = document.createElement("form");
var i_goods_id = document.createElement("input");
i_goods_id.name = "goods_id";
i_goods_id.value = goods_id;
formObj.appendChild(i_goods_id);
document.body.appendChild(formObj);
formObj.method = "get";
formObj.action = "${contextPath}/goods/goodsDetail.do?goods_id=" + goods_id;
formObj.submit();
}
</script>
<body>
<div id="sticky">
<ul>
<li><a href="#">
<img width="24" height="24" src="${contextPath}/resources/image/facebook_icon.png">
페이스북
</a></li>
<li><a href="#">
<img width="24" height="24" src="${contextPath}/resources/image/twitter_icon.png">
트위터
</a></li>
<li><a href="#">
<img width="24" height="24" src="${contextPath}/resources/image/rss_icon.png">
RSS 피드
</a></li>
</ul>
<div class="recent">
<h3>최근 본 상품</h3>
<ul>
<!-- 상품이 없습니다. -->
<c:choose>
<c:when test="${ empty quickGoodsList }">
<strong>상품이 없습니다.</strong>
</c:when>
<c:otherwise>
<form name="frm_sticky">
<%-- 세션에 저장된 빠른 메뉴 목록이 이미지 정보를 <hidden> 태그에 차례대로 저장한다. --%>
<c:forEach var="item" items="${quickGoodsList }" varStatus="itemNum">
<c:choose>
<c:when test="${itemNum.count==1 }">
<a href="javascript:goodsDetail();">
<img width="75" height="95" id="img_sticky" src="${contextPath}/thumbnails.do?goods_id=${item.goods_id}&fileName=${item.goods_fileName}">
</a>
<%-- 동일한 <hidden> 태그에 연속해서 저장하면 배열로 저장된다. --%>
<input type="hidden" name="h_goods_id" value="${item.goods_id}" />
<input type="hidden" name="h_goods_fileName" value="${item.goods_fileName}" />
<br>
</c:when>
<c:otherwise>
<input type="hidden" name="h_goods_id" value="${item.goods_id}" />
<input type="hidden" name="h_goods_fileName" value="${item.goods_fileName}" />
</c:otherwise>
</c:choose>
</c:forEach>
</c:otherwise>
</c:choose>
</ul>
</form>
</div>
<div>
<c:choose>
<c:when test="${ empty quickGoodsList }">
<h5> 0/0 </h5>
</c:when>
<c:otherwise>
<h5><a href='javascript:fn_show_previous_goods();'> 이전 </a> <span id="cur_goods_num">1</span>/${quickGoodsListNum} <a href='javascript:fn_show_next_goods();'> 다음 </a> </h5>
</c:otherwise>
</c:choose>
</div>
</div>
</body>
</html>
8. 실행 결과를 보자. 상품 상세를 요청하면 다음과 같이 페이지 오른쪽에 있는 빠른 메뉴에 최근 본 상품이 추가된다.
- http://localhost/bookshop01/main/main.do
9. 빠른 메뉴에서 다음을 클릭하면 두 번째 상품 이미지가 표시된다.
'Java_Spring mini project 쇼핑몰' 카테고리의 다른 글
(6) 도서 쇼핑몰 만들기 6 (0) | 2023.11.25 |
---|---|
(5) 도서 쇼핑몰 만들기 5 (0) | 2023.11.24 |
(4) 도서 쇼핑몰 만들기 4 (0) | 2023.11.24 |
(2) 도서 쇼핑몰 만들기 2 (0) | 2023.11.23 |
(1) 도서 쇼핑몰 만들기 1 (1) | 2023.11.23 |