Node.js(AWS SDK v3)의 DynamoDB Query
AWS SDK v3에서 완전한 Query는 대부분의 스니펫이 보여주는 단 한 번의 client.send()가 아니라 아래의 do/while입니다. 한 페이지는 1 MB에서 잘리고, 파티션의 나머지는 LastEvaluatedKey를 되먹여야만 도착합니다. 애초에 Query가 올바른 읽기인지는 Query vs. Scan을 참조하세요.
코드
import {DynamoDBClient, QueryCommand} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const items = [];
let lastEvaluatedKey;
do {
const response = await client.send(
new QueryCommand({
TableName: 'Music',
KeyConditionExpression: '#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)',
ExpressionAttributeNames: {
'#hashKey': 'Artist',
'#rangeKey': 'SongTitle'
},
ExpressionAttributeValues: {
':hashKeyValue': {S: 'Arturo Sandoval'},
':rangeKeyValue': {S: 'C'}
},
ExclusiveStartKey: lastEvaluatedKey
})
);
items.push(...(response.Items ?? []));
lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);
console.log(`Found ${items.length} items`);이 루프가 실제로 하는 일
곡마다 약 3.9 KB이고 모두 Artist = "Arturo Sandoval" 아래에 있는 600곡 픽스처를 상대로, 위 루프는 요청을 세 번 보냅니다:
| 왕복 | Count | ScannedCount | 읽기 단위 | LastEvaluatedKey |
|---|---|---|---|---|
| 1 | 271 | 271 | 128.5 | 있음 |
| 2 | 271 | 271 | 128.5 | 있음 |
| 3 | 58 | 58 | 27.5 | 없음 |
271을 설정한 사람은 아무도 없습니다. 그 지점에서 1 MB가 소진된 것이며, 따라서 항목 크기가 달라질 때마다 페이지 경계도 움직입니다. 오늘 한 번 페이징하는 파티션은 속성을 하나 추가하면 두 번 페이징하게 되고, 단 한 번의 send()에서 response.Items를 읽는 코드는 600곡 중 271곡을 오류 없이 조용히 반환합니다.
이제 같은 쿼리에 Limit: 10과 Year에 대한 FilterExpression을 추가해 보세요:
Count: 0 ScannedCount: 10 ConsumedCapacity: 5 LastEvaluatedKey: set열 개를 평가했고, 반환된 것은 0개이며, 그럼에도 요청은 읽기 용량을 소비했습니다. Limit은 DynamoDB가 읽는 양을 제한하고 필터는 그 뒤에 실행되므로, "결과 10개를 달라"는 의미로 고른 Limit은 0개에서 10개 사이를 줍니다.
2026-07-28에 node v24.18.0에서 @aws-sdk/client-dynamodb 3.1095.0으로 DynamoDB Local(amazon/dynamodb-local)을 상대로 측정했습니다. 개수와 용량은 엔진 자신의 응답 필드입니다.
설명
- 첫 회차의
ExclusiveStartKey: lastEvaluatedKey는undefined이며, 그것은 의도된 것입니다. v3 직렬화기가undefined멤버를 버리므로 같은 객체 리터럴이 첫 요청과 이후 모든 요청에 그대로 동작합니다. "처음부터 시작"의 뻔한 짐작인{}로 바꾸면ValidationException: The provided starting key is invalid로 실패합니다. @aws-sdk/client-dynamodb는 절대 대신 마샬링해 주지 않습니다. 값은{S: 'Arturo Sandoval'}로 들어가고 항목도 같은 방식으로 돌아옵니다. DocumentClient를 끌어들이지 않는 대가입니다. 평범한 JS 객체를 쓰고 싶다면@aws-sdk/lib-dynamodb가 손을 뻗을 래퍼입니다.- 숫자는 왕복 동안 문자열로 살아남습니다.
@aws-sdk/util-dynamodb의unmarshall로{N: '9007199254740993'}을 언마샬링하면 손실이 있는number가 아니라 JSbigint가 반환됩니다.{wrapNumbers: true}를 전달하면 대신{value: '9007199254740993'}을 받습니다. 어느 쪽이든, 크기를 확인하지 않은 DynamoDBN에Number()를 쓰지 마세요. KeyConditionExpression— 파티션 키에 대한 등호 하나에 정렬 키 조건 최대 하나(=,<,<=,>,>=,BETWEEN,begins_with)를 받습니다. 그 밖의 것은 읽기 뒤에 실행되는FilterExpression에 속합니다.ScanIndexForward: false— 정렬 키 순서를 뒤집습니다. 기본값은 오름차순입니다.IndexName은 같은 명령을 보조 인덱스로 전환합니다.
시각적으로 해보기
DynamoDB 쿼리 빌더가 이 전체 형태 — 키 조건, 이름과 값 맵, LastEvaluatedKey 루프 — 를 실행 가능한 SDK v3 프로그램으로 내보내므로, 페이지네이션이 깜빡 잊는 부분이 되지 않습니다.
키 조건 폼과 페이지네이션된 결과 그리드를 갖춘 GUI에서 실제 테이블을 상대로 쿼리를 실행하려면, DynoTable을 다운로드하세요.
관련 가이드
- Query vs. Scan —
Query가 올바른 기본값인 이유. - 키 조건 표현식 — 허용되는 모든 파티션/정렬 키 연산자.
- "Query condition missed key schema element" — 키 조건이 잘못된 속성을 지목하거나 파티션 키를 건너뛴 경우.
- "Query key condition not supported" — contains나 두 번째 정렬 키 조건처럼 키 조건이 쓸 수 없는 연산자.