OFFSET/LIMIT과 키셋 기반 페이징 기법 비교
Lukas Schneider
DevOps Engineer · Leapcell

소개
웹 애플리케이션과 대규모 데이터셋의 영역에서 효율적인 데이터 검색은 매우 중요합니다. 잠재적으로 수백만 건의 레코드를 다룰 때, 한 번에 모든 것을 가져오는 것은 거의 실현 가능하지 않거나 성능이 좋지 않습니다. 이때 페이징이 등장하여 데이터를 관리 가능한 청크로 표시할 수 있게 됩니다. 두 가지 주요 전략이 이 영역을 지배하고 있습니다: 익숙한 OFFSET/LIMIT
(또는 SKIP/TAKE
)와 덜 일반적이지만 종종 더 강력한 키셋 페이징(커서 기반 페이징이라고도 함)입니다. OFFSET/LIMIT
는 단순성으로 인해 일반적으로 첫 번째 선택이지만, 데이터셋이 커지고 사용자가 결과의 더 깊은 곳으로 탐색함에 따라 상당한 성능 병목 현상을 일으킬 수 있습니다. 이러한 내재된 한계는 응답성과 확장성을 개선하기 위한 강력한 대안을 제공하는 키셋 페이징을 더 자세히 살펴볼 필요성을 야기합니다. 이 두 접근 방식 간의 절충점을 이해하는 것은 대량의 데이터를 효과적으로 처리하는 고성능의 사용자 친화적인 애플리케이션을 구축하려는 개발자에게 매우 중요합니다. 이 문서는 두 가지 방법을 분석하고, 기본 메커니즘, 성능 특성 및 이상적인 애플리케이션 시나리오를 비교하여 정보에 입각한 결정을 내릴 수 있도록 돕겠습니다.
본문
비교에 들어가기 전에 핵심 용어에 대한 공통된 이해를 확립해 보겠습니다:
- 페이징 (Pagination): 대규모 데이터 세트를 작고 개별적인 페이지로 나누어 사용자가 데이터를 관리 가능한 청크로 보거나 처리할 수 있도록 하는 프로세스입니다.
- 페이지 크기 (Page Size): 페이지당 표시되거나 검색되는 레코드 수입니다.
OFFSET/LIMIT
(또는SKIP/TAKE
): 결과 세트에서 특정 행의 하위 집합을 검색하는 데 사용되는 SQL 절 조합입니다.LIMIT
은 반환할 최대 행 수를 지정하고,OFFSET
은 행을 반환하기 전에 결과 집합의 시작부터 건너뛸 행 수를 지정합니다.- 키셋 페이징 (커서 기반 페이징): 이전 페이지의 마지막 레코드의 특정 열(키) 값을 사용하여 다음 페이지의 시작점을 결정하는 페이징 기술입니다. 이는 이전 레코드를 다시 스캔하는 것을 피하면서 다음 페이지가 시작될 위치를 효과적으로 "가리킵니다".
- 커서 (Cursor): 키셋 페이징의 맥락에서 커서는 마지막으로 본 레코드를 표시하는 식별 정보(일반적으로 정렬된 열의 값)를 참조하며, 후속 레코드의 검색을 안내합니다.
OFFSET/LIMIT 페이징
원리:
OFFSET/LIMIT
페이징은 먼저 전체 결과 집합을 정렬한 다음, 지정된 수의 행(OFFSET
)을 건너뛰고 다음 행 집합(LIMIT
)을 반환하는 방식으로 작동합니다. 이는 매우 직관적이며 페이지 번호에 직접 매핑됩니다. 예를 들어, 페이지당 10개 항목의 3번째 페이지를 가져오려면 OFFSET 20 LIMIT 10
을 사용합니다.
구현 예제 (SQL):
id
, name
, price
열이 있는 products
테이블이 있다고 가정해 보겠습니다. name
으로 정렬된 제품을 검색하려고 합니다.
페이지 1 (처음 10개 제품):
SELECT id, name, price FROM products ORDER BY name LIMIT 10 OFFSET 0;
페이지 2 (다음 10개 제품):
SELECT id, name, price FROM products ORDER BY name LIMIT 10 OFFSET 10;
페이지 N (10개 제품의 N번째 페이지):
SELECT id, name, price FROM products ORDER BY name LIMIT 10 OFFSET ((N - 1) * 10);
장점:
- 단순성: 이해하고 구현하기 쉽습니다.
- 직접 페이지 번호 액세스: 사용자가 임의의 페이지 번호로 직접 이동할 수 있습니다.
단점:
- 성능 저하:
OFFSET
값이 증가함에 따라 데이터베이스는 시작점을 식별하기 위해OFFSET + LIMIT
까지의 모든 이전 행을 계속 스캔하고 정렬해야 합니다. 이는 데이터베이스가 각 후속 페이지에 대해 더 많은 낭비된 작업을 수행하기 때문에 더 큰 오프셋과 대규모 데이터셋에서 점점 더 비효율적이 됩니다. - 불안정한 결과: 페이지 요청 사이에 레코드가 추가되거나 삭제되면 레코드가 여러 페이지에 나타나거나 완전히 건너뛸 수 있어 사용자 경험이 일관되지 않습니다(예: 중복 항목 표시 또는 항목 누락). 이전에 검색된 페이지 내에 속하는 새 레코드가 추가되면 후속 페이지의 데이터가 예상과 달라집니다.
적용 시나리오:
- 소규모~중규모 데이터셋: 총 레코드 수가 상대적으로 적을 때 (수천에서 수만 개) 그리고 깊은 페이징이 드문 경우.
- 특정 페이지 이동: 사용자가 임의의 페이지 번호로 직접 이동해야 하는 경우 (예: "50페이지로 이동").
- 데이터 일관성이 덜 중요할 때: 페이지 로드 간의 데이터 수정으로 인한 약간의 불일치가 허용되는 시나리오.
키셋 페이징 (커서 기반 페이징)
원리:
키셋 페이징은 이전 페이지의 마지막 레코드 값을 "커서"로 사용하여 다음 레코드 집합을 가져옴으로써 OFFSET
문제를 피합니다. 행을 건너뛰는 대신, 다음 페이지가 정렬된 순서에서 시작해야 할 위치를 직접 식별합니다. 이를 위해서는 커서를 정의하기 위한 정렬되고 이상적으로는 고유한 일련의 열이 필요합니다.
구현 예제 (SQL):
name
으로 정렬된 products
테이블을 계속 사용합니다. 커서를 고유하고 안정적으로 만들기 위해 일반적으로 고유 식별자(예: id
)를 동점자(tie-breaker)로 포함하는 것이 가장 좋습니다.
초기 요청 (페이지 1):
SELECT id, name, price FROM products ORDER BY name ASC, id ASC LIMIT 10;
페이지 1에서 반환된 마지막 레코드가 name = 'Laptop'
이고 id = 105
라고 가정해 보겠습니다.
다음 페이지 요청 (페이지 2):
다음 10개 제품을 가져오려면, name
이 'Laptop'보다 크거나, name
이 'Laptop'인 경우 id
가 105보다 큰 제품을 쿼리합니다.
SELECT id, name, price FROM products WHERE (name > 'Laptop') OR (name = 'Laptop' AND id > 105) ORDER BY name ASC, id ASC LIMIT 10;
장점:
- 일관된 성능: 쿼리는 항상 특정 지점에서 시작하여
LIMIT
행을 가져옵니다. 건너뛴 행을 스캔하는 것을 피하므로 성능은 페이지 번호에 독립적입니다. 이는 대규모 데이터셋과 깊은 페이징에 특히 유익합니다. - 안정적인 결과: 이전 페이지에 삽입된 새 레코드 또는 삭제는 레코드가 여러 페이지에 나타나거나 건너뛰는 것을 방지합니다. "커서"는 항상 일관된 논리적 위치를 가리킵니다.
- 인덱스 활용: 이 방법은 정렬 열의 인덱스를 효과적으로 활용하여, 데이터베이스가 커서 위치로 직접 이동할 수 있으므로 쿼리가 매우 빠르게 실행됩니다.
단점:
- 직접 페이지 번호 액세스 불가: 사용자는 해당 페이지의 커서를 알지 못하는 한 일반적으로 임의의 페이지 번호로 직접 이동할 수 없습니다(예: "50페이지로 이동"). 이는 주로 "다음 페이지" / "이전 페이지" 탐색을 위해 설계되었습니다.
- 복잡성: 커서(마지막 레코드의 값)를 요청 간에 관리해야 하므로 구현이 약간 더 복잡합니다. 동점자(tie-breaker)가 있는 여러 열로 정렬하는 경우
WHERE
절이 복잡해질 수 있습니다. - 안정적인 순서 및 고유한 동점자(tie-breaker) 필요: 안정적이고 모호하지 않은 커서를 보장하기 위해 하나 이상의 고유한 열(또는 고유한 열 조합)에 의존합니다.
적용 시나리오:
- 대규모 데이터셋:
OFFSET/LIMIT
가 너무 느려질 수 있는 수백만 건의 레코드를 다룰 때. - 무한 스크롤 / "다음/이전" 탐색: 사용자가 주로 소셜 미디어 피드, 제품 목록 또는 활동 로그와 같은 결과 사이를 앞뒤로 이동하는 인터페이스에 이상적입니다.
- 높은 동시성 / 빈번한 데이터 변경: 동적인 환경에서 더 일관된 결과를 보장합니다.
- API 페이징: 서비스 간 결과 페이징을 위한 REST API의 일반적인 패턴입니다.
결론
OFFSET/LIMIT
과 키셋 페이징 중에서 선택하는 것은 특정 애플리케이션의 요구 사항, 데이터셋 크기 및 사용자 상호 작용 패턴을 이해하는 것에 달려 있습니다. OFFSET/LIMIT
은 즉각적인 단순성과 직접적인 페이지 번호 액세스를 제공하지만, 대규모 데이터셋 및 깊은 페이징으로 인해 성능이 크게 저하되어 동적인 환경에서 일관되지 않은 결과가 발생하기 쉽습니다. 반면에 키셋 페이징은 인덱싱된 열을 활용하고 비용이 많이 드는 OFFSET
작업을 피함으로써 대규모 데이터셋 및 "다음/이전" 탐색에 대해 더 나은 성능과 안정성을 제공합니다. 궁극적으로, 대규모의 동적인 결과 세트를 갖춘 높은 확장성과 일관된 성능을 요구하는 애플리케이션의 경우, 구현 복잡성이 약간 증가하더라도 키셋 페이징이 명백히 우수한 선택입니다.
따라서, 상당한 데이터를 다루는 대부분의 현대 애플리케이션의 경우, 성능과 안정성을 위해 키셋 페이징을 우선시하고, OFFSET/LIMIT
은 더 작고 성능에 덜 중요한 데이터셋을 위해 남겨 두십시오.