[스프링부트] - 페이징
페이지네이션(Pagination)
웹 사이트 게시판을 둘러볼 때, 게시글 목록 하단에 게시글을 일정 수로 나누어 페이지화 시켜놓은 것을 Pagination이라고 한다.
페이징 구현하기 위해서는?
- 전체 데이터 개수를 가져와서 전체 페이지를 계산
- 스타트 페이지 지정
- 엔드 페이지 지정
- 예상치 못한 페이지 범위 요청 예외처리
이렇게 4가지를 생각해야된다.
우리는 Spring-Data-JPA 라이브러리의 Page와 Pageable인터페이스를 이용해서 쉽게 접근이 가능하다.
Page
Pageable을 파라미터로 가져온 결과물은
Page<Object> 형태로 변환되면, Page사용시 복수개의 row를 가져오기 때문에
Page<List<Object>>형태로 반환한다.
Pageable
Pageable은 JPA에서 DB 쿼리에 쉽고 유연하게 limit 쿼리를 사용할 수 있게 해준다.
특히 JPA를 사용할 때, 자동으로 Pageable타입의 변수를 넘겨주면 JPA가 DB에 접근해 데이터를 가져올 때 자동으로 limit
조건을 붙여서 데이터를 가져온다.
필자는 데이터를 10건씩 10페이지씩 끊어서 나타내려고 한다.
컨트롤러에서
@GetMapping("/holidayparking")
public String holidayparking(
Model model, @RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "") String search) {
int startpage = (page-1) / 10 *10 +1;
int endpage = startpage+9;
Pageable pageable = PageRequest.of(page-1,10);
Page<HolidayParking> p = hpr.findByInstitutionContainingOrSidoContainingOrGuContaining(
search, search, search, pageable);
model.addAttribute("totalElement",p.getTotalElements());
model.addAttribute("totalpage",p.getTotalPages());
model.addAttribute("hp",p.getContent());
model.addAttribute("startpage",startpage);
model.addAttribute("endpage",endpage);
model.addAttribute("search", search);
return "holiday_parking";
}
Model객체로 값을 받아오고
@RequestParam으로 page를 받아온다.
search를 적어도 되고 안적어도 되고
- startpage = (page-1) / 10 * 10 +1;
시작 페이지를 나타낸다.
만약에 page값이 15면
- 15 -1 =14
- 14 / 10 = 1
- 1 * 10 = 10
- 10 + 1 = 11
이렇게 page가 15면 15가 해당하는 페이지의 시작점을 나타내준다.
- int endpage = startpage+9;
끝 페이지를 나타낸다.
page가 15면 startpage는 11 이고, endpage는 20이 되는 것이다.
핵심은
Pageable pageable = PageRequest.of(page-1,10);
Page<HolidayParking> p = hpr.findByInstitutionContainingOrSidoContainingOrGuContaining(
search, search, search, pageable);
이 부분인데
차근차근 설명해보겠다.
Pageable인터페이스
Spring-Data-JPA에서 페이징 및 정렬 정보를 추상화한 인터페이스이다.
몇번째 페이지를 가져올 것인지와 한페이지에 몇개의 데이터를 보여줄 것인지의 정보를 담고 있다.
PageRequest 클래스
Pageable의 가장 기본적인 구현체이다.
페이징 정보를 담는 간단한 객체이고 정렬 정보도 추가가 가능하다.
PageRequest.of(int page, int size);
이 메소드를 이용해서 PageRequest인스턴스를 만든다.
int page 파라미터: 가져오고 싶은 페이지의 번호이다.
int size 파라미터: 한 페이지에 보여줄 데이터의 개수, 페이지 크기이다.
Pageable pageable = PageRequest.of(page-1,10);
요청한 페이지(page)에 해당하는 데이터를 한 페이지당 10개씩 가져오기 위한 페이징 정보 객체를 생성하는 것이다.
Page<HolidayParking> findByInstitutionContainingOrSidoContainingOrGuContaining(
String ins, String sido, String gu, Pageable pageable);
HolidayParkingRepository에서
쿼리 메소드를 이용해서
Institution값이 ins문자열을 포함하거나 Sido속성이 sido를 포함하거나 Gu속성이 gu를 포함하는 모든 데이터를 찾아서
결과를 페이징 정보(pageable)에 맞게 페이지 단위로 변환해달라는 쿼리 메소드이다.
여기서 리턴타입이 Page<HolidayParking>인 이유는 매개변수에 pageable이 포함되어있기 때문이다.
pageable매개변수: 해당하는 페이징 정보를 가지고 데이터를 가져와달라 라는 요청
page객체: 그 요청에 대한 응답으로서, 요청된 데이터와 함께 페이징 UI를 구성하는데 필요한 메타데이터를 담아주는
Spring Data JPA의 반환타입이기 때문에 리턴타입이 Page<>로 되는것이다.
그럼 마지막으로 model객체를 이용해서 컨트롤러에서 뷰로 넘겨준다.
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
전체 데이터: [[${totalElement}]] / 전체 페이지: [[${totalpage}]]
<table class="table table-hover">
<tr>
<td>아이디</td>
<td>시도</td>
<td>구군</td>
<td>기관명</td>
<td>담당자</td>
<td>연락처</td>
</tr>
<tr th:each="h : ${hp}">
<td>[[${h.id}]]</td>
<td>[[${h.sido}]]</td>
<td th:utext = "${h.gu}"></td>
<td th:utext = "${h.institution}"></td>
<td>[[${h.manager}]]</td>
<td>[[${h.tel}]]</td>
</tr>
</table>
<div>
<!-- 생략 가능 -->
<form action="/holidayparking" method="get">
검색: <input type="text" name="search">
<button type="submit">검색</button>
</form>
</div>
<ul class="pagination">
<li
th:class="@{|page-item ${page == pageNumber ? 'active' : ''}|}"
th:each="pageNumber : ${#numbers.sequence(startpage, endpage)}">
<a class="page-link"
th:href="@{|/holidayparking?page=${pageNumber}&search=${search}|}"
th:text="${pageNumber}">
</a>
</li>
</ul>
그럼 view쪽에서 받은 데이터를 띄워준다.
해당 경로로 get요청을 보내면
이렇게 띄워준다.
아직 조금 더 정리가 필요한거 같다.