Node.js의 DynamoDB BatchGetItem (AWS SDK v3)
BatchGetItem은 한 번의 요청으로 기본 키 기준 최대 100개 항목을 가져옵니다. AWS SDK v3에서 이 호출은 반드시 루프여야 합니다. UnprocessedKeys가 오류가 아니라 성공한 응답에 담겨 오기 때문입니다. 무엇이 그 필드를 채우는지, 그리고 왜 16 MB와 파티션당 1 MB가 중요한 숫자인지는 DynamoDB의 배치 작업에서 다룹니다. 이 페이지는 v3 호출 자체와 그것이 돌려주는 것을 다룹니다.
코드
import {BatchGetItemCommand, DynamoDBClient} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
let requestItems = {
Music: {
Keys: [
{Artist: {S: 'Arturo Sandoval'}, SongTitle: {S: 'Cubano Chant'}},
{Artist: {S: 'Arturo Sandoval'}, SongTitle: {S: 'A Mis Abuelos'}},
{Artist: {S: 'Ella Fitzgerald'}, SongTitle: {S: 'Misty'}}
]
}
};
const items = [];
let attempt = 0;
do {
const response = await client.send(new BatchGetItemCommand({RequestItems: requestItems}));
items.push(...(response.Responses?.Music ?? []));
// A partial result is NOT an error: throttling, a >16 MB response, or an
// internal failure returns the leftovers in UnprocessedKeys. Retry them
// with exponential backoff.
requestItems = response.UnprocessedKeys;
if (requestItems && Object.keys(requestItems).length > 0) {
attempt += 1;
await sleep(Math.min(100 * 2 ** attempt, 5000));
}
} while (requestItems && Object.keys(requestItems).length > 0);
console.log(`Fetched ${items.length} items`);설명
- 옵셔널 체이닝은 방어적인 군더더기가 아닙니다. v3 타입에서
Responses와UnprocessedKeys는 모두 선택적이므로,response.Responses?.Music ?? []와Object.keys()가드는 컴파일러가 요구하는 것입니다. 순수 JavaScript에서는 첫 빈 응답이 예외를 던지는 것을 막아 주는 장치입니다. err.name으로 분기하세요. v3는 서비스 오류 코드를 거기에 담으며, 이 명령이 실제로 일으키는 두 가지 실패는 재시도할 수 없으므로 절대 백오프 루프에 들어가서는 안 됩니다. 키가 100개를 넘으면ValidationException/Too many items requested for the BatchGetItem call이 나오고, 같은 키를 두 번 넣으면Provided list of item keys contains duplicates가 나옵니다. 둘 다 Python 페이지에 그대로 재현되어 있습니다.UnprocessedKeys는 이미RequestItems형태로 도착하며, 루프가 그것을 곧바로 되돌려 할당할 수 있는 이유가 바로 이것입니다. 페이지네이션 커서가 아니며, 호출이 실패했다는 뜻도 아닙니다.- 백오프는 있으면 좋은 배려가 아니라 AWS의 지시입니다. API 레퍼런스는 "an exponential backoff algorithm"을 사용하라고 말합니다. 즉시 재시도하면 스로틀링된 바로 그 파티션에 다시 떨어지기 때문입니다.
ConsistentRead와ProjectionExpression은 테이블별 설정이며, 최상위가 아니라 각RequestItems항목 안에 지정합니다. 맵에 키가 하나뿐이어서 평평한 요청처럼 보일 때 놓치기 쉽습니다.
응답은 실제로 어떻게 돌아오나요
세 곡이 모두 존재하는 상태에서 위 코드를 DynamoDB Local 3.3.0에 대해 실행하고, ReturnConsumedCapacity: 'TOTAL'을 추가한 뒤, 개수 대신 곡 제목을 로그로 남겨 보세요:
order: [ 'A Mis Abuelos', 'Misty', 'Cubano Chant' ]
UnprocessedKeys: {}
ConsumedCapacity: [ { TableName: 'Music', CapacityUnits: 1.5 } ]요청은 Cubano Chant, A Mis Abuelos, Misty 순서로 나열했습니다. 응답은 그중 어느 자리와도 맞지 않으며, 루프가 오프셋으로 인덱싱하는 대신 평평한 배열에 밀어 넣는 이유가 바로 이것입니다. 항목은 키 속성을 기준으로 요청과 다시 맞추고, 맞출 대상이 남아 있도록 어떤 ProjectionExpression에도 그 키들을 포함하세요.
같은 Music 항목에 ConsistentRead: true를 설정하면 동일한 세 키의 비용이 1.5가 아니라 3 단위가 됩니다. 각각 4 KB 미만인 항목 세 개는 별개의 GetItem 읽기 세 번으로 청구되며, 최종적 일관성 읽기는 0.5단위, 강력한 일관성 읽기는 1단위입니다. 요금 계산기는 읽기 패턴을 확정하기 전에 그 항목별 산수를 월 단위 금액으로 바꿔 줍니다.
이제 Misty를 삭제하고 다시 실행해 보세요. 항목 두 개, 빈 UnprocessedKeys, 그리고 1.0 단위가 나옵니다. 없는 키에는 아무것도 청구되지 않았습니다. 이것은 DynamoDB Local의 산물이지 계약이 아닙니다. BatchGetItem 레퍼런스(2026-07-28에 조회)는 존재하지 않는 항목에 대한 요청도 해당 읽기 유형의 최소 읽기 용량을 소비한다고 밝힙니다. 캐시 미스가 섞인 배치의 용량을 로컬 실행으로 산정하지 마세요.
루프를 먼저 작성하지 않고도 키 묶음을 가져와 실제로 무엇이 돌아왔는지 살펴보려면 DynoTable을 다운로드하세요.
관련 예제
- Python의 DynamoDB BatchGetItem — boto3로 하는 동일한 배치 읽기.
- AWS CLI로 하는 DynamoDB BatchGetItem — 셸에서 하는 동일한 배치 읽기.
- Node.js의 DynamoDB GetItem — 이 배치가 묶어 주는 단일 항목 읽기.
- DynamoDB의 배치 작업 — 한도, 부분 실패, 그리고 배치가 이득이 되는 시점.
- "Too many items requested for the BatchGetItem call" — 한 요청에 100개가 넘는 키.
- "Provided list of item keys contains duplicates" — 한 배치에 같은 키가 두 번.
참고 자료
- BatchGetItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
위에 링크된 공식 AWS 문서를 기준으로 2026-07-28에 마지막으로 검증했습니다.