BACKEND/스프링 Spring

spring 게시판 인텔리제이로 페이징 처리

꾸준히개발하자 2024. 1. 4. 13:44

페이징 처리를 정리해본다

게시판 만드는데에서 난이도가 있는 기능이라고 생각한다.

페이징 처리를 어떤원리로 처리하고 데이터를 어떻게 화면에 보여주는지 중요하다고 생각한다 

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index</title>
</head>
<body>
    <h2>hello spring framework</h2>
    <a href="/board/save">글작성</a>
    <a href="/board/">글목록</a>
    <a href="/board/paging">페이징 목록</a>
</body>
</html>

 

페이징 처리를 위한 요청 주소를 추가 한다.

// /board/paging?page=2
    // 처음 페이지 요청은 1페이지를 보여줌
    @GetMapping("/paging")
    public String paging(Model model,
                         @RequestParam(value = "page", required = false, defaultValue = "1") int page) {
        System.out.println("page = " + page);
        // 해당 페이지에서 보여줄 글 목록
        List<BoardDTO> pagingList = boardService.pagingList(page);
        System.out.println("pagingList = " + pagingList);
        PageDTO pageDTO = boardService.pagingParam(page);
        model.addAttribute("boardList", pagingList);
        model.addAttribute("paging", pageDTO);
        return "paging";
    }

 

페이징 처리는 데이터를 가지고 화면에 출력하는 내용이니까 Model 객체가 필요하다

사용자가 어떤 페이지를 클릭하냐에 따라 화면에 보여줄 데이터는 바뀌게 된다.

 

/board/paging?page=2  페이지 값을 보내도록 할것이다. 페이징 처리를 해서 어떤 페이지를 보여줄것인지 

 

@RequestParam(value = "page", required = false, defaultValue = "1") int page)

value 파라미터 이름 표시

required 는 page 파라미터가 없어도 필수가 아니기 때문에 에러가 발생하지 않는다

defaultValue  는 없으면 기본값으로 1 설정 

 

목록이 필요하므로 DTO 가 담긴 List 필요 . 

 

 

페이징 처리를 하기 위해 게시글들을 추가해 준다 

 

 

mySQL은 간단한 쿼리를 사용하는데 

 

 

 

1page : 12 , 11 , 10 

2page : 9 , 8 , 7

3page : 6 , 5 , 4

사용자가 요청하는 페이지에 따라 앞자리 숫자가 바뀌게 된다. 

 

오라클에서는 서브쿼리를 사용해서 구현하고 , mySQL 은 리밋을 사용했다 

 

BoardService

public List<BoardDTO> pagingList(int page) {
        /*
        1페이지당 보여지는 글 갯수 3
            1page => 0
            2page => 3
            3page => 6
         */
        int pagingStart = (page - 1) * pageLimit;
        Map<String, Integer> pagingParams = new HashMap<>();
        pagingParams.put("start", pagingStart);
        pagingParams.put("limit", pageLimit);
        List<BoardDTO> pagingList = boardRepository.pagingList(pagingParams);

        return pagingList;
    }

 

 

BoardService

int pageLimit = 3; // 한 페이지당 보여줄 글 갯수
    int blockLimit = 3; // 하단에 보여줄 페이지 번호 갯수
    public List<BoardDTO> pagingList(int page) {
        /*
        1페이지당 보여지는 글 갯수 3
            1page => 0
            2page => 3
            3page => 6
         */
        int pagingStart = (page - 1) * pageLimit;
        Map<String, Integer> pagingParams = new HashMap<>();
        pagingParams.put("start", pagingStart);
        pagingParams.put("limit", pageLimit);
        List<BoardDTO> pagingList = boardRepository.pagingList(pagingParams);

        return pagingList;
    }

page - 몇페이지가 요청했냐에 따라 간단하게 식을 사용

limit - 한 페이지당 몇개씩 보여줄것인지 변수

 

숫자 2개를 전달하기 위해 Map를 선언해서 pagingStart 와 limit 를 전달 한다

 

   

BoardRepository

public List<BoardDTO> pagingList(Map<String, Integer> pagingParams) {
        return sql.selectList("Board.pagingList", pagingParams);
    }

 

 

BoardMapper

<select id="pagingList" parameterType="java.util.HashMap" resultType="board">
    select * from board_table order by id desc limit #{start}, #{limit}
</select>

Map으로 넘어온 키값 start 와 limit 매칭 시켜 준다

 

 

 

PageDTO

 

package com.codingrecipe.board.dto;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class PageDTO {
    private int page; // 현재 페이지
    private int maxPage; // 전체 필요한 페이지 갯수
    private int startPage; // 현재 페이지 기준 시작 페이지 값
    private int endPage; // 현재 페이지 기준 마지막 페이지 값

}

 

 

BoardController

// /board/paging?page=2
    // 처음 페이지 요청은 1페이지를 보여줌
    @GetMapping("/paging")
    public String paging(Model model,
                         @RequestParam(value = "page", required = false, defaultValue = "1") int page) {
        System.out.println("page = " + page);
        // 해당 페이지에서 보여줄 글 목록
        List<BoardDTO> pagingList = boardService.pagingList(page);
        System.out.println("pagingList = " + pagingList);
        PageDTO pageDTO = boardService.pagingParam(page);
        model.addAttribute("boardList", pagingList);
        model.addAttribute("paging", pageDTO);
        return "paging";
    }

 

목록과 계산된값을 모델에 담아서 paging 으로 리턴 

 

BoardService

int pageLimit = 3; // 한 페이지당 보여줄 글 갯수
int blockLimit = 3; // 하단에 보여줄 페이지 번호 갯수

public PageDTO pagingParam(int page) {
        // 전체 글 갯수 조회
        int boardCount = boardRepository.boardCount();
        // 전체 페이지 갯수 계산(10/3=3.33333 => 4)
        int maxPage = (int) (Math.ceil((double) boardCount / pageLimit));
        // 시작 페이지 값 계산(1, 4, 7, 10, ~~~~)
        int startPage = (((int)(Math.ceil((double) page / blockLimit))) - 1) * blockLimit + 1;
        // 끝 페이지 값 계산(3, 6, 9, 12, ~~~~)
        int endPage = startPage + blockLimit - 1;
        if (endPage > maxPage) {
            endPage = maxPage;
        }
        PageDTO pageDTO = new PageDTO();
        pageDTO.setPage(page);
        pageDTO.setMaxPage(maxPage);
        pageDTO.setStartPage(startPage);
        pageDTO.setEndPage(endPage);
        return pageDTO;
    }

 

pageLimit - 한 페이지에 보여줄 갯수

blockLimit - 하단에 숫자가 몇개씩 보여줄 것인가 

pageDTO 에 담아서 화면에 표현하기 위해 담아준다 

 

 

BoardRepository

public int boardCount() {
    return sql.selectOne("Board.boardCount");
}

 

BoardMapper.xml

<select id="boardCount" resultType="Integer">
        select count(id) from board_table
    </select>

 

paging.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>paging</title>
</head>
<body>
<div>
    <table>
        <tr>
            <th>id</th>
            <th>title</th>
            <th>writer</th>
            <th>date</th>
            <th>hits</th>
        </tr>
        <c:forEach items="${boardList}" var="board">
            <tr>
                <td>${board.id}</td>
                <td>
                    <a href="/board?id=${board.id}&page=${paging.page}">${board.boardTitle}</a>
                </td>
                <td>${board.boardWriter}</td>
                <td>${board.boardCreatedTime}</td>
                <td>${board.boardHits}</td>
            </tr>
        </c:forEach>
    </table>
</div>

<div>
    <c:choose>
        <%-- 현재 페이지가 1페이지면 이전 글자만 보여줌 --%>
        <c:when test="${paging.page<=1}">
            <span>[이전]</span>
        </c:when>
        <%-- 1페이지가 아닌 경우에는 [이전]을 클릭하면 현재 페이지보다 1 작은 페이지 요청 --%>
        <c:otherwise>
            <a href="/board/paging?page=${paging.page-1}">[이전]</a>
        </c:otherwise>
    </c:choose>

    <%--  for(int i=startPage; i<=endPage; i++)      --%>
    <c:forEach begin="${paging.startPage}" end="${paging.endPage}" var="i" step="1">
        <c:choose>
            <%-- 요청한 페이지에 있는 경우 현재 페이지 번호는 텍스트만 보이게 --%>
            <c:when test="${i eq paging.page}">
                <span>${i}</span>
            </c:when>

            <c:otherwise>
                <a href="/board/paging?page=${i}">${i}</a>
            </c:otherwise>
        </c:choose>
    </c:forEach>

    <c:choose>
        <c:when test="${paging.page>=paging.maxPage}">
            <span>[다음]</span>
        </c:when>
        <c:otherwise>
                <a href="/board/paging?page=${paging.page+1}">[다음]</a>
        </c:otherwise>
    </c:choose>
</div>
</body>
</html>

 

게시글 상세 조회 시 id 값만 넘겼는데 이제는 상세조회 시 페이지 값도 같이 넘긴다

div 로 묶어둔 부분이 이전 숫자 다음을 표현하는 부분

3페이지를 클릭시 이전이라는 부분이 보여진다 

jstl 태그를 이용하여 if - else 로 표현

현재 페이지가 1보다 작거나 같으면 링크 없는 이전 표시 

 

만약 1페이지가 아닌 경우 otherwise 부분이 보여지며 이전은 링크가 달리게 되고

현재 페이지보다 하나 작은 페이지로 controller 로 요청하는 링크가 생긴다

 

다음은 반대되는 역할 비교 대상은 현재 페이지가 maxpage 끝페이지 인 경우 다음은 텍스트만 나오게 한다 

그게 아닌 경우 링크가 붙으며 현재페이지 보다 1 큰 페이지를 요청한다 

 

 

@GetMapping
    public String findById(@RequestParam("id") Long id,
                           @RequestParam(value = "page", required = false, defaultValue = "1") int page,
                           Model model) {
        boardService.updateHits(id);
        BoardDTO boardDTO = boardService.findById(id);
        model.addAttribute("board", boardDTO);
        model.addAttribute("page", page);

    }

page 파라미터가 없으면 오류가 발생하기때문에 필수가 아닌정보로 오류가 안나도록 한다.

모델에다가 페이지 값을 같이 가져간다.

 

 

 

@PostMapping("/save")
    public String save(@ModelAttribute BoardDTO boardDTO) {
        int saveResult = boardService.save(boardDTO);
        if (saveResult > 0) {
            return "redirect:/board/paging";
        } else {
            return "save";
        }
    }

 

글작성 했을때도 기존 목록으로 갔는데 페이징 목록으로 가게 한다

 

 

detail.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>detail.jsp</title>
    <script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
</head>
<body>
    <table>
        <tr>
            <th>id</th>
            <td>${board.id}</td>
        </tr>
        <tr>
            <th>writer</th>
            <td>${board.boardWriter}</td>
        </tr>
        <tr>
            <th>date</th>
            <td>${board.boardCreatedTime}</td>
        </tr>
        <tr>
            <th>hits</th>
            <td>${board.boardHits}</td>
        </tr>
        <tr>
            <th>title</th>
            <td>${board.boardTitle}</td>
        </tr>
        <tr>
            <th>contents</th>
            <td>${board.boardContents}</td>
        </tr>
    </table>
    <button onclick="listFn()">목록</button>
    <button onclick="updateFn()">수정</button>
    <button onclick="deleteFn()">삭제</button>

<div>
    <input type="text" id="commentWriter" placeholder="작성자">
    <input type="text" id="commentContents" placeholder="내용">
    <button id="comment-write-btn" onclick="commentWrite()">댓글작성</button>
</div>

<div id="comment-list">
    <table>
        <tr>
            <th>댓글번호</th>
            <th>작성자</th>
            <th>내용</th>
            <th>작성시간</th>
        </tr>
        <c:forEach items="${commentList}" var="comment">
            <tr>
                <td>${comment.id}</td>
                <td>${comment.commentWriter}</td>
                <td>${comment.commentContents}</td>
                <td>${comment.commentCreatedTime}</td>
            </tr>
        </c:forEach>
    </table>
</div>
</body>
<script>
    const listFn = () => {
        const page = '${page}';
        location.href = "/board/paging?page=" + page;
    }
    const updateFn = () => {
        const id = '${board.id}';
        location.href = "/board/update?id=" + id;
    }
    const deleteFn = () => {
        const id = '${board.id}';
        location.href = "/board/delete?id=" + id;
    }
  }
</script>
</html>

 

detail.jsp 에서 목록 버튼을 누르면 그 이전에 사용자가 있었던 페이지로 다시 요청을 해줘야 한다

페이지 요청을 해서 몇페인지인지 줘야한다.

const listFn = () => {
        const page = '${page}';
        location.href = "/board/paging?page=" + page;
    }