반응형
Spring Boot 컨트롤러
스프링 부트에서 먼저 페이징 처리 코드를 살펴 보도록 하겠습니다.
컨트롤러
- @PageableDefault : 페이징 파라미터가 없을 경우 디폴트 값 입니다.
page 파라미터의 경우 디폴트가 0이러서 꼭 명시할 필요는 없습니다. - Spring Boot Jpa를 사용하여 Pageable로 직접 구현없이 페이징을 하고 싶다면 직접 jpql이나 네이티브 쿼리 등
커스텀 하여 사용하면 Srping Data Jpa의 페이징 기능을 사용할 수 없게 됩니다.
물론 Query Dsl을 사용해도 되고, 다른 방법도 있겠지만, 딱히 어렵게 할 필요가 없었기 때문에
jpql로 직접 쿼리를 작성 하였다가 쿼리 메서드르 변경이 가능한 쿼리여서 쿼리 메서드를 사용하여
페이징 기능을 사용할 수 있게 되었습니다. @RequiredArgsConstructor @Controller public class BlogController { private final BlogDetailRepository blogDetailRepository; @ResponseBody @GetMapping("/") public Page<BlogDetail> blogs(Model model, @PageableDefault(page = 0, size = 2, direction = Sort.Direction.DESC) Pageable pageable) { Page<BlogDetail> blogDetails = blogDetailRepository.findAllByOrderByPubDateDesc(pageable); return blogDetails; }
Pageable
Pageable은 인터페이스로써 PageRequest라는 구현체를 주입 받게 된다고 하네요.
아직 직접 뜯어보지는 않아서 자세히는 모르겠습니다 ㅜ.ㅜ
Api 요청 시 리턴 값
대략 이정도의 정보만 알면 페이징은 사용할 수 있습니다.
index는 0부터 시작 합니다.
- content : 컨트롤러에서 엔티티, Dto등 매핑된 데이터
- pageable.pageNumber, number : 현재 페이지 index( 내가 보고자 하는 페이지 )
- pageable.pageSize, size : 페이지 당 보여줄 글의 개수
- totalPages : 전체 페이수
- totalElements : 전체 글의 개수
- first : 현재 페이지의 첫 페이지 여부
- last : 현재 페이지의 끝 페이지 여부
Thymeleaf에서 사용하기
먼저 Api 방식이 아닌 템플릿 방식으로 컨트롤러를 변경 합니다.
@RequiredArgsConstructor
@Controller
public class BlogController {
private final BlogDetailRepository blogDetailRepository;
// @ResponseBody
@GetMapping("/")
public String blogs(Model model, @PageableDefault(page = 0, size = 2, direction = Sort.Direction.DESC) Pageable pageable) {
// public Page<BlogDetail> blogs(Model model, @PageableDefault(page = 0, size = 10, direction = Sort.Direction.DESC) Pageable pageable) {
Page<BlogDetail> blogDetails = blogDetailRepository.findAllByOrderByPubDateDesc(pageable);
// .stream()
// .map(BlogDetailDto::new)
// .collect(Collectors.toList());
model.addAttribute("blogDetails", blogDetails);
return "blogList";
// return blogDetails;
}
}
Thymeleaf 소스 분석
소스를 보면 기능 단위로 주석을 만들어 놓았다. 주석 단위로 소스를 분석 해보도록 하겠습니다.
프로그래밍을 하다 보면, index가 0부터 시작해서 헷갈리는 경우가 많은데
그 부분을 해결 하려다가 오히려 더 복잡해져서 그냥 그대로 사용 했습니다.
index를 1로 세팅하기 위해 paging 리턴 값에 + 1을 더해서 전역 변수로 사용 해보고 했는데..
오히려 더 헷갈리더라구요. 그래서 그대로 사용하기로 했고, 사용하는 측에서 웬만하면 연산을 하는 코드로 작성 했습니다.
참고 사이트 1
참고 사이트 2
<!-- 페이징 -->
<div th:if="${!blogDetails.isEmpty()}">
<!-- 전역 변수 선언 -->
<nav
th:with="
pageNumber = ${blogDetails.pageable.pageNumber},
pageSize = ${blogDetails.pageable.pageSize},
totalPages = ${blogDetails.totalPages},
startPage = ${T(Math).floor(pageNumber / pageSize) * pageSize + 1},
tempEndPage = ${startPage + pageSize - 1},
endPage = (${tempEndPage < totalPages ? tempEndPage : totalPages})"
aria-label="Page navigation"
>
<ul class="pagination ">
<!-- 처음으로 이동 -->
<li th:classappend="${pageNumber < pageSize} ? 'disabled'" class="page-item">
<a class="page-link" th:href="@{/(page=0)}">
<span>«</span>
<span class="sr-only">First</span>
</a>
</li>
<!-- 이전으로 이동 -->
<li th:classappend="${blogDetails.first} ? 'disabled'" class="page-item">
<a class="page-link" th:href="${blogDetails.first} ? '#' : @{/(page=${pageNumber - 1})}" aria-label="Previous">
<span aria-hidden="true"><</span>
<span class="sr-only">Previous</span>
</a>
</li>
<!-- 특정 페이지로 이동 -->
<li th:each="page: ${#numbers.sequence(startPage, endPage)}" th:classappend="${page == pageNumber + 1} ? 'active'" class="page-item">
<a th:text="${page}" class="page-link" th:href="@{/(page=${page - 1})}"></a>
</li>
<!-- 다음으로 이동 -->
<li th:classappend="${blogDetails.last} ? 'disabled'" class="page-item">
<a class="page-link" th:href="${blogDetails.last} ? '#' : @{/(page=${pageNumber + 1})}" aria-label="Next">
<span aria-hidden="true">></span>
<span class="sr-only">Next</span>
</a>
</li>
<!-- 마지막으로 이동 -->
<li th:classappend=" ${T(Math).floor(totalPages / pageSize) * pageSize - 1 <= startPage} ? 'disabled'" class="page-item">
<a class="page-link" th:href="@{/(page=${totalPages - 1})}">
<span>»</span>
<span class="sr-only">Last</span>
</a>
</li>
</ul>
</nav>
</div> <!-- /container -->
이제 주석 단위로 설명 하겠습니다.
위에 Api 결과 값을 참고해 주시기 바랍니다.
전역 변수 선언
- stratPage : + 1을 한 이유는 사용자 눈에 보이는 페이지는 0페이지 부터가 아닌 1페이지 부터이기 때문에 +1을 했습니다.
- tmpEndPage : 단순히 endPage 연산에서 사용하기 쉽게 만들어 놓은 변수 입니다.
현재 페이지의 끝 페이지 번호를 구하는 변수 입니다.
- endPage : 현재 페이지의 끝 번호가 전체 페이지 번호 보다 작으면 아직 끝 페이지 라인에 온 것이 아니기 때문에
현재 페이지의 끝 번호를 endPage로 지정 하고, 현재 페이지의 끝 번호가 전체 페이지 번호 이상이 나오면 안되기 때문에 전체 페이지 번호를 끝 번호로 사용 합니다.
처음으로 이동
- 현재 페이지의 인덱스(0부터 시작)가 페이지 사이즈 보다 작을 경우 아직 첫 페이지 라인에 머물로 있는 것이므로,
처음으로 이동 버튼을 비활성화 시킵니다.
이전으로 이동
- 첫 페이지라면 이전으로 이동 버튼을 비활성화 하고 href 링크도 없는데요. 첫 페이지는 이전이 없기 때문입니다.
그리고 첫 페이지가 아니라면, 현재 페이지 인덱스(0부터 시작) - 1을 하면 이전으로 가겠죠?
특정 페이지로 이동
- 내가 클릭한 페이지 번호로 이동하는 소스 입니다.
startPage 부터 endPage까지 반복문을 돌리는 소스 입니다. 즉, startPage 1 부터 시작하는 것 입니다.
그 값은 page라는 변수에 담겨 사용 됩니다. - page와 현재 페이지(인덱스 0 부터) + 1이 같다면 버튼을 찐하게 표시해 줍니다.
현재 페이지를 표시하기 위함이죠.
href 링크에 - 1을 한 이유는 page는 1부터 시작하기 때문에 실제 페이지 값은 인덱스 0부터 시작하는 현상 때문 입니다.
다음으로 이동
- 처음으로 이동과 동일하고, frist 대신 last를 체크, - 1 대신 + 1을 해줍니다.
마지막으로 이동
- startPage가 앞의 연산 결과 이상 이라면 마지막 페이지 라인에 위치한 상태로 마지막 페이지로 이동할 필요 없이 직접 마지막 페이지 번호를
누르면 되기 때문에 disable 처리합니다.
그게 아니라면, 전체 페이지 수 - 1만큼 이동합니다. 전체 페이지 수는 index가 0 부터 시작하기 때문에 - 1이 필요 합니다.
반응형
'Spring' 카테고리의 다른 글
@Configuration과 싱글톤 (0) | 2021.09.24 |
---|---|
AOP란 (0) | 2021.09.24 |
SpringBoot 2.4 설정파일(yml) 사용법 (0) | 2021.06.20 |
Spring Boot -Dspring으로 환경설정 파일 적용하기 (0) | 2021.05.09 |
spring boot 2.4에서 properties include 변경 사항 (0) | 2021.05.02 |