| 개념 | 설명 |
|---|---|
Pageable | 페이징 요청 정보를 담는 인터페이스 (page, size, sort 등) |
Page<T> | 전체 데이터 수, 페이지 수 등 메타데이터 포함한 페이징 결과 |
Slice<T> | 다음 페이지 여부만 제공하는 경량 페이징 결과 |
List<T> | 단순 목록 (페이징 정보 없음) |
public interface MemberRepository extends JpaRepository<Member, Long> {
Page<Member> findByAge(int age, Pageable pageable);
}
Pageable pageable = PageRequest.of(0, 10, Sort.by("username").descending());
Page<Member> result = memberRepository.findByAge(20, pageable);
PageRequest.of(page, size) → page: 0부터 시작 Sort는 여러 필드 조합 가능| 구분 | Page | Slice |
|---|---|---|
| 전체 count 조회 | O | X |
| 다음 페이지 존재 여부 | O | O |
| 총 페이지 수 | O | X |
| 성능 | 낮음 (count 쿼리 포함) | 높음 (count 없음) |
Slice<Member> findByUsername(String username, Pageable pageable);
Slice가 유리엔티티 그대로 반환하면 안 되므로, DTO로 변환 필요
Page<MemberDto> dtoPage = result.map(m -> new MemberDto(m.getUsername(), m.getAge()));
복잡한 조인 등으로 인해 count 쿼리 최적화가 필요할 때는 다음과 같이 분리
@Query(
value = "SELECT m FROM Member m LEFT JOIN m.team t",
countQuery = "SELECT count(m) FROM Member m"
)
Page<Member> findCustomPage(Pageable pageable);
@GetMapping("/members")
public Page<MemberDto> getMembers(@PageableDefault(size = 10) Pageable pageable) {
return memberRepository.findAll(pageable)
.map(MemberDto::new);
}
@PageableDefault로 기본 사이즈 지정 가능?page=0&size=10&sort=username,desc와 같이 요청처음엔 그냥 List<T>로 다 가져오는 게 편했지만 사용자 수가 늘어나고 데이터가 많아지면서 페이징 처리가 꼭 필요하다는 걸 알게 되었다. Page, Slice, Pageable을 잘 활용하면 성능과 사용자 경험 모두를 잡을 수 있다는 점에서 꼭 숙지해야 할 기능이다.
QueryDSL과 함께 사용할 때도 .offset()과 .limit()로 수동 페이징이 가능하니 추후에 그 내용도 함께 익혀야겠다.