Paging
페이징 알고리즘
EX
<%@page import="kr.co.jsp.board.commons.PageVO"%>
<%@page import="kr.co.jsp.board.model.BoardDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%-- <%
// 테스트 데이터 생성
for(int i = 1; i <= 500; i++){
String writer = "테스트문" + i;
String title = "테스트용" + i;
String content = i + "번째 테스트 중입니당~" ;
BoardDAO.getInstance().regist(writer, title, content);
}
out.print("insert 완료!");
%> --%>
<%
out.print("----------------------------- <br>");
out.print("총 게시물 수 : " + BoardDAO.getInstance().countboards() + "<br>");
out.print("----------------------------- <br>");
// 끝 페이지 번호 계산 테스트
PageVO paging = new PageVO();
paging.setPage(53);
// 총 페이지 개수
paging.setCountPerPage(10);
// 한페이지 당 보여질 게시물 개수
int displayBtn = 10;
// 버튼 수
int endPage =(int) Math.ceil((double) paging.getPage() / displayBtn) * displayBtn;
out.print("끝 페이지 번호 : " + endPage +"번 <br>");
// 시작 페이지 번호 계산 테스트
int beginPage = (endPage - displayBtn) + 1;
out.print("시작 페이지 번호 : " + beginPage +"번 <br>");
// 이전버튼 활성화 여부
boolean isPrev = (beginPage == 1) ? false : true;
out.print("이전버튼 활성화 여부 : " + isPrev +"<br>");
// 다음버튼 활성화 여부
boolean isNext = (BoardDAO.getInstance().countboards() <= (endPage * paging.getCountPerPage()) ? false:true);
// 총 게시물의 수가 끝페이지 * 한화면에 보여줄 게시물수 보다 작으면 false
out.print("다음버튼 활성화 여부 : " + isNext +"<br>");
// 끝 페이지 보정
if (!isNext) {
endPage = (int) Math.ceil(BoardDAO.getInstance().countboards() / (double) paging.getCountPerPage());
}
out.print("보정 후 끝 페이지 번호 : " + endPage + "번");
%>
</body>
</html>
Create Table
DB 명령어 (10개씩 출력)
값만 ?로 표현 (PageVO활용)
SELECT * FROM
(SELECT ROWNUM as rn, tbl.* FROM
(SELECT * FROM my_board
ORDER BY board_id DESC) tbl);
-- WHERE rn > 30 and rn < 39;
-- 조건을 실행하고 싶으면
BoardDAO
DAO의 listBorad메서드의 sql 내용을 수정
@Override
public List<BoardVO> listBoard(PageVO paging) {
List<BoardVO> articles = new ArrayList<>();
String sql = "SELECT * FROM "
+ "(SELECT ROWNUM AS rn, tbl.* FROM "
+ " (SELECT * FROM my_board ORDER BY board_id DESC) tbl"
+ ") WHERE rn > " +(paging.getPage() -1) * paging.getCountPerPage()
+ "AND rn <= " + (paging.getPage() * paging.getCountPerPage());
try(Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()){
while (rs.next()) {
BoardVO board = new BoardVO(
rs.getInt("board_id"),
rs.getString("writer"),
rs.getString("title"),
rs.getString("content"),
rs.getTimestamp("reg_date"),
rs.getInt("hit")
);
articles.add(board);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return articles;
}
PageVO
페이지 번호와 한 화면에 보여질 게시물 수를 구하기 위한 클래스
package kr.co.jsp.board.commons;
// 사용자가 선택하는 페이지의 전반적인 정보를 담아놓을 클래스
public class PageVO {
private int page;
// 사용자가 선택한 페이지 번호
private int countPerPage;
// 한 화면에 보여질 게시물의 수
public PageVO() {
// 사용자가 처음에 들어왔을 때 기본값을 설정해줘야 해서 기본값을 생성자에 설정
page = 1;
countPerPage = 10;
}
public PageVO(int page, int countPerPage) {
super();
this.page = page;
this.countPerPage = countPerPage;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getCountPerPage() {
return countPerPage;
}
public void setCountPerPage(int countPerPage) {
this.countPerPage = countPerPage;
}
@Override
public String toString() {
return "PageVO [page=" + page + ", countPerPage=" + countPerPage + "]";
}
}
PageCreator
paging 알고리즘을 구현하기 위한 클래스
package kr.co.jsp.board.commons;
public class PageCreator {
/*
* 페이징을 하기 위해서 필요한 것들 - 페이지 번호와 한 페이지당 들어갈 게시물의 개수를 가지고 있는 객체
*/
private PageVO paging;
private int BoardTotalCount;
// 총 게시물 개수
private int beginPage;
// 시작 페이지 번호
private int endPage;
// 끝 페이지 번호
private boolean prev;
// 이전 버튼 활성화 여부
private boolean next;
// 다음 버튼 활성화 여부
// 한 화면에 보여질 페이지 버튼 수
private final int displayBtn = 10;
// final로 지정했으니 수정불가
// 페이징 알고리즘을 수행할 메서드 선언
private void calcDataOfPage() {
// 보정 전 끝 페이지
this.endPage = (int) (Math.ceil(paging.getPage() / (double) displayBtn)) * this.displayBtn;
System.out.println(paging);
System.out.println(endPage);
// 시작 페이지 번호
this.beginPage = (this.endPage - this.displayBtn + 1);
// 현재 시작 페이지가 1이라면 이전 버튼 비활성화 여부 판단
this.prev = (this.beginPage == 1) ? false : true;
// 마지막 페이지인지 여부 확인 후 다음 버튼 비활성 여부 판단
this.next = ((this.BoardTotalCount <= (this.endPage * paging.getCountPerPage())) ? false : true);
// 다음 버튼 비활성화라면 끝 페이지 보정
if (!this.next) {
this.endPage = (int) Math.ceil(this.BoardTotalCount / (double) paging.getCountPerPage());
}
}
public PageVO getPaging() {
return paging;
}
public void setPaging(PageVO paging) {
this.paging = paging;
}
public int getBoardTotalCount() {
return BoardTotalCount;
}
public void setBoardTotalCount(int BoardTotalCount) {
this.BoardTotalCount = BoardTotalCount;
// service 클래스가 이 메서드를 통해 총 게시물의 개수를 전달할 때
// 밑의 calcDataOfPage()가 가동될 수 있도록 하는 것
calcDataOfPage();
}
public int getBeginPage() {
return beginPage;
}
public void setBeginPage(int beginPage) {
this.beginPage = beginPage;
}
public int getEndPage() {
return endPage;
}
public void setEndPage(int endPage) {
this.endPage = endPage;
}
public boolean isPrev() {
return prev;
}
public void setPrev(boolean prev) {
this.prev = prev;
}
public boolean isNext() {
return next;
}
public void setNext(boolean next) {
this.next = next;
}
@Override
public String toString() {
return "PageCreator [paging=" + paging + ", BoardTotalCount=" + BoardTotalCount + ", beginPage=" + beginPage
+ ", endPage=" + endPage + ", prev=" + prev + ", next=" + next + ", displayBtn=" + displayBtn + "]";
}
}
GetListService
사용자가 처음 게시판에 들어왔을 때 페이지 선택을 하지 않으므로 조건문 추가
http://localhost:8181/MyWeb/list.board?page=4&cpp=10 ← 직접 url에 값을 지정할 수 있음
package kr.co.jsp.board.service;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import kr.co.jsp.board.commons.PageCreator;
import kr.co.jsp.board.commons.PageVO;
import kr.co.jsp.board.model.BoardDAO;
import kr.co.jsp.board.model.BoardVO;
public class GetListService implements IBoardService {
// 글목록을 가져오는 로직 구현
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) {
// controller에서 수행하던 작업(이동 제어제외) 모두 가져옴
PageVO paging = new PageVO();
// 사용자가 처음 게시판에 들어올 때는 페이지 선택을 하지 않기 때문에 페이지 선택을 1페이지로 게시물 개수를 10개로 지정
// 사용자가 버튼을 눌렀을때만 밑의 2개의 코드를 실행해야함
if (request.getParameter("page") != null) {
paging.setPage(Integer.parseInt(request.getParameter("page")));
// 사용자가 클릭햇을때의 버튼을 page로 설정
paging.setCountPerPage(Integer.parseInt(request.getParameter("cpp")));
System.out.println("선택한 페이지 : " + paging.getPage());
System.out.println("게시물 수 : " + paging.getCountPerPage());
}
List<BoardVO> boardList = BoardDAO.getInstance().listBoard(paging);
// request에 boardlist에 담으면 응답을 한 뒤 소멸하기 때문에 관리하기 좋음
// 페이지 버튼 배치를 위해 PageCreator 객체 생성
PageCreator pc = new PageCreator();
// 페이징 버튼 알고리즘을 위해 PageVO 객체와 총 게시물 수를 setter메서드로 전달해야
// 페이징 알고리즘 구현 가능
pc.setPaging(paging);
pc.setBoardTotalCount(BoardDAO.getInstance().countboards());
System.out.println(pc);
// pc.toString()을 overwriting해서 알고리즘의 결과가 잘 초기화 되었는지 확인
// boardlist에서 BoardVO를 하나씩 뽑아서 현재 날짜 기준으로 1일이 지났는지 파악
// 이후 1일이 지나지 않았으면 newMark를 사용하는 로직 구현
for(BoardVO board: boardList) {
// 운영체제의 현재 시간을 읽어서 밀리초로 리턴하는 메서드
// currentTimeMillis은 1970년 1월 1일 자정을 기준으로 현재까지 흐른 시간을 밀리초로 리턴
//
long now = System.currentTimeMillis();
// 게시물의 작성 시간을 밀리초로 읽어오기
long regTime = board.getRegDate().getTime();
if (now - regTime < 60 * 60 * 24 * 1000) {
// 하루의 시간보다 작으면
board.setNewMark(true);
}
// else는 굳이 쓸 필요가 없음 (boolean은 default type이 false)
}
request.setAttribute("bList", boardList);
//sendRedirect를 하면 안되는 이유
//request객체에 list를 담아서 전달하려 하는데, sendRedirect를 사용하면
//응답이 나가면서 request 객체가 소멸해 버립니다.
// response.sendRedirect("board/board_list.jsp"); (x)
// jsp 파일에서 버튼 배치를 위해 모든 정보가 완성된 PageCreator 객체를
// pc라는 이름의 request 객체에 담아서 forwarding ~~~
request.setAttribute("pc", pc);
}
}
boardList.jsp
Service와 PageCreator에서 진행한 결과를 화면에 출력
<%@ 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"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
tbody {
font-size: 20px;
}
.page-link {
background-color: orange;
}
.btn-countPerPage{
background-color : gray;
color : white;
}
</style>
</head>
<body>
<%--
로그인하지 않은 사용자가 게시판에 들어왔을 경우 돌려보내는 코드를 작성.
--%>
<c:if test="${user == null}">
<script>
alert("회원만 이용 가능한 게시판입니다. 로그인 해 주세요.");
//board_list.jsp로 직접 요청이 들어가는 경우는 없기 때문에
//컨트롤러를 기준으로 상대 경로로 작성하시든지, 절대 경로로 작성해야 합니다.
location.href = "user/user_login.jsp";
</script>
</c:if>
<c:if test="${searchFail}">
<script>
alert("조회 결과가 없습니다.");
location.href = "/MyWeb/list.board";
</script>
</c:if>
<jsp:include page="../include/header.jsp" />
<div class="container">
<h2>My Web게시판</h2>
<span style="float: right; margin-bottom: 15px"> <input
class="btn btn-countPerPage" type="button" value="10"
onclick="location.href='/MyWeb/list.board?page=1&cpp=10'">
<input class="btn btn-countPerPage" type="button" value="20"
onclick="location.href='/MyWeb/list.board?page=1&cpp=20'">
<input class="btn btn-countPerPage" type="button" value="30"
onclick="location.href='/MyWeb/list.board?page=1&cpp=30'">
<!-- countPerPage를 바꿀 수 있도록 개별 지정했구나! -->
</span>
<table class="table table-secondary table-hover table-bordered">
<thead style="font-size: 25px">
<tr>
<th>글 번호</th>
<th>작성자</th>
<th>제목</th>
<th>날짜</th>
<th>조회수</th>
</tr>
</thead>
<tbody>
<c:forEach var="b" items="${bList}">
<tr>
<td>${b.boardId}</td>
<td>${b.writer}</td>
<td><a href="/MyWeb/content.board?bId=${b.boardId}">${b.title}</a>
<c:if test="${b.newMark }">
<!-- newMark가 true면 (하루가 지나지 않았으면) -->
<img alt="newMark" src="/MyWeb/img/icon_new.gif">
</c:if></td>
<td><fmt:formatDate value="${b.regDate }"
pattern="yyyy년 MM월 dd일 a hh시 mm분" /> <!-- fmt태그를 이용하여 format 지정 -->
</td>
<td>${b.hit}</td>
</tr>
</c:forEach>
</tbody>
<%-- 페이징을 처리할 구간 --%>
<tbody>
<tr>
<td colspan="5" align="center">
<ul class="pagination pagination-lg">
<%-- 이전 버튼 --%>
<c:if test="${pc.prev }">
<!-- request에서 받아온 이름이 pc라는 얘의 prev가 true면 밑에 로직 실행 -->
<li class="page-item"><a class="page-link"
href="/MyWeb/list.board?page=${ pc.beginPage-1}&cpp=${ pc.paging.countPerPage}"
style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #f78f24; opacity: 0.8">이전</a>
</li>
</c:if>
<%-- 페이지 버튼 --%>
<c:forEach var="pageNum" begin="${pc.beginPage}"
end="${pc.endPage}">
<!-- 반복을 사용해서 진행 -->
<li class="page-item"><a
href="/MyWeb/list.board?page=${pageNum }&cpp=${pc.paging.countPerPage}"
class="page-link"
style="margin-top: 0; height: 40px; color: pink; border: 1px solid #643691; ${pageNum == pc.paging.page ? 'background-color: orange;' : ''}">${pageNum}</a>
<!-- 목록을 요청하고 값을 반복문의 값을 넣어줘서 페이지를 넘겨줌 --> <!-- 현재 페이지와 사용자가 요청한 page와 같으면 background color를 색칠 -->
</li>
</c:forEach>
<%-- 다음 버튼 --%>
<c:if test="${pc.next }">
<!-- request에서 받아온 이름이 pc라는 얘의 next가 true면 밑에 로직 실행 -->
<li class="page-item"><a class="page-link"
href="/MyWeb/list.board?page=${ pc.endPage+1}&cpp=${ pc.paging.countPerPage}"
style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #f78f24; opacity: 0.8">다음</a>
</li>
</c:if>
</ul>
</td>
</tr>
</tbody>
<tbody>
<tr>
<td colspan="5" align="right">
<form action="/MyWeb/search.board" class="form-inline">
<div class="form-group">
<select name="category" class="form-control">
<option value="title">제목</option>
<option value="writer">작성자</option>
<option value="content">내용</option>
</select> <input type="text" name="search" placeholder="검색어 입력"
class="form-control"> <input type="submit" value="검색"
class="btn btn-default"> <input type="button"
value="글 작성" class="btn btn-default"
onclick="location.href='/MyWeb/write.board'">
</div>
</form>
</td>
</tr>
</tbody>
</table>
</div>
<jsp:include page="../include/footer.jsp" />
</body>
</html>