본문으로 바로가기
2222

안녕하세요, 백엔드 개발자입니다.

 

지난 편에서는 OFFSET을 사용한 페이지네이션이 데이터 규모가 커질수록 왜 성능 문제를 일으킬 수 있는지 알아보았습니다. 불필요한 데이터를 대량으로 읽고 버리는 내부 동작 방식 때문이었죠.

이번 글에서는 이에 대한 해결책으로, 대규모 서비스에서 안정적인 성능을 위해 사용하는 '커서 기반 페이지네이션(Cursor-based Pagination)' 기법을 소개하겠습니다.

1. 접근 방식의 전환: 페이지 번호에서 데이터 기준으로

커서 기반 페이지네이션의 핵심 아이디어는 '페이지 번호' 대신 '특정 데이터의 위치'를 기준으로 다음 목록을 조회하는 것입니다.

  • OFFSET 기반: "3번째 페이지의 목록을 줘"
  • 커서 기반: "지난번 마지막으로 본 게시글 ID 12345 다음 목록을 줘"

여기서 '게시글 ID 12345'와 같이 기준점이 되는 값을 커서(Cursor) 라고 부릅니다. 커서는 보통 정렬의 기준이 되는, 중복되지 않는 유니크한 컬럼(대부분 id나 created_at)을 사용합니다.

2. 커서 기반 페이지네이션 구현

id를 기준으로 내림차순 정렬하는 목록 API를 예로 들어보겠습니다.

OFFSET 기반 방식

-- 2페이지 조회 (OFFSET 10)
SELECT * FROM posts ORDER BY id DESC LIMIT 10 OFFSET 10;

커서 기반 방식

-- 1. 첫 페이지 조회 (커서 없음)
SELECT * FROM posts ORDER BY id DESC LIMIT 10;

첫 페이지를 조회한 결과, 마지막 게시글의 id가 991이라고 가정해 보겠습니다. 클라이언트는 이 991 값을 '다음 페이지를 요청하기 위한 커서'로 가지고 있습니다.

-- 2. 다음 페이지 조회 (클라이언트가 마지막 id '991'을 커서로 전달)
SELECT *
FROM posts
WHERE id < 991      -- 마지막으로 본 id보다 작은 데이터를 조회
ORDER BY id DESC
LIMIT 10;

이처럼 OFFSET 대신 WHERE 조건절을 사용하여 조회 시작점을 명확하게 지정하는 것이 핵심입니다.

3. 인덱스를 활용한 성능 개선 원리

커서 기반 방식이 OFFSET보다 빠른 이유는 **인덱스(Index)**를 효율적으로 활용하기 때문입니다.

OFFSET 쿼리는 ORDER BY를 위해 많은 데이터를 읽어야 하므로 인덱스의 이점을 온전히 활용하기 어렵습니다.

반면 커서 기반 쿼리의 WHERE id < 991 조건은 다릅니다. 데이터베이스는 id 컬럼의 인덱스를 사용해 id가 991인 지점을 빠르게 찾은 뒤, 그 지점부터 순서대로 10개의 데이터만 읽으면 됩니다. 조회하려는 페이지가 아무리 뒤쪽에 있어도, DB가 수행하는 작업은 '특정 지점을 찾아 그 뒤부터 N개 읽기'로 동일합니다. 이 덕분에 일관된 성능을 유지할 수 있습니다.

4. 장점과 한계

커서 기반 페이지네이션은 강력하지만, 모든 상황에 적합한 것은 아닙니다.

  • 장점
    • 일관된 성능: 페이지가 깊어져도 쿼리 속도가 거의 일정하게 유지됩니다.
    • 데이터 정확성: 조회 도중 데이터가 추가/삭제되어도 중복이나 누락이 발생할 가능성이 적습니다.
  • 한계
    • 페이지 점프 불가: "50페이지로 바로가기"와 같은 기능을 구현할 수 없습니다. '다음' 또는 '이전' 목록만 순차적으로 가져올 수 있습니다.
    • 구현 복잡도: 클라이언트가 마지막 커서 값을 관리해야 하는 등, OFFSET 방식보다 구현이 조금 더 복잡합니다.

이러한 특성 때문에, 커서 기반 페이지네이션은 주로 무한 스크롤(Infinite Scroll) 이나 더 보기(Load More) 형태의 UI에 가장 적합합니다.

5. 정리하며

지금까지 OFFSET의 대안으로 커서 기반 페이지네이션을 알아보았습니다.

대용량 데이터를 다루는 서비스에서 안정적인 성능을 확보하려면 커서 기반 페이지네이션은 매우 효과적인 해결책이 될 수 있습니다. 만약 구현하려는 기능이 무한 스크롤 방식이라면, 처음부터 이 방식을 도입하는 것을 적극적으로 고려해 보시길 바랍니다.

긴 글 읽어주셔서 감사합니다.