Spring/Spring boot icia 75일차

Spring boot 게시판 페이징처리

swkn 2023. 6. 9. 09:27

1. nav

    <div class="menu">
<!--        /board?page=1
            7페이지에 있는 33번 글을 조회
            rest api > /board/7/33
            /board/33 => 33번글 /board/33?page=7
            -->
        <a th:href="@{/board(page=1)}">페이징 목록</a>
    </div>
http://localhost:8084/board?page=1

링크를 누르면 아래 주소로 이동이 된다.

 

2. Controller

Pageable에 java를 import하는것이 아닌 위에 체크되있는 부분을 import한다.

    @GetMapping
    public String paging(@PageableDefault(page = 1) Pageable pageable, Model model) {
        System.out.println("page = "+ pageable.getPageNumber());
        return null;
    }

페이지 번호가 잘 넘어오는지 확인하려고 sout를 찍어보았다.

잘 넘어온다.

만약 페이지 요청이 오지않고 이상한 값이 오게된다면 PageableDefault으로 page = 1을 Defalut로 설정했기 때문에 1페이지로 이동하게 된다.

 

int blockLimit = 3;

페이지 번호를 몇개까지 보여줄 것인지 설정한다.

int startPage = (((int) (Math.ceil((double) pageable.getPageNumber() / blockLimit))) - 1) * blockLimit + 1;

시작 페이지를 계산한다.

int endPage = ((startPage + blockLimit - 1) < boardDTOPage.getTotalPages()) ? startPage + blockLimit - 1 : boardDTOPage.getTotalPages();

 

보여줄 페이지의 마지막 번호를 계산한다.

삼항 연산자를 사용하였다.

Spring Framework때 MaxPage(페이지의 마지막 페이지)를 구했었는데 Spring boot에서는 endPage 하나로 처리한다.

더보기
        if((startPage+blockLimit-1)<boardDTOPage.getTotalPages()) {
            endPage = startPage+blockLimit-1;
        }else {
            endPage = boardDTOPage.getTotalPages();
        }

위의 삼항연산자를 if문으로 쓴다면 이렇게 표현한다.

 

    // /board?page=1
    @GetMapping
    public String paging(@PageableDefault(page = 1) Pageable pageable, Model model) {
        System.out.println("page = "+ pageable.getPageNumber());
        Page<BoardDTO> boardDTOPage = boardService.paging(pageable);
        model.addAttribute("boardList",boardDTOPage);
        // 시작페이지(startPage), 마지막페이지(endPage) 값 계산
        // 하단에 보여줄 페이지 갯수 3개
        int blockLimit = 3;
        int startPage = (((int) (Math.ceil((double) pageable.getPageNumber() / blockLimit))) - 1) * blockLimit + 1;
        int endPage = ((startPage + blockLimit - 1) < boardDTOPage.getTotalPages()) ? startPage + blockLimit - 1 : boardDTOPage.getTotalPages();
        model.addAttribute("startPage",startPage);
        model.addAttribute("endPage",endPage);
        return "boardPages/boardPaging";
    }

model로 값을 담아서 HTML로 전송한다.

 

3. Service

Spring boot의 JPA에서는 페이지 번호가 0부터 시작하기 때문에 페이지 번호를 1번을 요청하면 2페이지 부터 보여질 것이다.

int page = pageable.getPageNumber() - 1;

Service에서 번호에 -1을 해줘서 번호를 맞춰준다.

int pageLimit = 5;

페이지를 5개까지 보여준다.

boardRepository.findAll(PageRequest.of(page,pageLimit,Sort.by(Sort.Direction.DESC,"id")));
    public Page<BoardDTO> paging(Pageable pageable) {
        int page = pageable.getPageNumber() - 1;
        int pageLimit = 5;
        Page<BoardEntity> boardEntities =
                boardRepository.findAll(PageRequest.of(page, pageLimit, Sort.by(Sort.Direction.DESC, "id")));
        Page<BoardDTO> boardDTOS = boardEntities.map(boardEntity -> BoardDTO.builder()
                .id(boardEntity.getId())
                .boardTitle(boardEntity.getBoardTitle())
                .boardWriter(boardEntity.getBoardWriter())
                .createdAt(UtilClass.dateFormat(boardEntity.getCreatedAt()))
                .boardHits(boardEntity.getBoardHits())
                .build());
        return boardDTOS;
    }

 

 

4. BoardPaging.HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <th:block th:replace="component/config :: config"></th:block>
</head>
<body>
<div th:replace="component/header :: header"></div>
<div th:replace="component/nav :: nav"></div>

<div id="section">
    <div class="container" id="paging-list">
        <table class="table table-hover">
            <thead>
            <tr>
                <th scope="col">id</th>
                <th scope="col">writer</th>
                <th scope="col">title</th>
                <th scope="col">date</th>
                <th scope="col">hits</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="board: ${boardList}">
                <td th:text="${board.id}"></td>
                <td th:text="${board.boardWriter}"></td>
                <td><a th:href="@{|/board/${board.id}|}" th:text="${board.boardTitle}">제목</a></td>
                <td th:text="${board.createdAt}"></td>
                <td th:text="${board.boardHits}"></td>
            </tr>
            </tbody>
        </table>

        <div class="container">
            <ul class="pagination justify-content-center">
                <li class="page-item">
                    <!--첫페이지 요청 링크 /board?page=1 -->
                    <a class="page-link" th:href="@{/board(page=1)}">
                        <span>First</span>
                    </a>
                </li>

                <li th:class="${boardList.first} ? 'disabled'" class="page-item">
                    <!--boardList.first: isFirst()
                        boardList.number: getNumber()-->
                    <a class="page-link" th:href="${boardList.first} ? '#' : @{/board(page=${boardList.number})}">
                        <!-- 사용자 3페이지, number 2 /board?page=2 -->
                        <span><</span> <!-- < -->
                    </a>
                </li>

                <!-- startPage ~ endPage 까지 숫자를 만들어주는 역할
                    for(int page=startPage; page<=endPage; page++) -->
                <li th:each="page: ${#numbers.sequence(startPage, endPage)}"
                    th:class="${page == boardList.number + 1} ? 'page-item active'" class="page-item">
                    <a class="page-link" th:text="${page}" th:href="@{/board(page=${page})}"></a>
                </li>

                <!-- 다음 페이지 요청
                    현재 3페이지를 보고 있다면 다음 페이지는 4페이지임.
                    getNumber() 값은 2임.
                    따라서 4페이지를 보고 싶다면 getNumber()+2를 해야 컨트롤러에 4를 요청할 수 있음. -->
                <li th:class="${boardList.last} ? 'disabled'">
                    <a class="page-link" th:href="${boardList.last} ? '#' : @{/board(page=${boardList.number + 2})}">
                        <!-- 사용자 3페이지, number 2 /board?page=4 -->
                        <span>&gt;</span>
                    </a>
                </li>

                <li class="page-item">
                    <a class="page-link" th:href="@{/board(page=${boardList.totalPages})}">
                        <span>Last</span>
                    </a>
                </li>
            </ul>
        </div>

    </div>

    <div th:replace="component/footer :: footer"></div>
</body>
</html>
                    <!--첫페이지 요청 링크 /board?page=1 -->
                    <a class="page-link" th:href="@{/board(page=1)}">
                        <span><i class="bi bi-chevron-double-left"></i></span>
                    </a>
                </li>

첫 페이지는 1번이므로 맨 처음으로 가는 버튼을 설정한다.

                <li th:class="${boardList.first} ? 'disabled'" class="page-item">
                    <!--boardList.first: isFirst()
                    th:class 조건에 따라서 'disabled' 속성을 적용한다.
                        boardList.number: getNumber()-->
                    <a class="page-link" th:href="${boardList.first} ? '#' : @{/board(page=${boardList.number})}">
                        <!-- 사용자 3페이지, number 2 /board?page=2 -->
                        <span><i class="bi bi-chevron-left"></i></span> <!-- < -->
                    </a>
                </li>

th:class 타임리프 문법을 사용하여 해당 조건이 만족하면 disabled 속성을 적용 , 아니라면 page-item 속성을 적용한다.

페이지의 StartPage를 설정하는 문구다.

                <!-- startPage ~ endPage 까지 숫자를 만들어주는 역할
                    for(int page=startPage; page<=endPage; page++) -->
                <li th:each="page: ${#numbers.sequence(startPage, endPage)}"
                    th:class="${page == boardList.number + 1} ? 'page-item active'" class="page-item">
                    <a class="page-link" th:text="${page}" th:href="@{/board(page=${page})}"></a>
                </li>

startPage 에서 endPage까지 숫자를 만들어주려고

th:each문법으로 , #numbers.sequence(startPage,endPage)를 사용했다.

                <!-- 다음 페이지 요청
                    현재 3페이지를 보고 있다면 다음 페이지는 4페이지임.
                    getNumber() 값은 2임.
                    따라서 4페이지를 보고 싶다면 getNumber()+2를 해야 컨트롤러에 4를 요청할 수 있음. -->
                <li th:class="${boardList.last} ? 'disabled'">
                    <a class="page-link" th:href="${boardList.last} ? '#' : @{/board(page=${boardList.number + 2})}">
                        <!-- boardList.last라면 # , 아니라면  @{/board(page=${boardList.number + 2})
                        사용자 3페이지, number 2 /board?page=4 -->
                        <span><i class="bi bi-chevron-right"></i></span>
                    </a>
                </li>

다음 페이지 버튼을 설정하는데 3페이지를 보고 있다면 다음 페이지는 4페이지 지만 getNumber()의 값은 2이기에

+2를 해주어야 컨트롤러에서 맞는 페이지를 찾아서 리턴할 수 있다.

만약 보여주는 페이지 번호의 마지막 ( last ) 라면 # 를 주고 , 아니라면 다음 페이지로 가는 버튼을 설정한다.

                <li class="page-item">
                    <a class="page-link" th:href="@{/board(page=${boardList.totalPages})}">
                        <span><i class="bi bi-chevron-double-right"></i></span>
                    </a>
                </li>

boardList.totalPages로 마지막 번호로 보내는 버튼도 생성한다.

 

1페이지
4페이지

적용이 잘 되었다.

 

 

5. 특정 페이지에서 상세조회후 다시 특정 페이지로

<td><a th:href="@{|/board/${board.id}?page=${boardList.number+1}|}" th:text="${board.boardTitle}">제목</a>

제목을 눌러서 상세조회하는 a 태그에 ?page=${boardList.number+1}을 추가한다.

 

@GetMapping("/{id}")
    @Transactional
    public String findById(@PathVariable Long id,@RequestParam("page") int page, Model model) {
        boardService.updateHits(id);
        BoardDTO boardDTO = null;
        model.addAttribute("page",page);
        try {
            boardDTO = boardService.findById(id);
            model.addAttribute("board", boardDTO);
            List<CommentDTO> commentDTOList = commentService.findById(id);
            if(commentDTOList.size()>0) {
                model.addAttribute("comment", commentDTOList);
            }else {
                model.addAttribute("comment",null);
            }
            return "boardPages/boardDetail";
        } catch (NoSuchElementException e) {
            return "boardPages/boardNotFound";
        }
    }

컨트롤러 매개변수에 RequestParam으로 page를 받고 , model로 값을 전송

 

<button class="btn btn-primary" onclick="list_req()">목록</button>

목록가는 버튼에

    const list_req = () => {
        location.href = "/board/?page="+[[${page}]];
        // const page = [[${page}]];
        // location.href = "/board/?page="+page;
    }