Spring
Spring Pagination으로 무한스크롤 구현
- -
반응형
Pagination 하는법
1. 파라미터를 Pageable 객체로 받거나 sort, page, size를 따로 받기.
1-1. 파라미터를 Pageable 객체로 받기
@GetMapping("/api/posts")
public Page<Post> getPosts(@RequestParam String category ,
Pageable pageable) {
...생략
}
- Pageable 객체로 받을 때 주의할 점은 @RequestParam 없이 받아야한다.
- @RequestParam을 넣었더니 status code 400 Bad Request 에러발생!
- pageable을 출력하면 어떻게 출력될까?
- ?category=ALL&page=0&size=5&sort=default
- 참고로 controller단에서 sort를 따로 받는 코드였는데 아래와 같이 sort : default로 담긴다.
-
@GetMapping("/api/posts") public Page<Post> getPosts(@RequestParam String sort, @RequestParam String category , Pageable pageable) { ...생략 }
- Page request [number: 0, size 5, sort: default: ASC]
- ?category=ALL&page=0&size=5
- sort 파라미터가 없다면 UNSORTED로 담긴다.
- Page request [number: 0, size 5, sort: UNSORTED]
- ?category=ALL&page=0&size=5&sort=createdAt
- 만약 정렬기준이 없이 sort만 파라미터에 있다면 Default값은 ASC 오름차순이다.
- Page request [number: 0, size 5, sort: createdAt: ASC]
- ?category=ALL&page=0&size=5&sort=CREATED_AT,DESC
- 만약 정렬기준을 DESC로 설정하고 싶다면 ,DESC로 추가해준다.
- Page request [number: 0, size 5, sort: CREATED_AT: DESC]
- ?category=ALL&page=0&size=5&sort=default
1-2. 파라미터를 sort, page, size를 따로 받기.
- postController.java
@GetMapping("/api/posts") public Page<GetPostsResponseDto> getPosts(@RequestParam String sort, @RequestParam String category , @RequestParam int page, @RequestParam int size) { return postService.getPosts(sort, category, page, size); }
- postrepository를 커스텀할 때 sort와 category가 필요하다. 그래서 sort부분을 따로 받아주기위해 sort, page, size를 따로 받았다.
2. Service단에서 PageRequest.of메소드로 Pageable객체 생성
- postService.java
public Page<GetPostsResponseDto> getPosts(@RequestParam String sort, @RequestParam String category, @RequestParam int page, @RequestParam int size) { Pageable pageable = PageRequest.of(page,size); return postRepositoryImpl.findAllByCategoryOrderBySort(sort, category, pageable); }
- Pageabele 객체를 생성한 pageable을 출력해봤다.
- Page request [number: 0, size 5, sort: UNSORTED]
- page와 size만 들어가서 sort는 UNSORTED가 담긴다.
- 참고로 반환타입은 Page다. 반환타입은 Slice, Page, List 3가지가 있다.
3. postRepository 커스텀 클래스에서 return해줄 때 new PageImpl<>()해서 반환!
- postRepositoryImpl.java
@Override public Page<GetPostsResponseDto> findAllByCategoryOrderBySort(String sort, String category, Pageable pageable) { List<GetPostsResponseDto> returnPost = queryFactory.select(Projections.fields( GetPostsResponseDto.class, post.title, post.category, post.deadline, post.numberPeople, post.currentNumberPeople, post.contactMethod, post.viewCount, post.user.nickname, post.imageUrl )) .from(post) .where(categoryContains(category)) .orderBy(orderByValidDeadline(),getOrderSpecifier(sort)) .fetch(); return new PageImpl<>(returnPost,pageable,returnPost.size()); }
- 여기서 가장 중요한 부분은 return부분이다.
- return new PageImpl<>(returnPost,pageable,returnPost.size());
- PageImpl<>(select 결과값, 생성된 pageable 객체, select 결과값의 크기);
결과값
{
"content": [
{
"title": "amo",
"category": "DELIVERY",
"deadline": 1663243762507,
"numberPeople": 9,
"currentNumberPeople": 4,
"viewCount": 65,
"nickname": "123",
},
{
"title": "dpu",
"category": "EXHIBITION",
"deadline": 1660593585499,
"numberPeople": 4,
"currentNumberPeople": 3,
"viewCount": 40,
"nickname": "123",
},
...
],
"pageable": {
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"offset": 0,
"pageNumber": 0,
"pageSize": 5,
"paged": true,
"unpaged": false
},
"last": false,
"totalElements": 99,
"totalPages": 20,
"size": 5,
"number": 0,
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"first": true,
"numberOfElements": 99,
"empty": false
}
- 결과값을 보면 자동으로 select결과값은 contents로 싸여지고 자동으로 "pagealbe", "last", "totalElements"..등이 생성된다.
- last(boolean타입) : 이 페이지가 마지막인가?
- totalElements(int타입) : 요소의 총 수, 즉 contents의 크기 혹은 길이
- totalPages : 만들 수 있는 페이지 총 수
- size(int타입) : 페이지 당 나타낼 수 있는 요소 수(참고로 default : 20)
- number(int타입) : 현재 페이지번호
- first(boolean타입) : 첫 번째 페이지인가?
- numberOfElements(int타입) : 실제 데이터 개수
- empty(boolean타입) : 리스트가 비어있가?
- 참고로 페이지 시작은 0부터 시작이다!
Slice, Page, List 차이점은?
Slice<User> findByLastname(String lastname, Pageable pageable);
Page<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
Slice
- Slice는 Page에서 카운트 쿼리에 많은 비용이 발생하는 경우에 Slice를 사용하면 된다.
- Slice는 다음 Slice가 존재하는지 여부만 알기 때문에 전체 데이터의 셋의 크기가 큰 경우에는 Slice를 사용하는 것이 성능상 유리하다.
- 무한스크롤에 적합하다.
Page
- page는 사용 가능한 데이터의 총 개수 및 전체 페이지 수를 알 수 있다.
- 총 개수를 알아내기 위해 추가적으로 카운트 쿼리가 실행된다.
- 기본적으로 카운트 쿼리는 실제로 실행되는 쿼리에서 파생된다.
List
- Pageable을 통해서 정렬을 할 수 있지만, 정렬만 하는 경우 Sort를 사용하는 것이 좋다.
- 결과를 단순히 List로 받을 수 있다.
- 이 경우 Page 인스턴스를 생성하기 위한 메타데이터가 생성되지 않기 때문에 카운트 쿼리가 실행되지 않는다.
- 단순히 주어진 범위내의 엔티티를 검색하기 위한 쿼리만 실행된다.
참고 블로그
- https://velog.io/@dltkdgns3435/SpringBoot-Spring-Data-JPA-%EC%97%90%EC%84%9C-Page%EC%99%80-Slice
- 성능비교하는 것이 인상적이었다.
- https://n1tjrgns.tistory.com/263
참고자료
- Pageable을 이용한 Pagination을 처리하는 다양한 방법
- 기본적인 내용들이 잘 나타나있음.
- https://tecoble.techcourse.co.kr/post/2021-08-15-pageable/
- Pageable 사용하기
- 기본적인 내용들이 잘 나타나있음.
- https://jiyongpark-dev.tistory.com/15
- [JPA] Paging
- 기본적인 내용들이 잘 나타나있음.
- https://tmdrl5779.tistory.com/61
- [JPA] Querydsl에 pageable을 적용하며... 2가지 방법을
- Page객체뿐만 아니라 Slice 객체도 사용하는 예제가 있음.
- https://jessyt.tistory.com/55
- [일지] List 를 pageable 과 PageImpl 로 구현하기 ( List to pageImpl )
- [Spring Boot] JPA + Pageable 을 이용한 페이징 처리
- PagingAndSortingRepository의 상속관계를 잘 알 수 있음.
- https://ibks-platform.tistory.com/277
- [QueryDSL] Page와 Slice
- [Querydsl, pageable] slice를 이용한 무한 스크롤
작성일자 : 2022년 7월 26일
반응형
Contents
소중한 공감 감사합니다