현재 2개의 프로젝트 진행하고 있다.
두 프로젝트 모두 글 또는 영상을 페이징해서 프론트에 뿌려주는 API가 필요했다.
그래서 이번에 PageRequest를 사용해 페이징 하는 것을 공부했고 공유하고자 한다.
사용해보면서 느끼고 배운 지식이라 잘못된 내용이 있을 수 있습니다.
피드백 부탁드립니다.
DB에 저장된 Entity들을 페이지로 나누는 것이다.
예를들어, DB에 책이 20권 저장되어있다.
프론트에서 "DB에 있는 책을 5권씩 분류해서, 두 번째 파트를 줘!" 라고 요청한다.
그러면 백엔드에서는 5권씩 분류하고, 분류된 책들의 두 번째 파트를 프론트에게 넘겨준다.
위 상황과 같이, 일정 갯수만큼 분류하고, 분류된 부분들 중 어떤 부분을 보내주는 것이 JPA Paging이다.
사용법은 매우 간단하다.
repository의 findAll 메서드의 parameter에 Pageable 또는 Pageable의 구현체인 PageRequest를 넣어주면 된다.
인터넷을 찾아보면 Controller에서 Pageable을 받아서 사용하는 코드들이 있는데 따라서 해보니 안된다.....
그래서 내가 이번에 구현한 PageRequest를 사용하는 방법만 소개한다.
몇 페이지, 한 페이지의 사이즈, Sorting 방법(Option)을 가지고,
Repository에 Paging을 요청할 때 사용하는 것
위와 같이 인터페이스인 Pageable과 Serializable을 implements하는 AbstractPageRequest라는 추상 클래스가 있다.
그리고 PageRequest class는 이 AbstractPageRequest를 상속한다.
PageRequest의 생성에는 찾을 page와 한 페이지의 size를 필수 인자로 받는다.
그리고 정렬해서 paging을 하는 경우에, Sort를 생성자 인자로 추가해서 PageRequest를 생성할 수 있다.
아래는 PageRequest class의 생성자 코드.
현재 다른 생성자들은 deprecated되고,
가장 아래 코드에서 보이는 정적 팩토리 메서드로 생성해야 한다. (of 메서드)
Repository
에서 findAll
메서드를 살펴보면 위와 같이 Pageable
을 인자로 줄 수 있다.
PageRequest
는 Pageable 클래스를 implements한 AbstractPageReqeust 추상 클래스의 구현체이므로 findAll의 인자로 넣을 수 있다.
따라서 page,size,sort(option)으로 PageRequest를 생성하고,
Repository의 findAll 메서드의 인자에 PageRequest를 넣어주면 된다.
그러면 반환은 Page이 된다.
코드를 보면서 알아보자.
다른 복잡한 것들은 생략하고 설명에 필요한 구조는 다음과 같다.
Package - Class Name
controller - BookController
service - BookService
repository - BookRepository
domain - Book
몇 번째 페이지인지 의미하는page
와 한 페이지의 사이즈를 의미하는size
를 controller에 주고 해당 페이지의 Book들을 가져오는 API 구현하기
/paging URL에 RequestParam으로page
와size
가 주어짐
// BookController class
@GetMapping("/paging")
public List<Book> findBooksByPageRequest(@RequestParam Integer page, Integer size) {
return bookService.findBooksByPageRequest(page,size);
}
// BookService class
public List<Book> findBooksByPageRequest(Integer page, Integer size) {
PageRequest pageRequest = PageRequest.of(page,size);
return bookRepository.findAll(pageRequest).getContent();
}
// BookService class
public List<Book> findBooksByPageRequest(Integer page, Integer size) {
PageRequest pageRequest = PageRequest.of(page, size, Sort.by("createdAt").descending());
return bookRepository.findAll(pageRequest).getContent();
}
PageRequest의 마지막 인자로 Sort를 추가하면 된다.
작성자는 Book을 추가한 날짜를 Book에 createdAt 이라고 했기 때문에 Sort.by의 인자에 "createdAt"을 넣었다.
그리고 최신순으로 Paging해야 하므로 내림차순으로 정렬해야한다. 따라서 descending을 해줬다.
(2019. 8.25 추가 내용)
위의 내용대로 page, size을 컨트롤러에서 인자로 받도록 구현했더니 다음과 같은 피드백이 왔다.
< 우아한 형제들 개발자 강현구님의 피드백>
(피드백 받은 코드는 다른 프로젝트에서 똑같이 jpa paging한 것)
내가 했던 방법의 문제점
위와 같은 문제점이 있다는 것을 피드백 받고 PageRequest를 클라이언트에서 전달 받도록 수정했다.
// BookController class
@GetMapping("/paging")
public Page<Book> findBooksByPageRequest(final Pageable pageable) {
return bookService.findBooksByPageRequest(pageable);
}
// BookService class
public Page<Book> findBooksByPageRequest(Pageable pageable) {
return bookRepository.findAll(pageable);
}
직접 써보면서, 해당 클래스들(PageRequest, Pageable 등등)에 직접 들어가보면서 알아본 내용들을 토대로 했기 때문에 내용이 부실할 수 있습니다.
본 프로젝트에는 ResponseEntity와 DTO를 사용했지만, 보기 쉽도록 생략했음을 알려드립니다.
궁금한 점, 피드백 언제나 환영합니다!
이미지 참고 : http://www.shangyan.site/2018-02-13-spring-boot-jpa-2/
Pagerequest 를 컨트롤러에서 받으려면 클라이언트에서는 어떻게 작성해야 할까요 ?ㅜㅜ
클라이언트는 vue를 사용중인데 vue에서 어떻게 Pagerequest 를 작성해서 보내야 되는지 감이 안잡히네요