Node.js의 DynamoDB Scan (AWS SDK v3)

아래의 do/while은 방어적 코딩이 아닙니다. 필터가 걸린 Scan 페이지는 빈 Items 배열로 돌아오면서도 테이블에 남은 부분이 여전히 있을 수 있으므로, 첫 응답에서 멈추는 것은 일치 항목이 있는 테이블에서 스캔이 0건을 보고하게 만드는 방법입니다. 이 작업을 아예 피해야 할 때는 Query vs. Scan에서 다룹니다.

코드

import {DynamoDBClient, ScanCommand} from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});

const items = [];
let lastEvaluatedKey;

do {
  const response = await client.send(
    new ScanCommand({
      TableName: 'Music',
      FilterExpression: '#filter0 >= :filterValue0',
      ExpressionAttributeNames: {
        '#filter0': 'Year'
      },
      ExpressionAttributeValues: {
        ':filterValue0': {N: '2010'}
      },
      ExclusiveStartKey: lastEvaluatedKey
    })
  );

  items.push(...(response.Items ?? []));
  lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);

console.log(`Matched ${items.length} items`);

빈 페이지 두 개, 284.5 읽기 단위, 항목 8개

픽스처는 각각 약 3.9 KB인 곡 600개이고, 그중 정확히 8개가 Year >= 2010이며 정렬상 마지막에 놓입니다. 위 루프가 실제로 받는 것은 다음과 같습니다:

왕복Items.lengthScannedCount읽기 단위LastEvaluatedKey
10271128.5있음
20271128.5있음
385827.5없음

연속된 두 페이지가 아무것도 반환하지 않으면서 각각 128.5 읽기 단위를 씁니다. if (!response.Items.length) return을 하는 코드는 테이블이 비었다고 보고합니다. API 레퍼런스는 규칙을 분명히 밝힙니다. "a scan result can result in no items meeting the criteria and the Count will result in zero", 그리고 별도로 "a FilterExpression is applied after the items have already been read; the process of filtering does not consume any additional read capacity units".

두 번째 문장은 청구서가 읽는 방식으로 읽으세요. 필터는 공짜이고, 필터가 버린 것들은 공짜가 아닙니다. 항목 8개를 내주는 데 284.5 읽기 단위이며, 필터를 전혀 걸지 않았을 때와 같은 금액입니다.

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로 실패합니다.
  • response.Items ?? []는 실제 일을 합니다. 위 표와 함께 보세요. 널 병합 연산자는 아무것도 일치하지 않은 페이지에서 누적기를 정직하게 유지하고, while은 그 페이지들을 지나서도 루프를 살려 둡니다.
  • #filter0은 장식이 아닙니다. Year는 AWS 예약어 목록에 있으며, 별칭 없이 쓰면 ValidationException: Invalid FilterExpression: Attribute name is a reserved keyword; reserved keyword: Year가 반환됩니다.
  • Limit은 반환된 항목이 아니라 읽은 항목을 셉니다. 이 필터에서 Limit: 10Count: 0ScannedCount: 10을 내놓습니다. 이것은 결과 열 개를 달라는 방법이 아니라 용량 급증을 막는 조절 손잡이입니다.
  • Segment / TotalSegments 는 테이블 전체 스캔을 워커들에게 나눕니다. 이는 비용이 아니라 실제 소요 시간을 나누는 것입니다. 동일한 284.5 단위를 더 빠르게, 더 동시에 쓸 뿐입니다.

실제 테이블에서 드는 비용

항목 8개에 284.5 읽기 단위라는 것이 문제의 형태이며, 이는 결과가 아니라 테이블 크기에 비례해 선형으로 커집니다. 핫 경로에 필터 스캔을 올리기 전에 DynamoDB 요금 계산기에서 여러분의 항목 크기와 트래픽으로 테이블 전체 읽기의 값을 매겨 보고, 같은 액세스 패턴을 Query로 바꿔 주는 GSI와 비교해 보세요.

스크립트에서 앞이 안 보이는 채로 스캔하는 대신, 필터와 페이지네이션이 적용된 결과 그리드로 테이블을 GUI에서 탐색하려면 DynoTable을 다운로드하세요.

관련 가이드

참고 자료

이 요청을 시각적으로 만들기

무료 DynamoDB 쿼리 빌더에서 이 작업을 구성하세요 — 키 조건, 필터, 인덱스, Limit, 정렬 순서, 페이지네이션 루프 — 그리고 실행 가능한 SDK v3, CLI, boto3 프로그램으로 다시 복사하세요.

DynamoDB 쿼리 빌더 열기

Console 없이 DynamoDB 작업하기

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

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