[B -2-20] 페이징 화면 처리 1

2019. 10. 3. 13:45Project B (SPMS)/Project B 파트3

반응형

페이징 화면 처리 단계 과정

 

  • 브라우저 주소창에 페이지 번호를 전달해서 결과를 확인하는 단계
  • JSP에서 페이지 번호를 출력하는 단계
  • 페이지 번호 클릭 이벤트 처리
  • 전체 데이터 개수를 반영해서 페이지 번호 조절

목록 페이지에서 조회/수정/삭제 페이지 페이지 번호가 계속해서 유지되어야만 하기때문에 신경써야 하는 부분이 많다.


페이징 처리 시 필요 정보들

 

현재 페이지 번호

이전 (prev) 과 다음 (next) 으로 이동 가능한 링크 표시 여부

화면에서 보여지는 페이지의 시작번호 (startPage) 끝번호 (endPage)

 


페이징 처리 클래스 설계

 

src/main/java

com.spms.domain

PageDTO.java

 

...더보기
package com.spms.domain;

import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
public class PageDTO {

  private int startPage;
  private int endPage;
  private boolean prev, next;

  private int total;
  private Criteria cri;

  public PageDTO(Criteria cri, int total) {

    this.cri = cri;
    this.total = total;

    this.endPage = (int) (Math.ceil(cri.getPageNum() / 10.0)) * 10;

    this.startPage = this.endPage - 9;

    int realEnd = (int) (Math.ceil((total * 1.0) / cri.getAmount()));

    if (realEnd <= this.endPage) {
      this.endPage = realEnd;
    }

    this.prev = this.startPage > 1;

    this.next = this.endPage < realEnd;
  }
  
}

Criteria 전체 데이터 수파라미터로 지정한다.

Criteria 내부에는 페이지에서 보여주는 데이터 수현재 페이지 번호를 갖고있기때문에 이를 이용해서 모든 내용을 계산할 수 있다.

 

페이징의 끝 번호 계산

this.endPage = (int) (Math.ceil(cri.getPageNum() / 10.0)) * 10;

페이지 계산 시 끝 번호부터 먼저 계산하는게 수월하다.

페이지 번호는 10개씩 표시

 

Math.ceil() 

소수점을 올림으로 처리한다.

  • 1페이지의 경우 : Math.ceil(0.1) * 10 = 10
  • 10페이지의 경우 : Math.ceil(0.1) * 10 = 10
  • 11페이지의 경우 : Math.ceil(0.1) * 10 = 20

만약 전체 데이터 수가 적으면, 10페이지로 끝나면 안되는 상황이 생길 수도 있기때문에

마지막 번호를 먼저 계산하는 이유는 시작 번호를 계산하기 수월하기 때문이다.

 

만일 화면에 10개씩 보여준다면 시작 번호는 무조건 끝 번호에서 9라는 값을 뺀 값이 된다.

 


페이징의 시작 번호 계산

this.startPage = this.endPage - 9;

 


레코드 전체 건수에 의한 마지막 페이지 재계산

    int realEnd = (int) (Math.ceil((total * 1.0) / cri.getAmount()));

    if (realEnd <= this.endPage) {
      this.endPage = realEnd;
    }

 

끝 번호전체 데이터 건수에 의해 영향을 받는다.

예를 들어 10개씩 보여주는 경우 전체 데이터 수가 80개라고 가정하면 끝 번호는 10이 아닌 8 이 되어야만 한다.

 

만약 끝 번호와 한 페이지당 출력되는 데이터 수의 곱이 전체 데이터 수보다 크면 끝 번호는 다시 total을 이용해서 다시 계산되어야 한다.

 

먼저 전체 데이터 수를 이용해서 진짜 끝 페이지가 몇 번까지 되는지 계산한다.

만약 진짜 끝 페이지가 구해놓은 끝 번호보다 작으면 끝 번호는 작은 값이 되어야만 한다.


이전 계산

this.prev = this.startPage > 1;

시작 번호가 1보다 큰 경우이면 존재


다음 계산

this.next = this.endPage < realEnd;

realEnd가 끝 번호보다 큰 경우이면 존재


게시판 컨트롤러 수정

 

src/main/java

com.spms.controller

BoardController.java

@GetMapping list()

...더보기
package com.spms.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.spms.domain.BoardVO;
import com.spms.domain.Criteria;
import com.spms.domain.PageDTO;
import com.spms.service.BoardService;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@Controller
@Log4j
@RequestMapping("/board/*")
@AllArgsConstructor
public class BoardController {

	private BoardService boardService;
	
	@GetMapping("/list")
	public void list(Criteria cri, Model model) {
		log.info("list" + cri);
		boardService.getList(cri).forEach(board -> log.info(board));
		model.addAttribute("list", boardService.getList(cri));
		model.addAttribute("pageMaker", new PageDTO(cri, 111));
	}
	
	@GetMapping("/register")
	public void register() {
		
	}
	
	@PostMapping("/register")
	public String register(BoardVO board, RedirectAttributes rttr) {

		log.info("register : " + board);
		boardService.register(board);
		rttr.addFlashAttribute("result", board.getBno());
		return "redirect:/board/list";
	}

	@GetMapping({"/get", "/modify"})
	public void get(@RequestParam("bno") Long bno, Model model) {
		log.info("/get or modify");
		model.addAttribute("board", boardService.get(bno));
	}

	@PostMapping("/modify")
	public String modify(BoardVO board, RedirectAttributes rttr) {
		log.info("modify:" + board);

		if (boardService.modify(board)) {
			rttr.addFlashAttribute("result", "success");
		}
		return "redirect:/board/list";
	}

	@PostMapping("/remove")
	public String remove(@RequestParam("bno") Long bno, RedirectAttributes rttr) {
		log.info("remove..." + bno);
		if (boardService.remove(bno)) {
			rttr.addFlashAttribute("result", "success");
		}
		return "redirect:/board/list";
	}

}

 

 


JSP에서의 페이지네이션 (페이지 번호 출력)

 

views/board

list.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="../includes/header.jsp"%>
<div class="row">
	<div class="col-lg-12">
		<h1 class="page-header">게시판 목록</h1>
	</div>
	<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
<div class="row">
	<div class="col-lg-12">
		<div class="panel panel-default">
			<div class="panel-heading">게시글 목록 페이지<button id='regBtn' type="button" class="btn btn-xs pull-right btn-primary">게시글 등록</button></div>
			<!-- /.panel-heading -->
			<div class="panel-body">
				<table class="table table-striped table-bordered table-hover">
					<tbody>
					<c:forEach items="${list }" var="board">
						<tr>
							<td>
								<font color="Gray">
									<c:out value="${board.bno}"/>&nbsp;/&nbsp;
									<c:out value="${board.writer}"/>&nbsp;/&nbsp;
									<fmt:formatDate value="${board.regdate}" pattern="yyyy-MM-dd"/>&nbsp;/&nbsp;
									<fmt:formatDate value="${board.updateDate}" pattern="yyyy-MM-dd"/>
								</font><br />
								<font size="3px">
									<a href="/board/get?bno=<c:out value="${board.bno}"/>"><c:out value="${board.title}"></c:out></a>
								</font>
							</td>
						</tr>
					</c:forEach>
					</tbody>
				</table>
				
				<!-- 페이지네이션 시작 -->
				<div class='pull-right'>
					<ul class="pagination">
						<!-- 이전 -->
						<c:if test="${pageMaker.prev}">
							<li class="paginate_button previous"><a
								href="${pageMaker.startPage -1}">Previous</a></li>
						</c:if>

						<!-- 현재 -->
						<c:forEach var="num" begin="${pageMaker.startPage}" end="${pageMaker.endPage}">
							<li class='paginate_button  ${pageMaker.cri.pageNum == num ? "active":""} '>
								<a href="${num}">${num}</a>
							</li>
						</c:forEach>
						
						<!-- 다음 -->
						<c:if test="${pageMaker.next}">
							<li class="paginate_button next"><a
								href="${pageMaker.endPage +1 }">Next</a></li>
						</c:if>
					</ul>
				</div>
				<!--  페이지네이션 끝 -->
				
			<!-- 이전 현재 다음 버튼의 a 태그동작을 막기위한 폼 태그 -->
			<form id='actionForm' action="/board/list" method='get'>
				<input type='hidden' name='pageNum' value='${pageMaker.cri.pageNum}'>
				<input type='hidden' name='amount' value='${pageMaker.cri.amount}'>
			</form>
			<!-- 폼 태그 종료 -->
			
			<!-- Modal  추가 -->
			<div class="modal fade" id="myModal" tabindex="-1" role="dialog"
				aria-labelledby="myModalLabel" aria-hidden="true">
				<div class="modal-dialog">
					<div class="modal-content">
						<div class="modal-header">
							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
							<h4 class="modal-title" id="myModalLabel">게시글 처리 결과</h4>
						</div>
						<div class="modal-body">처리가 완료되었습니다.</div>
						<div class="modal-footer">
							<button type="button" class="btn btn-default"
								data-dismiss="modal">Close</button>
							<button type="button" class="btn btn-primary">Save changes</button>
						</div>
					</div>
					<!-- /.modal-content -->
				</div>
				<!-- /.modal-dialog -->
				
				
			</div>
			<!-- /.panel-body -->
		</div>
		<!-- /.panel -->
	</div>
	<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
</div>
<!-- /#page-wrapper -->
<%@ include file="../includes/footer.jsp"%>

<script type="text/javascript">
	$().ready(function() {

						var result = '<c:out value="${result}"/>';

						checkModal(result);

						history.replaceState({}, null, null);

						function checkModal(result) {

							if (result === '' || history.state) {
								return;
							}

							if (parseInt(result) > 0) {
								$(".modal-body").html(
										"게시글 " + parseInt(result) + " 번이 등록되었습니다."
								);
							}

							$("#myModal").modal("show");
						}

						$("#regBtn").on("click", function() {

							self.location = "/board/register";

						});
						
						var actionForm = $("#actionForm");

						$(".paginate_button a").on("click", function(e) {
							e.preventDefault();
							console.log('click');
							actionForm.find("input[name='pageNum']").val($(this).attr("href"));
							actionForm.submit();
						});
					});
</script>

 

페이지네이션 코드를 작성

테이블 종료 태그 밑에 작성하고, 페이지네이션 구성은 부트스트랩 예제를 보면서 기본 페이지네이션 샘플 소스를 가지고 활용한다.

 

PageDTO를 사용할 수 있도록 모델에 담아서 화면에 전달한다.

PageDTO 를 구성하기 위해 전체 데이터 건수가 필요한데, 일단, 임의의 값으로 대충 111 건을 지정한다.

 

자바단에서 만들어놓은 모델 pageMaker 를 가지고 화면에 페이지 번호들을 출력한다.

 

a 태그의 직접 링크 처리 방식의 경우 검색 조건이 붙고 난 후의 처리가 복잡하게 되므로 자바스크립트를 통해 처리하는 방식을 이용한다.

 

페이지와 관련된 a 태그의 href 속성값으로 페이지 번호를 갖도록 수정한다.

 

액션 폼 폼태그를 추가해서 a 태그가 원래의 동작을 못하도록 자바스크립트를 처리하고, URL 의 이동을 처리하도록 변경한다. 

자바스크립트에서는 a 태그를 클릭해도 페이지 이동이 없도록 preventDefault() 처리로 막고, 폼 태그 내의 pageNum 값은 href 속성값으로 변경 후 마지막으로 submit() 한다.

 


페이지네이션이 적용된 게시판 목록 페이지 

 

 

반응형