노트

페이지네이션 (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로 리포지터리 메서드에 직접 붙인다.

  • 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의 반복 작업(연결 열고 닫기, 예외 변환, 결과 순회)을 대신해 주는 클래스다.

  • JPA 연관관계 매핑

    JPA(Jakarta Persistence, 옛 이름 Java Persistence API) 연관관계 매핑(association mapping)은 테이블의 외래키(foreign key) 관계를 엔티티 사이의 참조로 표현하는 방법이다. 핵심은 외래키를 관리하는 쪽(연관관계의 주인, owning side)을 정하는 것이고, 양방향 관계에서 반대쪽은 mappedBy로 "나는 읽기 전용 거울"이라고 표시한다.

보기 옵션