Paging

Let's Just Go·2022년 6월 11일
0

JSP

목록 보기
12/12

MVC2

Paging Algorithm

  • Paging

    • 페이징 알고리즘

      1. 사용자가 보게 될 페이지 화면
        • 한 화면에 페이지 버튼을 10개씩 끊어서 보여준다면?
          ex) 1 2 3 4 ... 9 10 [다음] // [이전] 31 32 33 ... 40 [다음]
        • 만약에 총 게시물의 수가 67개라면?
          • (만약 한 페이지 당 게시물 10개씩 배치) 1, 2, 3, 4, 5, 6페이지 필요하고 7페이지까지 필요
        • 총 게시물 수가 142개이고 현재 사용자가 12페이지를 클릭했다면? (버튼 10개 단위)-
          • [이전] 11 12 13 14 15
      2. 총 게시물의 개수를 조회
        • 총 게시물 수는 DB로 부터 수를 조회하는 SQL문 작성
          • SELECT COUNT(*) FROM my_board;
      3. 사용자가 현재 위치한 페이지를 기준으로 끝 페이지 번호를 계산하는 로직을 작성
        • 만약 현재 사용자가 보고 있는 페이지가 3페이지고 한 화면에 보여줄 페이지 버튼이 10개라면?
        • 끝 페이지 번호 : 10
        • 만약 현재 페이지가 36페이지고 한 화면에 보여줄 페이지 수가 20개라면?
          • 끝 페이지 번호 : 40 ( 1~20, 21 ~ 40)
        • Math.ceil(현재 위치 페이지 번호 / 한 화면당 보여질 페이지 수 ) * 한 화면당 보여질 페이지 수
      4. 시작 페이지 번호 계산
        • 현재 위치한 페이지가 15페이지고, 한 화면에 보여줄 페이지 버튼이 10개면
          • 시작 페이지 번호 : 11
        • 현재 위치한 페이지가 73페이지고 한 화면에 보여줄 페이지 버튼 20개면
          • 시작 페이지 번호 : 61
        • (끝 페이지 번호 - 한 화면당 보여질 페이지 수 ) + 1
        • Math.floor(현재 위치 페이지 / 한 화면에 보여줄 페이지 ) * 한 화면에 보여줄 페이지 + 1
      5. 끝 페이지 보정
        • 총 게시물 수가 324개이고 한 페이지당 10개의 게시물을 보여줌
        • 한 화면에 페이지 버튼은 10개가 배치 그렇다면 위 공식에 의한 끝 페이지는 몇번으로 계산?
          • Math.ceil(총 게시물 수 / 한 화면에 보여줄 페이지 ) +1
      6. 이전 버튼 활성화 여부 설정
        • 시작 페이지 번호가 1로 구해진 시점에서는 비활성, 나머지는 활성 처리
      7. 다음 버튼 활성화 여부 설정
        • 보정 전 끝 페이지 번호 * 한 페이지에 들어갈 게시물 수 ≥ 총 게시물 수이면 비활성
      8. 끝 페이지 값 보정
        • 다음 버튼이 비활성화 되었다면 총 게시물 수에 맞춰 끝 페이지 번호를 재 보정
          • Math.ceil(총 게시물의 개수 / 한 페이지에 보여줄 게시물 수)

    • 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>
        							&nbsp;&nbsp; <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>

profile
안녕하세요! 공부한 내용을 기록하는 공간입니다.

0개의 댓글