JSP

[JSP] Pagination

shb 2022. 4. 11. 14:40

Pagination (페이징)
MySQL + MVC2

간혹 프로젝트가 서버에서 꼬이는수가 있다
서버에서 다른 프로젝트 를 제거하고, 
현재 작동하는 프로젝트만 add 한뒤.  clean 해보고 시작하길 추천

구현시 고려할 요소들
한 ‘페이지’당 몇개의 글목록을 나타낼 것인가
한 [페이징]당 몇개의 페이지를 표현할 것인가?

고려 해야 하는 것들
[페이징]을 구현하기 위해서는  아래 두가지가 구현되어야 한다 
1. 한 ‘페이지’에 몇개의 ‘목록’을 표시 할 것인가?  → 세팅값
2. 한 [페이징]에는 몇개의 ‘페이지’를 표시 할 것인가? → 세팅값
3. ‘페이지’ 리스트에 표시되어야 할 ‘목록’이 총 몇개인가?     
    → SQL 함수  count(*)  사용
4. ‘몇번째(fromRow)’ 부터 ‘몇개(pageRows)’를 SELECT 할 것인가? 
    → ROWNUM 사용  (*ORACLE 의 경우) 
    → LIMIT 사용  (*MySQL 의 경우)

 

다량의 데이터 필요. 일단, 목록의 개수가 많아야함 
아래 쿼리 실행할때마다 기존의 레코드들을 그대로 복사 생성(x2 씩 늘어남)
약 400개 이상 준비

list.do 돌려보면
n 번째 페이지 목록, 쿼리문은?
MySQL 은 limit 키워드 사용
-- 첫번째 페이지는?  ★limit 는 0 부터 시작한다★  처음부터 5개
SELECT * FROM t2_write ORDER BY uid desc limit 5;  

-- 두번째 페이지는? 5부터 5개
SELECT * FROM t2_write ORDER BY uid desc limit 5, 5; 

-- 세번째 페이지는? 10부터 5개
SELECT * FROM t2_write ORDER BY uid desc limit 10, 5;

 

* n 번째 페이지 목록, 쿼리문은?

pageRows : 한 페이지에 보여지는 글의 개수
page 번째 페이지의 글록록을 보여주는 쿼리문은?

SELECT * FROM t2_write ORDER BY uid desc 
limit (page - 1) * pageRows, pageRows;


* 쿼리문 준비 
D.java

	// 글 전체 개수 가져오기
	public static final String SQL_WRITE_COUNT_ALL = 
			"SELECT count(*) FROM t2_write";

	// fromRow 로부터 row 만큼 SELECT 하기 : LIMIT 은 0 부터 시작함 주의!
	public static final String SQL_WRITE_SELECT_PAGE = 
			SQL_WRITE_USER
			+ "ORDER BY w.uid DESC\r\n"
			+ "LIMIT ?, ?"
			;


* DAO 에 추가

 

list.do 에 page parameter  추가  운영

페이징을 하려면 list.do 에  몇페이지를 보여줄지 에 대한 parameter 가 필요하다
list.do            ← page parameter 없으면 기본 1 page
list.do?page=2     
list.do?page=10

..   

	// 페이징 관련
	// 몇번째(from) 부터 몇개(rows) 를 SELECT
	public List<WriteDTO> selectFromRow(int from, int rows) throws SQLException {
		List<WriteDTO> list = null;
		try{
			pstmt = conn.prepareStatement(D.SQL_WRITE_SELECT_PAGE);
			pstmt.setInt(1, from);
			pstmt.setInt(2, rows);
			rs = pstmt.executeQuery();
			list = buildList(rs);
		}finally{
			close();
		} // end try
		return list;		
	}
	
	
	// 전체 글의 개수
	public int countAll() throws SQLException {
		int cnt = 0;
		try{
			pstmt = conn.prepareStatement(D.SQL_WRITE_COUNT_ALL);
			rs = pstmt.executeQuery();
			rs.next();
			cnt = rs.getInt(1);
		}finally{
			close();
		} // end try
		return cnt;
	} // end countAll()

* ListCommand 작성

package command.write;

import java.sql.SQLException;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import command.Command;
import domain.WriteDAO;
import domain.WriteDTO;

public class ListCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		
		int page = 1;  // 현재 페이지 (디폴트는 1 page)
		String pageParam = request.getParameter("page");
		if(pageParam != null && !pageParam.trim().equals("")) {
			try {				
				page = Integer.parseInt(pageParam);
			} catch (NumberFormatException e) {
				// 별도의 처리 안함
			}
		} // end if
		
		// 페이징
		// writePages: 한 [페이징] 당 몇개의 페이지가 표시되나
		// pageRows: 한 '페이지'에 몇개의 글을 리스트 할것인가?
		HttpSession session = request.getSession();
		Integer writePages = (Integer)session.getAttribute("writePages");
		if(writePages == null) writePages = 10; // session 에 없으면 기본값으로
		Integer pageRows = (Integer)session.getAttribute("pageRows");
		if(pageRows == null) pageRows = 10;  // session 에 없으면 기본값으로
		
		int cnt = 0;  // 글 전체의 개수
		int totalPage = 0;  // 총 몇 '페이지' 분량인가?
		
		List<WriteDTO> list = null;
		
		try {			
			cnt = new WriteDAO().countAll();  // 글 전체의 개수
			totalPage = (int)Math.ceil(cnt / (double)pageRows);  // 총 페이지 수
			
			// 현재 페이지 보정 (범위를 벗어난 경우)
			if(page < 1) page = 1;
			if(page > totalPage) page = totalPage;
			
			// 쿼리 실행
			int fromRow = (page - 1) * pageRows; // (MySQL)
			list = new WriteDAO().selectFromRow(fromRow, pageRows);
			
			// [페이징] 에 표시할 '시작페이지' 와 '마지막페이지' 계산
		    int startPage = ((int)((page - 1) / writePages) * writePages) + 1;
		    int endPage = startPage + writePages - 1;
		    if (endPage >= totalPage) endPage = totalPage;
		    
			request.setAttribute("cnt", cnt);  // 전체 글 개수
			request.setAttribute("writePages", writePages); // [페이징] 에 표시할 숫자 개수
			request.setAttribute("pageRows", pageRows);  // 한 '페이지' 에 표시할 글 개수
			session.setAttribute("page", page);  // 현재 페이지 (세션에 저장해두자)
			request.setAttribute("startPage", startPage);  // [페이징] 에 표시할 시작 페이지
			request.setAttribute("endPage", endPage);   // [페이징] 에 표시할 마지막 페이지
			request.setAttribute("totalPage", totalPage); // 총 페이지 개수
			request.setAttribute("url", request.getRequestURI());  // 목록 url 
		    
			
			// "list" 란 name 으로 request 에 list 를 담아 컨트롤러에 전달
			request.setAttribute("list", list);
		} catch(SQLException e) {
			e.printStackTrace();
			// ※ 예외 처리 필요 
			//  ex) 예외 페이지 설정, 혹은 예외 페이지로 redirect
		}
		
	}

}


pageRows 값 변화에 따른 페이징



list.jsp 상단에 select 수정
<form> 감싸기
select 값이 변화될때 submit 되기 → jQuery 동작
현재 page 값이 

<%@ 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="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%-- JSTL 버젼으로 바뀌니, import 의 번잡함도 사라진다!  JAVA 변수 선언도 사라진다! --%>
<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>

	<script src="https://kit.fontawesome.com/51772bd9bd.js"></script>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
	<script src="${pageContext.request.contextPath }/js/list.js"></script>

    <title>목록</title>
</head>

<body>

	<%-- 인증 헤더 --%>
	<jsp:include page="/WEB-INF/views/common/header.jsp"/>

    <div class="container mt-3">
        <h2>목록</h2>
        <hr>
		<div class="mb-3 mt-3 clearfix">
            <span class="float-start me-2">총 ${cnt }개</span>
            <span class="float-start">page ${page }/${totalPage }</span>
            <span class="float-end">
            	<form name="frmPageRows">
            		<input type="hidden" name="page" value="${page }">
	                <select class="form-select" name="pageRows">
	                    <option value="10" ${pageRows == 10 ? 'selected' : '' }>10</option>
	                    <option value="15" ${pageRows == 15 ? 'selected' : '' }>15</option>
	                    <option value="20" ${pageRows == 20 ? 'selected' : '' }>20</option>
	                    <option value="50" ${pageRows == 50 ? 'selected' : '' }>50</option>
	                </select>
            	</form>
            </span>
        </div>
        <table class="table table-hover">
            <thead class="table-success">
                <tr>
                    <th>UID</th>
                    <th>제목</th>
                    <th>작성자</th>
                    <th>조회수</th>
                    <th>작성일</th>
                </tr>
            </thead>
            <tbody>
			<c:forEach var="dto" items="${list }">
                <tr>
                    <td>${dto.uid }</td>
                    <td><a href="view.do?uid=${dto.uid }">${dto.subject }</a></td>
                    <td>${dto.user.username }</td>
                    <td>${dto.viewCnt }</td>
                    <td>${dto.regDateTime }</td>
                </tr>
			</c:forEach>
            </tbody>
        </table>
        <div class="row">
        	<c:if test="${not empty sessionScope.PRINCIPAL }">
            <div class="col-12">
                <a class="btn btn-outline-dark" href="write.do">작성</a>
            </div>
        	</c:if>
        </div>
    </div>

    <!-- pagination -->
    <div class="container mt-1">
        <ul class="pagination justify-content-center">
        	<%-- << 표시여부 --%>
        	<c:if test="${page > 1 }">
        	<li class="page-item"><a class="page-link" href="${url }" title="처음"><i class="fa fa-angle-double-left"></i></a></li>
        	</c:if>
        	
        	<%-- < 표시 여부 --%>
        	<c:if test="${startPage > 1 }">
            <li class="page-item"><a class="page-link" href="${url }?page=${startPage - 1}"><i class="fas fa-angle-left"></i></a></li>
        	</c:if>
            
        	<%-- 페이징 안의 '숫자' 표시 --%> 
        	<c:if test="${totalPage > 1 }">
        		<c:forEach var="k" begin="${startPage }" end="${endPage }">
        		<c:choose>
        			<c:when test="${k != page }">
        				<li class="page-item"><a class="page-link" href="${url }?page=${k }">${k }</a></li>        			
        			</c:when>
        			<c:otherwise>
        				<li class="page-item active"><a class="page-link" href="javascript:void(0);">${k }</a></li>
        			</c:otherwise>
        		</c:choose>
        		</c:forEach>    
        	</c:if>
                        
            <%-- > 표시 여부 --%>
            <c:if test="${totalPage > endPage }">
            <li class="page-item"><a class="page-link" href="${url }?page=${endPage + 1 }"><i class='fas fa-angle-right'></i></a></li>
            </c:if>
            
            <%-- >> 표시 여부 --%>
            <c:if test="${page < totalPage }">
            <li class="page-item"><a class="page-link" href="${url }?page=${totalPage }"><i class='fas fa-angle-double-right'></i></a></li>
            </c:if>
            
            
        </ul>
    </div>
    <!-- pagination -->


</body>

</html>


js 코드 추가
js/list.js 추가 및
list.jsp 에는 jQuery 및 js 파일 포함시키기

list.js 작성
pageRows.do 가 작동하는지 확인하자.

pageRows.do 작성하기
DoController.java 에 추가

 

ViewCommand.java     view.jsp 수정
SelectCommand.java   update.jsp 수정

조회, 혹은 수정 중에 '목록' 으로 돌아오려면
기존의 page 로 돌아오게 해주어야 한다.

'JSP' 카테고리의 다른 글

[JSP] File Update, Delete  (0) 2022.04.12
[JSP] FileUpload / Download  (0) 2022.04.11
[JSP] Authentification - 회원제 게시판  (0) 2022.04.10
[JSP] Authentication - 로그인/로그아웃  (0) 2022.04.10
[JSP] Authentication - 회원가입  (0) 2022.04.07