/repository/BoardRepository.java findAll(pageable) 인터페이스 메서드 작성
// 페이징을 위한 네임스페이스
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
// 인터페이스만 있어도 CRUD가 가능
@Repository
public interface BoardRepository extends JpaRepository<Board, Long>{
// 페이징용 JPA 쿼리 자동생성 인터페이스 메서드 작성
@SuppressWarnings("null") // 경고 메시지 없애주는 어노테이션
Page<Board> findAll(Pageable pageable);
}
/service/BoardService.java getList(page) 메서드 작성
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@Service
@RequiredArgsConstructor
public class BoardService {
private final BoardRepository boardRepository;
public Page<Board> getList(int page) {
Pageable pageable = PageRequest.of(page, 10); // pageSize를 동적으로도 변경할 수 있음. 나중에...
return this.boardRepository.findAll(pageable);
}
}
- Spring-Data-JPA 라이브러리의 Page와 Pageable을 이용
더보기
💡 Spring Data JPA 제공 메서드
- Pageable
- Pageable은 Spring JPA에서 DB 쿼리에 쉽고 유연하게 limit 쿼리를 사용할 수 있게 해준다.
- 특히 JPA를 사용할 때, 자동으로 Pageable 타입의 변수를 넘겨주면 JPA가 DB에 접근해 데이터를 가져올 때 자동으로 limit 조건을 붙여 데이터를 가져온다.
- Page
- Pageable을 파라미터로 하여 가져온 결과물은 Page<SomeObject> 형태로 반환되며
- Page를 사용한다면 대부분 다수의 row를 가져오기 때문에 의 Page<List<SomeObject>> 형태로 반환한다.
더보기
📑 Pageable과 PageRequest
- Pageable과 PageRequest는 Spring Data에서 제공하는 페이지네이션 정보를 담기 위한 인터페이스 구현체이다.
- 페이지 벊로와 단일 페이지의 개수를 담을 수 있다.
- 이를Spring Data JPA 레포지토리의 파라미터로 전달하여, 반환되는 엔티티의 컬렉션에 대해 페이징할 수 있다.
<참고자료>
https://velog.io/@dani0817/Spring-Boot-%ED%8E%98%EC%9D%B4%EC%A7%95Paging-%EC%A0%81%EC%9A%A9
/controller/BoardController.java list() 메서드 수정
- 수정 전
@GetMapping("/list")
public String list(Model model) {
List<Board> boardList = this.boardService.getList(); // Thymeleaf, JSP, mustache 등 VIEW로 보내는 기능
model.addAttribute("boardList", boardList);
return "board/list"; // templates/board/list.html 랜더링해서 리턴해라!
}
- 수정 후
@GetMapping("/list")
public String list(Model model, @RequestParam(value="page", defaultValue = "0") int page) {
Page<Board> paging = this.boardService.getList(page);
model.addAttribute("paging", paging); // 페이징된 보드를 view로 전달!
return "board/list"; // templates/board/list.html 랜더링해서 리턴해라!
}
/templates/board/list.html 수정
// 수정 전
<tr th:each="board, loop: ${boardList}">
// 수정 후
<tr th:each="board, loop: ${paging}">
실행
- 실행시키면 위와 같은 코드가 콘솔에 찍힌다.
- 해당 쿼리를 자동으로 생성해주는 것
-- Oracle 전용(11g 이하는 이 쿼리가 동작 안함)
select b1_0.bno,b1_0.content,b1_0.create_date,b1_0.title
from board b1_0 offset 0 -- 0부터 시작해서 페이지 사이즈만큼 증가
rows fetch first 10 rows only -- 페이지사이즈
- H2 Database에서 쿼리문을 실행시키면 10행씩 나오는 것을 확인할 수 있음!!
Bootstrap 적용시키기
/templates/board/list.html
<!-- 페이징 시작 -->
<div th:if="${!paging.isEmpty()}">
<ul class="pagination justify-content-center">
<!-- 이전버튼 -->
<li th:classappend="${!paging.hasPrevious} ? disabled" class="page-item">
<a th:href="@{|?page=0|}" class="page-link">《</a>
</li>
<li th:classappend="${!paging.hasPrevious} ? disabled" class="page-item">
<a th:href="@{|?page=${paging.number-1}|}" class="page-link">〈</a>
</li>
<!-- 페이지번호버튼 -->
<li th:each="page : ${#numbers.sequence(0, paging.totalPages-1)}"
th:if="${page >= paging.number-5 and page <= paging.number+5}"
th:classappend="${page == paging.number} ? active" class="page-item">
<a th:href="@{|?page=${page}|}" th:text="${page + 1}" class="page-link"></a>
</li>
<!-- 다음버튼 -->
<li th:classappend="${!paging.hasNext} ? disabled" class="page-item">
<a th:href="@{|?page=${paging.number+1}|}" class="page-link">〉</a>
</li>
<li th:classappend="${!paging.hasNext} ? disabled" class="page-item">
<a th:href="@{|?page=${paging.totalPages-1}|}" class="page-link">》</a>
</li>
</ul>
</div>
<!-- 페이징 끝 -->
- 적용된 것을 확인할 수 있음!
list.html 전체코드
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org" layout:decorate="~{layout}">
<div layout:fragment="main-content" class="container my-3">
<!-- 게시글 리스트 -->
<table class="table table-light table-striped">
<thead class="table-dark">
<tr>
<th>번호</th>
<th>제목</th>
<th>작성일</th>
</tr>
</thead>
<tbody>
<!-- <tr th:each="board, loop: ${boardList}"> -->
<tr th:each="board, loop: ${paging}">
<td th:text="${paging.getTotalElements - (paging.number * paging.size) - loop.index}"></td>
<!-- <td th:text="${loop.count}"></td> -->
<td>
<a th:href="@{|/board/detail/${board.bno}|}" th:text="${board.title}"></a>
</td>
<td th:text="${#temporals.format(board.createDate, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
</table>
<!-- 페이징 시작 -->
<div th:if="${!paging.isEmpty()}">
<ul class="pagination justify-content-center">
<!-- 이전버튼 -->
<li th:classappend="${!paging.hasPrevious} ? disabled" class="page-item">
<a th:href="@{|?page=0|}" class="page-link">《</a>
</li>
<li th:classappend="${!paging.hasPrevious} ? disabled" class="page-item">
<a th:href="@{|?page=${paging.number-1}|}" class="page-link">〈</a>
</li>
<!-- 페이지번호버튼 -->
<li th:each="page : ${#numbers.sequence(0, paging.totalPages-1)}"
th:if="${page >= paging.number-5 and page <= paging.number+5}"
th:classappend="${page == paging.number} ? active" class="page-item">
<a th:href="@{|?page=${page}|}" th:text="${page + 1}" class="page-link"></a>
</li>
<!-- 다음버튼 -->
<li th:classappend="${!paging.hasNext} ? disabled" class="page-item">
<a th:href="@{|?page=${paging.number+1}|}" class="page-link">〉</a>
</li>
<li th:classappend="${!paging.hasNext} ? disabled" class="page-item">
<a th:href="@{|?page=${paging.totalPages-1}|}" class="page-link">》</a>
</li>
</ul>
</div>
<!-- 페이징 끝 -->
<!-- 등록 버튼 -->
<div class="d-flex justify-content-end">
<a th:href="@{/board/create}" class="btn btn-sm btn-primary my-2">게시글 등록</a>
</div>
</div>
</html>
<td th:text="${paging.getTotalElements - (paging.number * paging.size) - loop.index}"></td>
- paging.getTotalElements
- 전체 게시물 개수 조회
- paging.number
- 현재 페이지 번호 조회
- paging.size
- 페이지당 게시물 개수 조회
- loop.index
- 나열 인덱스 조회(0부터 시작됨)
'Spring Boot > STUDY' 카테고리의 다른 글
[Spring Boot] JPA 프로젝트 - 회원가입 기능 구현 (1) | 2024.06.21 |
---|---|
[Spring Boot] JPA 프로젝트 - Spring Security (0) | 2024.06.20 |
[Spring Boot] JPA 프로젝트 - @Test를 통해 게시판 데이터 등록 (0) | 2024.06.19 |
[Spring Boot] BuildResult란? (0) | 2024.06.19 |
[Spring Boot] @Valid을 통한 게시글 등록 유효성 검사 (1) | 2024.06.19 |