Spring 쇼핑몰 - 상품 구매, 리뷰

하승·2022년 8월 2일
2


메인화면에 있는 상품의 구매하러 가기 버튼을 클릭하면 상품명, 상품가격, 상품설명, 리뷰가 나와있는 페이지로 이동 시켜준다.

내 주소보기에 보러가기 버튼을 클릭하면 자신이 회원가입때 입력한 주소를 보여주는 Modal창을 띄워준다.

또한 자신이 회원가입때 입력한 주소로 배달을 받고 싶지 않은 고객들을 위해 우편번호 변경하기
버튼을 클릭시 주소를 변경할 수 있도록 주소 검색 창을 띄워준다.

그 후 이 배송지로 받기를 누르고 구매하기를 누르고 구매에 성공 했다면 해당 alert창을 보여준다.

DB에 들어가 select를 해보면 잘 저장되있는것을 볼 수 있다.


상품 리뷰 작성하기

작성


평소에는 리뷰를 작성하는 form이 보이지 않지만 리뷰 작성하기 버튼을 누른다면

이 처럼 리뷰를 작성하는 form을 보여준다.
이름과 email은 readonly를 줘서 사용자 임의대로 바꾸지 못한다.
쓰고 싶은 내용을 적고 작성하기 버튼을 누른다면

alert창을 띄워주며 location.reload()를 이용해 페이지를 새로고침 시켜준다.

새로고침 된 후 페이지를 다시 보면 자신이 쓴 리뷰가 페이지에 잘 보이는 것을 확인할 수 있다.

리뷰 수정, 삭제

수정

리뷰 삭제와 수정버튼은 리뷰를 쓴 작성자의 아이디와 현재 로그인중인 유저의 아이디를 비교해서 리뷰를 쓴 작성자라면 삭제버튼과 수정버튼을 보여준다.

수정 버튼을 누를 시 수정버튼은 감추고 수정 완료버튼을 보여주고 input태그의 readonly를 false로 바꿔준다.

내용을 수정하고 수정완료를 누르면 수정이된다.

삭제

삭제 버튼 클릭시 리뷰를 삭제해준다.


코드

board.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Myshop</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
 <link href="/resources/css/styles.css" rel="stylesheet" />
 <link href="/resources/css/product/board.css" rel="stylesheet" />
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css">
<style>
		.zipModal{
			position: absolute;
			width: 100%;
			height: 100%;
			background: rgba(0,0,0,0.8);
			top:0;
			left:0;
			display:none;
		}
		.zipModal_body {
		  position: absolute;
		  top: 50%;
		  left: 50%;
		  padding: 190px;
		  background-color: rgb(255, 255, 255);
		  border-radius: 10px;
		  box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
		  transform: translateX(-50%) translateY(-50%);
		  z-index: 1;
		}
		input[type="text"]{
			border-color: rgba(144, 144, 144, 0.25);
			height: 3.25rem;
			appearance: none;
		    border-radius: 4px;
		    border: solid 1px;
		    color: inherit;
		    outline: 0;
		    padding: 2px;
		    text-align: center;
		    width:15%;
		    text-decoration: none;
		}
		input{
			font-family: 'SLEIGothicTTF';
		    font-weight: 300;
		    font-size: 1rem;
		    line-height: 2.15;
		}
		form > :last-child {
		    margin-bottom: 0;
		}
		.wrapper.special {
		    text-align: center;
		}
		.wrapper {
		    padding: 6rem 0 4rem 0;
		}
		.py-5 .bg-dark{
			margin-top:90px;
		}
		.zipnum.inputt{
			width:100%;
		}
		.reviewBTN{
			float : right;
			margin-right:10px;
		}
		.inlinereview{
			display:inline;
		}
</style>
</head>
<body>
<!-- nav 시작 -->
<%@include file="../user/nav.jsp" %>
<!-- nav 끝 -->
        <div class="main">
        	<div class="container">
        		<div class="row margin-bottom-40">
        			<div class="col-md-9 col-sm-7">
        				<div class="product-page">
        					<div class="row">
        						<div class="col-md-6 col-sm-6">
        							<div class="product-main-img" style="position: relative; overflow:hidden;">
        								<img src="/resources/img/${filename}" class="img-responsive">
        							</div>
        						</div>
        						<div class="col-md-6 col-sm-6">
        							<h1>${product.productname}</h1>
        							<div class="price-availability-block clearfix">
        								<div class="price">
        									<strong>
        										${product.productprice}원
        									</strong>
        								</div>
        							</div>
        							<form action="/user/buyproduct">
        							<div id="container" class="container">
										<div id="content" class="content">
											<div class="subindex_wrap" role="main">
			        							<div class="subindex_item">
													<div class="subindex_onebox">
														<div class="onebox_title desc">
															<h2 class="subindex_title">내 주소 보기</h2>
														</div>
														<button type="button" class="link_right">
															<span class="case">보러가기</span>
														</button>>
														<div class="subindex_desc">구매 하시기전에 배송지를 설정해주세요~~</div>
													</div>
												</div>
											</div>
										</div>
									</div>
        							<input type="hidden" value="${product.productnum}">
        							<div class="product-page-cart">
        								<div class="product-quantity">
        									<button class="btn btn-primary buy" type="submit">구매하기</button>
        								</div>
        							</div>
        							</form>
        						</div>
        					</div>
        				</div>
        			</div>
        		</div>
        	</div>
        </div>
        <div class="textarea-div">
        <textarea rows="10" cols="10" readonly style="word-break:break-all;width:100%;text-align:center;">${product.productcontents}</textarea>
        </div>
        
        <!-- 리뷰 작성 -->
		<button type="button" name="review" id="onReview">리뷰 작성하기</button>
		<div class="product-page-content" id="review" >
			<ul id="myTap" class="nav nav-tabs">
				<li class="active">
					<a href="#" data-togle="tab">리뷰</a>
				</li>
			</ul>
			 <div class="tab-content" style="width:100%;">
				<div class="tab-pane fade in active">
				<c:forEach items="${review}" var="review">
					<div class="review-item clearfix">
						<div class="review-item-submitted">
							<strong>${review.username}</strong>
							<em>${review.regdate}</em>
						</div>
						<div class="reivew-item-content">
							<input class="inlinereview" value = "${review.reviewcontents}" readonly>
							<c:if test="${loginUsername == review.username}">
							<a href="${review.reviewnum}" class="reviewBTN del">삭제</a>
							<a href="#" class="reviewBTN mdf" id="mdf">수정</a>
							<a href="${review.reviewnum}" class="reviewBTN mdfOk" id="mdfOk" style="display:none;">수정 완료</a>
 							</c:if>
						</div>
					</div>
				</c:forEach>
					<form class="reviews-form" role="form" style="display:none;">
						<h2>리뷰 작성</h2>
						<div class="form-group">
							<label for="username">
								이름
								<span class="require"></span>
							</label>
							<input type="text" class="form-control" id="username" value="${loginUsername}" readonly style="width:14%;">
						</div>
						<div class="form-group">
							<label for="useremail">Email</label>
							<input type="text" value="${loginUserid}" id="useremail" readonly>
						</div>
						<div class="form-group">
							<label for="review">리뷰</label>
							<textarea class="form-control" rows="8" id="reviewTxt" style="word-break:break-all;width:100%;text-align:center;"></textarea>
						</div>
						<div class="padding-top-20">
							<button type="submit" class="btn btn-primary reviewBtn">작성하기</button>
						</div>
					</form>
				</div>
			</div> 
		</div>
        <!-- Footer-->
        <footer class="py-5 bg-dark">
            <div class="container"><p class="m-0 text-center text-white">Copyright &copy; Your Website 2022</p></div>
        </footer>
        
        <!-- 우편번호 Modal -->
        <form action="/" method="get" class="buyForm">
        </form>
        <div class="zipModal">
			<div class="zipModal_body">
				<div class="wrapper style1 special">
					<div class="inner">
					<form name="buy">
						<input type="hidden" value="${loginUserid}" name="useremail">
							<table class = "zipnum-container">
								<tr class="zipcode_area">
									<th>우편번호</th>
									<td>
										<input readonly class="zipnum inputt" name="postnum" type="text" id="postnum" value="${postnum}"><input type="button" onclick="DaumPostcode()" value="우편번호 변경하기">
									</td>
								</tr>
								<tr class="addr_area">
									<th>주소</th>
									<td><input readonly class="zipnum inputt" name="addr" type="text" id="addr" value="${addr}"></td>
								</tr>
								<tr>
									<th>상세주소</th>
									<td><input class="zipnum inputt" name="detailaddress" type="text" id="detailaddress" value="${detailaddress}"></td>
								</tr>
								<tr>
									<th>참고항목</th>
									<td><input readonly class="zipnum inputt" name="seealso" type="text" id="seealso" value="${seealso}"></td>
								</tr>
								<tr>
									<th colspan="2">
										<input type="submit" id="zipSubmit" value="이 배송지로 받기">
										<input type="submit" id="zipCancle" value="나가기">
									</th>
								</tr>
							</table>
					</form>
					</div>
				</div>
			</div>
		</div>
</body>
<script src="/resources/js/daum.js"></script>
<script src="/resources/js/board.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.js"></script>
<script>
let postnum = $("#postnum").val();
let addr = $("#addr").val();
let seealsos = $("#seealso").val();
let detailaddress = $("detailaddress").val();
let useremail = "${loginUserid}";
let productnum = "${product.productnum}";
let productname = "${product.productname}";
let productprice = "${product.productprice}";
let username = "${loginUsername}";
let buyForm = $(".buyForm");
//구매하기 클릭시 이벤트
 $(".buy").on("click",function(e){
	 postnum = $("#postnum").val();
	 addr = $("#addr").val();
	 detailaddress = $("#detailaddress").val();
	 seealso = $("#seealso").val();
	if(useremail == ""){
		alert("로그인 후 진행해주세요");
		return false;
	}
	e.preventDefault();
	buyService.add(
			{useremail:useremail,username:username,productnum:productnum,productname:productname,postnum:postnum
				,addr:addr,detailaddress:detailaddress,seealso:seealso
			},
			function(result){
				alert(productname+"상품 구매완료");				
			}
	);
	
});

// 주소 보러가기 클릭시 미 로그인 상태라면 alert창 띄움
$(".link_right").on("click",function(e){
	let useremail = "${loginUserid}";
	
	if(useremail == ""){
		alert("로그인 후 이용해주세요");
		return false;
	}
	
//로그인 되어있다면 모달창 띄우기
		e.preventDefault();
		$(".zipModal").show();
	});

//모달창 나가기 클릭시 모달창을 닫아주면서 입력 된 값들을 초기로 돌려준다.
$("#zipCancle").on("click",function(e){
	e.preventDefault();
	$(".zipModal").hide();
	$("[name = 'postnum']").val("${postnum}");
	$("[name = 'addr']").val("${addr}");
	$("[name = 'detailaddress']").val("${detailaddress}");
	$("[name = 'seealso']").val("${seealso}");
});

//이 주소로 받기 클릭시 유효성검사를하고 모두 통과한다면 모달창을 닫아준다.
$("#zipSubmit").on("click",function(e){
	e.preventDefault();
	const zipForm = $(".zipForm");
	
	let npostnum = $("#postnum").val();
	let ndetailaddress = $("#detailaddress").val();

	if(postnum ==""){
    	alert("우편번호를 입력해주세요");
    }
    else if(detailaddress == ""){
    	alert("상세주소를 입력해주세요");
    }
    else{
    	$(".zipModal").hide();
    }
});

//리뷰 작성 버튼 클릭시 리뷰 작성 폼 보여주거나 닫기
let flaut = false;
$("#onReview").on("click",function(e){
	if(flaut == true){
		$(".reviews-form").hide();
		$("#reviewTxt").val("");		
		flaut = false;
	}
	else{
	flaut = true;
	e.preventDefault();
	$(".reviews-form").show();
	}
})

//작성하기 버튼 클릭시 
$(".reviewBtn").on("click",function(e){
	e.preventDefault();
	let reviewcontents = $("#reviewTxt").val();
	if(useremail == ""){
		alert("로그인 후 이용해주세요");
		return;
	}
	else if(reviewcontents == null){
		alert("내용을 입력해주세요");
		return;
	}
	buyService.addreview(
		{useremail:useremail, username:username, productnum:productnum, reviewcontents:reviewcontents},		
		function(result){
			if(result > 0){
				alert(result+"번 리뷰 작성 성공!");
				location.reload();
			}
		}
	);
});

//리뷰 삭제
$(".del").on("click",function(e){
	e.preventDefault();
	let reviewnum = $(this).attr('href');
	buyService.drop(
			reviewnum,
		function(result){
			if(result == "success"){
				alert(reviewnum+"번 리뷰 삭제 성공!");
				location.reload();
			}
		}
	)
})

//리뷰 수정 버튼 눌렀을 시 수정 버튼은 숨기고 수정 완료버튼 보여주기 
let mf = false;
$("#mdf").on("click",function(e){
	e.preventDefault();
	if(mf == true){
		alert("이미 수정중인 리뷰가 있습니다");
		return;
	}
	mf = true;
	$(".inlinereview").attr("readonly",false);
	$(this).hide();
	$(this).next().show();
})

//수정 완료 버튼
$("#mdfOk").on("click",function(e){
	e.preventDefault();
	mf == false;
	let reviewcontents = $(".inlinereview").val();
	let reviewnum = $(this).attr('href');
	 buyService.modify(
		{reviewcontents:reviewcontents, reviewnum:reviewnum},
		function(result){
			if(result=="success"){
				alert("리뷰를 수정 하였습니다.");
				$(".inlinereview").attr("readonly",true);
				$(this).show();
				$(this).prev().hide();
				location.reload();
			}
		} 
	)
})
</script>
</html>

board.js

const buyService = (function(){
	//상품 구매 ajax
	function insert(buy,callback){
		$.ajax({
			type:"POST",
			url:"/buy/buyProduct",
			data:JSON.stringify(buy),
			contentType:"application/json; charset=utf-8",
			success:function(result){
				if(callback){
					callback(result);
				}
			},
			error:function(status){
				alert("구매 실패");
			}
		})
	}
	//리뷰 작성 ajax
	function review(review, callback){
		$.ajax({
			type:"POST",
			url:"/buy/review",
			data:JSON.stringify(review),
			contentType:"application/json; charset=utf-8",
			success:function(result){
				if(callback){
					callback(result);
				}
			},
			error:function(err){
				alert("리뷰 작성 실패!");
			}
		})
	}
	
	//리뷰 삭제
	function reviewDelete(review,callback){
		$.ajax({
			type:"POST",
			url:"/buy/reviewDelete",
			data:JSON.stringify(review),
			contentType:"application/json; charset=utf-8",
			success:function(result){
				if(callback){
					callback(result);
				}
			},
			error:function(err){
				alert("리뷰를 삭제하지 못했습니다. 다시 시도해 주세요.");
			}
		})
	}
	
	//리뷰 수정
	function reviewModify(review,callback){
		$.ajax({
			type:"PUT",
			url:"/buy/"+review.reviewnum,
			data:JSON.stringify(review),
			contentType:"application/json; charset=utf-8",
			success:function(result){
				if(callback){
					callback(result);
				}
			},
			error:function(err){
				alert("리뷰 수정 실패. 다시 시도해주세요~");
			}
		})
	}
	
	
	return {add:insert,addreview:review, drop:reviewDelete, modify:reviewModify};
})();

ProductController

productController 밑에 코드를 추가해준다.

@GetMapping("/board")
	public void goboard(ProductDTO productnum, Model model) {
		String filename = service.getFilename(productnum.getProductnum());
		ProductDTO prod = service.getproduct(productnum.getProductnum());
		List<ReviewDTO> review = service.getReview(productnum.getProductnum());
		//board.jsp로 넘겨줄 데이터
		model.addAttribute("filename", filename);
		model.addAttribute("product", prod);
		model.addAttribute("review", review);
	}

BuyController

package com.my.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.my.domain.BuyProductDTO;
import com.my.domain.ReviewDTO;
import com.my.service.UserService;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RestController
@RequestMapping("/buy/*")
@Log4j
public class BuyController {
	@Setter(onMethod_ = @Autowired)
	private UserService service;
	
	//상품 구매
	@PostMapping(value ="/buyProduct", consumes = "application/json")
	public ResponseEntity<String> buyProduct(@RequestBody BuyProductDTO buy){
		boolean check = service.buyProduct(buy);
		String productname = buy.getProductname();
		return check ? new ResponseEntity<String>(productname,HttpStatus.OK) : new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
	}
	
	//리뷰 등록
	@PostMapping(value ="/review", consumes = "application/json")
	public ResponseEntity<String> review(@RequestBody ReviewDTO review){
		return service.review(review) ? new ResponseEntity<String>(service.getReviewnum()+"",HttpStatus.OK) : new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
	}
	
	//리뷰 삭제
	@PostMapping(value="/reviewDelete", consumes = "application/json")
	public ResponseEntity<String> reviewDelete(@RequestBody int reviewnum){
		return service.reviewDelete(reviewnum) ? new ResponseEntity<String>("success",HttpStatus.OK) : new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
	}
	
	//리뷰 수정
	@RequestMapping(method = {RequestMethod.PUT, RequestMethod.PATCH}, value="/{reviewnum}", consumes = "application/json")
	public ResponseEntity<String> reviewModify(@RequestBody ReviewDTO review){
		return service.reviewModify(review) ? new ResponseEntity<String>("success",HttpStatus.OK):new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
	}
}
profile
화이팅!

0개의 댓글