添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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이런 식으로 요청하면 된다!