[JPA] Spring Data JPA를 이용한 Pagination

Jayong·2025년 10월 19일

JPA

목록 보기
3/3
post-thumbnail

Woori.log 홈화면의 블로그 및 프로젝트들을 제공하는 API를 확장하여, 페이지네이션(Pagination) 을 통해 모든 블로그 및 프로젝트들을 사용자들이 확인할 수 있도록 기획 방향이 정해졌다.
이에 Spring Data JPA를 통해 쉽게 Pagination을 구현하고 정리해보려 한다.

Pageable

Spring에서 제공하는 인터페이스로, 페이지에 대한 정보를 담고있는 객체이다.

  • getPageNumber() : 현재 페이지 번호 (0부터 시작)
  • getPageSize() : 한 페이지당 최대 항목 수
  • getSort() : 정렬 정보를 반환

핵심적인 메서드는 위와 같으며, Page에 대한 다양한 정보를 얻을 수 있다.

PageRequest

Pageable의 구현체 중 하나로, 페이지 정보를 생성하는 클래스이다.
페이지 번호, 페이지당 항목 수, 정렬 정보를 지정하여 Pageable인터페이스를 구현할 수 있다.

  • page : 조회할 페이지 번호 (0부터 시작)
  • size : 한 페이지 당 최대 항목 수
  • sort : 정렬 기준 (Optional)
  • direction : 정렬 방향 (ASC, DESC)
  • properties : 정렬 대상 속성명

Pageable 객체를 JpaRepository에 매서드 파라미터로 전달하면 Page 객체를 반환한다.
이를 이용하여 Pagination을 구현할 수 있다.


Woori.log에는 어떻게 적용하였는지 알아보자.

	@Cacheable(value = CacheNames.HOME_BLOGS)
    public BlogHomeRes getBlogBasicInfos(int page) {

        Page<Blog> blogPage = blogRepository.findAll(
                PageRequest.of(page - 1, BLOG_PAGE_SIZE, Sort.by(SORT_CRITERIA).descending())
        );

        List<BlogBasicInfoDto> blogBasicInfoDtoList = blogPage.stream()
                .map(BlogBasicInfoDto::create)
                .toList();

        return BlogHomeRes.of(blogPage.getNumber() + 1, blogPage.getTotalPages(), blogBasicInfoDtoList);
    }

page를 파라미터로 전달받은 뒤, PageRequest.of 메서드를 통해 Pageable 객체를 생성한 뒤, findAll의 메서드 파라미터로 전달하고 있다.


PageRequest.of(page - 1, BLOG_PAGE_SIZE, Sort.by(SORT_CRITERIA).descending());

  • page를 1부터 전달받으므로, -1을 해줌으로써 페이지를 맞춰준다.
  • BLOG_PAGE_SIZE : 조회할 사이즈를 지정한다. (현재 6으로 지정하였다.)
  • Sort.by(SORT_CRITERIA) : 정렬 기준을 설정하는 것으로, 현재는 생성일인 createdAt을 기준으로 내림차순 정렬하였다. (최신 블로그)

프로젝트의 경우도 동일한 방식으로 로직을 구현하여 페이지네이션을 적용하였다.


JPA에서 Pagination의 동작 방식

JPA에서 Pageable을 통해 페이지네이션을 적용할 경우 Offset을 이용해 쿼리문을 작성하게 된다.
이를 offset-based pagination이라 한다.

offset-based pagination

  • limit : size, 조회할 record의 수를 지정
  • offset : 건너뛸 개수
    위 두가지를 통해 페이지를 매기는 방식이다.
 SELECT * FROM Blog LIMIT 10 OFFSET 0; // 0 페이지

위와 같은 쿼리를 통해 데이터를 조회하게 된다.
Offset-based pagination의 경우 offset이 커질 경우, 성능이 나빠지는 단점이 존재하는데, 이는 추후 cursor-based pagination을 소개하며 설명할 예정이다.

LIMITOFFSET을 이용한 쿼리로 페이지네이션을 구현하여 Page 객체를 제공하며, 해당 Page 객체에서 전체 페이지 수, 현재 페이지 수, 현재 페이지의 요소의 갯수 등의 정보를 얻을 수 있다.

확장

적용예시와 같이 간단하게 생성일 기준으로 페이지네이션을 구현할 수도 있지만, 다양한 조건과 정렬기준을 통해 폭넓게 활용 가능하다.

예시로, 내가 작성한 글중에서 댓글이 많이 달린 블로그를 기준으로 정렬할 수 있을 것이다.

    public BlogRes getBlogAtMyPage(Long userId, int page) {
    
        Page<Blog> blogPage = blogRepository.findAllByAuthorId(
                userId, 
                PageRequest.of(page - 1, BLOG_PAGE_SIZE, Sort.by("commentCount").descending())
        );

        List<BlogBasicInfoDto> blogBasicInfoDtoList = blogPage.stream()
                .map(BlogBasicInfoDto::create)
                .toList();

        return BlogRes.of(blogPage.getNumber() + 1, blogPage.getTotalPages(), blogBasicInfoDtoList);
    }

마무리

PageablePage, PageRequest에 대해 정리하며 JPA에서 제공하는 페이지네이션 관련 객체들의 메서드와 쿼리, 작동원리에 대해 학습할 수 있어 추후 JPA를 사용하지 않을 때의 페이지네이션 구현시에 많은 도움이 될 것같다.
또한, offset-based Pagination의 특징을 알아보며, 장단점을 고려하며 도입해야함을 인지하였고, 추후에는 기회가 된다면 cursor-based Pagination도 적용해보고 싶다.

참고

[JPA] Spring Data Jpa + Pageable로 Pagination 쉽게 구현하기

profile
다양한 경험을 추구하는 개발자

0개의 댓글