Spring Pageable, PageRequest, Page<T> 개념

최원석·2025년 4월 16일
post-thumbnail

☘️ Spring Pageable, PageRequest, Page 개념

Pageable, PageRequest, Page Spring 에서 개발을 하다보면 한번 쯤 본적있는 코드?
한번 쯤 본적있는 코드가 아니라 제대로 알고 쓰기위해서 글을 적어본다.

💫 Pageable, PageRequest, Page

Page, Pageable, PageRequest는 Spring Data JPA의 페이징 기능을 위한 핵심 3요소이다.

📁 Pageable

페이징 요청 정보 인터페이스

클라이언트 에서 어떤 페이지를 보고 싶은지 전달할 때 쓰는 인터페이스이다.

page, size, sort 같은 정보를 담고 있다.

 ❗️ ex) Pageable pageable = PageRequest.of(0, 10); // 0번째 페이지, 10개

🔎 code

package org.springframework.data.domain;

import java.util.Optional;
import org.springframework.util.Assert;

public interface Pageable {
    static Pageable unpaged() {
        return unpaged(Sort.unsorted());
    }

    static Pageable unpaged(Sort sort) {
        return Unpaged.sorted(sort);
    }

    static Pageable ofSize(int pageSize) {
        return PageRequest.of(0, pageSize);
    }

    default boolean isPaged() {
        return true;
    }

    default boolean isUnpaged() {
        return !this.isPaged();
    }

    int getPageNumber();

    int getPageSize();

    long getOffset();

    Sort getSort();

    default Sort getSortOr(Sort sort) {
        Assert.notNull(sort, "Fallback Sort must not be null");
        return this.getSort().isSorted() ? this.getSort() : sort;
    }

    Pageable next();

    Pageable previousOrFirst();

    Pageable first();

    Pageable withPage(int pageNumber);

    boolean hasPrevious();

    default Optional<Pageable> toOptional() {
        return this.isUnpaged() ? Optional.empty() : Optional.of(this);
    }

    default Limit toLimit() {
        return this.isUnpaged() ? Limit.unlimited() : Limit.of(this.getPageSize());
    }

    default OffsetScrollPosition toScrollPosition() {
        if (this.isUnpaged()) {
            throw new IllegalStateException("Cannot create OffsetScrollPosition from an unpaged instance");
        } else {
            return this.getOffset() > 0L ? ScrollPosition.offset(this.getOffset() - 1L) : ScrollPosition.offset();
        }
    }
}

주요 함수

  • int getPageNumber(); - 몇 번째 페이지인지
  • int getPageSize(); - 한 페이지에 몇 개 담을지
  • Sort getSort(); - 정렬 조건

📁 PageRequest

Pageable 구현체

Pageable 인터페이스를 구현한 구현체로

페이징 요청을 만들기 위해 항상 사용하는 클래스이다.

 ❗️ ex) PageRequest pageRequest = PageRequest.of(0, 20, [Sort.by](http://sort.by/)("createdAt").descending());

🔎 code

package org.springframework.data.domain;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

public class PageRequest extends AbstractPageRequest {
    private static final long serialVersionUID = -4541509938956089562L;
    private final Sort sort;

    protected PageRequest(int pageNumber, int pageSize, Sort sort) {
        super(pageNumber, pageSize);
        Assert.notNull(sort, "Sort must not be null");
        this.sort = sort;
    }

    public static PageRequest of(int pageNumber, int pageSize) {
        return of(pageNumber, pageSize, Sort.unsorted());
    }

    public static PageRequest of(int pageNumber, int pageSize, Sort sort) {
        return new PageRequest(pageNumber, pageSize, sort);
    }

    public static PageRequest of(int pageNumber, int pageSize, Sort.Direction direction, String... properties) {
        return of(pageNumber, pageSize, Sort.by(direction, properties));
    }

    public static PageRequest ofSize(int pageSize) {
        return of(0, pageSize);
    }

    public Sort getSort() {
        return this.sort;
    }

    public PageRequest next() {
        return new PageRequest(this.getPageNumber() + 1, this.getPageSize(), this.getSort());
    }

    public PageRequest previous() {
        return this.getPageNumber() == 0 ? this : new PageRequest(this.getPageNumber() - 1, this.getPageSize(), this.getSort());
    }

    public PageRequest first() {
        return new PageRequest(0, this.getPageSize(), this.getSort());
    }

    public boolean equals(@Nullable Object obj) {
        if (this == obj) {
            return true;
        } else if (!(obj instanceof PageRequest)) {
            return false;
        } else {
            PageRequest that = (PageRequest)obj;
            return super.equals(that) && this.sort.equals(that.sort);
        }
    }

    public PageRequest withPage(int pageNumber) {
        return new PageRequest(pageNumber, this.getPageSize(), this.getSort());
    }

    public PageRequest withSort(Sort.Direction direction, String... properties) {
        return new PageRequest(this.getPageNumber(), this.getPageSize(), Sort.by(direction, properties));
    }

    public PageRequest withSort(Sort sort) {
        return new PageRequest(this.getPageNumber(), this.getPageSize(), sort);
    }

    public int hashCode() {
        return 31 * super.hashCode() + this.sort.hashCode();
    }

    public String toString() {
        return String.format("Page request [number: %d, size %d, sort: %s]", this.getPageNumber(), this.getPageSize(), this.sort);
    }
}

📁 Page

페이징된 결과 객체

Spring Data JPA에서 findAll(Pageable pageable) 같은 메서드로 조회하면 반환되는 결과

내부에 데이터 리스트 + 페이징 메타데이터가 들어 있음

❗️ ex)

Page<Post> postPage = postRepository.findAll(pageRequest);

List<Post> posts = postPage.getContent(); int totalPages = postPage.getTotalPages();

🔎 code

package org.springframework.data.domain;

import java.util.Collections;
import java.util.function.Function;

public interface Page<T> extends Slice<T> {
    static <T> Page<T> empty() {
        return empty(Pageable.unpaged());
    }

    static <T> Page<T> empty(Pageable pageable) {
        return new PageImpl(Collections.emptyList(), pageable, 0L);
    }

    int getTotalPages();

    long getTotalElements();

    <U> Page<U> map(Function<? super T, ? extends U> converter);
}

주요함수

  • List<T> getContent(); - 실제 데이터 리스트
  • int getTotalPages(); - 전체 페이지 수
  • long getTotalElements(); - 전체 데이터 수
  • int getNumber(); - 현재 페이지 번호
  • boolean hasNext(); - 다음 페이지 존재 여부
  • boolean isFirst(); - 첫 페이지 여부

0개의 댓글