본문 바로가기
[ Project ]/Team

[ Team ] 회원관리 - 페이지네이션 처리 기능 구현

by 환이s 2023. 6. 20.


담당 기능

 

 

이전 포스팅에서 관리자 페이지 CRUD 기능 구현 코드 및 해석에 대해서 알아봤습니다.

CRUD 기능에 대해서 알아보시는 분들은 아래 포스팅을 참고해 보시면 좋을 거 같습니다!

 

 

[ Team ] 관리자 페이지 생성/ CUID 구현 (목록/상세정보)

담당 기능 ADMIN 페이지 회원 관리 기능 구현 담당을 맡아서 책임감 갖고 기능 구현을 해보겠습니다. 프로젝트 구조는 다음과 같습니다. 기능 구현할 때 사용된 객체/메서드/패턴은 이전 포스팅에

drg2524.tistory.com

 

 

[ Team ] 관리자 페이지 생성/ CUID 구현 (수정 페이지)

담당 기능 이전 관리자 페이지 CUID 기능 추가 이어서 수정 페이지 기능 구현을 해보겠습니다. 프로젝트 구조는 다음과 같습니다. 기능 구현할 때 사용된 객체/메서드/패턴은 이전 포스팅에서 자

drg2524.tistory.com

 

 

오늘은 회원관리 기능의 마지막으로, 페이지네이션 처리 및 검색 기능을 추가 구현을 해보겠습니다.

페이지네이션은 다양한 방법으로 코드 구현을 할 수 있지만, 팀 프로젝트에서 게시판 기능이 2~3개 정도 들어가는 사이트를 프로젝트로 정하셨다면 반복 코드를 줄이고, 공통 코드화로 생성하시는 게 좋습니다.

 

아래 포스팅은 페이지네이션에 필요한 코드를 해석한 포스팅 글입니다.

설명은 포스팅에서 자세하게 했으므로 생략하겠습니다.

 

 

[ Spring ] 페이지네이션 코드 해석 및 기록

이전에 JSP 포스팅할 때 페이지 나누기 기능에 대해서 개념 및 기능 구현을 했습니다. JSP 페이지 나누기 기능에 대해서 찾아보시는 분들은 아래 포스팅 참고해 주시면 감사합니다..! [ JSP ] 페이지

drg2524.tistory.com

 

프로젝트 구조는 다음과 같습니다.

 

프로젝트 구조

 

 

 

(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>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            
            
            <%-- 검색 기능 추가--%>
            <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>

구현 결과

 

 

< 페이지네이션 >

 

 

< 검색 기능 >


마치며

 

오늘은 회원 목록 페이지네이션 및 검색 기능을 추가했습니다.

반복 코드를 줄이고, 가독성을 생각하면서 코드를 구현하다 보니 어렵기도 하지만... 

구현하고 나면 성장했다는 기분에 행복해집니다 ^^ 

 

다음 포스팅에서 뵙겠습니다.

 

728x90