스프링 프레임워크/쇼핑몰 프로젝트

[Spring][쇼핑몰 프로젝트][35] 상품 상세 페이지 - 2

Kim VamPa 2021. 11. 10. 18:32
728x90
반응형
프로젝트 Github : https://github.com/sjinjin7/Blog_Project
프로젝트 포스팅 색인(index) : https://kimvampa.tistory.com/188

 

목표

상품 상세 페이지

 저번 포스팅에 이서 이번 포스팅에선 '상품 상세 페이지' 뷰 쪽 처리를 목표로 합니다. 

 

 

 

 

순서

 

1. search.jsp

2. goodsDetail.jsp

3. 이미지, 출판일 출력

 

 

 

1. search.jsp

 검색 페이지 에서 상품 상세 페이지로 이동할 수 있도록 <a> 태그를 추가해주겠습니다. 사용자가 책 이름을 클릭하였을 때 이동할 수 있도록 해주겠습니다.

 

 search.jsp에서 class속성 값이 'title'인 <div>태그에 책 제목이 작성되어 잇습니다. 태그 내부에 작성한 코드를 <a> 태그로 감싸줍니다. 그리고 상품 상세 페이지를 요청하는 URL을 <a> 태그의 href 속성 값으로 부여해주었습니다.

 

<a href="/goodsDetail/${list.bookId}">
</a>

 

그림 1-1 변경 전

 

그림 1-2 변경 후

 

 

2. goodsDetail.jsp

 home.jsp와 main.jsp 파일이 위치한 'views' 폴더에 goodsDetail.jsp 파일을 생성해줍니다.

 

그림 2-1

 

 

 더불어 goodsDetail.jsp에 사용될 css 코드를 가지는 goodsDetail.css 파일을 css 폴더에 추가 해주었습니다.

 

그림 2-2

 

 

 '상품 상세페이지'는 home.jsp와 main.jsp의 큰 틀은 그대로 사용하고 contents <div> 태그만 다르게 해 줄 것이기 때문에 search.jsp의 코드를 모두 복사 후 "goodsDetail.jsp" 코드를 붙여 넣어주었습니다.

 

그림 2-3

 

 

 goodsDetail.jsp의 <head> 태그 부분에 css 연결 코드를 "goodsDetail.css"와 연결이 되도록 수정을 해주었습니다.

 

 

 

  "content_area" <div> 태그 안의 모든 코드를 지워 주었습니다.

 

그림 2-5

 Javascript 코드가 작성된 <script> 태그도 모두 지워주었습니다.

 

그림 2-6

 

 

goodsDetail.jsp 전체 코드입니다.

※ 로고 이미지 소스코드 부분에 상대경로('resource/img...')을 절대경로('/resource/img...')로 수정해주었습니다.

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>          
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Welcome BookMall</title>
<link rel="stylesheet" href="/resources/css/goodsDetail.css">
<script
  src="https://code.jquery.com/jquery-3.4.1.js"
  integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
  crossorigin="anonymous"></script>
</head>
<body>

<div class="wrapper">
	<div class="wrap">
		<div class="top_gnb_area">
			<ul class="list">
				<c:if test = "${member == null}">	<!-- 로그인 x -->	
					<li >
						<a href="/member/login">로그인</a>
					</li>
					<li>
						<a href="/member/join">회원가입</a>
					</li>
				</c:if>
				<c:if test="${member != null }">	<!-- 로그인 o -->		
					<c:if test="${member.adminCk == 1 }">	<!-- 관리자 계정 -->
						<li><a href="/admin/main">관리자 페이지</a></li>
					</c:if>							
					<li>
						<a id="gnb_logout_button">로그아웃</a>
					</li>
					<li>
						마이룸
					</li>
					<li>
						장바구니
					</li>
				</c:if>				
				<li>
					고객센터
				</li>			
			</ul>			
		</div>
		<div class="top_area">
			<!-- 로고영역 -->
			<div class="logo_area">
				<a href="/main"><img src="/resources/img/mLogo.png"></a>
			</div>
			<div class="search_area">
                	<div class="search_wrap">
                		<form id="searchForm" action="/search" method="get">
                			<div class="search_input">
                				<select name="type">
                					<option value="T">책 제목</option>
                					<option value="A">작가</option>
                				</select>
                				<input type="text" name="keyword" value="<c:out value="${pageMaker.cri.keyword}"/>">
                    			<button class='btn search_btn'>검 색</button>                				
                			</div>
                		</form>
                	</div>
			</div>
			<div class="login_area">
			
				<!-- 로그인 하지 않은 상태 -->
				<c:if test = "${member == null }">
					<div class="login_button"><a href="/member/login">로그인</a></div>
					<span><a href="/member/join">회원가입</a></span>				
				</c:if>				
				
				<!-- 로그인한 상태 -->
				<c:if test="${ member != null }">
					<div class="login_success_area">
						<span>회원 : ${member.memberName}</span>
						<span>충전금액 : <fmt:formatNumber value="${member.money }" pattern="\#,###.##"/></span>
						<span>포인트 : <fmt:formatNumber value="${member.point }" pattern="#,###" /></span>
						<a href="/member/logout.do">로그아웃</a>
					</div>
				</c:if>
				
			</div>
			<div class="clearfix"></div>			
		</div>
		<div class="content_area">


		</div>
		
		<!-- Footer 영역 -->
		<div class="footer_nav">
			<div class="footer_nav_container">
				<ul>
					<li>회사소개</li>
					<span class="line">|</span>
					<li>이용약관</li>
					<span class="line">|</span>
					<li>고객센터</li>
					<span class="line">|</span>
					<li>광고문의</li>
					<span class="line">|</span>
					<li>채용정보</li>
					<span class="line">|</span>
				</ul>
			</div>
		</div> <!-- class="footer_nav" -->
		
		<div class="footer">
			<div class="footer_container">
				
				<div class="footer_left">
					<img src="/resources/img/Logo.png">
				</div>
				<div class="footer_right">
					(주) VamBook    대표이사 : OOO
					<br>
					사업자등록번호 : ooo-oo-ooooo
					<br>
					대표전화 : oooo-oooo(발신자 부담전화)
					<br>
					<br>
					COPYRIGHT(C) <strong>kimvampa.tistory.com</strong>	ALL RIGHTS RESERVED.
				</div>
				<div class="clearfix"></div>
			</div>
		</div> <!-- class="footer" -->		
		
	</div>	<!-- class="wrap" -->
</div>	<!-- class="wrapper" -->



</body>
</html>

 

 

 goodsDetail.css 파일에도 search.css 코드를 모두 복사해준 후 붙여 넣어줍니다. 그리고 복사된 코드들 중 jsp에서 지운 콘텐츠 관련 css 코드들을 모두 지워 주었습니다.

 

goodsDetail.css

더보기
@charset "UTF-8";
*{
	margin: 0;
	padding:0;
}
a{
	text-decoration: none;
}
/* 화면 전체 렙 */
.wrapper{
	width: 100%;
}
/* content 랩 */
.wrap{
	width : 1080px;
	margin: auto;
}
/* 홈페이지 기능 네비 */ 
.top_gnb_area{
	width: 100%;
    height: 50px;
    background-color: #f0f0f1;
    position:relative;    
}
.top_gnb_area .list{
	position: absolute;
    top: 0px;
    right: 0;
    
}
.top_gnb_area .list li{
	list-style: none;	
    float : left;
    padding: 13px 15px 0 10px;
    font-weight: 900;
    cursor: pointer;
}
/* 로고, 검색, 로그인 */
.top_area{
	width: 100%;
    height: 150px;
    /* background-color: #f7f0b9; */
}
/* 로고 영역 */
.logo_area{
	width: 25%;
	height: 100%;
	float:left;
}
.logo_area img{
	width: 100%;
    height: 100%;
}

/* 검색 박스 영역 */
.search_area{
	width: 50%;
	height: 100%;
	float:left;	
}
.search_wrap{
	width: 100%;
    height: 100%;
}
#searchForm{
	width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}
.search_input{
    display: flex;
    height: 30%;
    width: 80%;	
}
.search_input select{
	width: 20%;
    text-align: center;
    font-size: 15px;	
}
.search_input input{
    margin-left: 10px;
    width: 57%;	
    font-size: 18px;
    padding-left: 2%;
}
.search_btn{
	margin-left: 10px;
    width: 17%;
    border-radius: 14px;
    font-size: 17px;
    font-weight: 600;	
}	


/* 로그인 버튼 영역 */
.login_area{
	width: 25%;
	height: 100%;
	display: inline-block;	
	text-align: center;		
}
.login_button{
	height: 50%;
    background-color: #D4DFE6;
    margin-top: 30px;
    line-height: 77px;
    font-size: 40px;
    font-weight: 900;
    border-radius: 10px;
    cursor: pointer;	
}
.login_area>span{
	margin-top: 10px;
    font-weight: 900;
    display: inline-block;
}
.login_button{
	height : 50%;
	background-color: #D4DFE6;
	margin-top:30px;
}

/* 제품 목록 네비 */
.navi_bar_area{
	width: 100%;
    height: 70px;
    background-color: #7696fd;
}

/* 홈페이지 메인 제품 목록  */
.content_area{
	width: 100%;
    min-height: 1000px;
}




/* 로그인 성공 영역 */
.login_success_area{
	height: 62%;
    width: 80%;
    border: 2px solid #7474ad;
    border-radius: 15px;
    margin: 5% auto;
    padding-top: 5%;
}
.login_success_area>a{
    font-size: 15px;
    font-weight: 900;
    display: inline-block;
    margin-top: 5px;
    background: #e1e5e8;
    width: 82px;
    height: 22px;
    line-height: 22px;
    border-radius: 25px;
    color: #606267;    
}
.login_success_area>span{
	display : block;
	text-align: left;
    margin-left: 10%;
}

/* 검색결과 없음 */
.table_empty{
	height: 50px;
    text-align: center;
    margin: 200px 0 215px 0px;
    font-size: 25px;
}

/* 필터정보 */
.search_filter {
    width: 85%;
    margin: auto;
	margin-top: 30px;
    margin-bottom: 50px;    
} 
.filter_button_wrap {
    width: 100%;
}

.filter_button_wrap button {
    width: 50%;
} 
.filter_button{
	background-color: #04AA6D;
	border: 1px solid green;
	color: white;
	padding: 10px 24px;
	cursor: pointer;
	float: left;
} 
.filter_button_wrap:after {
    content: "";
    clear: both;
    display: table;
}  	
.filter_button_wrap button:not(:last-child) {
    border-right: none;
} 
.filter_button:hover {
    background-color: #3e8e41;
}
.filter_active{
	background-color: #045d3c;
}
.filter_content{
	padding:20px 50px 20px 50px;
	border: 1px solid gray;
}
.filter_content a:not(:first-child){
	margin-left: 10px;
}
.filter_a{
	display: block;
}

.filter_b{
	display: none;
}



/* footer navai 영역 */
.footer_nav{
	width:100%;
	height:50px;
}
.footer_nav_container{
	width: 100%;
	height: 100%;
	background-color:#8EC0E4;
}
.footer_nav_container>ul{
	font-weight : bold;
	float:left;
	list-style:none;
	position:relative;
	padding-top:10px;
	line-height: 27px;
	font-family: dotum;
	margin-left: 10px;
}
.footer_nav_container>ul>li{
	display:inline;
	width: 45px;
	height: 19px;
	padding: 10px 9px 0 10px;
}
.footer_nav_container>ul>span{
	margin: 0 4px;
}
/* footer 영역 */
.footer{
	width:100%;
	height:130px;
	background-color:#D4DFE6;
	padding-bottom : 50px;
}
.footer_container{
	width: 100%;
	height: 100%;
	margin: auto;
}
.footer_left>img {
	width: 150%;
    height: 130px;
    margin-left: -20px;
    margin-top: -12px;
}
.footer_left{
	float :left;
	width: 170px;
	margin-left: 20px;
	margin-top : 30px;
	
}
.footer_right{
	float :left;
	width: 680px;
	margin-left: 70px;
	margin-top : 30px;
}


/* float 속성 해제 */
.clearfix{
	clear: both;
}

 

그림 2-7

 

 


 "content_area" <div> 태그 내부 부분은 교보문고 상품 페이지를 참고하여 단순하게 만들었습니다.

 

goodsDetail.jsp

더보기
			<div class="line">
			</div>			
			<div class="content_top">
				<div class="ct_left_area">
					<div class="image_wrap" data-bookid="${goodsInfo.imageList[0].bookId}" data-path="${goodsInfo.imageList[0].uploadPath}" data-uuid="${goodsInfo.imageList[0].uuid}" data-filename="${goodsInfo.imageList[0].fileName}">
						<img>
					</div>				
				</div>
				<div class="ct_right_area">
					<div class="title">
						<h1>
							${goodsInfo.bookName}
						</h1>
					</div>
					<div class="line">
					</div>
					<div class="author">
						 <span>
						 	${goodsInfo.authorName} 지음
						 </span>
						 <span>|</span>
						 <span>
						 	${goodsInfo.publisher}
						 </span>
						 <span>|</span>
						 <span class="publeyear">
						 	${goodsInfo.publeYear}
						 </span>
					</div>
					<div class="line">
					</div>	
					<div class="price">
						<div class="sale_price">정가 : <fmt:formatNumber value="${goodsInfo.bookPrice}" pattern="#,### 원" /></div>
						<div class="discount_price">
							판매가 : <span class="discount_price_number"><fmt:formatNumber value="${goodsInfo.bookPrice - (goodsInfo.bookPrice*goodsInfo.bookDiscount)}" pattern="#,### 원" /></span> 
							[<fmt:formatNumber value="${goodsInfo.bookDiscount*100}" pattern="###" />% 
							<fmt:formatNumber value="${goodsInfo.bookPrice*goodsInfo.bookDiscount}" pattern="#,### 원" /> 할인]</div>							
					</div>			
					<div class="line">
					</div>	
					<div class="button">						
						<div class="button_quantity">
							주문수량
							<input type="text" value="1">
							<span>
								<button>+</button>
								<button>-</button>
							</span>
						</div>
						<div class="button_set">
							<a class="btn_cart">장바구니 담기</a>
							<a class="btn_buy">바로구매</a>
						</div>
					</div>
				</div>
			</div>
			<div class="line">
			</div>				
			<div class="content_middle">
				<div class="book_intro">
					${goodsInfo.bookIntro}
				</div>
				<div class="book_content">
					${goodsInfo.bookContents }
				</div>
			</div>
			<div class="line">
			</div>				
			<div class="content_bottom">
				리뷰
			</div>

 

그림 2-8

 

 

goodsDetail.css

더보기
  	.content_top{
	    width: 100%;
	    height: 400px;  	
  	}
	.content_top:after {
	    content: "";
	    clear: both;
	    display: table;
	}    	
  	.ct_left_area{
		float: left;
	    width: 30%;
	    height: 100%;
  	}
  		.image_wrap{
		    height: 80%;
		    width: 80%;
		    margin: auto;
		    top: 10%;
		    position: relative;  		
  		}
  		.image_wrap img{
	  		max-width: 100%;
		    height: auto;
		    display: block;  		
  		}
  		.line{
		    width: 100%;
		    border-top:1px solid #c6c6cf;  		
  		}
  		
  		

  	.ct_right_area{
	    float: left;
	    width: 70%;
	    height: 100%;
  	}
  		.title{
		    height: 28%;
		    font-size: 17px;
		    line-height: 110px;
		    color: #3a60df;
		    padding-left: 3%;
  		}
  		.author{
		    font-size: 16px;
		    line-height: 50px;
		    padding-left: 3%;  		
  		}
  		.price{
		    line-height: 30px;
		    padding: 2% 0 2% 3%;  		
  		}
  			.discount_price_number{
			    line-height: 30px;
			    font-size: 22px;
			    color: #f84450;
			    font-weight: bold;  			
  			}
  		.button{
  			padding: 2% 0 2% 3%;
  		}
  			.button_quantity{
  				margin-bottom: 2%;
  				
  			}
	  			.button_quantity input{
				    height: 26px;
				    width: 40px;
				    text-align: center;
				    font-weight: bold;  			
	  			}
		  	  	.button_quantity button{
					border: 1px solid #aaa;
				    color: #3a60df;
				    width: 20px;
				    height: 20px;
				    padding: 3px;
				    background-color: #fff;
				    font-weight: bold;
				    font-size: 15px;
				    line-height: 15px;	  	  	
		  	  	}
		  	  		.btn_cart{
						display: inline-block;
    					width: 140px;
					    text-align: center;
					    height: 50px;
    					line-height: 50px;
    					background-color: #5e6b9f;
    					border: 1px solid #5e6b9f;
    					color: #fff;
    					margin-right: 2px;  	  		
		  	  		}
		  	  		.btn_buy{
						display: inline-block;
    					width: 140px;
					    text-align: center;
					    height: 50px;
    					line-height: 50px;
    					background-color: #7b8ed1;
    					border: 1px solid #7b8ed1;
    					color: #fff;			  	  		
		  	  		}
  	
  	.content_middle{
		width: 100%;
	    min-height: 600px;  	
  	}

		.book_intro{
			width: 80%;
			margin: auto;
			margin-top: 40px;
		}
		.book_content{
			width: 80%;
			margin: auto;
			margin-top: 40px;
			margin-bottom: 40px;
		}
  	
  	.content_bottom{
		width: 100%;
		min-height: 400px;
	    background-color: #e7dbdb;  	
  	}

 

그림 2-9

 

 

결과는 다음과 같습니다. 

 

그림 2-10

 

 

 이미지가 들어갈 부분은 search.jsp 에서 사용한 방법과 동일합니다. 태그에 심어둔 이미지 데이터를 사용하여 Javascript코드를 통해 이미지를 출력시킬 것입니다.

 

그림 2-11

 

 

 '가격'관련 부분은 fmt 태그를 통해서 원하는 방식으로 출력되도록 하였습니다.

 

그림 2-12

 

 

 날짜 부분은 이전 포스팅에서 말하였지만 BookVO에 선언한 publeYear 변수의 타입이 Data인 경우 fmt태그를 화용하여 원하는 형식으로 변경이 가능하지만 String 타입이어서 fmt태그를 활용할 수 없습니다. 따라서 Javascript 코드를 통해서 원하는 형식으로 출력시킬 것입니다.

 

그림 2-13

 

 

3. 이미지, '출판일' 출력

 이미지와 '출판일'을 원하는 형식으로 출력되도록 해보겠습니다. 둘 다 사용자가 페이지에 들어갔을 때 출력이 되어야 하기 때문에 페이지가 로딩될대 실행이 되도록 <scirpt> 태그와 docuemt ready 메서드를 <body> 태그 하단에 추가해주었습니다.

 

<script>
$(document).ready(function(){
	
	

	
});	
</script>

 

그림 3-1

 

 

 이미지가 출력되도록 아래의 코드를 추가해주었습니다. 구현 방식은 search.jsp에서 사용했던 방식과 동일합니다.

 

	/* 이미지 삽입 */
	const bobj = $(".image_wrap");
	
	if(bobj.data("bookid")){
		const uploadPath = bobj.data("path");
		const uuid = bobj.data("uuid");
		const fileName = bobj.data("filename");
		
		const fileCallPath = encodeURIComponent(uploadPath + "/s_" + uuid + "_" + fileName);
		
		bobj.find("img").attr('src', '/display?fileName=' + fileCallPath);
	} else {
		bobj.find("img").attr('src', '/resources/img/goodsNoImage.png');
	}

 

그림 3-2

 

 

 '출판일' 날짜를 '연월일' 형식으로 출력되도록 아래의 코드를 추가해주었습니다.(substr(), split() 메서드를 활용하였습니다.)

 

	/* publeyear */
	const year = "${goodsInfo.publeYear}";
	
	let tempYear = year.substr(0,10);
	
	let yearArray = tempYear.split("-")
	let publeYear = yearArray[0] + "년 " + yearArray[1] + "월 " + yearArray[2] + "일";
	
	$(".publeyear").html(publeYear);

 

그림 3-3

 

 

 결과물은 아래와 같습니다.

 

그림 3-4

 

REFERENCE

  •  

 

 

DATE

  • 2020.11.10

 

728x90
반응형