Develop/Spring 공략

[Spring] Spring JPA - Page 와 Slice

쿼카홀릭 2025. 4. 1. 23:01

 

JPA를 쓰면서 동적 페이징처리가 매우 쉬워졌다.

PageRequest 객체가 자동으로 limit, offset으로 페이징하던 이전 쿼리를 자동으로 생성해주기 때문이다.

 

Page 와 Slice

PageRequest 객체를 통해 페이징을 할 때, 반환 타입으로 Page와 Slice를 사용한다.

두 객체의 결과물과 성능이 어떤 차이가 있는지 비교 해보자.

 

1. 우선 Page 와 Slice를 반환해주는 레포지토리를 만든다.

//MemberRepository

...

	Page<Member> findPageBy(Pageable pageable);
    Slice<Mebmer> findSliceBy(Pageable pageable);
    
...

 

2. 아래 테스트 코드를 실행시킨다.

@Test
    public void paging() throws Exception {
        //given
        memberRepository.save(new Member("member1", 10));
        memberRepository.save(new Member("member2", 10));
        memberRepository.save(new Member("member3", 10));
        memberRepository.save(new Member("member4", 10));
        memberRepository.save(new Member("member5", 10));

        PageRequest pageRequest = PageRequest.of(1, 2, Sort.by(Sort.Direction.ASC, "age"));

        //when
        Page<Member> page = memberRepository.findPageBy(pageRequest);
        Slice<Member> slice = memberRepository.findSliceBy(pageRequest);

        //then
        ...
    }

 

3. 

Page

select
    member0_.member_id as member_i1_0_,
    member0_.age as age2_0_,
    member0_.team_id as team_id4_0_,
    member0_.username as username3_0_ 
from
    member member0_ 
order by
    member0_.age asc limit ? offset ?

select
    count(member0_.member_id) as col_0_0_ 
from
    member member0_
select member0_.member_id as member_i1_0_, member0_.age as age2_0_, member0_.team_id as team_id4_0_, member0_.username as username3_0_ from member member0_ order by member0_.age asc limit 2 offset 2;

Slice

select
    member0_.member_id as member_i1_0_,
    member0_.age as age2_0_,
    member0_.team_id as team_id4_0_,
    member0_.username as username3_0_ 
from
    member member0_ 
order by
    member0_.age asc limit ? offset ?
select member0_.member_id as member_i1_0_, member0_.age as age2_0_, member0_.team_id as team_id4_0_, member0_.username as username3_0_ from member member0_ order by member0_.age asc limit 3 offset 2;

 

 

4. 결론

  • 두 쿼리를 비교해 보면, Page는 조회 쿼리 이후 전체 데이터 갯수를 한번 더 조회하는 카운트 쿼리가 실행된다.

//Page에만 존재
select
    count(member0_.member_id) as col_0_0_ 
from
    member member0_

 

  • Slice는 limit(size)+1 된 값을 가져오는것을 확인할 수 있다.
//Page
limit 2 offset 2;

//Slice
limit 3 offset 2;

 

 

특징 Page Slice
전체 데이터 개수 제공 제공 (getTotalElements(), getTotalPages()) 제공하지 않음
추가적인 페이징 정보 제공 (전체 페이지 수, 전체 데이터 개수 등) 제공하지 않음
성능 성능이 상대적으로 느림
(두 개의 쿼리 실행: 데이터 조회 + 전체 개수 조회)
성능이 좋음
(Only 데이터조회 쿼리 실행)
다음 페이지 존재 여부 제공 (hasNextPage()) 제공 (hasNext())
사용 적합 상황 - 전체 데이터 개수나 페이지 수가 필요한 경우
- 페이지 하단에 "전체 페이지 수"
  또는 "총 데이터 개수" 표시 필요
- 성능이 중요한 대용량 데이터 처리
- 무한 스크롤 또는 "더보기" 기능 구현
단점 - 쿼리 성능이 느려질 수 있음 (두 개의 쿼리 실행) - 전체 데이터 수나 페이지 수 정보를
  제공하지 않음