담당 기능
이전 포스팅에서 관리자 페이지 CRUD 기능 구현 코드 및 해석에 대해서 알아봤습니다.
CRUD 기능에 대해서 알아보시는 분들은 아래 포스팅을 참고해 보시면 좋을 거 같습니다!
오늘은 회원관리 기능의 마지막으로, 페이지네이션 처리 및 검색 기능을 추가 구현을 해보겠습니다.
페이지네이션은 다양한 방법으로 코드 구현을 할 수 있지만, 팀 프로젝트에서 게시판 기능이 2~3개 정도 들어가는 사이트를 프로젝트로 정하셨다면 반복 코드를 줄이고, 공통 코드화로 생성하시는 게 좋습니다.
아래 포스팅은 페이지네이션에 필요한 코드를 해석한 포스팅 글입니다.
설명은 포스팅에서 자세하게 했으므로 생략하겠습니다.
프로젝트 구조는 다음과 같습니다.
프로젝트 구조
(utils 디렉터리에 있는 페이지네이션 공통 코드는 위 포스팅을 참고하셔야 이해할 수 있습니다. )
adminMapper
페이지네이션 코드는 역순으로 생성했기 때문에, mapper 부터 진행하겠습니다.
mapper 파일에 검색 기능, 페이지네이션 처리 쿼리문을 작성해 줍니다.
<!--유저 목록 페이징-->
<select id="listPage" parameterType="hashMap" resultType="member">
SELECT *
FROM (
SELECT rn, mem_num, mem_name, mem_email, mem_address1, mem_join_date
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY mem_num DESC) AS rn,
mem_num,
mem_name,
mem_email,
mem_address1,
mem_join_date
FROM member
)
<if test='searchType.equals("mem_name")'>
WHERE mem_name LIKE '%' || #{keyword} || '%'
</if>
<if test='searchType.equals("mem_email")'>
WHERE mem_email LIKE '%' || #{keyword} || '%'
</if>
<if test='searchType.equals("all")'>
WHERE mem_name LIKE '%' || #{keyword} || '%'
or mem_email LIKE '%' || #{keyword} || '%'
</if>
)
WHERE rn between ${(pageNum -1) * pageRowCount} +1 and ${(pageNum -1) * pageRowCount} + ${pageRowCount}
</select>
<select id="listPageCount" parameterType="hashMap" resultType="Integer">
SELECT count(*)
FROM member
WHERE 1=1
<if test='searchType.equals("mem_name")'>
AND mem_name LIKE '%' || #{keyword} || '%'
</if>
<if test='searchType.equals("mem_email")'>
AND mem_email LIKE '%' || #{keyword} || '%'
</if>
<if test='searchType.equals("all")'>
AND mem_name LIKE '%' || #{keyword} || '%'
or mem_email LIKE '%' || #{keyword} || '%'
</if>
</select>
MemberDAO
DAO 파일에서는 쿼리문을 작성했을 때 id 설정했던 메서드를 생성해서 구현체로 연결해 줍니다.
위 코드에서 생성한 listPage 메서드에 앞서 작성했던 쿼리를 담아줘야 합니다.
<Impl>
@Override
public ResponseResultList listPage(ItemSearchVO vo) {
HashMap<String, Object> data = new HashMap<>();
// 페이징 처리
data.put("pageNum", vo.getPageNum());
data.put("pageRowCount", vo.getPageRowCount());
data.put("searchType", vo.getStype());
data.put("keyword", vo.getSdata());
//쿼리
List<MemberDTO> resultdata = sqlSession.selectList("admin.listPage",data);
//쿼리
Integer cnt =(Integer) sqlSession.selectOne("admin.listPageCount", data);
//PaginInfoVO 생성자에 Count 담아준다.(가방)
PagingInfoVO pagingInfoVO = new PagingInfoVO(vo.getPageNum(),cnt,vo.getPageRowCount());
ResponseResultList responseResultList = new ResponseResultList();
//데이터를 responseResultList에 담아준다.
responseResultList.setPagingInfo(pagingInfoVO);
responseResultList.setBody(resultdata);
// return
return responseResultList;
}
Service
Service단에서는 간단하게 DAO와 연결해 주는 코드만 생성해 줍니다.
//유저 목록 페이징
public ResponseResultList listPage(ItemSearchVO vo){
return memberDao.listPage(vo);
}
Controller
다음으로는 View단과 연결해 주는 Controller입니다.
앞서 DAO에서 데이터를 가방에 넣었다면, Controller는 가방에서 데이터를 꺼내서 View 단에 보내줘야 합니다.
@RequestMapping(value = "/admin_listPage", method = RequestMethod.GET)
public void getListPage(Model model, @RequestParam("num") Integer num,
@RequestParam(value = "searchType",required = false, defaultValue = "mem_name") String searchType,
@RequestParam(value = "keyword",required = false, defaultValue = "") String keyword) throws Exception {
ItemSearchVO vo = new ItemSearchVO();
vo.setPageNum(num);
vo.setStype(searchType);
vo.setSdata(keyword);
ResponseResultList responseResultList = adminService.listPage(vo);
model.addAttribute("list", responseResultList.getBody());
model.addAttribute("page", responseResultList.getMeta().get("pagingInfo"));
model.addAttribute("select", num);
model.addAttribute("search", searchType);
model.addAttribute("keyword",keyword);
}
admin_listPage.jsp
마지막으로 view 페이지에 검색 기능과 페이지네이션 처리 코드를 작성해서 Controller와 연결해 줍니다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<c:set var="path" value="${pageContext.request.contextPath}" />
<script src="${path}/include/js/bootstrap.js"></script>
<link rel="stylesheet" href="${path}/include/style.css">
<script src="${path}/include/jquery-3.6.3.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.container {
display: flex;
flex-direction: column;
align-items: flex-start;
}
table {
width: 700px;
border-collapse: collapse;
}
th, td {
border: 1px solid black;
padding: 8px;
}
div a.menubar {
text-decoration: none;
display: flex;
color: #000;
padding: 25px 25px 25px 25px;
font-weight: bold;
}
#container {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
}
#category {
display: flex;
flex-direction: column;
height: 100%;
width: 15%;
gap: 25px;
align-items: center;
border-right: 1px solid #000000;
padding-top: 50px;
}
.searchView{
width: 100%;
flex-direction: row-reverse;
}
</style>
<body>
<%@ include file="../header.jspf" %>
<div id="container">
<div style="display: flex; height: auto;">
<div id="category" class="menu" style="width:10%; height:auto;">
<a class="menubar" href="${path}/admin/admin_listPage?num=1">회원관리</a>
<a class="menubar" href="${path}/trip/list_admin.do">관광명소 관리</a>
<a class="menubar" href="${path}/review/list.do" >리뷰리스트 관리</a>
<a class="menubar" href="${path}/faq/list.do">FAQ</a>
</div>
<div class="container" >
<h2>유저 목록</h2>
<%-- 검색 기능 추가--%>
<div class="col-xl-3">
<form class="d-flex" role="search">
<input type="hidden" name="num" value="${page.pageNum}">
<select class="border-success rounded-3" name="searchType">
<option value="mem_name" <c:if test="${search == 'mem_name'}">selected</c:if>>이름</option>
<option value="mem_email" <c:if test="${search == 'mem_email'}">selected</c:if>>이메일</option>
<option value="all" <c:if test="${search == 'all'}">selected</c:if>>이름+이메일</option>
</select>
<input class="form-control ms-2 me-2" type="text" name="keyword" placeholder="Search" aria-label="Search" value="${keyword}">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>회원번호</th>
<th>이메일</th>
<th>이름</th>
<th>주소</th>
<th>가입일자</th>
</tr>
</thead>
<tbody>
<c:forEach var="row" items="${list}">
<tr>
<td>${row.mem_num}</td>
<td>${row.mem_email}</td>
<td><%-- 클릭 시 상세페이지 호출--%>
<input type="hidden" id="mem_num" name="mem_num" value="${row.mem_num}">
<a href="${path}/admin/view.do?mem_num=${row.mem_num}">${row.mem_name}</a>
</td>
<td>${row.mem_address1}</td>
<td><fmt:formatDate value="${row.mem_join_date}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
</tr>
</c:forEach>
</tbody>
</table>
<%--페이지 네이션 추가--%>
<div class="d-flex justify-content-center align-items-center pt-4 my-5 h5" style="padding-left: 35%">
<span>
<c:if test="${page.pageNum > 2}">
<c:set var="firstPageNum" value="1" />
<a class="ms-3 text-muted" href="${path}/admin/admin_listPage?num=${firstPageNum}">
<i class="bi bi-chevron-double-left"></i>
</a>
</c:if>
<c:if test="${page.pageNum > 1}">
<a class="ms-3 text-muted" href="${path}/admin/admin_listPage?num=${page.pageNum - 1}">
<i class="bi bi-chevron-left"></i></a>
</c:if>
</span>
<c:forEach begin="1" end="${page.lastPageNum}" var="num">
<span class="ms-3 text-muted">
<c:if test="${select != num}"> <a class="ms-3 text-muted" href="${path}/admin/admin_listPage?num=${num}">${num}</a></c:if>
<c:if test="${select == num}"><b class="ms-3 text-muted">${num}</b></c:if>
</span>
</c:forEach>
<span>
<c:if test="${page.pageNum != page.lastPageNum }">
<a class="ms-3 text-muted" href="${path}/admin/admin_listPage?num=${page.pageNum + 1}">
<i class="bi bi-chevron-right"></i>
</a>
</c:if>
<c:if test="${page.pageNum != page.lastPageNum}">
<a class="ms-3 text-muted" href="${path}/admin/admin_listPage?num=${page.lastPageNum}">
<i class="bi bi-chevron-double-right"></i>
</a>
</c:if>
</span>
</div>
</div>
</div>
</div>
<%@include file="../footer.jspf" %>
</body>
</html>
구현 결과
< 페이지네이션 >
< 검색 기능 >
마치며
오늘은 회원 목록 페이지네이션 및 검색 기능을 추가했습니다.
반복 코드를 줄이고, 가독성을 생각하면서 코드를 구현하다 보니 어렵기도 하지만...
구현하고 나면 성장했다는 기분에 행복해집니다 ^^
다음 포스팅에서 뵙겠습니다.
'[ Project ] > Team' 카테고리의 다른 글
[ Team ] 리뷰게시판 - 별점 기능 구현 (0) | 2023.06.23 |
---|---|
[ Team ] 인터셉터(Interceptor) 구현 (0) | 2023.06.21 |
[ Team ] OAuth - Kakao API 로그인 구현 (0) | 2023.06.17 |
[ Team ] OAuth - NAVER API 로그인 구현 (0) | 2023.06.16 |
[ Team ] 관리자 페이지 생성/ CRUD 구현 (수정 페이지) (0) | 2023.06.15 |