Day59 - [JSP]페이징

2021. 5. 10. 22:03JSP 공부

이번에는 JSP 환경에서 페이징 을 알아 봅시다.

 

페이징

페이징은 아래와 같이, 게시판의 글이 많으면 나눠서 보여주는 것을 말하는 데요

※ 페이징을 구현하기 전에, 글의 개수를 늘렸습니다.

 

페이징의 특성

1. 반드시 GET 방식으로만 처리한다

2. 이동할 때 페이지 번호, 보여줄 페이지 개수 를 가지고 다녀야 한다 -> 목록 으로 나올 때 현재 페이지를 유지하기 위해

3. 페이징 처리하는 로직을 클래스로 분류한다 -> PageVO클래스는 페에징 관련 모든 페이지를 계산한다

 

먼저, 페이징을 구현하기 위해서는 각각의 페이지에서 몇개의 게시판 목록을 보여줄지 에 대한 메서드를 먼저 구현해야 합니다.

 

페이징 구현

그러기 위해서는 게시판의 특성을 잘 생각해 봅시다.

게시판은 최신글이 위에 나타나는 특성을 가지고 있습니다. ( 내림차순 )

각각의 페이지에서는 보여지는 글 목록 ( 글번호 ) 가 다릅니다.

 

그러면, sql 문을 생각 해 봅시다.

어떠한 기준으로 내림차순을 한 후 번호를 매겨서 원하는 번호만 가져오는 방법. ROWNUM 을 이용하면 되겠죠?

위의 쿼리문 처럼  3중 쿼리문을 작성해주면 됩니다.

그리고 WHERE 조건에 있는 숫자를 조정하면 되겠죠?

물음표에 들어가는 숫자에 따라서 화면에서 보여지는 각각의 페이지 마다 글개수 가 달라지겠습니다.

그러면, 물음표에 들어가는 숫자를 어떻게 넣어주면 될까요?

 

예를 들어서 1 페이지 마다 10개씩 보여진다고 생각을 해보면,

1 페이지 = 1 ~ 10   -> rn > 0 and rn <= 10

2 페이지 = 11 ~ 20  -> rn > 10 and rn <= 20

3 페이지 = 21 ~ 30   -> rn > 20 and rn <= 30

이런 식으로 나오겠죠?

첫번째 물음표 = (현재 페이지 - 1) * 보여줄 게시글 개수

두번째 물음표 = 현재 페이지 * 보여줄 게시글 개수

가 되겠네요

그러면 pageNum(페이지 번호) 와 amount(보여줄 게시글 수) 를 매개변수로 받아서 뿌려주면 되겠습니다.

public List<BoardVO> getList(int pageNum, int amount){
			
			List<BoardVO> list = new ArrayList<>();
			
			String sql = "select * "
					+ 	 "from (select rownum rn,"
					+ 				   " a.* "
					+ 			"from (select *"
					+ 				 " from board order by bno desc) a ) "
					+ 	 "where rn > ? and rn <= ?";
			
			try {
				conn = ds.getConnection(); // 연결
				
				pstmt = conn.prepareStatement(sql); // sql준비
				pstmt.setInt(1, (pageNum - 1) * amount);
				pstmt.setInt(2, pageNum * amount);
				
				rs = pstmt.executeQuery(); // sql문 실행
				
				while(rs.next()) {
					// 한바퀴 회전당 VO를 하나씩 생성
					BoardVO vo = new BoardVO();
					
					vo.setBno(rs.getInt("bno"));
					vo.setWriter(rs.getString("writer"));
					vo.setTitle(rs.getString("title"));
					vo.setContent(rs.getString("content"));
					vo.setRegdate(rs.getTimestamp("regdate")); // 날짜형은 Timestamp() or Date()
					vo.setHit(rs.getInt("hit"));
					
					list.add(vo);
				}
				
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				JdbcUtil.close(conn, pstmt, rs);
			}
			
			return list;
		}

자 이제, 원하는 개수만큼 잘라서 화면에 나타내 주었습니다.

 

그러면, 페이지를 구분 지을 수 있도록 해주어야 겠죠? ( PageVO 라는 클래스를 만듭니다. )

 

페이지를 나타낼 때 필요한 변수들을 생각해 봅시다.

먼저, 화면에 보여질 시작 페이지번호 와 끝 페이지 번호. 이전 , 다음 여부.

현재 조회하는 페이지번호 , 화면에 보여질 게시글 수 , 총 게시글 수 가 필요합니다.

그리고 계산이 좀 필요한데요.

pageNum, amout, total 은 매개변수로 넘겨 받아서 저장을 해주면 됩니다.

 

하지만, startPage , endPage, prev , next 는 계산을 해주어야 하죠.

 

1. endPage

ex) 조회하는 페이지 1 -> 끝번호 10
ex) 조회하는 페이지 9 -> 끝번호 10
ex) 조회하는 페이지 11 -> 끝번호 20

공식 = (int)Math.ceil(페이지번호 / 페이지네이션개수) * 페이지네이션개수

2. startPage

공식 = 끝페이지 - 페이지네이션개수 + 1

3. realEnd(진짜 끝번호) 구해서 endPage의 값을 다시 결정

ex) 만약 게시글이 52개라면 -> 진짜 끝번호 6
ex) 만약 게시글이 105개라면 -> 진짜 끝번호 11

공식 = (int)Math.ceil( 전체게시글 수 / 화면에 보여질 게시글 수 )

마지막페이지 도달했을 때 보여져야 하는 끝번호가 달라집니다.
ex) 131개 게시물
1번 페이지 클릭시 -> endPage = 10, realEnd = 14 ( endPage로 세팅 )
11번 페이지 클릭시 -> endPage = 20, realEnd = 14 ( realEnd로 세팅 )

4. prev 결정 ( 이전 글이 있을 경우에만 활성화 )

5. next 결정 ( 다음 글이 있을 경우에만 활성화 )

ex) 131개 게시물
1~10 클릭시 endPage = 10, realEnd = 14 -> 다음버튼 true
11 클릭시 endPage = 14 , realEnd = 14 -> 다음버튼 false

그리고 데이터들이 private 처리 되어 있기 때문에, setter 와 getter 를 만들어 줍니다.

 

PageVO

public class PageVO {

	/*
	 * 화면에 그려질 pageNation을 계산하는 클래스 ( pageNum, amount값을 가지고 다님 )
	 */

	private int startPage; // 게시글 화면에 보여질 첫번째 번호
	private int endPage; // 게시글 화면에 보여질 마지막 번호
	private boolean prev, next; // 이전버튼, 다음버튼 활성화여부
	
	private int pageNum; // 현재 조회하는 페이지번호
	private int amount = 10; // 화면에 그려질 데이터
	private int total; // 전체게시글 수
	
	// 생성자에서는 객체가 생성될때 계산을 처리
	public PageVO(int pageNum, int amount, int total) {
		this.pageNum = pageNum;
		this.amount = amount;
		this.total = total;
		
		// 1. endPage결정
//		 ex) 조회하는 페이지 1 -> 끝번호 10
//		 ex) 조회하는 페이지 9 -> 끝번호 10
//		 ex) 조회하는 페이지 11 -> 끝번호 20
//		 공식 = (int)Math.ceil(페이지번호 / 페이지네이션개수) * 페이지네이션개수
		this.endPage = (int)Math.ceil(this.pageNum * 0.1) * 10;
		
		// 2. startPage결정
		// 공식 = 끝페이지 - 페이지네이션개수 + 1
		this.startPage = this.endPage - 10 + 1;
		
		// 3. realEnd(진짜 끝번호) 구해서 endPage의 값을 다시 결정
//		 만약 게시글이 52개라면 -> 진짜 끝번호 6
//		 만약 게시글이 105개라면 -> 진짜 끝번호 11
//		 공식 = (int)Math.ceil(전체게시글수 / 화면에보여질데이터개수)
		int realEnd = (int)Math.ceil(this.total / (double)this.amount);
		
//		 마지막페이지 도달했을 때 보여져야 하는 끝번호가 달라집니다.
//		 ex) 131개 게시물
//		 1번 페이지 클릭시 -> endPage = 10, realEnd = 14 ( endPage로 세팅 )
//		 11번 페이지 클릭시 -> endPage = 20, realEnd = 14 ( realEnd로 세팅 )
		if(this.endPage > realEnd) {
			this.endPage = realEnd;
		}
		
		// 4. prev결정 ( startPage의 번호는 1, 11, 21... )
		this.prev = this.startPage > 1;
		
		// 5. next결정
//		 ex: 131개 게시물
//		 1~10 클릭시 endPage = 10, realEnd = 14 -> 다음버튼 true
//		 11 클릭시 endPage = 14 , realEnd = 14 -> 다음버튼 false
		this.next = this.endPage < realEnd;
		
		// 확인
		System.out.println("시작페이지:" + this.startPage + ", 끝페이지:" + this.endPage);
		
		// GetListService에서 페이지VO 계산처리 코드작성...
	}

만들어진 PageVO 를 보면, 전체 게시글 수가 필요하기 때문에, DAO 에 전체 게시글 수를 구하는 메서드를 구합니다.

// 전체 게시글 수
		public int getTotal() {
			int result = 0;
			
			String sql = "select count(*) as total from board";
			
			try {
				conn = ds.getConnection();
				
				pstmt = conn.prepareStatement(sql);
				
				rs = pstmt.executeQuery();
				
				if(rs.next()) {
					result = rs.getInt("total");
				}
				
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				JdbcUtil.close(conn, pstmt, rs);
			}
			
			
			return result;
		}

이제 페이징을 구현하기 위한, 모든 준비는 끝이 났습니다.

 

list.board 로 넘어갈 때, 처리를 해주어야 겠죠?

 

파일명 : GetListServiceImpl.java

public class GetListServiceImpl implements IBoardService{

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		
		// 1. 화면전환 시에 조회하는 페이지번호 and 화면에 그려질 데이터개수 2개를 전달받음
		// 첫 페이지 경우
		int pageNum = 1;
		int amount = 10;
		
		// 페이지번호를 클릭하는 경우
		if(request.getParameter("pageNum") != null && request.getParameter("amount") != null) {
			pageNum = Integer.parseInt(request.getParameter("pageNum"));
			amount = Integer.parseInt(request.getParameter("amount"));
		}
		
		
		// DAO생성
		BoardDAO dao = BoardDAO.getInstance();
		
		// 2. pageVO생성
		List<BoardVO> list = dao.getList(pageNum, amount);
		int total = dao.getTotal(); // 전체게시글수
		PageVO pageVO = new PageVO(pageNum, amount, total);
		
		// 3. 페이지네이션을 화면에 전달
		request.setAttribute("pageVO", pageVO);
		
		// 화면에 가지고 나갈 list를 request에 저장 !!
		request.setAttribute("list", list);
	}
}

위의 코드를 보면 알겠지만, pageNum 과 amount 는 클릭했을 때 그 값이 넘겨져 와야 합니다.

위의 <a> 태그를 보면 pageNum 과 amount 를 get 방식으로 넘겨주고 있습니다.

 

이전 버튼과 다음 버튼도 조건에 따라 활성화 밑 구현 시켜 주어야 겠죠?

 

이렇게, 페이징에 대해서 알아 보았습니다.

 

고생하셨습니다.

 

※ 아래는 해당하는 게시판 목록 화면의 .jsp 파일 입니다.

<%@ 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" %> 
<%@ include file="../include/header.jsp" %>   
	<style>
        .btn {
          
            border: 0; 
            border-radius: 0; /*윤곽 0*/
            padding: 5px 10px; 
            margin: 20px 0px;
        }
	</style>

	<div class="container">
		<h3>My Web게시판</h3>

		<div>
			<select onchange="change(this)">
				<option value="10" ${pageVO.amount eq 10 ? 'selected' : '' }>10개씩 보기</option>
				<option value="20" ${pageVO.amount eq 20 ? 'selected' : '' }>20개씩 보기</option>
				<option value="50" ${pageVO.amount eq 50 ? 'selected' : '' }>50개씩 보기</option>
				<option value="100" ${pageVO.amount eq 100 ? 'selected' : '' }>100개씩 보기</option>
			</select>
		</div>

		<table class="table table-bordered">
			<thead>
				<tr>
					<th>글 번호</th>
					<th>작성자</th>
					<th>제목</th>
					<th>날짜</th>
					<th>조회수</th>
				</tr>
			</thead>
			
			<tbody>
				<c:forEach var="vo" items="${list }">
					<tr>
						<td>${vo.bno }</td>
						<td>${vo.writer}</td>
						<td><a href="getContent.board?bno=${vo.bno }">${vo.title }</a></td>
						<td><fmt:formatDate value="${vo.regdate }" pattern="yyyy년 MM월dd일 HH시 mm분 ss초" /></td>
						<td>${vo.hit }</td>
					</tr>
				</c:forEach>
			</tbody>
			
			<tbody>
				<tr>
					<td colspan="5" align="center">
	               			<ul class="pagination pagination-sm">
	               			
	               				<!-- 2. 이전버튼 활성화 여부 -->
	               				<c:if test="${pageVO.prev }">
                        			<li><a href="list.board?pageNum=${pageVO.startPage - 1 }&amount=${pageVO.amount}">이전</a></li>
								</c:if>
								                        		
                        		<!-- 1. 페이지번호 처리 -->
                        		<c:forEach var="num" begin="${pageVO.startPage }" end="${pageVO.endPage }">
	                        		<li  class="${pageVO.pageNum eq num ? 'active' : '' }">
	                        		<a href="list.board?pageNum=${num }&amount=${pageVO.amount}">${num }</a></li>
                        		</c:forEach>
                        		
                        		<!-- 3. 다음버튼 활성화 여부 -->
                        		<c:if test="${pageVO.next }">
                        			<li><a href="list.board?pageNum=${pageVO.endPage + 1 }&amount=${pageVO.amount}">다음</a></li>
                        		</c:if>
                    			</ul>
					<input type="button" value="글 작성" class="btn btn-default pull-right" onclick="location.href='write.board'">
					
					</td>
				</tr>
			</tbody>
		
		</table>
	</div>

<%@ include file="../include/footer.jsp" %>

<script>
	function change(a){
		//console.log(a);
		//console.log(a.value);
		location.href="list.board?pageNum=1&amount=" + a.value;
	}
</script>

'JSP 공부' 카테고리의 다른 글

Day59 - [JSP]필터  (0) 2021.05.10
Day 58 -[JSP]MVC2 게시판 만들기  (0) 2021.05.09
Day57 - [JSP]URL패턴 , MVC2 전체적인 컴포넌트 설계  (0) 2021.05.06
Day56 - [JSP]연결풀(Connection pool)  (0) 2021.05.05
Day56 - [JSP]JSTL  (0) 2021.05.05