게시판 서비스에 작성된 게시글을 페이징 처리 하는 것을 구현 해야 한다.
※ 조건 ※
1. 게시글의 'id' 를 기준으로 내림차순 정렬
2. '이전' '1' '2' '3' '4' '5' '다음' 과 같이 페이지 번호 구현
1. PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
// Post 전체 검색 (페이징)
Page<Post> findAll(Pageable pageable);
}
Spring Data JPA 로 구현된 'PostRepository' 인터페이스에
반환 타입을 Page<Post> 로 하고, 파라미터를 페이징 조건을 담을 수 있는 'Pageable' 로 한다.
2. PostService
// PostService.class 中 ...
/**
* 게시글 목록
*/
public Page<Post> pageList(Pageable pageable) {
return postRepository.findAll(pageable);
}
게시글 목록을 Page<Post> 타입으로 반환 하는 서비스 로직을 구현한다.
3. Controller
//게시글 페이징 테스트
@GetMapping("/paging-test")
public String paging_test(@PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable,
Model model) {
Page<Post> list = postService.pageList(pageable);
//Post 응답 DTO 로 변환
Page<PostResponseDto> posts = list.map(PostResponseDto::new);
model.addAttribute("posts", posts); //응답 DTO-PAGE
model.addAttribute("previous", pageable.previousOrFirst().getPageNumber()); //이전 페이지 정보
model.addAttribute("next", pageable.next().getPageNumber()); //다음 페이지 정보
model.addAttribute("hasPrevious", list.hasPrevious()); //이전 페이지 존재 여부
model.addAttribute("hasNext", list.hasNext()); //다음 페이지 존재 여부
/** 페이지 블록 계산
* currentPage = 5
* User : 5 , Spring : 4
* startPage = 1
* endPage = 5
* <= 1 2 3 4 5 =>
*/
int currentPage = pageable.getPageNumber() + 1; //현재 페이지 정보(User side)
model.addAttribute("current", currentPage);
int blockSize = 5;
int startPage = ((currentPage - 1) / blockSize) * blockSize + 1; //블럭 시작 페이지
int endPage = Math.min(startPage + blockSize - 1, list.getTotalPages()); //블럭 마지막 페이지
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
return "test/paging-test";
}
크게 '페이징'에 필요한 정보 와 '페이지 번호 부여' 에 필요한 정보로 나뉜다.
1. '페이징'에 필요한 정보
- 페이지에 표시할 게시글 : 'posts'
- 이전 페이지 정보 (이전 페이지 번호) : 'previous'
- 다음 페이지 정보 (다음 페이지 번호) : 'next'
- 이전 페이지 존재 여부 (boolean type) : 'hasPrevious'
- 다음 페이지 존재 여부 (boolean type) : 'hasNext'
2.' 페이지 번호 부여'에 필요한 정보
- 현재 페이지 정보 (현재 페이지 번호) : 'current'
- 페이지 블럭의 시작 페이지 번호 : 'startPage'
- 페이지 블럭의 끝 페이지 번호 : 'endPage'
위의 총 8가지 정보를 model 을 통해, view 단으로 보낸다.
4. VIEW
※ 정말 필요한 부분을 제외하고 모든 부분을 생략했다. (Thymeleaf 를 이용)
1. 게시글 목록
<!--게시글 목록-->
<table class="table table-striped">
<thead>
<tr class="table-dark">
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>조회수</th>
<th>작성일자</th>
</tr>
</thead>
<tbody>
<tr th:each="post : ${posts}">
<td th:text="${post.id}">1</td>
<td>
<a th:href="@{|/post/${post.id}|}" th:text="${post.title}">제목</a>
</td>
<td th:text="${post.memberNickname}">작성자</td>
<td th:text="${post.view}">조회수</td>
<td th:text="${post.createdAt}">작성일자</td>
</tr>
</tbody>
</table>
페이징된 게시글 목록 'posts' 를 Table 형태로 배치하였다.
2. 페이지 번호
<!--페이지 번호 구현-->
<ul class="pagination">
<li class="page-item">
<a th:if="${hasPrevious}" th:href="@{/paging-test(page=${previous})}"
role="button" class="page-link">이전</a>
<a th:if="${!hasPrevious}" th:href="@{/paging-test(page=${previous})}"
role="button" class="page-link disabled">이전</a>
</li>
<li class="page-item" th:each="pageNum : ${#numbers.sequence(startPage, endPage)}"
th:classappend="${pageNum == current} ? active : ''">
<a th:href="@{/paging-test(page=${pageNum - 1})}" th:text="${pageNum}"
role="button" class="page-link">페이지 번호</a>
</li>
<li class="page-item">
<a th:if="${hasNext}" th:href="@{/paging-test(page=${next})}"
role="button" class="page-link">다음</a>
<a th:if="${!hasNext}" th:href="@{/paging-test(page=${next})}"
role="button" class="page-link disabled">다음</a>
</li>
</ul>
구성은 다음과 같다.
'이전' 버튼
: 현재 페이지로 부터 이전 페이지가 존재할 경우 활성화 되며, 이를 누르면 '현재 페이지 - 1' 인 페이지로 이동한다.
'페이지 번호' 버튼
: 현재 페이지 블럭에 포함되는 페이지 번호를 나열하며, 버튼이 눌리면 해당 페이지로 이동한다.
이와 같이 URL 에 '?page={pageNum}' 조건을 추가하여, 클릭한 페이지로 이동하는 것을 볼 수 있다.
※ Spring Data JPA 에서 Paging은 0 부터 시작한다.
'다음' 버튼
: 현재 페이지로 부터 다음 페이지가 존재할 경우 활성화 되며, 이를 누르면 '현재 페이지 + 1' 인 페이지로 이동한다.
완성 화면
페이징 기능이 잘 작동하는 것을 볼 수 있다.
'PROJECT > [SpringBoot] 게시판 서비스' 카테고리의 다른 글
[게시판 서비스] 회원 탈퇴시, 게시글 / 댓글 처리 (0) | 2024.06.02 |
---|---|
[게시판 서비스] 게시글 키워드 검색 + 정렬 + 페이징 기능 구현 (0) | 2024.06.02 |
[ERROR] Refused to apply style from 'http://localhost:8080/member/login' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled. (0) | 2024.06.02 |
[게시판 서비스] SpringBoot 게시판 서비스 - 1 (0) | 2024.04.23 |
[게시판 서비스] 개발 과정 오류 해결 (0) | 2024.04.14 |