DynamoDB의 페이지네이션
DynamoDB는 한 번의 호출로 "전체" 결과를 반환하는 법이 없다. Query나 Scan은 최대
1 MB의 데이터를 반환한 뒤, 이어서 진행할 LastEvaluatedKey를 건네준다. 페이지네이션을
제대로 한다는 것은 카운터가 아니라 그 키를 기준으로 반복한다는 뜻이다.
DynamoDB에서 페이지네이션은 어떻게 동작하나?
Query나 Scan은 호출당 최대 1 MB를 반환한 다음 LastEvaluatedKey를 돌려준다. 페이지를 넘기려면 그 키를 다음 호출의 ExclusiveStartKey로 전달하고, DynamoDB가 키를 반환하지 않을 때까지 반복한다. 페이지 번호도, 총 개수도 없으며, Limit은 반환된 항목이 아니라 평가된 항목의 상한을 정한다.
let key;
do {
const out = await client.send(new QueryCommand({...params, ExclusiveStartKey: key}));
process(out.Items);
key = out.LastEvaluatedKey;
} while (key);LastEvaluatedKey가 undefined이면 끝에 도달한 것이다. 다음 조각을 가져오려면 그것을
ExclusiveStartKey로 다시 전달하자.
제어 흐름은 키가 없을 때만 빠져나가는 하나의 반복문이다:
매 순회는 반환된 키에서 재개하거나 멈춘다 — 카운터는 없다.
Limit은 페이지 크기가 아니다
Limit은 DynamoDB가 평가하는 항목 수의 상한이지, FilterExpression 이후 반환하는
항목 수가 아니다. 필터 뒤에 있는 Limit: 25 쿼리는 3개의 항목을 반환하면서도 여전히
LastEvaluatedKey를 건넬 수 있다 — 페이지가 짧아 보여도 키가 빌 때까지 계속 넘겨야
한다. 비어 있지 않은 LastEvaluatedKey가 일치하는 항목이 더 있다고 약속하는
것도 아니다. 없는 키만이 끝에 도달했음을 증명한다.
SDK가 페이지네이션하게 하기
두 SDK 모두 위의 반복문을 감싸므로 페이지를 직접 순회할 수 있다:
// AWS SDK for JavaScript v3
import {paginateQuery} from '@aws-sdk/lib-dynamodb';
for await (const page of paginateQuery({client}, params)) {
process(page.Items);
}# boto3
paginator = client.get_paginator('query')
for page in paginator.paginate(**params):
process(page['Items'])페이지 번호는 없다
DynamoDB에는 총 개수도 임의 페이지 접근도 없다 — 커서를 다시 재생하지 않고는
"7페이지"로 건너뛰거나 뒤로 넘길 수 없다. 번호 매긴 페이지가 아니라 무한 스크롤 /
"더 보기"를 중심으로 UI를 설계하자. (Select: 'COUNT' 쿼리도 개수를 세기 위해 일치한
모든 항목을 읽고 — 청구한다.)
API를 위한 무상태 커서
LastEvaluatedKey는 마지막 항목의 키 속성일 뿐이다. 이를 base64로 인코딩해 불투명한
nextToken으로 클라이언트에 건네고, 다음 요청에서 다시 ExclusiveStartKey로 디코딩하자.
서버 측 커서 상태가 없다.
그 토큰은 DynamoDB-JSON이다 — DynamoDB-JSON 변환기로 직접 살펴보거나 손으로 만들 수 있다. 그리고 Scan을 우회하려고 페이지를 넘기고 있다면, 그건 보통 대신 인덱스를 추가하라는 신호다.
루프 자체를 아예 쓰지 않으려면, 쿼리 빌더가 전체 Query/Scan 요청을 구성하고 페이지네이션 루프까지 포함된 실행 가능한 SDK v3, CLI, boto3 프로그램을 출력한다.
커서가 대신 추적되는 채로 쿼리 결과를 시각적으로 넘겨 보려면 DynoTable을 사용해 보자.