• Home
  • About
    • GreenteaPIE photo

      GreenteaPIE

      GreenteaPIE's Development Story.

    • Learn More
    • Email
    • Github
    • Youtube
  • Posts
    • All Posts
    • All Tags
  • Projects

20 - DB Spring 어드민 상품 관리

30 May 2023

Reading time ~26 minutes

사용한 플랫폼 : Spring, Oracle

1. 상품 관리 리스트 페이지(페이징 처리 + 검색 기능)

AdminMapper.java 에 추가

	public List<ProductVO> getProductList(Criteria cri); // 등록된 상품 전체 가져오기

	public int productGetTotal(Criteria cri);// 등록된 상품의 수

AdminMapper.xml 에 추가

	<!-- 등록된 상품목록 전체 가져오기 -->
	<select id="getProductList" resultType="com.db.model.ProductVO">
		<![CDATA[
			select * from (
				select /*+INDEX_DESC(SYS_C008634)*/
					rownum as rn, num, bname, pname, pGender, kind, imgurl, psize, balance, price, explain, writedate, readcount, purchasedNum, discountrate
				from product
				where 
				]]>
		<if test="keyword !=null">
			pname like '%'||#{keyword}||'%' and
		</if>
		<![CDATA[
			rownum <= #{pageNum}*#{amount}
			)
			where rn > (#{pageNum}-1)*#{amount}
			order by writedate desc
			]]>
	</select>

	<!-- 등록된 상품 갯수 가져오기 -->
	<select id="productGetTotal" resultType="int">
		select count(*) from product
		<if test="keyword!=null">
			where pname like '%' || #{keyword} || '%'
		</if>
	</select>

AdminService.java 에 추가

 public List<ProductVO> getProductList(Criteria cri); // 등록된 상품 전체 가져오기

 public int productGetTotal(Criteria cri);// 등록된 상품의 수

AdminServiceImpl.java 에 추가

	@Override
	public List<ProductVO> getProductList(Criteria cri) {
		return mapper.getProductList(cri);
	}

	@Override
	public int productGetTotal(Criteria cri) {
		return mapper.productGetTotal(cri);
	}

AdminController.java 에 추가

	// 상품 리스트
	@GetMapping("adminProductList")
	public void adminProductListGET(Criteria cri, Model model) {
		logger.info("adminController, adminProductListGET 진입 .... ");
		/* 상품 목록 출력 */
		List productList = adminService.getProductList(cri);
		model.addAttribute("productList", productList);
        
         /* 페이지 이동 인터페이스 데이터 */
		int total = adminService.productGetTotal(cri);
		PageMakerDTO pageMaker = new PageMakerDTO(cri, total);
		model.addAttribute("pageMaker", pageMaker);
    }

adminProductList.jsp 에 추가

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css" href="../resources/css/adminTheme.css">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.0/css/all.min.css" rel="stylesheet">
<!-- Libraries Stylesheet -->
<link href="../resources/css/owl.carousel.min.css" rel="stylesheet">
<!-- Customized Bootstrap Stylesheet -->
<link href="../resources/css/style.css" rel="stylesheet">
<!-- font-awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
* {
	margin: 0 auto;
}

/* 페이지 버튼 인터페이스 */
.pageMaker_wrap {
	text-align: center;
	margin-top: 30px;
	margin-bottom: 40px;
}

.pageMaker_wrap a {
	color: black;
}

.pageMaker {
	list-style: none;
	display: inline-block;
}

.pageMaker_btn {
	float: left;
	width: 40px;
	height: 40px;
	line-height: 40px;
	margin-left: 20px;
}

.next, .prev {
	border: 1px solid #ccc;
	padding: 0 10px;
}

.active { /* 현재 페이지 버튼 */
	border: 2px solid black;
	font-weight: 400;
}
</style>
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<jsp:include page="../header.jsp"></jsp:include>
</head>
<body>
	<hr>
	<div>
		<!-- Page Header Start -->
		<div class="container bg-secondary mb-3" style="max-width: 800px;">
			<div class="d-flex flex-column align-items-center justify-content-center" style="min-height: 150px">
				<h1 class="font-weight-semi-bold text-uppercase mb-3">상품 관리</h1>
				<p class="m-0">
					<!-- 					<a href="adminProductList">목록으로</a> -->
				</p>
			</div>
		</div>
	</div>
	<!-- Page Header End -->
	<div id="wrap">
		<div align="center" style="width: 90%">
			<div align="right" style="margin-bottom: 10px;">
				<button class="btn btn-primary px-3" onclick="location.href='/admin/adminProductWriteForm'">상품등록</button>
				<!-- 	<button class="btn btn-primary px-3">선택삭제</button>
				<button class="btn btn-primary px-3">전체삭제</button> -->
			</div>
			<div align="center">
				<table class="table" style="text-align: center; border: 1px solid #dddddd;">
					<thead>
						<tr>
							<!-- <th style="background-color: #eeeeee; text-align: center;">
							<input type="checkbox" class="individual_cart_checkbox input_size_20">
							</th> -->
							<th style="background-color: #eeeeee; text-align: center;">상품번호</th>
							<th style="background-color: #eeeeee; text-align: center;">브랜드</th>
							<th style="background-color: #eeeeee; text-align: center;">상품이름</th>
							<th style="background-color: #eeeeee; text-align: center;">성별</th>
							<th style="background-color: #eeeeee; text-align: center;">카테고리</th>
							<th style="background-color: #eeeeee; text-align: center;">사이즈</th>
							<th style="background-color: #eeeeee; text-align: center;">재고</th>
							<th style="background-color: #eeeeee; text-align: center;">상품 등록일</th>
						</tr>
					</thead>
					<tbody>
						<c:forEach items="${productList }" var="productList">
							<tr>
								<!-- <td>
									<input type="checkbox">
								</td> -->
								<td><c:out value="${productList.num }"></c:out></td>
								<td><c:out value="${productList.bname }"></c:out></td>
								<td><a class="move" href='<c:out value="${productList.num }"/>'>
										<c:out value="${productList.pname }"></c:out>
									</a></td>
								<td><c:if test="${productList.pGender==1 }">남자</c:if> <c:if test="${productList.pGender==2 }">여자</c:if> <c:if test="${productList.pGender==3 }">공용</c:if></td>
								<td><c:if test="${productList.kind==1 }">상의	</c:if> <c:if test="${productList.kind==2 }">하의	</c:if> <c:if test="${productList.kind==3 }">잡화	</c:if></td>
								<td><c:out value="${productList.psize }"></c:out></td>
								<td><c:out value="${productList.balance }"></c:out></td>
								<td><fmt:formatDate value="${productList.writedate  }" type="date" /></td>
							</tr>
						</c:forEach>
					</tbody>
				</table>
				<!-- 페이지 이동 인터페이스 영역 -->
				<div class="pageMaker_wrap">
					<ul class="pageMaker">
						<!-- 이전 버튼 -->
						<c:if test="${pageMaker.prev}">
							<li class="pageMaker_btn prev"><a href="${pageMaker.startPage - 1}">
									<i class="fa fa-angles-left"></i>
								</a></li>
						</c:if>
						<!-- 페이지 번호 -->
						<c:forEach begin="${pageMaker.startPage}" end="${pageMaker.endPage}" var="num">
							<li class="pageMaker_btn ${pageMaker.cri.pageNum == num ? "active":""}"><a href="${num}">${num}</a></li>
						</c:forEach>
						<!-- 다음 버튼 -->
						<c:if test="${pageMaker.next}">
							<li class="pageMaker_btn next"><a href="${pageMaker.endPage + 1 }">
									<i class="fa fa-angles-right"></i>
								</a></li>
						</c:if>
					</ul>
				</div>
				<!-- 페이징 끝  -->
				<!-- 검색 영역 -->
				<div class="search_wrap">
					<form id="searchForm" action="/admin/adminProductList" method="get">
						<div class="input-group" style="max-width: 400px;">
							<input type="text" placeholder="상품명을 입력 해주세요." class="form-control" name="keyword" style="width: 10%" value='<c:out value="${pageMaker.cri.keyword}"></c:out>'> <input type="hidden" name="pageNum" value='<c:out value="${pageMaker.cri.pageNum }"></c:out>'> <input type="hidden" name="amount" value='${pageMaker.cri.amount}'>
							<div class="input-group-append">
								<button class="btn btn-primary px-3">
									<i class="fa fa-search"></i>
								</button>
							</div>
						</div>
					</form>
				</div>
				<form id="moveForm" action="/admin/adminProductList" method="get">
					<input type="hidden" name="pageNum" value="${pageMaker.cri.pageNum}"> <input type="hidden" name="amount" value="${pageMaker.cri.amount}"> <input type="hidden" name="keyword" value="${pageMaker.cri.keyword}">
				</form>
			</div>
		</div>
	</div>
	<hr>
	<script>


		$(".table tbody tr").each(
				function() {
					var row = $(this);

					// Get the original price
					var goodsPriceText = row.find("td:eq(8)").text().trim();
					var goodsPrice = parseFloat(goodsPriceText.replace(
							/[^0-9.-]+/g, ""));

					// Get the discount rate
					var discountRateText = row.find("td:eq(9)").text().trim();
					var discountRate = parseFloat(discountRateText) / 100;

					// Calculate the discounted price
					var discountPrice = goodsPrice * (1 - discountRate);

					// Format the discounted price with the currency symbol
					var formattedDiscountPrice = '₩'
							+ new Intl.NumberFormat('ko-KR')
									.format(discountPrice.toFixed(0));

					// Set the formatted discounted price in the span_discount element
					row.find(".span_discount").text(formattedDiscountPrice);
				});

		let moveForm = $('#moveForm');

		/* 페이지 이동 버튼 */
		$(".pageMaker_btn a").on("click", function(e) {

			e.preventDefault();
			moveForm.find("input[name='pageNum']").val($(this).attr("href"));
			moveForm.submit();
		});

	</script>
</body>
<jsp:include page="../footer.jsp"></jsp:include>
</html>

_config.yml

2. 상품 등록 기능

AdminController.java 에 추가

	// 상품 등록
	@GetMapping("adminProductWriteForm")
	public void adiminProductWriteFormGET() {
		logger.info("adminController, adminProductWriteForm 진입 ... ");
	}

adminProductWriteForm.jsp 추가

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://cdn.ckeditor.com/ckeditor5/38.0.1/classic/ckeditor.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="//code.jquery.com/ui/1.8.18/jquery-ui.min.js"></script>
<style>
.ck-content {
	height: 200px;
}

input {
	width: 98%;
	padding: 5px 1%;
	border: 0;
}

select {
	width: 98%;
	padding: 5px 1%;
	border: 0;
}

tr th td {
	vertical-align: middle;
	border-collapse: collapse;
}

.step_val { /* 할인 가격 문구 */
	padding-top: 5px;
	font-weight: 500;
}

.ck_warn { /* 입력란 공란 경고 태그 */
	display: none;
	padding-top: 10px;
	text-align: center;
	color: #e05757;
	font-weight: 300;
}

#result_card img {
	max-width: 100%;
	height: auto;
	display: block;
	padding: 5px;
	margin-top: 10px;
	margin: auto;
}

#result_card {
	position: relative;
}

.imgDeleteBtn {
	position: absolute;
	top: 0;
	right: 5%;
	background-color: #ef7d7d;
	color: wheat;
	font-weight: 900;
	width: 30px;
	height: 30px;
	border-radius: 50%;
	line-height: 26px;
	text-align: center;
	border: none;
	display: block;
	cursor: pointer;
}
</style>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.0/css/all.min.css" rel="stylesheet">
<!-- Libraries Stylesheet -->
<link href="../resources/css/owl.carousel.min.css" rel="stylesheet">
<!-- Customized Bootstrap Stylesheet -->
<link href="../resources/css/style.css" rel="stylesheet">
<jsp:include page="../header.jsp"></jsp:include>
</head>
<body>
	<hr>
	<div>
		<!-- Page Header Start -->
		<div class="container bg-secondary mb-3" style="max-width: 800px;">
			<div class="d-flex flex-column align-items-center justify-content-center" style="min-height: 150px">
				<h1 class="font-weight-semi-bold text-uppercase mb-3">상품 등록</h1>
				<p class="m-0"></p>
			</div>
		</div>
	</div>
	<!-- Page Header End -->
	<!-- Shop Detail Start -->
	<div align="center">
		<form action="/admin/adminProductWriteForm" method="post" id="enrollForm" enctype="multipart/form-data">
			<table class="table" style="text-align: center; border: 1px solid #dddddd; width: 70%;">
				<tr>
					<th style="background-color: #eeeeee; text-align: center; width: 200px;">브랜드</th>
					<td colspan="4"><input id="bName_select" placeholder="옆의 버튼을 눌러 브랜드를 선택하세요." readonly="readonly" style="Width: 75%"> <input id="bName_input" name="bname" type="hidden">
						<button id="select_brand" class="btn btn-primary px-3" style="width: 150px">선택</button>
						<div>
							<span class="ck_warn bname_warn">브랜드를 선택해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">상품명</th>
					<td colspan="4"><input id="pnameId" name="pname" placeholder="상품명을 입력하세요.">
						<div>
							<span class="ck_warn pname_warn">등록하실 상품명을 입력해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">성별</th>
					<td colspan="4"><label> <input type="radio" name="pGender" value="1"> 남성용
					</label> <label> <input type="radio" name="pGender" value="2"> 여성용
					</label> <label> <input type="radio" name="pGender" value="3"> 공용
					</label>
						<div>
							<span class="ck_warn pGender_warn">등록하실 상품의 성별을 선택해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">카테고리</th>
					<td colspan="4"><label> <input type="radio" name="kind" value="1"> 상의
					</label> <label> <input type="radio" name="kind" value="2"> 하의
					</label> <label> <input type="radio" name="kind" value="3"> 잡화
					</label>
						<div>
							<span class="ck_warn kind_warn">등록하실 상품의 카테고리를 선택해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">사이즈</th>
					<td><select name="psize">
							<option value="" selected>Select</option>
							<option value="Free">Free</option>
							<option value="XS">XS</option>
							<option value="S">S</option>
							<option value="M">M</option>
							<option value="L">L</option>
							<option value="XL">XL</option>
							<option value="XXL">XXL</option>
					</select>
						<div>
							<span class="ck_warn psize_warn">등록하실 상품 사이즈를 선택해주세요.</span>
						</div></td>
					<th style="background-color: #eeeeee; text-align: center; width: 200px">재고</th>
					<td><input name="balance" type="number" placeholder="숫자만 입력하세요.">
						<div>
							<span class="ck_warn balance_warn">등록하실 상품의 재고를 입력해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">원가</th>
					<td><input name="price" type="number" value="0">
						<div>
							<span class="step_val">
								할인된 가격 (판매가) :
								<span class="span_discount"></span>
								원
							</span>
							<span class="ck_warn price_warn">등록하실 상품의 가격을 입력해주세요.</span>
						</div></td>
					<th style="background-color: #eeeeee; text-align: center; width: 200px">할인율</th>
					<td><input id="discount_interface" maxlength="2" value="0"> <input name="discountrate" type="hidden" value="0"></td>
				</tr>
				<tr>
					<th colspan="4" style="background-color: #eeeeee; text-align: center;">상품 설명</th>
				</tr>
				<tr>
					<td colspan="4">
						<div class="explainTextarea">
							<textarea name="explain" id="productExplainEditor"></textarea>
						</div>
						<div>
							<span class="ck_warn explain_warn">등록하실 상품의 설명을 입력해주세요.</span>
						</div>
					</td>
				</tr>
				<tr>
					<th colspan="2" style="background-color: #eeeeee; text-align: center;">상품 이미지 등록</th>
					<td colspan="4"><input type="file" multiple id="fileItem" name='uploadFile' style="height: 30px;"></td>
				</tr>
			</table>
		</form>
		<button class="btn btn-primary px-3" style="width: 150px" id="enrollBtn">등 록</button>
		<button class="btn btn-primary px-3" style="width: 150px" id="cancelBtn">취 소</button>
	</div>
	<hr>
	<script>
	let enrollForm=$("#enrollForm");
	
	/*위지윅*/
	/*상품설명 등록 */
	ClassicEditor
		.create(document.querySelector('#productExplainEditor'))
		.catch(error=>{
			console.error(error);
	});
	
	/*취소 버튼 */
	$("#cancelBtn").click(function(){
		location.href="/admin/adminProductList"
	});
	
	/*등록 버튼*/
	$("#enrollBtn").on("click",function(e){
		e.preventDefault();
		
		/*유효성 검사 체크 변수 선언*/
		let bnameCk = false;
		let pnameCk = false;
		let pGenderCk = false;
		let kindCk = false;
		let psizeCk = false;
		let balanceCk = false;
		let priceCk = false;
		let explainCk = false;
		
		/* 체크 대상 변수 */
		let bname = $("input[id='bName_select']").val();
		let pname = $("input[id='pnameId']").val().trim();
		let pGender = $("input[name='pGender']:checked").val();
		let kind = $("input[name='kind']:checked").val();
		let psize = $("select[name='psize']").val();
		let balance = parseInt($("input[name='balance']").val(), 10);
		let price = parseInt($("input[name='price']").val(), 10);
		let explain = $(".explainTextarea p").html();
		
		if(bname!== ''){
			$(".bname_warn").css('display','none');
			bnameCk = true;
		} else {
			$(".bname_warn").css('display','block');
			bnameCk = false;
		}
		
		if(pname!== ''){
			$(".pname_warn").css('display','none');
			pnameCk = true;
		} else {
			$(".pname_warn").css('display','block');
			pnameCk = false;
		}
		
		if(pGender){
			$(".pGender_warn").css('display','none');
			pGenderCk = true;
		} else {
			$(".pGender_warn").css('display','block');
			pGenderCk = false;
		}
		
		if(kind){
			$(".kind_warn").css('display','none');
			kindCk = true;
		} else {
			$(".kind_warn").css('display','block');
			kindCk = false;
		}
		
		if(psize){
			$(".psize_warn").css('display','none');
			psizeCk = true;
		} else {
			$(".psize_warn").css('display','block');
			psizeCk = false;
		}
		
		if (!isNaN(balance) && balance >= 0) {
			  $(".balance_warn").css('display', 'none');
			  balanceCk = true;
			} else {
			  $(".balance_warn").css('display', 'block');
			  balanceCk = false;
			}
		
		if (!isNaN(price) && price > 0) {
			  $(".price_warn").css('display', 'none');
			  priceCk = true;
			} else {
			  $(".price_warn").css('display', 'block');
			  priceCk = false;
			}
		
		if(explain != '<br data-cke-filler="true">'){
			$(".explain_warn").css('display','none');
			explainCk = true;
		} else {
			$(".explain_warn").css('display','block');
			explainCk = false;
		}			
		
		if(bnameCk && pnameCk && pGenderCk && kindCk && psizeCk && priceCk && balanceCk && explainCk){
			//alert('통과');
			enrollForm.submit();
		} else {
			return false;
		}
	});
	
	/*브랜드 선택*/
	$("#select_brand").click(function(e){
		e.preventDefault();
		
		let popUrl = "/admin/brandPop";
		let popOption = "width = 400px, height=500px, top=300px, left=300px, scrollbars=yes";
		
		window.open(popUrl,"브랜드 검색",popOption);	
		
	});
	
	
	/* 할인율 Input 설정 */
	$("#discount_interface").on("propertychange change keyup paste input", function(){
		
		let userInput = $("#discount_interface");
		let discountInput = $("input[name='discountrate']");
		
		let discountRate = userInput.val();					// 사용자가 입력할 할인값
		let sendDiscountRate = discountRate ;				// 서버에 전송할 할인값
		
		discountInput.val(sendDiscountRate);	
		
		let goodsPrice = $("input[name='price']").val();			// 원가
		let discountPrice = goodsPrice * (1 - sendDiscountRate / 100);		// 할인가격
		        
		$(".span_discount").html(discountPrice);
		
	});	
	
	$("input[name='price']").on("change", function(){
		
		let userInput = $("#discount_interface");
		let discountInput = $("input[name='discountrate']");
		
		let discountRate = userInput.val();					// 사용자가 입력할 할인값
		let sendDiscountRate = discountRate ;				// 서버에 전송할 할인값
		
		discountInput.val(sendDiscountRate);	
		
		let goodsPrice = $("input[name='price']").val();			// 원가
		let discountPrice = goodsPrice * (1 - sendDiscountRate / 100);		// 할인가격
		        
		$(".span_discount").html(discountPrice);
		
	});
	
	/*이미지 업로드*/
	$("input[type='file']").on("change", function(e){
		alert("동작 확인");

		/* /*이미지 존재 시 삭제
		if($(".imgDeleteBtn").length > 0){
			deleteFile();
		} */
		
		let formData = new FormData();
		let fileInput=$('input[name="uploadFile"]');
		let fileList=fileInput[0].files;
		let fileObj=fileList[0];
		
		console.log("fileList : "+ fileList);
		console.log("fileObj : " + fileObj);
		console.log("fileName : "+ fileObj.name);
		console.log("fileSize : " + fileObj.size);
		console.log("fileType(MimeType) : " + fileObj.type);

		if(!fileCheck(fileObj.name, fileObj.size)){
			return false;
		}
		
		alert("통과");
		formData.append("uploadFile", fileObj);
		
		$.ajax({
			url: '/admin/uploadAjaxAction', //서버로 요청을 보낼 url
			processData : false, // 서버로 전송할 데이터를 queryString 형태로 변환할지 여부
			contentType : false, // 서버로 전송되는 데이터의 content-type 
			data : formData, // 서버로 전송할 데이터
			type : 'POST', // 서버로 요청 타입 (get, post)
			dataType : 'json', // 서버로부터 반환받을 데이터 타입 
			success : function(result){
				console.log(result);
				showUploadImage(result);
			},
			error: function(result){
				console.log(result);
				alert("업로드 실패");
			}
		});
	});
	
	
	/*var, mehtod related with attachFile*/
	let regex  = new RegExp("(.*?)\.(jpg|png)$");
	let maxSize = 1048576; //1MB
	
	function fileCheck(fileName, fileSize){
		if(fileSize >= maxSize){
			alert("파일 사이즈 초과");
			return false;	
		}
		
		if(!regex.test(fileName)){
			alert("해당 종류의 파일은 업로드 할 수 없습니다.");
			return false;
		}
		return true;
	}
	
	function showUploadImage(uploadResultArr) {
		console.log("showUploadImage called");
		console.log(uploadResultArr);

		  let uploadResult = $("#uploadResult");

		  let str = "";
		 
		  for (let i = 0; i < uploadResultArr.length; i++) {
		    let obj = uploadResultArr[i];

		    if (!obj || !obj.imgUrl) {
		      console.log("Invalid object or missing imgUrl property");
		      continue;
		    }

		    let fileCallPath = "/admin/display?fileName=" + obj.imgUrl;
		    
			console.log("obj.imgUrl : " + obj.imgUrl);
		    console.log("fileCallPath: " + fileCallPath);
		    str += "<div id='result_card'>";
		    str += "<img src='"+fileCallPath+"'>";
		    str += "<div class='imgDeleteBtn'>x</div>";
		    str += "</div>";
		    
		    console.log("Appending the following HTML string: " + str);
		  }
			uploadResult.html(str);
		}
	

	</script>
</body>
<jsp:include page="../footer.jsp"></jsp:include>
</html>

_config.yml

AdminController.java 에 추가

이미지 업로드에 사용하는 uploadAjaxAction 메서드를 만들어 준다.

	@PostMapping(value = "uploadAjaxAction", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<String> uploadAjaxActionPOST(@RequestParam("uploadFile") MultipartFile[] uploadFile,
			HttpSession session) {
		logger.info("uploadAjaxActionPOST 진입 ... ");

		String osName = System.getProperty("os.name").toLowerCase();
		String uploadFolder;

		if (osName.contains("win")) { // Windows
			System.out.println("현재 접속 중인 os : " + osName);
			uploadFolder = "C:\\Users\\User\\OneDrive\\바탕 화면\\Develop\\DiamondBlakc-SpringVer\\src\\main\\webapp\\resources\\img\\";
			System.out.println("지금 설정된 uploadFolder 경로 : " + uploadFolder);
		} else if (osName.contains("nix") || osName.contains("nux") || osName.contains("mac")) { // Linux, macOS
			System.out.println("현재 접속 중인 os : " + osName);
			uploadFolder = System.getProperty("user.home") + "/workspace/upload/tmp";
			System.out.println("지금 설정된 uploadFolder 경로 : " + uploadFolder);
		} else {
			throw new UnsupportedOperationException("지원되지 않는 운영체제입니다.");
		}

		/* 폴더 생성 */
		File uploadPath = new File(uploadFolder);

		if (!uploadPath.exists()) {
			uploadPath.mkdirs();
		}

		List<ProductVO> productVOList = new ArrayList<>();

		for (MultipartFile multipartFile : uploadFile) {

			/* 파일 이름 */
			String uploadFileName = multipartFile.getOriginalFilename();

			/* uuid 적용 파일 이름 */
//				String uuid = UUID.randomUUID().toString();

//				uploadFileName = uuid + "_" + uploadFileName;
			uploadFileName = uploadFileName;

			/* 파일 위치, 파일 이름을 합친 File 객체 */
			File saveFile = new File(uploadPath, uploadFileName);

			/* 파일 저장 */
			try {
				multipartFile.transferTo(saveFile);

				/* 이미지 객체 */
				ProductVO productVO = new ProductVO();
				productVO.setImgUrl(uploadFileName); // 파일 경로를 imgUrl로 저장
				productVOList.add(productVO);

			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		// imgUrl 값을 세션에 저장
		session.setAttribute("imgUrlList", productVOList);

		// You can build a JSON response object here with the desired data
		String jsonResponse = "{\"message\": \"File uploaded successfully.\"}";
		return ResponseEntity.status(HttpStatus.OK).body(jsonResponse);
	}

AdminMapper.java 에 추가

브랜드 목록을 불러와 상품 브랜드를 선택할 수 있는 메서드를 만들어 준다.

	public List<BrandVO> getBrandList(Criteria cri); // 등록된 브랜드 전체 가져오기

     public int brandGetTotal(Criteria cri);// 등록된 브랜드의 수

AdminMapper.xml 에 추가

	<!-- 등록된 브랜드 목록 가져오기 + 검색 -->
	<select id="getBrandList" resultType="com.db.model.BrandVO">
	<![CDATA[
			select * from (
				select /*+INDEX_DESC(SYS_C008746)*/
					rownum as rn, bname
				from brand
				where 
				]]>
		<if test="keyword !=null">
			bname like '%'||#{keyword}||'%' and
		</if>
		<![CDATA[
			rownum <= #{pageNum}*#{amount}
			)
			where rn > (#{pageNum}-1)*#{amount}
			]]>
	</select>

	<!-- 등록된 브랜드 갯수 가져오기 -->
	<select id="brandGetTotal" resultType="int">
		select count(*) from brand
		<if test="keyword!=null">
			where bname like '%' || #{keyword} || '%'
		</if>
	</select>

AdminService.java 에 추가

	public List<BrandVO> getBrandList(Criteria cri); // 등록된 브랜드 전체 가져오기

	public int brandGetTotal(Criteria cri);// 등록된 브랜드의 수

AdminServiceImpl.java 에 추가

	@Override
	public List<BrandVO> getBrandList(Criteria cri) {
		return mapper.getBrandList(cri);
	}

	@Override
	public int brandGetTotal(Criteria cri) {
		return mapper.brandGetTotal(cri);
	}

AdminController.java 에 추가

	// 상품등록 시 브랜드 검색창
	@GetMapping("brandPop")
	public void brandPopGET(Criteria cri, Model model) throws Exception {
		logger.info("adminController, brandPop 진입 ... ");

		/* 브랜드 목록 출력 */
		List brandList = adminService.getBrandList(cri);

		if (!brandList.isEmpty()) {
			model.addAttribute("brandList", brandList); // 브랜드 존재 경우
		} else {
			model.addAttribute("listCheck", "empty"); // 브랜드 존재하지 않을 경우
		}

		/* 페이지 이동 인터페이스 데이터 */
		int total = adminService.brandGetTotal(cri);
		PageMakerDTO pageMaker = new PageMakerDTO(cri, total);
		model.addAttribute("pageMaker", pageMaker);

	}

brandPop.jsp 추가

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.0/css/all.min.css" rel="stylesheet">
<!-- Libraries Stylesheet -->
<link href="../resources/css/owl.carousel.min.css" rel="stylesheet">
<!-- Customized Bootstrap Stylesheet -->
<link href="../resources/css/style.css" rel="stylesheet">
<style>
/* 페이지 버튼 인터페이스 */
.pageMaker_wrap {
	text-align: center;
	margin-top: 30px;
	margin-bottom: 40px;
}

.pageMaker_wrap a {
	color: black;
}

.pageMaker {
	list-style: none;
	display: inline-block;
}

.pageMaker_btn {
	float: left;
	width: 40px;
	height: 40px;
	line-height: 40px;
	margin-left: 20px;
}

.next, .prev {
	border: 1px solid #ccc;
	padding: 0 10px;
}

.next a, .prev a {
	color: #ccc;
}

.active { /* 현재 페이지 버튼 */
	border: 2px solid black;
	font-weight: 400;
}

.table_empty {
	text-align: center;
	font-weight: 400;
}
</style>
</head>
<body>
	<div>
		<!-- Page Header Start -->
		<div class="container bg-secondary mb-3" style="max-width: 800px;">
			<div class="d-flex flex-column align-items-center justify-content-center" style="min-height: 150px">
				<h1 class="font-weight-semi-bold text-uppercase mb-3">브랜드 목록</h1>
				<!-- 검색 영역 -->
				<div class="search_wrap">
					<form id="searchForm" action="/admin/brandPop" method="get">
						<div class="input-group" style="max-width: 400px;">
							<input type="text" placeholder="브랜드 명을 입력 해주세요." class="form-control" name="keyword" style="width: 10%" value='<c:out value="${pageMaker.cri.keyword}"></c:out>'> <input type="hidden" name="pageNum" value='<c:out value="${pageMaker.cri.pageNum }"></c:out>'> <input type="hidden" name="amount" value='${pageMaker.cri.amount}'>
							<div class="input-group-append">
								<button class="btn btn-primary px-3">
									<i class="fa fa-search"></i>
								</button>
							</div>
						</div>
					</form>
				</div>
			</div>
		</div>
	</div>
	<!-- 게시물 O -->
	<c:if test="${listCheck != 'empty'}">
		<div class="table_exist">
			<table class="table" style="text-align: center; border: 1px solid #dddddd;">
				<c:forEach items="${brandList}" var="brandList">
					<tr>
						<td><a class="move" href='<c:out value="${brandList.bname}"/>' data-bname='<c:out value="${brandList.bname}"/>'>
								<c:out value="${brandList.bname}" />
							</a></td>
					</tr>
				</c:forEach>
			</table>
		</div>
	</c:if>
	<!-- 게시물 x -->
	<c:if test="${listCheck == 'empty'}">
		<div class="table_empty">등록된 브랜드가 없습니다.</div>
	</c:if>
	<form id="moveForm" action="/admin/brandPop" method="get">
		<input type="hidden" name="pageNum" value="${pageMaker.cri.pageNum}"> <input type="hidden" name="amount" value="${pageMaker.cri.amount}"> <input type="hidden" name="keyword" value="${pageMaker.cri.keyword}">
	</form>
	<script>
		let searchForm = $('#searchForm');
		let moveForm = $('#moveForm');

		/* 브랜드 검색 버튼 동작 */
		$("#searchForm button").on("click", function(e) {

			e.preventDefault();

			/* 검색 키워드 유효성 검사 */
			if (!searchForm.find("input[name='keyword']").val()) {
				alert("키워드를 입력하십시오");
				return false;
			}

			searchForm.find("input[name='pageNum']").val("1");

			searchForm.submit();

		});

		/* 브랜드 선택 및 팝업창 닫기 */
		$(".move").on("click", function(e) {

			e.preventDefault();
			let brandHref = $(this).attr("href");
			let brandName = $(this).data("bname");

			// 			console.log("brandHref:", brandHref);
			// 			console.log("brandName:", brandName);
			// 			console.log("bName_select:", $(opener.document).find("#bName_select").length); // 디버깅을 위한 출력
			// 			console.log("bName_input:", $(opener.document).find("#bName_input").length); // 디버깅을 위한 출력

			$(opener.document).find("#bName_select").val(brandHref);
			$(opener.document).find("#bName_input").val(brandName);

			window.close();

		});
	</script>
</body>
</html>

_config.yml

AdminMapper.java 에 추가

상품 등록 쿼리를 실행할 메서드를 만들어 준다.

	public void productEnroll(ProductVO product); // 상품 추가

AdminMapper.xml 에 추가

	<!-- 상품등록 -->
	<insert id="productEnroll">
		<selectKey keyProperty="num" resultType="int" order="BEFORE">
			select
			product_seq.nextval + 256 from dual
		</selectKey>
		insert into product(num, pGender, bname, pname, kind,
		imgurl, psize,
		price, discountrate, balance, purchasednum, explain)
		values(#{num},
		#{pGender}, #{bname}, #{pname}, #{kind}, #{imgUrl, jdbcType=VARCHAR},
		#{psize}, #{price},
		#{discountrate}, #{balance}, #{purchasedNum},
		#{explain})
	</insert>

AdminService.java 에 추가

	public void productEnroll(ProductVO product); // 상품 추가

AdminServiceImpl.java 에 추가

	@Override
	public void productEnroll(ProductVO product) {
		mapper.productEnroll(product);

	}

AdminController.java 에 추가

	// 상품 등록
	@PostMapping("adminProductWriteForm")
	public String adminProductWriteFormPOST(ProductVO product, HttpSession session, RedirectAttributes rttr) {
		logger.info("adminController, adminProductWriteForm POST 진입 ... ");

		// 세션에서 imgUrl 값 가져오기
		List<ProductVO> imgUrlList = (List<ProductVO>) session.getAttribute("imgUrlList");

		// imgUrl 값이 null이 아니고 리스트에 값들이 있는 경우 설정
		if (imgUrlList != null && !imgUrlList.isEmpty()) {
			// 첫 번째 이미지 url을 가져옵니다.
			String imgUrl = imgUrlList.get(0).getImgUrl();
			product.setImgUrl(imgUrl);
		}

		// imgUrl 값 사용 후 세션에서 제거 (선택사항)
		session.removeAttribute("imgUrlList");

		adminService.productEnroll(product); // 데이터베이스에 저장
		rttr.addFlashAttribute("enroll_result", product.getPname());
		return "redirect:/admin/adminProductList";
	}

3. 상품 수정 기능

AdminMapper.java 에 추가

상품의 디테일을 보여주는 페이지를 먼저 만들어 준다.

	public ProductVO productGetDetail(int num); // 상품 정보 읽기

AdminMapper.xml 에 추가

	<!-- 상품정보 -->
	<select id="productGetDetail"
		resultType="com.db.model.ProductVO">
		select * from product where num=#{num}
	</select>

AdminService.java 에 추가

	public ProductVO productGetDetail(int num); // 상품 정보 읽기

AdminServiceImpl.java 에 추가

	@Override
	public ProductVO productGetDetail(int num) {
		System.out.println("(service) productGetDetail ..... " + num);
		return mapper.productGetDetail(num);
	}

adminProductDetail.jsp 에 추가

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.0/css/all.min.css" rel="stylesheet">
<!-- Libraries Stylesheet -->
<link href="../resources/css/owl.carousel.min.css" rel="stylesheet">
<!-- Customized Bootstrap Stylesheet -->
<link href="../resources/css/style.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<script src="https://cdn.ckeditor.com/ckeditor5/38.0.1/classic/ckeditor.js"></script>
<style>
.ck-content {
	height: 200px;
}

input {
	width: 98%;
	padding: 5px 1%;
	border: 0;
	text-aling: center;
}

select {
	width: 98%;
	padding: 5px 1%;
	border: 0;
}

tr th td {
	vertical-align: middle;
	border-collapse: collapse;
}

.step_val { /* 할인 가격 문구 */
	padding-top: 5px;
	font-weight: 500;
}

.ck_warn { /* 입력란 공란 경고 태그 */
	display: none;
	padding-top: 10px;
	text-align: center;
	color: #e05757;
	font-weight: 300;
}
</style>
<jsp:include page="../header.jsp"></jsp:include>
</head>
<body>
	<hr>
	<div>
		<!-- Page Header Start -->
		<div class="container bg-secondary mb-3" style="max-width: 800px;">
			<div class="d-flex flex-column align-items-center justify-content-center" style="min-height: 150px">
				<h1 class="font-weight-semi-bold text-uppercase mb-3">상품 정보</h1>
				<p class="m-0">
					<a href="adminProductList">Product Info</a>
				</p>
			</div>
		</div>
	</div>
	<!-- Page Header End -->
</head>
<!-- Shop Detail Start -->
<div align="center">
	<table class="table" style="text-align: center; border: 1px solid #dddddd; width: 70%;">
		<tr>
			<th style="background-color: #eeeeee; text-align: center; width: 200px;">브랜드</th>
			<td colspan="3"><input id="bname" value="<c:out value="${productDetail.bname }"/>" disabled="disabled"></td>
		</tr>
		<tr>
			<th style="background-color: #eeeeee; text-align: center;">상품명</th>
			<td colspan="3"><input id="pname" value="<c:out value="${productDetail.pname }"/>" disabled="disabled"></td>
		</tr>
		<tr>
			<th style="background-color: #eeeeee; text-align: center;">성별</th>
			<td><c:if test="${productDetail.pGender==1 }">남성용</c:if> <c:if test="${productDetail.pGender==2 }">여성용</c:if> <c:if test="${productDetail.pGender==3 }">공용</c:if></td>
			<th style="background-color: #eeeeee; text-align: center;">카테고리</th>
			<td><c:if test="${productDetail.kind==1 }">상의</c:if> <c:if test="${productDetail.kind==2 }">하의</c:if> <c:if test="${productDetail.kind==3 }">잡화</c:if></td>
		</tr>
		<tr>
			<th style="background-color: #eeeeee; text-align: center;">사이즈</th>
			<td><c:choose>
					<c:when test="${productDetail.psize =='Free'}">
						<c:out value="${productDetail.psize }"></c:out>
					</c:when>
					<c:when test="${productDetail.psize =='XS'}">
						<c:out value="${productDetail.psize }"></c:out>
					</c:when>
					<c:when test="${productDetail.psize =='S'}">
						<c:out value="${productDetail.psize }"></c:out>
					</c:when>
					<c:when test="${productDetail.psize =='M'}">
						<c:out value="${productDetail.psize }"></c:out>
					</c:when>
					<c:when test="${productDetail.psize =='L'}">
						<c:out value="${productDetail.psize }"></c:out>
					</c:when>
					<c:when test="${productDetail.psize =='XL'}">
						<c:out value="${productDetail.psize }"></c:out>
					</c:when>
					<c:when test="${productDetail.psize =='XXL'}">
						<c:out value="${productDetail.psize }"></c:out>
					</c:when>
				</c:choose></td>
			<th style="background-color: #eeeeee; text-align: center; width: 200px">재고</th>
			<td><input name="balance" value="${productDetail.balance }" disabled="disabled"></td>
		</tr>
		<tr>
			<th style="background-color: #eeeeee; text-align: center;">원가</th>
			<td><input name="price" value="${productDetail.price }" disabled="disabled"> <span class="step_val">
					할인된 가격 (판매가) :
					<span class="span_discount"></span>
				</span></td>
			<th style="background-color: #eeeeee; text-align: center; width: 200px">할인율</th>
			<td><input name="discountrate_input" value="<fmt:formatNumber value="${productDetail.discountrate/100}" type="percent" />" disabled="disabled"> <input type="hidden" name="discountrate" value="${productDetail.discountrate}" /></td>
		</tr>
		<tr>
			<th colspan="4" style="background-color: #eeeeee; text-align: center;">상품 설명</th>
		</tr>
		<tr>
			<td colspan="4">
				<div class="explainTextarea">
					<textarea name="explain" id="productExplainEditor"><c:out value="${productDetail.explain }"></c:out></textarea>
				</div>
			</td>
		</tr>
		<tr>
			<th colspan="4" style="background-color: #eeeeee; text-align: center;">등록된 상품 이미지 확인</th>
		</tr>
		<tr>
			<td colspan="4"><c:choose>
					<c:when test="${empty productDetail.imgUrl }">
						등록된 이미지가 없습니다.
					</c:when>
					<c:otherwise>
						<img style="width: 600px; height: 400px" src="/admin/display?fileName=${productDetail.imgUrl}" />
					</c:otherwise>
				</c:choose></td>
		</tr>
	</table>
	<button class="btn btn-primary px-3" style="width: 150px" id="modifyBtn">수 정</button>
	<button class="btn btn-primary px-3" style="width: 150px" id="cancelBtn">취 소</button>
	<button class="btn btn-primary px-3" style="width: 150px" id="deleteBtn">삭 제</button>
	<form id="moveForm" action="/admin/adminProductList" method="get">
		<input type="hidden" name="pageNum" value="${pageMaker.cri.pageNum}"> <input type="hidden" name="amount" value="${pageMaker.cri.amount}"> <input type="hidden" name="keyword" value="${pageMaker.cri.keyword}">
	</form>
</div>
<hr>
<script>
/*취소 버튼 */
$("#cancelBtn").click(function(){
	location.href="/admin/adminProductList"
});

/* 수정 페이지 이동 */
$("#modifyBtn").on("click", function(e){
	e.preventDefault();
	let addInput = '<input type="hidden" name="num" value="${productDetail.num}">';
	$("#moveForm").append(addInput);
	$("#moveForm").attr("action", "/admin/adminProductModify");
	$("#moveForm").submit();
});	

/*삭제 버튼*/
$("#deleteBtn").on("click", function(e){
	e.preventDefault();
	if(confirm("정말 삭제하시겠습니까? 삭제 후 내용 복구는 할 수 없습니다.")) {
		let moveForm = $("#moveForm");
		moveForm.find("input").remove();
		moveForm.append('<input type="hidden" name="num" value="${productDetail.num}">');
		moveForm.attr("action", "/admin/adminProductDelete");
		moveForm.attr("method", "post");
		moveForm.submit();
	}
});


$(document).ready(function(){
	
		  let discountRate = $("input[name='discountrate']").val();  // 할인율 값 가져오기
		  let goodsPrice = $("input[name='price']").val();           // 원가
		  let discountPrice = goodsPrice * (1 - discountRate / 100); // 할인가격

		  $(".span_discount").html(discountPrice.toFixed(0) + "원");

	/*위지윅*/
	/*상품설명 등록 */
	ClassicEditor
		.create(document.querySelector('#productExplainEditor'))
		.then(editor => {
					console.log(editor);
					editor.enableReadOnlyMode( 'my-feature-id' ); 
				})
		.catch(error=>{
			console.error(error);
	});
	
});

</script>
</body>
<jsp:include page="../footer.jsp"></jsp:include>
</html>

_config.yml

AdminController.java 에 추가

	// 상품 정보
	@GetMapping("adminProductDetail")
	public void adiminProductDetailGET(int num, Criteria cri, Model model) {
		logger.info("adminController, adminProductDetail 진입 ... " + num);

		/* 목록 페이지 조건 정보 */
		model.addAttribute("cri", cri);

		/* 조회 페이지 정보 */
		model.addAttribute("productDetail", adminService.productGetDetail(num));
	}

AdminController.java 에 추가

	// 상품 수정 페이지
	@GetMapping("adminProductModify")
	public void adimProductModifyGET(int num, Model model) {
		logger.info("adminController, adminProductModify 진입 ... " + num);
		/* 조회 페이지 정보 */
		model.addAttribute("productDetail", adminService.productGetDetail(num));
	}

adminProductModify.jsp 추가

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://cdn.ckeditor.com/ckeditor5/38.0.1/classic/ckeditor.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<style>
.ck-content {
	height: 200px;
}

input {
	width: 98%;
	padding: 5px 1%;
	border: 0;
	text-aling: center;
}

select {
	width: 98%;
	padding: 5px 1%;
	border: 0;
}

tr th td {
	vertical-align: middle;
	border-collapse: collapse;
}

.step_val { /* 할인 가격 문구 */
	padding-top: 5px;
	font-weight: 500;
}

.ck_warn { /* 입력란 공란 경고 태그 */
	display: none;
	padding-top: 10px;
	text-align: center;
	color: #e05757;
	font-weight: 300;
}
</style>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.0/css/all.min.css" rel="stylesheet">
<!-- Libraries Stylesheet -->
<link href="../resources/css/owl.carousel.min.css" rel="stylesheet">
<!-- Customized Bootstrap Stylesheet -->
<link href="../resources/css/style.css" rel="stylesheet">
</head>
<jsp:include page="../header.jsp"></jsp:include>
<body>
	<hr>
	<div>
		<!-- Page Header Start -->
		<div class="container bg-secondary mb-3" style="max-width: 800px;">
			<div class="d-flex flex-column align-items-center justify-content-center" style="min-height: 150px">
				<h1 class="font-weight-semi-bold text-uppercase mb-3">상품 수정</h1>
				<p class="m-0"></p>
			</div>
		</div>
	</div>
	<!-- Page Header End -->
	<!-- Shop Detail Start -->
	<div align="center">
		<form action="/admin/adminProductModify" method="post" id="modifyForm" enctype="multipart/form-data">
			<input type="hidden" name="num" value="${productDetail.num}">
			<table class="table" style="text-align: center; border: 1px solid #dddddd; width: 70%;">
				<tr>
					<th style="background-color: #eeeeee; text-align: center; width: 200px;">브랜드</th>
					<td colspan="4"><input id="bName_select" value="${productDetail.bname}" style="Width: 75%" readonly="readonly"> <input id="bName_input" name="bname" type="hidden" value="${productDetail.bname}">
						<button id="select_brand" class="btn btn-primary px-3" style="width: 150px">선택</button>
						<div>
							<span class="ck_warn bname_warn">브랜드를 선택해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">상품명</th>
					<td colspan="4"><input id="pnameId" name="pname" value="${productDetail.pname }">
						<div>
							<span class="ck_warn pname_warn">상품명을 입력해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">성별</th>
					<td colspan="4"><label> <input type="radio" name="pGender" value="1" ${productDetail.pGender == "1" ? "checked" : ""}> 남성용
					</label> <label> <input type="radio" name="pGender" value="2" ${productDetail.pGender == "2" ? "checked" : ""}> 여성용
					</label> <label> <input type="radio" name="pGender" value="3" ${productDetail.pGender == "3" ? "checked" : ""}> 공용
					</label>
						<div>
							<span class="ck_warn pGender_warn">상품의 성별을 선택해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">카테고리</th>
					<td colspan="4"><label> <input type="radio" name="kind" value="1" ${productDetail.kind == "1" ? "checked" : ""}> 상의
					</label> <label> <input type="radio" name="kind" value="2" ${productDetail.kind == "2" ? "checked" : ""}> 하의
					</label> <label> <input type="radio" name="kind" value="3" ${productDetail.kind == "3" ? "checked" : ""}> 잡화
					</label>
						<div>
							<span class="ck_warn kind_warn">상품의 카테고리를 선택해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">사이즈</th>
					<td><select name="psize">
							<option value="Free" ${productDetail.psize == "Free" ? "selected" : ""}>Free</option>
							<option value="XS" ${productDetail.psize == "XS" ? "selected" : ""}>XS</option>
							<option value="S" ${productDetail.psize == "S" ? "selected" : ""}>S</option>
							<option value="M" ${productDetail.psize == "M" ? "selected" : ""}>M</option>
							<option value="L" ${productDetail.psize == "L" ? "selected" : ""}>L</option>
							<option value="XL" ${productDetail.psize == "XL" ? "selected" : ""}>XL</option>
							<option value="XXL" ${productDetail.psize == "XXL" ? "selected" : ""}>XXL</option>
					</select>
						<div>
							<span class="ck_warn psize_warn">상품 사이즈를 선택해주세요.</span>
						</div></td>
					<th style="background-color: #eeeeee; text-align: center; width: 200px">재고</th>
					<td><input name="balance" type="number" value="${productDetail.balance }">
						<div>
							<span class="ck_warn balance_warn">상품의 재고를 입력해주세요.</span>
						</div></td>
				</tr>
				<tr>
					<th style="background-color: #eeeeee; text-align: center;">원가</th>
					<td><input name="price" type="number" value="${productDetail.price }">
						<div>
							<span class="step_val">
								할인된 가격 (판매가) :
								<span class="span_discount"></span>
								원
							</span>
							<span class="ck_warn price_warn">상품의 가격을 입력해주세요.</span>
						</div></td>
					<th style="background-color: #eeeeee; text-align: center; width: 200px">할인율</th>
					<td><input id="discount_interface" maxlength="2" value="<fmt:formatNumber value="${productDetail.discountrate/100}" type="percent" />"> <input name="discountrate" type="hidden" value="0"></td>
				</tr>
				<tr>
					<th colspan="4" style="background-color: #eeeeee; text-align: center;">상품 설명</th>
				</tr>
				<tr>
					<td colspan="4">
						<div class="explainTextarea">
							<textarea name="explain" id="productExplainEditor">${productDetail.explain }</textarea>
						</div>
						<div>
							<span class="ck_warn explain_warn">상품의 설명을 입력해주세요.</span>
						</div>
					</td>
				</tr>
				<tr>
					<th colspan="4" style="background-color: #eeeeee; text-align: center;">등록된 상품이미지 확인</th>
				</tr>
				<tr>
					<td colspan="4"><c:choose>
							<c:when test="${empty productDetail.imgUrl}">
								<span>등록된 이미지가 없습니다.</span>
							</c:when>
							<c:otherwise>
								<img style="width: 600px; height: 400px" src="/admin/display?fileName=${productDetail.imgUrl}" />
							</c:otherwise>
						</c:choose></td>
				</tr>
				<tr>
					<th colspan="2" style="background-color: #eeeeee; text-align: center;">상품 이미지 변경</th>
					<td colspan="4"><input type="file" id="fileItem" name="uploadFile" style="height: 30px;"></td>
				</tr>
			</table>
		</form>
		<button class="btn btn-primary px-3" style="width: 150px" id="modifyBtn">수 정</button>
		<button class="btn btn-primary px-3" style="width: 150px" id="cancelBtn">취 소</button>
	</div>
	<hr>
	<script>
	/*위지윅*/
	/*상품설명 등록 */
	ClassicEditor
		.create(document.querySelector('#productExplainEditor'))
		.catch(error=>{
			console.error(error);
	});
	
	/*취소 버튼 */
	$("#cancelBtn").click(function(){
		location.href="/admin/adminProductList"
	});
	
	/* 수정 버튼 */
	$("#modifyBtn").on("click", function(e){
		e.preventDefault();
		
		/*유효성 검사 체크 변수 선언*/
		let bnameCk = false;
		let pnameCk = false;
		let pGenderCk = false;
		let kindCk = false;
		let psizeCk = false;
		let balanceCk = false;
		let priceCk = false;
		let explainCk = false;
		
		/* 체크 대상 변수 */
		let bname = $("input[id='bName_select']").val();
		let pname = $("input[id='pnameId']").val().trim();
		let pGender = $("input[name='pGender']:checked").val();
		let kind = $("input[name='kind']:checked").val();
		let psize = $("select[name='psize']").val();
		let balance = parseInt($("input[name='balance']").val(), 10);
		let price = parseInt($("input[name='price']").val(), 10);
		let explain = $(".explainTextarea p").html();
		
		if(bname!== ''){
			$(".bname_warn").css('display','none');
			bnameCk = true;
		} else {
			$(".bname_warn").css('display','block');
			bnameCk = false;
		}
		
		if(pname!== ''){
			$(".pname_warn").css('display','none');
			pnameCk = true;
		} else {
			$(".pname_warn").css('display','block');
			pnameCk = false;
		}
		
		if(pGender){
			$(".pGender_warn").css('display','none');
			pGenderCk = true;
		} else {
			$(".pGender_warn").css('display','block');
			pGenderCk = false;
		}
		
		if(kind){
			$(".kind_warn").css('display','none');
			kindCk = true;
		} else {
			$(".kind_warn").css('display','block');
			kindCk = false;
		}
		
		if(psize){
			$(".psize_warn").css('display','none');
			psizeCk = true;
		} else {
			$(".psize_warn").css('display','block');
			psizeCk = false;
		}
		
		if (!isNaN(balance) && balance >= 0) {
			  $(".balance_warn").css('display', 'none');
			  balanceCk = true;
			} else {
			  $(".balance_warn").css('display', 'block');
			  balanceCk = false;
			}
		
		if (!isNaN(price) && price > 0) {
			  $(".price_warn").css('display', 'none');
			  priceCk = true;
			} else {
			  $(".price_warn").css('display', 'block');
			  priceCk = false;
			}
		
		if(explain != '<br data-cke-filler="true">'){
			$(".explain_warn").css('display','none');
			explainCk = true;
		} else {
			$(".explain_warn").css('display','block');
			explainCk = false;
		}			
		
		if(bnameCk && pnameCk && pGenderCk && kindCk && psizeCk && priceCk && balanceCk && explainCk){
			alert('통과');
			$("#modifyForm").submit();
		} else {
			return false;
		}
	});
	
	/*브랜드 선택*/
	$("#select_brand").click(function(e){
		e.preventDefault();
		
		let popUrl = "/admin/brandPop";
		let popOption = "width = 400px, height=500px, top=300px, left=300px, scrollbars=yes";
		
		window.open(popUrl,"브랜드 검색",popOption);	
		
	});
	
	
	$(document).ready(function() {
		  let userInput = $("#discount_interface");
		  let discountInput = $("input[name='discountrate']");
		  let goodsPriceInput = $("input[name='price']");
		  let spanDiscount = $(".span_discount");

		  let discountRate = parseFloat(userInput.val()); // 사용자가 입력한 할인값 (숫자로 변환)
		  let goodsPrice = parseFloat(goodsPriceInput.val()); // 원가 (숫자로 변환)

		  // 할인율과 원가에 입력된 값으로 할인가격 계산
		  let discountPrice = goodsPrice * (1 - discountRate / 100);

		  discountInput.val(discountRate); // 서버에 전송할 할인값
		  spanDiscount.text(discountPrice); // 할인가격을 출력합니다.

		  // 할인율 입력란 변경 시
		  userInput.on("propertychange change keyup paste input", function() {
		    discountRate = parseFloat(userInput.val());
		    discountPrice = goodsPrice * (1 - discountRate / 100);

		    discountInput.val(discountRate);
		    spanDiscount.text(discountPrice);
		  });

		  // 원가 입력란 변경 시
		  goodsPriceInput.on("change", function() {
		    goodsPrice = parseFloat(goodsPriceInput.val());
		    discountPrice = goodsPrice * (1 - discountRate / 100);

		    spanDiscount.text(discountPrice);
		  });
		});


	
	/*이미지 업로드*/
	$("input[type='file']").on("change", function(e){
		alert("동작 확인");

		/* /*이미지 존재 시 삭제
		if($(".imgDeleteBtn").length > 0){
			deleteFile();
		} */
		
		let formData = new FormData();
		let fileInput=$('input[name="uploadFile"]');
		let fileList=fileInput[0].files;
		let fileObj=fileList[0];
		
		console.log("fileList : "+ fileList);
		console.log("fileObj : " + fileObj);
		console.log("fileName : "+ fileObj.name);
		console.log("fileSize : " + fileObj.size);
		console.log("fileType(MimeType) : " + fileObj.type);

		if(!fileCheck(fileObj.name, fileObj.size)){
			return false;
		}
		
		alert("통과");
		formData.append("uploadFile", fileObj);
		
		$.ajax({
			url: '/admin/uploadAjaxAction', //서버로 요청을 보낼 url
			processData : false, // 서버로 전송할 데이터를 queryString 형태로 변환할지 여부
			contentType : false, // 서버로 전송되는 데이터의 content-type 
			data : formData, // 서버로 전송할 데이터
			type : 'POST', // 서버로 요청 타입 (get, post)
			dataType : 'json', // 서버로부터 반환받을 데이터 타입 
			success : function(result){
				console.log(result);
				showUploadImage(result);
			},
			error: function(result){
				console.log(result);
				alert("업로드 실패");
			}
		});
	});
	
	
	/*var, mehtod related with attachFile*/
	let regex  = new RegExp("(.*?)\.(jpg|png)$");
	let maxSize = 1048576; //1MB
	
	function fileCheck(fileName, fileSize){
		if(fileSize >= maxSize){
			alert("파일 사이즈 초과");
			return false;	
		}
		
		if(!regex.test(fileName)){
			alert("해당 종류의 파일은 업로드 할 수 없습니다.");
			return false;
		}
		return true;
	}
	
	function showUploadImage(uploadResultArr) {
		console.log("showUploadImage called");
		console.log(uploadResultArr);

		  let uploadResult = $("#uploadResult");

		  let str = "";
		 
		  for (let i = 0; i < uploadResultArr.length; i++) {
		    let obj = uploadResultArr[i];

		    if (!obj || !obj.imgUrl) {
		      console.log("Invalid object or missing imgUrl property");
		      continue;
		    }

		    let fileCallPath = "/admin/display?fileName=" + obj.imgUrl;
		    
			console.log("obj.imgUrl : " + obj.imgUrl);
		    console.log("fileCallPath: " + fileCallPath);
		    
		    str += "<div id='result_card'>";
		    str += "<img src='"+fileCallPath+"'>";
		    str += "<div class='imgDeleteBtn'>x</div>";
		    str += "</div>";
		    
		    console.log("Appending the following HTML string: " + str);
		  }
			uploadResult.html(str);
		}
	
	
	</script>
</body>
<jsp:include page="../footer.jsp"></jsp:include>
</html>

_config.yml

AdminMapper.java 에 추가

상품 수정 쿼리를 실행할 메서드를 만들어 준다.

	public int productModify(ProductVO product); // 선택 상품 수정

AdminMapper.xml 에 추가

	<!-- 상품 수정 -->
	<update id="productModify">
		update product set
		bname = #{bname},
		pname = #{pname},
		pGender = #{pGender},
		kind = #{kind},
		psize = #{psize},
		price = #{price},
		discountrate =
		#{discountrate},
		balance = #{balance},
		explain =
		#{explain},
		imgUrl=#{imgUrl, jdbcType=VARCHAR}
		where num=#{num}
	</update>

AdminService.java 에 추가

	public int productModify(ProductVO product); // 선택 상품 수정

AdminServiceImpl.java 에 추가

	@Override
	public int productModify(ProductVO product) {
		System.out.println("(service) productModify ..... ");
		return mapper.productModify(product);
	}

AdminController.java 에 추가

	// 상품 정보 수정
	@Transactional
	@PostMapping("/adminProductModify")
	public String adminProductModifyPOST(ProductVO product, HttpSession session, RedirectAttributes rttr)
			throws Exception {
		logger.info("adminProductModifyPOST.........." + product);

		// 세션에서 imgUrl 값 가져오기
		List<ProductVO> imgUrlList = (List<ProductVO>) session.getAttribute("imgUrlList");

		// imgUrl 값이 존재하면 새로운 파일 업로드 후 가져온 이미지 경로를 저장하고, imgUrl 값이 없다면 기존 경로를 그대로 저장하기
		if (imgUrlList != null && !imgUrlList.isEmpty()) {
			String imgUrl = imgUrlList.get(0).getImgUrl();
			product.setImgUrl(imgUrl);
		} else {
			ProductVO oldProduct = adminService.productGetDetail(product.getNum());
			product.setImgUrl(oldProduct.getImgUrl());
		}

		// imgUrl 값 사용 후 세션에서 제거 (선택사항)
		session.removeAttribute("imgUrlList");

		int result = adminService.productModify(product);
		logger.info("result : " + result);
		rttr.addFlashAttribute("modify_result", result);

		return "redirect:/admin/adminProductList";
	}

4. 상품 삭제 기능

AdminMapper.java 에 추가

	public int productDelete(int num); // 선택 상품 삭제

AdminMapper.xml 에 추가

	<!-- 상품 삭제 -->
	<delete id="productDelete">
		delete from product where num=#{num}
	</delete>

AdminService.java 에 추가

	public int productDelete(int num); // 선택 상품 삭제

AdminServiceImpl.java 에 추가

	@Override
	public int productDelete(int num) {
		System.out.println("(service) productDelete ..... " + num);
		return mapper.productDelete(num);
	}

AdminController.java 에 추가

	// 상품 삭제
	@PostMapping("/adminProductDelete")
	public String productDeletePOST(int num, RedirectAttributes rttr) {
		logger.info("productDeletePOST..........");
		int result = adminService.productDelete(num);
		rttr.addFlashAttribute("delete_result", result);
		return "redirect:/admin/adminProductList";

	}

프로젝트 주소



projectjavajspOraclecssHTMLBootStrapAPIJQueryJavaScriptSpringFrameWork Share Tweet +1