Spring Boot | PageRequest
Spring Data JPA를 사용해 페이징을 하려는데, 컨트롤러에서 바로
Pageable
을 받고싶지는 않았다..
Pageable
사실 Pageable
을 이용하면 정말 쉽게 바로 받아진다.
import org.springframework.data.domain.Pageable;
@RestController
@RequestMapping("api/v1/orders")
public class OrderController {
@GetMapping()
public Response getOrders(final Pageable pageable){
// ...
}
}
api/v1/orders?page=0&size=20&sort=id,desc
이렇게 요청하면, 쿼리 스트링이 바로 pageable
에 매핑된다.
PageRequest
하지만 Pageable
인터페이스를 구현한 PageRequest
라는 객체가 있다.
package org.springframework.data.domain;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Basic Java Bean implementation of {@link Pageable}.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Anastasiia Smirnova
* @author Mark Paluch
*/
public class PageRequest extends AbstractPageRequest {
private static final long serialVersionUID = -4541509938956089562L;
private final Sort sort;
// 생략...
컨트롤러를 수정(pageable → pagerequest)한 다음,
동일하게 api/v1/orders?page=0&size=20&sort=id,desc
요청하면 기본 생성자가 없다는 에러가 발생한다.
No primary or single public constructor found for class org.springframework.data.domain.PageRequest
import org.springframework.data.domain.PageRequest;
@RestController
@RequestMapping("api/v1/orders")
public class OrderController {
@GetMapping()
public Response getOrders(final PageRequest pagerequest){
// ...
}
}
PageRequest를 만들자!
PageRequest
가 있다는 걸 알게된 이상.. 컨트롤러에서 바로 Pageable
을 사용하고 싶지는 않고..
그렇다고 각각 @RequestParam
으로 받고 싶지도 않았다.
이 글 (Spring Data JPA를 활용한 페이징 API 만들기)을 참고해서 따로 PageRequest
를 작성하기로 한다.
common패키지를 생성한 후 PageRequest
클래스를 작성해주었다.
@Getter
public class PageRequest {
private int page;
private int size;
private List<String> sort;
public void setPage(int page) {
this.page = page <= 0 ? 1 : page;
}
public void setSize(int size) {
int DEFAULT_SIZE = 10;
int MAX_SIZE = 50;
this.size = size > MAX_SIZE ? DEFAULT_SIZE : size;
}
public void setSort(List<String> sort) {
this.sort = sort;
}
}
sort
항목을 문자열로 받아서 다시 Sort
객체로 변환해야 한다.
처음에는
&sort=fieldname,asc
이런식으로 pageable사용법과 동일하게 하려다가,
requestParam은 중간에 콤마가 있으면 자동으로 배열이나 리스트로 변환해주기 때문에 sort항목이 하나만 올 경우 fieldname과 asc가 각각 나뉘어져 버리는 문제 때문에 콤마를 사용해 구분하지 않기로 했다.
콤마 대신 #
을 이용해서 필드명과 정렬 방식을 구분하도록 했다.PageRequest
는 public생성자가 없기 때문에 of()
메서드를 호출하여 반환해야 한다.Pageable
의 page는 0부터 시작하는데, 1부터 받고 있어서 -1 해주었음.
public org.springframework.data.domain.PageRequest of() {
if (sort == null || sort.isEmpty()) {
return org.springframework.data.domain.PageRequest.of(page - 1, size);
}
return org.springframework.data.domain.PageRequest.of(page - 1, size, Sort.by(getOrders(sort)));
}
private List<Sort.Order> getOrders(List<String> sort) {
List<Sort.Order> orders = new ArrayList<>();
sort.forEach(str ->
orders.add(
new Sort.Order(Sort.Direction.valueOf(str.split("#")[1].toUpperCase(Locale.ROOT)),
str.split("#")[0])
)
);
return orders;
}
sort항목의 값이 &sort=fieldname#desc
와 같은 형식이 지켜지지 않으면 에러가 발생하므로, 유효성 검사를 해주기로 한다.
spring-boot-starter-validation
가 필요하다.
private List<@Pattern(regexp = "\\w*#+(desc|DESC|asc|ASC)\\Z",
message = "정렬하고자 하는 필드 명과 정렬방향을 정확히 입력하세요.") String> sort;
이제 /api/v1/orders?page=1&size=20&sort=createdAt#desc&sort=name#asc
이런 식으로 요청하면 된다!