게시판이나 댓글, 블로그를 개발할 때 페이징은 아주 중요한 역할을 합니다.
페이징은 많은 정보, 이를테면 게시판에 존재하는 수백 수천개의 게시글과 같은 정보들을 페이지로 나눠 효과적으로 정보를 제공하게 하는 역할을 한다.
이러한 페이징을 개발하기 위해서는 page 관련 쿼리를 파라미터로 받아서 직접 처리하는 방법이 있었지만 JPA에서 또 Spring Data 프로젝트에서는 효과적으로 페이징을 처리할 수 있게 방법을 제공한다.
JpaRepository 인터페이스의 상속 다이어그램을 살펴보자.🤔
JpaRepository의 부모 인터페이스인 PagingAndSortingRepository에서 페이징과 소팅이라는 기능을 제공한다.
findAll() 메서드의 반환 타입과 파라미터를 보면
Pageable 인터페이스로 파라미터를 넘기면 페이징을 사용할 수 있게된다.
PageRequest pageRequest = PageRequest.of(offset, limit, Sort.by(direction,"updatedDate"));
return quizRepository.findByMemberId(memberId, pageRequest);
PageRequest 객체는 Pageable 인터페이스를 상속받는다. 이 정보에는 정렬 정보, 페이지 offset, page와 같은 정보가 담겨있다.
아래와 같이 사용자의 식별자로 쿼리 메서드를 만들고 두 번째 파라미터로 Pageable을 넘겨줄 수 있다.
public Slice<Quiz> findAll(Pageable pageable);
public Slice<Quiz> findByMemberId(Long memberId, Pageable pageable);
Spring Data JPA에는 반환 타입에 따른 각기 다른 결과를 제공한다.
Page<T>
타입Slice<T>
타입List<T>
타입Page<T>
타입을 반환 타입으로 받게 된다면 offset과 totalPage를 이용하여 서비스를 제공할 수 있게된다.
Page<T>
는 아래와 같은 일반적인 게시판 형태의 페이징에서 사용된다.
여기서 특히 중요한 정보는 총 페이지의 수 이다.
Page<T>
는 총 페이지 수와 같은 totalPage를 포함하여 반환한다.
Page<T>
타입은 count 쿼리를 포함하는 페이징으로 카운트 쿼리가 자동으로 생성되어 함께 나간다.
Paging에 대한 결과
"content":[
{
"content": [
{"id": 1, "username": "User 0", "address": "Korea", "age": 0},
// 중간 생략
{"id": 5, "username": "User 4", "address": "Korea", "age": 4}
],
"pageable": {
"sort": {
"sorted": false, // 정렬 상태
"unsorted": true,
"empty": true
},
"pageSize": 5, // 한 페이지에서 나타내는 원소의 수 (게시글 수)
"pageNumber": 0, // 페이지 번호 (0번 부터 시작)
"offset": 0, // 해당 페이지에 첫 번째 원소의 수
"paged": true,
"unpaged": false
},
"totalPages": 20, // 페이지로 제공되는 총 페이지 수
"totalElements": 100, // 모든 페이지에 존재하는 총 원소 수
"last": false,
"number": 0,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"size": 5,
"numberOfElements": 5,
"first": true,
"empty": false
}
Slice<T>
타입을 반환 타입으로 받게 된다면 더보기 형태의 페이징에서 사용된다.
Slice에 대한 결과
{
"content": [
{ "id": 13, "username": "User 12", "address": "Korea", "age": 12 },
{ "id": 14, "username": "User 13", "address": "Korea", "age": 13 },
{ "id": 15, "username": "User 14", "address": "Korea", "age": 14 },
{ "id": 16, "username": "User 15", "address": "Korea", "age": 15 }
],
"pageable": {
"sort": { "sorted": false, "unsorted": true, "empty": true },
"pageNumber": 3,
"pageSize": 4,
"offset": 12,
"paged": true,
"unpaged": false
},
"number": 3,
"numberOfElements": 4,
"first": false,
"last": false,
"size": 4,
"sort": { "sorted": false, "unsorted": true, "empty": true },
"empty": false
}
Page<T>
타입에는 존재하던 totalPages, totalElements가 없어지고 number과 numberOfElements가 존재한다.
이와같이 Slice<T>
타입은 추가 count 쿼리 없이 다음 페이지 확인 가능하다. 내부적으로 limit + 1 조회를 해서 totalCount 쿼리가 나가지 않아 성능상 약간의 이점을 볼 수도 있다.
List반환 타입은 가장 기본적인 방법으로 count 쿼리 없이 결과만 반환한다.