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 타입에서 ResponsesUnprocessedKeys는 모두 선택적이므로, 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"을 사용하라고 말합니다. 즉시 재시도하면 스로틀링된 바로 그 파티션에 다시 떨어지기 때문입니다.
  • ConsistentReadProjectionExpression은 테이블별 설정이며, 최상위가 아니라 각 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을 다운로드하세요.

관련 예제

참고 자료

위에 링크된 공식 AWS 문서를 기준으로 2026-07-28에 마지막으로 검증했습니다.

Console 없이 DynamoDB 작업하기

DynamoDB로는 실행할 수 없는 진짜 SQL(JOINs, GROUP BY, 집계)을 실행하는 빠른 DynamoDB 데스크톱 클라이언트. 시각적 편집과 여러분 자신의 Bedrock 키로 동작하는 AI 에이전트를 제공합니다.

30일 무료 체험, 신용카드 불필요 — 이후 기간 제한 없는 무료 요금제.