😎 STS3 Spring 쇼핑몰

STS3 쇼핑몰 프로젝트[20] 상품 목록 기능 구현

개발자 린다씨 2022. 8. 4. 14:19
반응형

순서

0. Criteria.java 생성

1. Mapper 메서드

2. Service 메서드

3. Controller 

4. View 처리

0. Criteria.java 생성

com.swan.model 패키지에 Criteria.java 클래스를 생성합니다.

Criteria.java

더보기
package com.swan.model;

// 추가적인 데이터들을  Mapper에 전달하기 위해서, 쿼리문에 생성에 필요로 한 데이터를 전달하는 용도로 생성
public class Criteria {
	/* 현재 페이지 번호 */
	private int pageNum;
	
	/* 페이지 표시 개수 */
	private int amount;
	
	/* 검색 타입 */
	private String type;
	
	/* 검색 키워드 */
	private String keyword;
	
	/* 카테고리 코드 */
	private String kind_id;
	
	/* 상품 번호(리뷰 기능에서 사용) */
	private int product_id;
	
	/* 문의사항 번호(답변 기능에서 사용) */
	private int q_id;
	
	/* Criteria 생성자 */
	public Criteria(int pageNum, int amount) {
		this.pageNum = pageNum;
		this.amount = amount;
	}
	
	/* Criteria 기본 생성자 */
	public Criteria(){
		this(1,10);
	}
	
	/* 검색 타입 데이터 배열 변환 */
	public String[] getTypeArr() {
		return type == null? new String[] {}:type.split("");
	}

	public Criteria(int pageNum, int amount, String type, String keyword, String kind_id, int product_id, int q_id) {
		super();
		this.pageNum = pageNum;
		this.amount = amount;
		this.type = type;
		this.keyword = keyword;
		this.kind_id = kind_id;
		this.product_id = product_id;
		this.q_id = q_id;
	}

	public int getPageNum() {
		return pageNum;
	}

	public void setPageNum(int pageNum) {
		this.pageNum = pageNum;
	}

	public int getAmount() {
		return amount;
	}

	public void setAmount(int amount) {
		this.amount = amount;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getKeyword() {
		return keyword;
	}

	public void setKeyword(String keyword) {
		this.keyword = keyword;
	}

	public String getkind_id() {
		return kind_id;
	}

	public void setkind_id(String kind_id) {
		this.kind_id = kind_id;
	}

	public int getProduct_id() {
		return product_id;
	}

	public void setProduct_id(int product_id) {
		this.product_id = product_id;
	}

	public int getQ_id() {
		return q_id;
	}

	public void setQ_id(int q_id) {
		this.q_id = q_id;
	}

	@Override
	public String toString() {
		return "Criteria [pageNum=" + pageNum + ", amount=" + amount + ", type=" + type + ", keyword=" + keyword
				+ ", kind_id=" + kind_id + ", product_id=" + product_id + ", q_id=" + q_id + "]";
	}
	
}

1.  Mapper 메서드

상품 목록 페이지에서 기본적으로 필요한 쿼리 문과 이를 호출하는 Mapper 메서드를 먼저 작업합니다.

1-1. AdminMapper.java

 AdminMapper.java 인터페이스에 아래 두 개의 메서드를 추가합니다.

'productsGetList()' 메서드는 각각 상품 목록 페이지에 출력될 페이징화 된 상품 데이터입니다.

'productsGetTotal()' 메서드는 페이지 이동 인터페이스 객체를 인스턴스화 하는데 필요로 한 '상품 목록 총 개수' 데이터입니다.

/* 상품 리스트 */
public List<SwanVO> productsGetList(Criteria cri);
	
/* 상품 총 개수 */
public int productsGetTotal(Criteria cri);

1-2. AdminMapper.xml

'상품 목록(상품 관리)' 페이지에 출력시킬 열들의 정보는 'product_id', 'product_title', 'product_stock',   'product_price', 'kind_name'입니다. 문제는 'kind_name' 열의 정보는 "product" 테이블에 있지 않습니다.

 

따라서 같이 출력 되록해주기 위해선 '서브 쿼리' 혹은 '조인'을 사용해야 합니다.

 

저는 Oracle 11g에서 서브 쿼리 사용하였습니다.

 

 아래는 AmdinMapper.xml에 작성한 코드입니다.

<!-- 상품 리스트 -->
	<select id="productsGetList" resultType="com.swan.model.SwanVO">
	  	<![CDATA[
		select * from(
		    select /*+INDEX_DESC(product seq_pid)*/ rownum as rn,  product_id, product_title,
		        product_stock, product_price, (select kind_name from swan_cate where product.kind_id = swan_cate.kind_id)kind_name, kind_id, product_create_date, product_status
		    from product 
		    where 
		]]>

		<if test="keyword != null">
			product_title like '%' || #{keyword} || '%' and
		</if>
		     
		<![CDATA[    
		    rownum <= #{pageNum} * #{amount} 
		    order by product_id desc
		    )
		where rn > (#{pageNum} -1) * #{amount}		  	
	  		order by product_id 
  		]]>
	</select>

	<!-- 상품 총 개수 -->
	<select id="productsGetTotal" resultType="_int">
		select count(*) from product
		<if test="keyword != null">
			where product_title like '%' || #{keyword} || '%'
		</if>
	</select>

2. Service 메서드

 AdminController.java 에서 위에서 작성한 메서드를 실행할 수 있도록 연결해주는 Service단계의 메서드를 작성합니다.

 

 AdminService.java 인터페이스에 아래의 메서드를 추가해줍니다.

/* 상품 리스트 */
public List<SwanVO> productsGetList(Criteria cri);

/* 상품 총 개수 */
public int productsGetTotal(Criteria cri);

AdminServiceImpl.java 클래스에 인터페이스에서 작성한 메서드를 오버라이드 구현부를 작성해줍니다.

/* 상품 리스트 */
@Override
public List<SwanVO> productsGetList(Criteria cri) {
	log.info("productsGetList()..........");

	return adminMapper.productsGetList(cri);
}

/* 상품 총 개수 */
@Override
public int productsGetTotal(Criteria cri) {
	log.info("goodsGetTotal().........");

	return adminMapper.productsGetTotal(cri);
}

3. Controller 메서드

기존의 '상품 관리(상품 목록)' 페이지 이동 URL("/admin") 매핑 메서드에 Criteria, model 파라미터를 추가해줍니다.

/* 관리자 메인 페이지 이동 */
@RequestMapping(value = "admin", method = RequestMethod.GET)
public void adminMainGET(Criteria cri, Model model) throws Exception {

}

구현부에는 '상품 목록 데이터', '페이지 인터페이스 데이터'를 뷰(View)에 전송하는 아래의 코드를 추가합니다.

/* 상품 리스트 데이터 */
List list = adminService.productsGetList(cri);

if (!list.isEmpty()) {
	model.addAttribute("list", list);
} else {
	model.addAttribute("listCheck", "empty");
	return;
}

/* 페이지 인터페이스 데이터 */
model.addAttribute("pageMaker", new PageDTO(cri, adminService.productsGetTotal(cri)));

4. View 처리

admin.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"%>
<c:set var="path" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<link href='https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css'
	rel='stylesheet'>
<link rel="stylesheet"
	href="https://unicons.iconscout.com/release/v4.0.0/css/line.css">
<title>Admin</title>
<link href="${path}/resources/css/admin_css.css" rel="stylesheet" />
<link href="${path}/resources/css/orderList.css" rel="stylesheet" />
</head>
<body>
	<div class="sidebar">
		<div class="logo-details">
			<i class='bx bxl-c-plus-plus'></i> <span class="logo_name">SWAN</span>
		</div>
		<ul class="nav-links">
			<li><a href="/swan"> <i class='bx bx-grid-alt'></i> <span
					class="links_name">HOME</span>
			</a></li>
			<li><a href="/admin/admin" class="active"> <i
					class='bx bx-box'></i> <span class="links_name">상품 관리</span>
			</a></li>
			<li><a href="/admin/orderList"> <i class='bx bx-list-ul'></i>
					<span class="links_name">주문 관리</span>
			</a></li>
			<li><a href="/admin/adminMember"> <i
					class='bx bx-pie-chart-alt-2'></i> <span class="links_name">회원
						관리</span>
			</a></li>
			<li><a href="/admin/adminQuestion"> <i
					class='bx bx-coin-stack'></i> <span class="links_name">문의사항
						관리</span>
			</a></li>
			<li class="log_out"><a href="/member/logout.me"> <i
					class='bx bx-log-out'></i> <span class="links_name">로그아웃</span>
			</a></li>
		</ul>
	</div>
	<section class="home-section">
		<nav>
			<div class="sidebar-button">
				<i class='bx bx-menu sidebarBtn'></i> <span class="dashboard">상품
					관리</span>
			</div>
		</nav>

		<div class="home-content">
			<table class="order_table">
				<colgroup>
					<col width="10%">
					<col width="20%">
					<col width="40%">
					<col width="20%">
				</colgroup>
				<thead>
					<tr>
						<td class="th_column_1">번호</td>
						<td class="th_column_2">카테고리 명</td>
						<td class="th_column_3">상품 명</td>
						<td class="th_column_4">상품 수량</td>
					</tr>
				</thead>
				<c:if test="${listcheck != 'empty' }">
					<c:forEach items="${list}" var="list">
						<tr>
							<td><c:out value="${list.product_id}" /></td>
							<td><c:out value="${list.kind_name}" /></td>
							<td><a class="move"
								href='<c:out value="${list.product_id}"/>'> <c:out
										value="${list.product_title}" />

							</a></td>
							<td><c:out value="${list.product_stock}" /></td>
						</tr>


					</c:forEach>
				</c:if>
			</table>
			<form id="moveForm" action="/admin/admin" 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 class="search_wrap">
				<form id="searchForm" action="/admin/admin" method="get">
					<div class="search_input">
						<input type="text" name="keyword"
							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}'>
						<button class='btn search_btn'><i class="fas fa-search"></i></button>
					</div>
				</form>
			</div>
			<!-- 페이지 이름 인터페이스 영역 -->
			<div class="pageMaker_wrap">
				<ul class="pageMaker">

					<!-- 이전 버튼 -->
					<c:if test="${pageMaker.prev }">
						<li class="pageMaker_btn prev"><a
							href="${pageMaker.pageStart -1}">이전</a></li>
					</c:if>

					<!-- 페이지 번호 -->
					<c:forEach begin="${pageMaker.pageStart }"
						end="${pageMaker.pageEnd }" 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.pageEnd + 1 }">다음</a></li>
					</c:if>
				</ul>
			</div>
			<div class="button">
				<a href="/admin/insertProduct">상품 등록</a>
			</div>
		</div>
	</section>
	<script>
		$(document).ready(function() {
			/* 등록 성공 이벤트 */
			let eResult = '<c:out value="${enroll_result}"/>';
				
			checkResult(eResult);

			function checkResult(result) {

				if (result === '') {
					return;
				}

				alert("상품'" + eResult + "'을 등록하였습니다.");

			}
		});
	</script>



	<script>
		let sidebar = document.querySelector(".sidebar");
		let sidebarBtn = document.querySelector(".sidebarBtn");
		sidebarBtn.onclick = function() {
			sidebar.classList.toggle("active");
			if (sidebar.classList.contains("active")) {
				sidebarBtn.classList.replace("bx-menu", "bx-menu-alt-right");
			} else
				sidebarBtn.classList.replace("bx-menu-alt-right", "bx-menu");
		}
	</script>

</body>
</html>

위의 코드들을 추가 해준 후 서버를 구동시켜 해당 페이지를 들어가면 아래와 같이 출력됩니다.

아래의 페이지 이동 인터페이스 동작하도록 만들기 위해서 <script> 태그에 아래의 JS코드를 추가해줍니다.

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();

		});

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

			e.preventDefault();

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

			moveForm.submit();

		});

		/* 상품 조회 페이지 */
		$(".move")
				.on(
						"click",
						function(e) {

							e.preventDefault();

							moveForm
									.append("<input type='hidden' name='product_id' value='"
											+ $(this).attr("href") + "'>");
							moveForm.attr("action", "/admin/product_detail");
							moveForm.submit();

						});

위의 코드를 추가 해준 뒤 검색, 페이지 이동 인터페이스 정상적으로 동작하는지 테스트합니다.

반응형