노트

페이지네이션 (offset·cursor)

Pagination (Offset and Cursor)

백엔드#db#http · 연결된 개념 7개

쉽게 말하면

페이지네이션은 긴 목록을 한 번에 주지 않고 조각으로 나눠 주는 방법이에요. '37쪽 펴기'처럼 쪽수로 찾아가는 오프셋 방식과, 책갈피 꽂은 곳 다음부터 읽는 커서 방식이 있죠.

비유의 한계 책과 달리 목록은 읽는 동안 새 항목이 끼어들어요. 오프셋은 항목이 밀려 중복·누락이 생기고 뒤로 갈수록 느려져요. 커서는 일정하게 빠르지만 임의 페이지로 건너뛸 수 없어요.

페이지네이션(pagination)은 큰 목록을 한 번에 다 주지 않고 조각으로 나눠 주는 방법이다. 크게 오프셋 방식(offset-based, 몇 번째부터 몇 개)과 커서 방식(cursor-based, 이 항목 다음부터 몇 개)으로 나뉜다.

방식 비교

페이지 번호limit·offset커서
요청?page=3?limit=20&offset=40?cursor=eyJpZCI6...
SQLLIMIT 20 OFFSET 40같음WHERE id < 마지막id LIMIT 20
임의 페이지 이동가능가능불가(다음·이전만)
깊은 페이지 성능느려짐느려짐일정
데이터가 추가될 때항목이 밀려 중복·누락같음안정적
  • OFFSET은 앞의 행을 읽고 버려야 해서 뒤 페이지로 갈수록 느려진다
  • 커서 방식은 정렬 기준 컬럼(보통 생성 시각 + id)에 인덱스가 있어야 하고, 정렬 기준이 유일해야 경계가 꼬이지 않는다
  • 관리 화면처럼 "37페이지로 이동"이 필요하면 오프셋, 무한 스크롤 피드라면 커서가 맞다

Spring Data JPA

Pageable pageable = PageRequest.of(0, 20, Sort.by("createdAt").descending());
Page<Post> page = postRepository.findByAuthor(author, pageable);
page.getContent(); page.getTotalPages(); page.hasNext();
  • Page<T>는 전체 개수를 세는 count 쿼리가 추가로 나간다. 다음 페이지 여부만 필요하면 Slice<T>로 바꿔 count를 아낀다
  • 정렬은 고정(메서드 이름의 OrderBy)과 동적(Sort 파라미터) 둘 다 된다(Spring Data 리포지터리와 쿼리 메서드)
  • 컬렉션을 fetch join하면서 페이지네이션하면 Hibernate가 메모리에서 페이징하므로 피한다(N+1 문제)

DRF

  • PageNumberPagination, LimitOffsetPagination, CursorPagination 세 가지를 제공한다. 전역 설정이나 뷰별 pagination_class로 고른다
  • 제네릭 뷰·ViewSet에서만 자동으로 적용된다(DRF 뷰 계층)
  • CursorPagination은 정렬 필드(기본 -created)가 유일하고 잘 바뀌지 않아야 한다. OrderingFilter와 함께 쓸 때는 ordering_fields를 그런 필드로 좁힌다(DRF 필터·검색·정렬)

프론트엔드에서 커서 기반 목록을 이어 붙이는 쪽은 TanStack Query의 무한 쿼리, 긴 목록을 그리는 쪽은 리스트 가상화을 본다.

출처: Use The Index, Luke: No Offset Markus Winand · Spring Data JPA 문서: Paging and Sorting · DRF Pagination: CursorPagination

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • JPQL과 @Query

    JPQL(Jakarta Persistence Query Language)은 테이블이 아니라 엔티티와 필드를 대상으로 쓰는 JPA의 객체 지향 쿼리 언어다. 실행할 때 연결된 DB의 SQL로 번역된다. Spring Data에서는 @Query로 리포지터리 메서드에 직접 붙인다.

  • JPA 엔티티 기본 매핑

    엔티티 매핑(entity mapping)은 자바 클래스와 필드를 테이블과 컬럼에 대응시키는 JPA(Jakarta Persistence)의 기본 설정이다. 애너테이션은 jakarta.persistence 패키지에 있다. 테이블 사이의 외래키를 참조로 옮기는 일은 jpa-relationship에서 다룬다.

  • Pages Router에서 App Router로

    App Router는 Next.js 13에서 도입된 app/ 디렉터리 기반 라우터로, 서버 컴포넌트·중첩 레이아웃(Nested Layouts)·스트리밍을 기본으로 한다. pages/ 기반의 Pages Router에서 옮길 때 바뀌는 생각을 정리한다.

  • JDBC와 JdbcTemplate

    JDBC(Java Database Connectivity)는 자바가 여러 DB와 통신하기 위한 표준 API이고, Hibernate·MyBatis·스프링 JDBC 모두 그 위에 있다. JdbcTemplate은 스프링이 JDBC의 반복 작업(연결 열고 닫기, 예외 변환, 결과 순회)을 대신해 주는 클래스다.

  • 정렬 알고리즘

    컬렉션의 원소를 어떤 순서(크기, 사전순, 날짜…)로 다시 배치하는 알고리즘. 같은 일을 하는 방법이 여럿이고 각자 강점이 달라, 트레이드오프를 익히기 좋은 주제다.

보기 옵션