| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 |
- javascript
- 자바
- ChatGPT
- codingtest
- 개발
- jsp
- 그리디알고리즘
- JQuery
- 챗지피티
- 하루코딩
- 코딩테스트
- HTTP상태
- 네트워크
- 탐욕알고리즘
- SQLD
- Spring
- 개발자
- 백준
- API
- SQL
- 정렬알고리즘
- java
- 파이썬
- HTTP
- 프로그래머스
- 알고리즘
- Python
- SQLP
- 알고리즘코딩테스트
- 서버
- Today
- Total
개발자's Life
Java SpringBoot 게시판 목록 페이징 처리하기 본문
게시판을 CRUD에 맞춰서 구현해보았습니다.
간단한 게시판 만드는 게시글은 아래를 참고해주세요~
2022.01.01 - [Java] - Java SpringBoot를 이용하여 게시판 목록 만들기
Java SpringBoot를 이용하여 게시판 목록 만들기
SpringStarerProject를 이용하였습니다 JUnit을 이용하여 dummy 데이터를 입력한 후 작업을 하였습니다. 2021.12.10 - [Java] - Java Junit 이용하여 DB에 DummyData 입력하기! Build Path -> Congifure Build Pat..
rowen.tistory.com
2022.01.02 - [Java] - JavaSpringBoot를 이용하여 작성, 수정, 삭제 페이지 만들기
JavaSpringBoot를 이용하여 작성, 수정, 삭제 페이지 만들기
목록 만드는 페이지부터 시작하였고 궁금하신분들은 아래를 참고해주세요! 2022.01.01 - [Java] - Java SpringBoot를 이용하여 게시판 목록 만들기 Congifure Build Pat.." data-og-host="rowen.tistory.com" data..
rowen.tistory.com
페이징처리를 해보자!
우선 저는 Parameter 값으로 이용자가 보고있는 게시판 페이지 번호와 한 페이지에 보여줄 게시글 갯수
이 두개를 parameter 값으로 받았습니다.
Mapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lec.spring.domain.BoardDAO">
<insert id="insertBoard" flushCache="true"
parameterType="com.lec.spring.domain.BoardDTO"
keyProperty="uid" useGeneratedKeys="true" keyColumn="uid">
INSERT INTO board (userId, title, summary)
VALUES (#{userId}, #{title}, #{summary})
</insert>
<select id="selectAll" resultType="com.lec.spring.domain.BoardDTO">
SELECT * FROM board ORDER BY uid DESC
</select>
<select id="selectAllCnt" resultType="int">
SELECT count(*) FROM board
</select>
<select id="selectOne" resultType="com.lec.spring.domain.BoardDTO">
SELECT * FROM board WHERE uid = #{param}
</select>
<update id="updateBoard" flushCache="true">
UPDATE
board
SET
summary = #{summary}
WHERE
uid = #{uid}
</update>
<delete id="deleteBoard" flushCache="true">
DELETE FROM board WHERE uid = #{param}
</delete>
<select id="selectPageList" resultType="com.lec.spring.domain.BoardDTO">
SELECT A.* FROM (SELECT * FROM board ORDER BY uid DESC) A LIMIT #{pageCnt} OFFSET #{lastPage};
</select>
</mapper>
페이징 처리를 위해 쿼리문을 작성한 부분이 selectAllCnt, selectPageList 입니다.
- selectAllCnt는 전체 게시글을 카운트 해준 부분이구요
- selectPageList는 페이징 처리를 위한 부분입니다.
SELECT A.* FROM (SELECT * FROM board ORDER BY uid DESC) A LIMIT #{pageCnt} OFFSET #{lastPage};
- 서브쿼리를 이용하여 게시글 전체를 내림차순 정렬을 해주고 LIMIT과 OFFSET을 이용하여 출력했습니다.
- lastPage는 게시글의 인덱스 값을 매개변수로 받으며 해당하는 인덱스 번호부터 출력을 해줍니다.
- pageCnt는 한 페이지에 나타낼 게시글 수의 parameter 값입니다
예를들어
lastPage의 값이
0 이면 테이블의 출력된 값에서 1번째 정보부터 끝까지 출력이 됩니다.
10이면 테이블의 출력된 값에서 11번째 정보부터 끝까지 출력이 됩니다.
20이면 테이블의 출력된 값에서 21번째 정보부터 끝까지 출력이 됩니다.
예시를 보시면 감이 오실지는 모르겠지만 한 페이지에 나타낼 게시글 수의 코드를 작업하기 위한 첫 단계입니다.
pageCnt 값이 10이고 lastPage 값이 0이면
1번째(인덱스번호는 0)부터 끝까지가 아니라 10번째까지의 정보가 출력됩니다. 1페이지에 10개의 게시글을 출력하는것과 같은거죠
pageCnt 값이 10이고 lastPage 값이 10이면
11번째부터 20번째까지의 정보가 출력이 됩니다. 2페이지에 10개의 게시글을 출력하는것과 같은거구요
이런 작업 설명을 위해 DAO와 Service 부분을 빠르게 코드를 훑어보고 Controller와 JSP로 넘어가겠습니다.
DAO
package com.lec.spring.domain;
import java.util.List;
public interface BoardDAO {
// 전체 게시글 선택
public abstract List<BoardDTO> selectAll();
// 게시글 한개 선택
public abstract List<BoardDTO> selectOne(int uid);
// 게시글 작성
public abstract int insertBoard(BoardDTO dto);
// 게시글 수정
public abstract int updateBoard(BoardDTO dto);
// 게시글 삭제
public abstract int deleteBoard(int uid);
// 게시글 페이징 처리
public abstract List<BoardDTO> selectPageList(int pageCnt, int lastPage);
// 전체 게시글 수
public abstract int selectAllCnt();
}
package com.lec.spring.domain;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class BoardDAOImpl implements BoardDAO {
private BoardDAO mapper;
@Autowired
public BoardDAOImpl(SqlSession sqlSession) {
// System.out.println("BoardDAOImpl() 생성");
mapper = sqlSession.getMapper(BoardDAO.class);
}
@Override
public List<BoardDTO> selectAll() {
return mapper.selectAll();
}
@Override
public List<BoardDTO> selectOne(int uid) {
return mapper.selectOne(uid);
}
@Override
public int insertBoard(BoardDTO dto) {
return mapper.insertBoard(dto);
}
@Override
public int updateBoard(BoardDTO dto) {
return mapper.updateBoard(dto);
}
@Override
public int deleteBoard(int uid) {
return mapper.deleteBoard(uid);
}
@Override
public List<BoardDTO> selectPageList(int pageCnt, int lastPage) {
return mapper.selectPageList(pageCnt, lastPage);
}
@Override
public int selectAllCnt() {
return mapper.selectAllCnt();
}
}
게시글 전체 갯수를 카운트 해주는 selectAllCnt sql 호출과
페이징처리를 해주는 selectPageList sql을 호출했습니다.
Service
package com.lec.spring.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.lec.spring.domain.BoardDAO;
import com.lec.spring.domain.BoardDTO;
@Service
public class BoardService {
BoardDAO dao; // dao 호출
@Autowired
public void setDao(BoardDAO dao) {
this.dao = dao;
}
// 전체선택
public List<BoardDTO> selectAll(){
return dao.selectAll();
}
// 게시글 하나 선택
public List<BoardDTO> selectOne(int uid){
return dao.selectOne(uid);
}
// 게시글 수정
public int updateBoard(BoardDTO dto) {
return dao.updateBoard(dto);
}
// 게시글 삭제
public int deleteBoard(int uid) {
return dao.deleteBoard(uid);
}
// 게시글 작성
public int insertBoard(BoardDTO dto){
return dao.insertBoard(dto);
}
// 게시글 페이징 처리
public List<BoardDTO> selectPageList(int pageCnt, int lastPage){
return dao.selectPageList(pageCnt, lastPage);
}
public int selectAllCnt() {
return dao.selectAllCnt();
}
}
페이징 처리부분 selectPageList 메소드에
앞에 설명했던 값을 매개변수로 받아줍니다.
설명부분
lastPage는 게시글의 인덱스 값을 매개변수로 받으며 해당하는 인덱스 번호부터 출력을 해줍니다.
pageCnt는 한 페이지에 나타낼 게시글 수의 parameter 값입니다
Controller
package com.lec.spring.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.lec.spring.domain.BoardDTO;
import com.lec.spring.service.BoardService;
@Controller
@RequestMapping("/board")
public class BoardController {
BoardService boardService;
@Autowired
public void setBoardService(BoardService boardService) {
this.boardService = boardService;
}
// 전체 게시글 선택
@RequestMapping("/list") // 게시글 목록 보기 맵핑
public String list(@RequestParam(value="page", defaultValue="1") int page,
@RequestParam(value="pageCnt", defaultValue="10") int pageCnt, Model model) {
int lastPage = (page - 1) * pageCnt; // 페이지에 나타낼 게시글 수에 따라 곱하는 수가 바뀜
model.addAttribute("allCnt", boardService.selectAllCnt()); // 게시글 전체 갯수
model.addAttribute("pageCnt", pageCnt); // 한 페이지에 나타내는 게시글 수
model.addAttribute("list", boardService.selectPageList(pageCnt, lastPage)); // DB에 담겨있는 게시글 정보 전체 불러오기
model.addAttribute("page", page);
return "list";
}
// 게시글 작성
@RequestMapping("/write")
public String write() {
return "write"; // 게시글 작성 페이지 맵핑
}
// 게시글 작성 OK
@PostMapping("/writeOk")
public String writeOk(Model model, BoardDTO dto) {
model.addAttribute("result", boardService.insertBoard(dto)); // 게시글 작성 완료
return "writeOk";
}
// 게시글 하나 선택
@RequestMapping("/info")
public String info(int uid,Model model, BoardDTO dto) {
model.addAttribute("dto", boardService.selectOne(uid));
return "info";
}
@RequestMapping("/deleteOk")
public String delete(int uid, Model model) {
model.addAttribute("result", boardService.deleteBoard(uid));
return "deleteOk";
}
@RequestMapping("/update")
public String update(int uid, Model model) {
model.addAttribute("dto", boardService.selectOne(uid));
return "update";
}
@RequestMapping("/updateOk")
public String updateOk(Model model, BoardDTO dto) {
model.addAttribute("result", boardService.updateBoard(dto));
model.addAttribute("uid", dto.getUid());
return "updateOk";
}
}
컨트롤러 전체 코드 중 페이징 처리에 필요한 부분만 자세히 보겠습니다.
// 전체 게시글 선택
@RequestMapping("/list") // 게시글 목록 보기 맵핑
public String list(@RequestParam(value="page", defaultValue="1") int page,
@RequestParam(value="pageCnt", defaultValue="10") int pageCnt, Model model) {
int lastPage = (page - 1) * pageCnt; // 페이지에 나타낼 게시글 수에 따라 곱하는 수가 바뀜
model.addAttribute("allCnt", boardService.selectAllCnt()); // 게시글 전체 갯수
model.addAttribute("pageCnt", pageCnt); // 한 페이지에 나타내는 게시글 수
model.addAttribute("list", boardService.selectPageList(pageCnt, lastPage)); // DB에 담겨있는 게시글 정보 전체 불러오기
model.addAttribute("page", page);
return "list";
}
@RequestParam으로 Value 값을 page, pageCnt로 주어 JSP에서 맵핑될때 해당하는 변수명의 값을 가지고 오게 됩니다.
예를들어 선택된 페이지가 3이고 한 페이지에 나타낼 게시글 수가 15이면 page == 3 , pageCnt == 15가 됩니다.
최초로 mapping이 될 경우에는 parameter 값이 없는 상태로 mapping이 되기에 defalut 값을 지정해주었습니다.
parameter 값을 가공을 좀 해주어 매개변수로 넘겨주게 되는데 lastPage 변수는 (page - 1) * pageCnt 의 값을 담아줍니다. 이와같이 해주는 이유는 앞에서 설명드렸듯이 lastPage는 게시글 정보를 몇번째부터 끝까지 정보를 출력해주는 부분입니다.
예를들어 설명드리겠습니다.
선택한 페이지가 2이고 한 페이지에 나타낼 게시글의 수가 15이면
page == 2
pageCnt == 15
lastPage = (2 - 1) * 15 => 값은 15가 됩니다.
앞서 말했듯이 15는 인덱스번호가 되고 실제로 게시글은 16번째 게시글부터 출력하게 됩니다.
1페이지에서 15개의 글을 확인가능하기 때문에 2페이지에서는 16번째 게시글부터 15개의 게시글을 확인이 가능하게 구현해주면 됩니다.
이와같은 이유로 위와같은 식으로 가공을 해주었습니다.
매개변수를 pageCnt(한 페이지에 나타낼 게시글 수)와 lastPage(해당하는 순서부터 출력할 인덱스)로 주어 결과를 출력할 수 있습니다.
이렇게 해당하는 페이지와 페이지에 나타낼 갯수만 parameter 값으로 주면
해당하는 페이지에 나타낼 게시글 정보만 출력이 가능합니다.
JSP
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<%@ page import="java.text.SimpleDateFormat"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://sargue.net/jsptags/time" prefix="javatime" %> <!-- LocalDateTime의 값을 포멧해주기 위해 pom.xml에 의존성 주입 -->
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>LIST</title>
</head>
<style>
body{width:80%; margin:auto;}
.table{
width:80%;
margin:auto;
}
</style>
<style>
#test {
float: right;
position: relative;
left: -50%;
}
#test li {
float: left;
position: relative;
left: 50%;
}
</style>
<body>
<div style="text-align:center;padding:50px 0px;"><h1><b>글 목록</b></h1>
<form action="list" method="POST">
<input type="hidden" name="page" value="${page }">
<select name="pageCnt" id="pageCnt" style="float:right; margin-right:10%; border:1px solid black">
<c:choose>
<c:when test="${pageCnt == 10}">
<option value="10" selected>10개씩보기</option>
</c:when>
<c:otherwise>
<option value="10">10개씩보기</option>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${pageCnt == 15}">
<option value="15" selected>15개씩보기</option>
</c:when>
<c:otherwise>
<option value="15">15개씩보기</option>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${pageCnt == 20}">
<option value="20" selected>20개씩보기</option>
</c:when>
<c:otherwise>
<option value="20">20개씩보기</option>
</c:otherwise>
</c:choose>
</select>
<br>
<br>
<button type="submit" style="float:right; margin-right:10%;">적용하기</button>
</form>
</div>
<b style="float:right; margin-right:10%;">전체 게시글 수 : ${allCnt }</b>
<br>
<br>
<table class="table">
<thead>
<tr>
<th scope="col" class="col-md-2">uid</th>
<th scope="col" class="col-md-3">ID</th>
<th scope="col" class="col-md-6">제목</th>
<th scope="col" class="col-md-3">날짜</th>
</tr>
</thead>
<tbody>
<c:forEach var="dto" items="${list }">
<tr>
<th scope="row">${dto.uid }</th>
<td>${dto.userId }</td>
<td><a href="/board/info?uid=${dto.uid }">${dto.title }</a></td>
<td>
<javatime:format value="${dto.b_date}" pattern="yyyy-MM-dd HH:mm:ss" />
</td>
</tr>
</c:forEach>
</tbody>
</table>
<c:set var="doneLoop" value="false"/> <%-- forEach문 break와 같은 기능을 하기위한 변수선언 --%>
<%--선택한 페이지가 0번대일경우 10번대일경우 20번대일경우 나눠주기.
경우에 따라 1~10, 11~20, 21~30. . .
ex) 1~10일 경우 -1을 해주고 10을 나눠줄 경우 몫이 0으로 나오게 해주는데 실수로 나와서 Int형 타입으로 변환 후 변수선언 --%>
<fmt:parseNumber var= "pages" integerOnly= "true" value= "${(page - 1) / 10 }" />
<%-- 마지막 페이지의 갯수가 게시글 나타내는 수를 못 채울 경우 소수점으로 나오기 때문에 소수점을 날리고 Int형으로 변수 선언 --%>
<fmt:parseNumber var= "pageCntTest" integerOnly= "true" value= "${allCnt / pageCnt}" />
<%-- 페이징 마지막 페이지 나타낼 게시글 수에 미치지 못할 경우 처리. --%>
<c:choose>
<c:when test="${allCnt / pageCnt > pageCntTest }"> <%-- allCnt / pageCnt 전체게시글 나누기 한 페이지에 나타낼 게시글 수(소수점포함)
pageCntTest 전체게시글 나누기 한 페이지에 나타낼 게시글 수(소수점날린 변수) 두 수를 비교해서 소수점이 있는경우 마지막페이지 +1 추가--%>
<fmt:parseNumber var= "paging" integerOnly= "true" value= "${pageCntTest + 1}" />
</c:when>
<c:otherwise>
<fmt:parseNumber var= "paging" integerOnly= "true" value= "${pageCntTest }" /> <%-- 그렇지 않은 경우는 한 페이지에 나타나는 게시글 수에 맞게 페이징 처리가 되기 때문에 마지막페이지 +1 해주지 않아도 됩니다 --%>
</c:otherwise>
</c:choose>
<nav aria-label="Page navigation example" style="padding-bottom:50px; padding-top:20px;">
<ul class="pagination" id="test">
<c:if test="${pages > 0 }"> <%--선택한 페이지가 10을 초과하게 되면 if 조건문이 충족되어 if문이 실행 --%>
<li class="page-item"><a class="page-link" href="list?page=1&pageCnt=${pageCnt }">맨앞으로</a></li>
</c:if>
<li class="page-item"><a class="page-link" href="list?page=${page - 1 }&pageCnt=${pageCnt }">Previous</a></li>
<c:forEach var="i" begin="${pages * 10 + 1}" end="${paging }"> <%--선택한 페이지에 따라 출력가능한 페이지까지 반복문 실행--%>
<c:if test="${doneLoop ne true }"> <%--doneLoop가 true가 아닐 경우에 if문 실행 --%>
<c:choose>
<c:when test="${i % 10 != 0 }"> <%-- 1 ~ 10일 경우 반복문이 10번이 되기 전 까지 실행--%>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li> <%--위 <c:when> 조건문을 충족하지 못한 10을 출력해주기 --%>
<c:set var="doneLoop" value="true"/> <%-- 10번 반복문이 실행이 될 경우 doneLoop true로 바꿔주기 --%>
</c:otherwise>
</c:choose>
</c:if>
</c:forEach>
<li class="page-item"><a class="page-link" href="list?page=${page + 1 }&pageCnt=${pageCnt }">Next</a></li>
</ul>
</nav>
<button onClick="location.href='/board/write'" type="button" class="btn btn-secondary btn-lg btn-block" style="width:10%; margin:auto; margin-top:30px; margin-bottom:200px;">글 작성</button>
</body>
</html>
Controller 부분과 같이 JSP 부분도 생각을 아주 많이 했습니다.
이유는 페이지가 1~10개를 보여주고 11페이지로 넘어가면 11~20페이지를 보여주고 싶었기 때문입니다.
그래서 위와 같은 코드가 나왔고 코드를 설명해드리겠습니다.
<tbody>
<c:forEach var="dto" items="${list }">
<tr>
<th scope="row">${dto.uid }</th>
<td>${dto.userId }</td>
<td><a href="/board/info?uid=${dto.uid }">${dto.title }</a></td>
<td>
<javatime:format value="${dto.b_date}" pattern="yyyy-MM-dd HH:mm:ss" />
</td>
</tr>
</c:forEach>
</tbody>
</table>
우선 해당하는 페이지의 정보는 Controller에서 Model을 통해 받은 list 변수를 이용하여 정보를 출력하였습니다.
<c:set var="doneLoop" value="false"/> <%-- forEach문 break와 같은 기능을 하기위한 변수선언 --%>
<%--선택한 페이지가 0번대일경우 10번대일경우 20번대일경우 나눠주기.
경우에 따라 1~10, 11~20, 21~30. . .
ex) 1~10일 경우 -1을 해주고 10을 나눠줄 경우 몫이 0으로 나오게 해주는데 실수로 나와서 Int형 타입으로 변환 후 변수선언 --%>
<fmt:parseNumber var= "pages" integerOnly= "true" value= "${(page - 1) / 10 }" />
<%-- 마지막 페이지의 갯수가 게시글 나타내는 수를 못 채울 경우 소수점으로 나오기 때문에 소수점을 날리고 Int형으로 변수 선언 --%>
<fmt:parseNumber var= "pageCntTest" integerOnly= "true" value= "${allCnt / pageCnt}" />
<%-- 페이징 마지막 페이지 나타낼 게시글 수에 미치지 못할 경우 처리. --%>
<c:choose>
<c:when test="${allCnt / pageCnt > pageCntTest }"> <%-- allCnt / pageCnt 전체게시글 나누기 한 페이지에 나타낼 게시글 수(소수점포함)
pageCntTest 전체게시글 나누기 한 페이지에 나타낼 게시글 수(소수점날린 변수) 두 수를 비교해서 소수점이 있는경우 마지막페이지 +1 추가--%>
<fmt:parseNumber var= "paging" integerOnly= "true" value= "${pageCntTest + 1}" />
</c:when>
<c:otherwise>
<fmt:parseNumber var= "paging" integerOnly= "true" value= "${pageCntTest }" /> <%-- 그렇지 않은 경우는 한 페이지에 나타나는 게시글 수에 맞게 페이징 처리가 되기 때문에 마지막페이지 +1 해주지 않아도 됩니다 --%>
</c:otherwise>
</c:choose>
<nav aria-label="Page navigation example" style="padding-bottom:50px; padding-top:20px;">
<ul class="pagination" id="test">
<c:if test="${pages > 0 }"> <%--선택한 페이지가 10을 초과하게 되면 if 조건문이 충족되어 if문이 실행 --%>
<li class="page-item"><a class="page-link" href="list?page=1&pageCnt=${pageCnt }">맨앞으로</a></li>
</c:if>
<li class="page-item"><a class="page-link" href="list?page=${page - 1 }&pageCnt=${pageCnt }">Previous</a></li>
<c:forEach var="i" begin="${pages * 10 + 1}" end="${paging }"> <%--선택한 페이지에 따라 출력가능한 페이지까지 반복문 실행--%>
<c:if test="${doneLoop ne true }"> <%--doneLoop가 true가 아닐 경우에 if문 실행 --%>
<c:choose>
<c:when test="${i % 10 != 0 }"> <%-- 1 ~ 10일 경우 반복문이 10번이 되기 전 까지 실행--%>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li> <%--위 <c:when> 조건문을 충족하지 못한 10을 출력해주기 --%>
<c:set var="doneLoop" value="true"/> <%-- 10번 반복문이 실행이 될 경우 doneLoop true로 바꿔주기 --%>
</c:otherwise>
</c:choose>
</c:if>
</c:forEach>
<li class="page-item"><a class="page-link" href="list?page=${page + 1 }&pageCnt=${pageCnt }">Next</a></li>
</ul>
</nav>
<button onClick="location.href='/board/write'" type="button" class="btn btn-secondary btn-lg btn-block" style="width:10%; margin:auto; margin-top:30px; margin-bottom:200px;">글 작성</button>
주석에 설명을 참고하시면 되지만 한번 더 설명 해드리겠습니다.
페이지 버튼 처리 목표
forEach을 이용하여 페이징 버튼을 구현하였고 11페이지가 넘어갈 경우 11~20, 21페이지가 넘어갈 경우 21~30으로 작동이 됩니다.
1. JSTL forEach 구문에는 break 기능이 없습니다. 그래서 break 기능을 하기 위한 변수와 1번대 10번대 20번대의 기준이 되는 변수를 선언해줍니다.
<c:set var="doneLoop" value="false"/> <%-- forEach문 break와 같은 기능을 하기위한 변수선언 --%>
<%--선택한 페이지가 0번대일경우 10번대일경우 20번대일경우 나눠주기.
경우에 따라 1~10, 11~20, 21~30. . .
ex) 1~10일 경우 -1을 해주고 10을 나눠줄 경우 몫이 0으로 나오게 해주는데 실수로 나와서 Int형 타입으로 변환 후 변수선언 --%>
<fmt:parseNumber var= "pages" integerOnly= "true" value= "${(page - 1) / 10 }" />
예를들어
클릭한 페이지가 10일경우
(10 - 1) / 10은 0.9가 나오는데 이를 int 타입으로 변환 시키면 0의 값이 됩니다. ( 1 ~ 10 은 0번으로 통일)
클릭한 페이지가 19일경우
(19 - 1) / 10은 1.8이 나오는데 이를 int 타입으로 변화 시키면 1의 값이 됩니다 ( 11 ~ 20 은 1번으로 통일)
1번대는 0
10번대는 1
이런식으로 관리하기 쉽게 해당하는 페이지에 따라 달라지는 pages 라는 변수를 선언해주었습니다.
2. 게시글 수와 한 페이지에 나타낼 게시글 수에 따라 페이지의 갯수가 정해지기 때문에 페이지 갯수를 구하고 그 값을 변수로 선언해줍니다.
<%-- 마지막 페이지의 갯수가 게시글 나타내는 수를 못 채울 경우 소수점으로 나오기 때문에 소수점을 날리고 Int형으로 변수 선언 --%>
<fmt:parseNumber var= "pageCntTest" integerOnly= "true" value= "${allCnt / pageCnt}" />
allCnt(게시글 수) / pageCnt(한 페이지당 나타낼 게시글 수) 를 하고 int 타입으로 변환해주었습니다.
allCnt == 58
pageCnt == 10
일 경우
5.8의 값이 되는데 소수점을 날리고 pageCntTest의 값은 5가 됩니다
그렇게 되면 페이지가 5페이지까지만 출력이 되니 아래와 같은 작업을 해주었습니다.
<%-- 마지막 페이지에 게시글 수가 한 페이지에 나타낼 게시글 수에 미치지 못할 경우 처리. --%>
<c:choose>
<c:when test="${allCnt / pageCnt > pageCntTest }"> <%-- allCnt / pageCnt 전체게시글 나누기 한 페이지에 나타낼 게시글 수(소수점포함)
pageCntTest 전체게시글 나누기 한 페이지에 나타낼 게시글 수(소수점날린 변수) 두 수를 비교해서 소수점이 있는경우 마지막페이지 +1 추가--%>
<fmt:parseNumber var= "paging" integerOnly= "true" value= "${pageCntTest + 1}" />
</c:when>
<c:otherwise>
<fmt:parseNumber var= "paging" integerOnly= "true" value= "${pageCntTest }" /> <%-- 그렇지 않은 경우는 한 페이지에 나타나는 게시글 수에 맞게 페이징 처리가 되기 때문에 마지막페이지 +1 해주지 않아도 됩니다 --%>
</c:otherwise>
</c:choose>
소수점을 날리기 전의 allCnt/pageCnt 와 소수점을 날린 후 pageCntTest를 비교를 해주어 소수점을 날리기 전의 값이 더 크면 pageCntTest에 +1 을 해주었습니다.
allCnt == 58
pageCnt == 10
일 경우 위와 같은 작업이 컴파일 되고 pageCntTest는 6이 됩니다.
3. 구한 페이지 갯수에 따라서 1~10, 11~20, 21~30 ..... 으로 코드 구현
<nav aria-label="Page navigation example" style="padding-bottom:50px; padding-top:20px;">
<ul class="pagination" id="test">
<c:if test="${pages > 0 }"> <%--선택한 페이지가 10을 초과하게 되면 if 조건문이 충족되어 if문이 실행 --%>
<li class="page-item"><a class="page-link" href="list?page=1&pageCnt=${pageCnt }">맨앞으로</a></li>
</c:if>
<li class="page-item"><a class="page-link" href="list?page=${page - 1 }&pageCnt=${pageCnt }">Previous</a></li>
<c:forEach var="i" begin="${pages * 10 + 1}" end="${paging }"> <%--선택한 페이지에 따라 출력가능한 페이지까지 반복문 실행--%>
<c:if test="${doneLoop ne true }"> <%--doneLoop가 true가 아닐 경우에 if문 실행 --%>
<c:choose>
<c:when test="${i % 10 != 0 }"> <%-- 1 ~ 10일 경우 반복문이 10번이 되기 전 까지 실행--%>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li> <%--위 <c:when> 조건문을 충족하지 못한 10을 출력해주기 --%>
<c:set var="doneLoop" value="true"/> <%-- 10번 반복문이 실행이 될 경우 doneLoop true로 바꿔주기 --%>
</c:otherwise>
</c:choose>
</c:if>
</c:forEach>
<li class="page-item"><a class="page-link" href="list?page=${page + 1 }&pageCnt=${pageCnt }">Next</a></li>
</ul>
</nav>
앞서 설명했던 pages가 10번대로 넘어갈 경우 맨앞으로 갈 수 있는 버튼을 활성화 시켜 주었습니다. ( 0을 초과하는 경우) Previous, next 버튼은 조건 없이 무조건 출력되게 구현하였고
반복문의 시작은 앞에서 구했던 0번대 10번대 20번대에 따라 달라지는 변수 pages에 10을 곱해주고 1을 더해서 작성했습니다. (ex. 반복문의 시작은 1 or 11 or 21 or 31 . . . .)
예를들면 0번대일 경우 10을 곱하면 0이고 1을 더하면 1이 되어 1부터 시작할 수 있게 됩니다.
10번대일 경우는 pages는 1이 되고 거기에 10을 곱하면 10이고 1을 더하면 11이 되어 11부터 시작할 수 있게 됩니다.
반복문의 끝은 앞에서 구했던 페이지 개수를 담고 있는 변수 paging 만큼 반복해주었습니다.
여기서 break를 해주지 않으면 시작한부분부터 paging까지 다 출력해주니 10번대 20번대에 따른 조건문을 걸어 break를 해주겠습니다.
break 기능을 위해 조건문을 아래의 코드와 같이 주었습니다.
<c:if test="${doneLoop ne true }"> <%--doneLoop가 true가 아닐 경우에 if문 실행 --%>
doneLoop가 true가 되면 실행을 멈춥니다.
아래 코드는 doneLoop가 true가 아닐 경우 실행되는 if문 내부 코드입니다.
<c:choose>
<c:when test="${i % 10 != 0 }"> <%-- 1 ~ 10일 경우 반복문이 10번이 되기 전 까지 실행--%>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="list?page=${i }&pageCnt=${pageCnt }">${i }</a></li> <%--위 <c:when> 조건문을 충족하지 못한 10을 출력해주기 --%>
<c:set var="doneLoop" value="true"/> <%-- 10번 반복문이 실행이 될 경우 doneLoop true로 바꿔주기 --%>
</c:otherwise>
</c:choose>
페이지의 값이 10의 배수가 아닐 경우에 반복문이 계속 진행되고 10의 배수가 됬을때
해당하는 페이지를 출력해주고 doneLoop를 true로 바꾸어 break 기능과 동일하게 구현했습니다.
예를들어 총 페이지 갯수는 35개이고 클릭한 페이지는 22페이지일 경우
21~30 페이지만 출력을 해주어야합니다.
반복문의 시작은 21에서 시작할거고 35까지 가다가 30이 되면
30을 출력하고 doneLoop를 true로 바꾸어주어 반복문이 멈추게 됩니다.
그러면 화면에는 21 ~ 30 페이지만 출력이 되게 됩니다.

위와 같이 작동이 잘 되는 것을 알 수 있습니다.
이상으로 제 생각대로 작성한 코드이고 혹시나 궁금한 점 댓글 부탁드릴게요~
수정할 부분이 있는 부분도 댓글 부탁드리겠습니다.
더 완벽한 개발자가 되어 좋은 정보 공유하겠습니다.
해당하는 게시판 만드는 깃허브 주소
https://github.com/RowenKim/makeBoard
GitHub - RowenKim/makeBoard: JavaSpringBoot, MyBatis, MySQL, BootStrap, HTML/CSS, JavaScript, SpringValitdation,
JavaSpringBoot, MyBatis, MySQL, BootStrap, HTML/CSS, JavaScript, SpringValitdation, - GitHub - RowenKim/makeBoard: JavaSpringBoot, MyBatis, MySQL, BootStrap, HTML/CSS, JavaScript, SpringValitdation,
github.com
'Back-end > Java' 카테고리의 다른 글
| [JAVA] ChatGPT API 사용하여 간단한 채팅 시스템 만들기 (0) | 2023.03.11 |
|---|---|
| [JAVA] 기존 경로에 담겨있는 파일들 Zip 파일 만들기 (0) | 2023.02.20 |
| JavaSpringBoot를 이용하여 작성, 수정, 삭제 페이지 만들기 (0) | 2022.01.02 |
| Java SpringBoot를 이용하여 게시판 목록 만들기 (0) | 2022.01.01 |
| Java Junit 이용하여 DB에 DummyData 입력하기! (0) | 2021.12.10 |
