[JPA] Page vs Slice
개요
페이징을 처리하면서 Page, Slice, Window의 차이에 대해 명확하게 이해하고 넘어가고자 합니다.
TestEntityRepository 준비
interface TestEntityRepository: JpaRepository<TestEntity, UUID> {
fun findPageBy(pageable: Pageable): Page<TestEntity>
fun findSliceBy(pageable: Pageable): Slice<TestEntity>
}
Page
Page는 일반적인 게시판 형태의 페이징에서 많이 활용됩니다.
예시 코드
@DataJpaTest
class TestEntityTest @Autowired constructor(
private val testEntityRepository: TestEntityRepository,
){
@PostConstruct
fun `데이터 세팅`(){
repeat(1000){
testEntityRepository.save(TestEntity(name = it.toString()))
}
}
@Test
fun `Page 객체로 조회한다`(){
val pageRequest = PageRequest.of(3,10)
val actual: Page<TestEntity> = testEntityRepository.findPageBy(pageRequest)
println(actual)
}
}
1000개의 데이터를 세팅하고 testEntityRepository에서 구현한 findPageBy()을 활용하면 PageRequest객체를 넘기고 Page객체로 반환받을 수 있습니다.
실제 쿼리
Hibernate:
select
t1_0.id,
t1_0.name
from
test_entity t1_0 offset ? rows fetch first ? rows only
Hibernate:
select
count(t1_0.id)
from
test_entity t1_0
count 쿼리를 날려서 전체 페이지의 개수를 가져오고 총 몇 페이지가 존재하고 현재는 몇 페이지인지 계속 체크하면서 동작합니다.
Page의 단점
1000개의 데이터가 있다고 가정했을때 offset이 900이고 limit이 10이라면 900부터 10개의 데이터만 가져올 것이라 예상할 수 있습니다.
하지만 910개의 데이터를 모두 DB에서 불러오고 limit만큼만 우리에게 반환해주기 때문에 900개의 데이터는 읽기만 하고 버리게 됩니다.
페이지가 뒤로 이동할수록 읽어야 할 행의 수는 기하급수적으로 늘어나기 때문에 성능을 고려해주어야 합니다.
추가적으로 Count 쿼리를 날리게 되는데 100만건의 데이터가 있을 때 MyISAM의 경우에는 row 수가 테이블의 저장되므로 O(1)의 시간복잡도를 가지지만 InnoDB는 full scan을 수행하게 됩니다.
Slice
Slice는 일반적으로 무한스크롤을 구현할때 활용됩니다.
코드 예시
@Test
fun `Slice 객체로 조회한다`(){
val pageRequest = PageRequest.of(3,10)
val actual: Slice<TestEntity> = testEntityRepository.findSliceBy(pageRequest)
println(actual)
}
실제 쿼리
Hibernate:
select
t1_0.id,
t1_0.name
from
test_entity t1_0 offset ? rows fetch first ? rows only
Slice 3 containing com.example.study.entity.TestEntity instances
count 쿼리를 날리지 않고 다음 페이지만 확인 가능합니다.
Slice의 단점
count 쿼리를 날리지 않을 뿐 offset기반의 단점은 그대로 가지고 있습니다.
Slice가 다음페이지를 판단하는 방법
query를 binding하는 logging을 추가하고 보면 size가 10인경우 limit을 11까지 조회합니다.
즉, 11개가 조회된다면 다음 페이지가 있다고 판단합니다.
만약 10개이하라면 다음 페이지가 없다고 판단합니다.
Slice를 활용하여 offset을 사용하지 않는 페이징 구현하기
참고자료
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.special-parameters