[From Hello To QueryDSL] Add Paging (3/12)

By | 2020년 3월 12일
Table of Contents

Add Paging

게시판에 페이징 기능을 추가합니다.

개발환경

  • Spring Boot 2.1.x
  • Gradle 4.10.2

프로젝트는 이전 글에서 작성된 프로젝트에 파일을 추가 또는 수정하는 방식으로 진행됩니다. 이전 글을 따라 하지 않은 경우, 먼저 이전 글대로 프로젝트를 구성하시기 바랍니다.

파일추가

JpaRepositoryPagingAndSortingRepository 를 확장하고 있습니다.

클래스명에 있듯이 페이징과 정렬도 처리하는데 정렬기능은 사용하지 않겠습니다.

src/main/java/kr/co/episode/example/domain/posts/PostsPagingRepository.java

package kr.co.episode.example.domain.posts;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.JpaRepository;

public interface PostsPagingRepository extends JpaRepository<Posts, Long> {

    @Query(value = "SELECT p FROM Posts p ORDER BY p.id DESC")
    Page<Posts> findAllDesc(Pageable pageable);
}

src/main/java/kr/co/episode/example/service/posts/PostsPagingService.java

package kr.co.episode.example.service.posts;

import kr.co.episode.example.domain.posts.Posts;
import kr.co.episode.example.domain.posts.PostsPagingRepository;
import kr.co.episode.example.web.dto.PostsListResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
public class PostsPagingService {

    private final PostsPagingRepository postsPagingRepository;

    @Transactional(readOnly = true)
    public Page<PostsListResponseDto> findAllDesc(Pageable pageable) {
        Page<Posts> posts = postsPagingRepository.findAllDesc(pageable);
        List<PostsListResponseDto> results = posts.getContent().stream()
                .map(PostsListResponseDto::new)
                .collect(Collectors.toList());
        long totalCount = posts.getTotalElements();

        return new PageImpl<>(results, pageable, totalCount);
    }
}

파일수정

src/main/java/kr/co/episode/example/web/IndexController.java

    // ......
    private final PostsPagingService postsPagingService;

    @GetMapping("/")
    public String index(Model model, @PageableDefault Pageable pageable) {
        //model.addAttribute("posts", postsService.findAllDesc());

        // 페이지당 표시 게시글 수를 3개로 제한
        pageable = PageRequest.of(pageable.getPageNumber(), 3);
        model.addAttribute("posts", postsPagingService.findAllDesc(pageable));

        return "index";
    }
    // ......

src/main/resources/templates/index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>게시판</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    <style>
        .container p { display: inline }
    </style>
</head>
<body class="container">

<h1>스프링 부트 게시판</h1>

<div class="col-md-12">
    <div class="row">
        <div class="col-md-6">
            <a href="/posts/save" role="button" class="btn" btn-primary>글 등록</a>
        </div>
    </div>
</div>

<div style="height: 80px;">

</div>

<table class="table">
    <tr>
        <th>글 번호</th>
        <th>글쓴이</th>
        <th>글 제목</th>
        <th>최종수정</th>
    </tr>
    <tr th:each="posts: ${posts}">
        <td th:text="${posts.id}"></td>
        <td>
            <a th:href="${posts.link}" th:text="${posts.author}" />
        </td>
        <td>
            <a th:href="${posts.link}" th:text="${posts.title}" />
        </td>
        <td th:text="${posts.modifiedDate}"></td>
    </tr>
</table>

<nav style="text-align: center;">
    <ul class="pagination"
        th:with="start=${T(Math).floor(posts.number/10)*10},
                    last=(${start + 9 < posts.totalPages-1 ? start + 9 : posts.totalPages-1})">
        <li>
            <a th:href="@{/(page=0)}" aria-label="First">
                <span aria-hidden="true">First</span>
            </a>
        </li>

        <li th:class="${posts.first} ? 'disabled'">
            <a th:href="${posts.first} ? '#' :@{/(page=${posts.number-1})}" aria-label="Previous">
                <span aria-hidden="true"><</span>
            </a>
        </li>

        <li th:each="page: ${#numbers.sequence(start, last)}" th:class="${page == posts.number} ? 'active'">
            <a th:text="${page+1}" th:href="@{/(page=${page})}"></a>
        </li>

        <li th:class="${posts.last} ? 'disabled'">
            <a th:href="${posts.last} ? '#' : @{/(page=${posts.number+1})}" aria-label="Next">
                <span aria-hidden="true">></span>
            </a>
        </li>

        <li>
            <a th:href="@{/(page=${posts.totalPages-1})}" aria-label="Last">
                <span aria-hidden="true">Last</span>
            </a>
        </li>
    </ul>
</nav>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

<script src="/js/app/index.js"></script>

</body>
</html>

답글 남기기