Sping Data JPA는 간단한 페이징 처리를 지원한다.
페이징을 처리를 하기 위해선 한 페이지 당 보여줄 데이터의 개수와 몇 번째 페이지인지에 대한 정보가 필요하다.
@GetMapping
public ResponseEntity<?> searchPointDetail(@RequestParam(name = "page") int page, @RequestParam(name = "size") int size) {
// 로직
}
Spring Data JPA는 이러한 정보를 Pageable 객체를 이용하고, Pageable 객체를 JpaRepository에 전달하여 페이징 처리를 지원한다.
@Repository public interface PointRepository extends JpaRepository<Point, Long> {
Page<Point> findByIdLessThanAndMemberOrderByIdDesc(Long id, Member member, Pageable pageable);
Slice<Point> findByIdLessThanAndMemberOrderByIdDesc(Long id, Member member, Pageable pageable);
}
Pageable 객체는 PageRequest의 정적 팩토리 메소드인 of를 통해 구현하는 것이 가능하다.
PageRequest의 of 메소드를 확인해보면 기본적으로 현재 페이지를 의미하는 page와 페이지 당 보여줄 데이터를 받을 size를 파라미터로 필수적으로 받고 추가적으로 정렬 방식을 지정할 수 있는 것을 확인할 수 있다.
기본적으로 Web Layer (Controller)에서 page, size를 쿼리스트링을 통해 전달 받지만, Pageable pageable 형식으로 받는 것도 가능하기 때문에 Pageable 객체 자체를 JpaRepository에 전달해도 페이징 처리가 지원된다.
JpaRepository는 페이징 처리한 데이터를 리턴 데이터 타입으로 List이외에도 Page, Slice라는 특별한 반환 타입도 지원한다.
package org.springframework.data.domain;
import java.util.List;
import java.util.function.Function;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.util.Streamable;
public interface Slice<T> extends Streamable<T> {
int getNumber(); // 현재 페이지
int getSize(); //페이지 크기
int getNumberOfElements(); // 현재 페이지에 조회한 데이터 개수
List<T> getContent(); // 현재 페이지에 조회한 데이터
boolean hasContent(); // 현재 페이지에 데이터가 있는지 여부
Sort getSort(); // 정렬 여부
boolean isFirst(); // 첫 번째 페이지인지 여부
boolean isLast(); // 마지막 페이지인지 여부
boolean hasNext(); // 다음 페이지가 있는지 여부
boolean hasPrevious(); // 이전 페이지가 있는지 여부
// 페이지 요청 정보
default Pageable getPageable() {
return PageRequest.of(getNumber(), getSize(), getSort());
}
// 다음 페이지 정보
Pageable nextPageable();
// 이전 페이지 정보
Pageable previousPageable();
<U> Slice<U> map(Function<? super T, ? extends U> converter);
default Pageable nextOrLastPageable() {
return hasNext() ? nextPageable() : getPageable();
}
default Pageable previousOrFirstPageable() {
return hasPrevious() ? previousPageable() : getPageable();
}
}
Page 인터페이스는 부모 인터페이스인 Slice를 상속한다. 따라서, Slice 인터페이스 외에 getTotalPages(), getTotalElements() 메소드를 추가로 가지고 있다.
public interface Page<T> extends Slice<T> {
static <T> Page<T> empty() {
return empty(Pageable.unpaged());
}
static <T> Page<T> empty(Pageable pageable) {
return new PageImpl<>(Collections.emptyList(), pageable, 0);
}
int getTotalPages(); // 전체 페이지 개수
long getTotalElements(); // 전체 데이터 개수
<U> Page<U> map(Function<? super T, ? extends U> converter);
}
이를 통해, Slice 와 Page의 차이는 전체 페이지 수와 전체 데이터 개수를 조회하는지 안하는지에 대한 차이가 있다.
따라서, Page 리턴 타입은 전체 데이터 및 전체 페이지 수를 계산 하기 위해서 DB 내에서 주어진 조건에 따른 Count 쿼리를 추가로 호출한다.
반대로 Slice 리턴 타입은 다음 Slice가 있는지만 판단하기 때문에 Page 리턴 타입보다 성능적으로 좋다.
페이징 처리에 다양한 리턴타입을 제공하는 만큼 요구사항에 따라서 Page와 Slice를 사용하면 될 것 같다.
전체 페이지 개수와 전체 데이터 수를 이용하는 실제 게시판 형식의 페이징 처리를 구현해야한다면 Page를 이용하고,
요즘 많이 사용하는 무한 스크롤 방식을 사용한다면 전체 페이지와 데이터 개수가 필요없기 때문에 Slice 방식을 이용하는 것이 효율적이라 판단된다.
📄 References
Pageable을 이용한 Pagination을 처리하는 다양한 방법 : https://tecoble.techcourse.co.kr/post/2021-08-15-pageable/
[SpringBoot] Spring Data JPA 에서 Page와 Slice : https://velog.io/@dltkdgns3435/SpringBoot-Spring-Data-JPA-에서-Page와-Slice
'Backend > Spring Boot' 카테고리의 다른 글
Spring Cloud Bus와 RabbitMQ를 이용해 설정 정보 한번에 최신화하기! (0) | 2022.05.15 |
---|---|
싱글톤 스코프, 프로토타입 스코프 (0) | 2022.05.03 |
Spring Data JPA를 이용해 커서 페이징 구현하기 (0) | 2022.05.01 |
Spring Cloud Config Server를 Private Repository와 연동 (0) | 2022.04.28 |
Spring Cloud Config Server/Client 설정 (0) | 2022.04.28 |