Node.js(AWS SDK v3)의 DynamoDB PutItem

PutItem은 항목 전체를 쓰고 같은 기본 키를 가진 기존 항목을 대체합니다(항목 기반 작업에서 이것이 UpdateItem과 어떻게 다른지 다룹니다). v3 클라이언트는 DynamoDB JSON을 그대로 보내므로, Item은 평범한 JavaScript 값이 아니라 { S: … } / { N: … } 값을 담습니다.

코드

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

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

const command = new PutItemCommand({
  TableName: 'Music',
  Item: {
    Artist: {S: 'Arturo Sandoval'},
    SongTitle: {S: 'Cubano Chant'},
    AlbumTitle: {S: 'Danzon'},
    Year: {N: '1994'},
    Awards: {N: '0'}
  },
  ConditionExpression: 'attribute_not_exists(#cond0) AND attribute_not_exists(#cond1)',
  ExpressionAttributeNames: {
    '#cond0': 'Artist',
    '#cond1': 'SongTitle'
  }
});

try {
  await client.send(command);
  console.log('Song written');
} catch (err) {
  if (err.name === 'ConditionalCheckFailedException') {
    console.log('A song with that key already exists — not overwritten');
  } else {
    throw err;
  }
}

설명

err.name이 올바른 검사이며, 오류에 담긴 것은 그것만이 아닙니다. 위에서 실패한 조건을 잡아 객체를 출력하면 다음이 나왔습니다:

err.name                     ConditionalCheckFailedException
err instanceof Error         true
err.message                  The conditional request failed
err.$metadata.httpStatusCode 400

모든 v3 오류는 상태 코드, 요청 id, 시도 횟수를 담은 $metadata를 실어 오며, 로그 한 줄에 바로 넣고 싶은 것들입니다. err.name은 모듈화된 패키지 전반에서 안정적입니다. instanceof ConditionalCheckFailedException도 동작하지만 클래스를 값으로 임포트하게 되어 번들러가 그것을 남깁니다.

오류가 쓰기를 막은 항목을 건네줄 수 있습니다. 명령에 ReturnValuesOnConditionCheckFailure: 'ALL_OLD'를 추가하면 err.Item이 채워져 도착합니다. 위 실행에서는 속성 다섯 개였고 Year{"N":"1994"}였습니다. 대부분의 생성 전용 핸들러는 실패 후 이미 무엇이 있었는지 알아내려고 GetItem을 합니다. 그 왕복은 피할 수 있습니다. (ReturnValues: 'ALL_OLD'는 성공 경로 쪽 사촌이며, 나머지는 ReturnValues에서 다룹니다.)

marshall()은 여러분이 예상하는 것보다 더 많은 입력을 거부합니다. 이 페이지의 타입 지정 ItemDynamoDBDocumentClient와 평범한 객체로 바꾸는 것이 보통의 다음 단계이고, @aws-sdk/util-dynamodb는 기본적으로 엄격합니다. 실제로 발생한 세 가지 예외를 그대로 옮기면:

{Genre: undefined}   Pass options.removeUndefinedValues=true to remove undefined values from map/array/set.
{tags: new Set()}    Pass a non-empty set, or options.convertEmptyValues=true.
{n: 9007199254740993} Number 9007199254740992 is greater than Number.MAX_SAFE_INTEGER. Use NumberValue from @aws-sdk/lib-dynamodb.

첫 번째가 프로덕션까지 도달하는 것입니다. 없는 것이 아니라 undefined인 선택적 필드는 마샬링 시점에 예외를 던지며, DynamoDBDocumentClient.from(client, {marshallOptions})removeUndefinedValues: true를 주는 것이 표준 해결책입니다.

세 번째 줄을 다시 읽어 보세요. 리터럴은 9007199254740993이었는데 메시지는 9007199254740992를 인용합니다. SDK가 값을 보기도 전에 JavaScript가 이미 반올림했고, SDK는 자신이 받은 것을 보고하는 것입니다. 이것이 DynamoDB가 N을 문자열로 전송하는 이유 전부입니다. N은 38자리 정밀도를 유지하고 JS number는 15~17자리를 유지합니다. 사실상 식별자인 것은 S에, 사실상 소수인 것은 NumberValue나 직접 형식을 맞춘 문자열에 속합니다.

ConditionExpression은 거절할 때에도 쓰기 용량을 씁니다. AWS는 이렇게 말합니다. "if the expression evaluates to false, DynamoDB still consumes write capacity units from the table"(2026-07-28에 가져옴). 빡빡한 생성 전용 재시도 루프는 시도마다 요금이 부과됩니다. 기준을 잡자면, 약 15 KB 항목의 성공적인 put은 "CapacityUnits": 15를 보고했습니다. 쓰기는 읽기가 쓰는 4 KB가 아니라 1 KB 단위로 올림합니다.

별칭은 장식이 아닙니다. #cond0/#cond1ExpressionAttributeNames를 통해 Artist/SongTitle로 해석됩니다. 인라인 이름은 하나가 예약어와 충돌하기 전까지만 동작하며, 그때는 손대지도 않은 속성 때문에 표현식이 실패합니다.

시각적으로 해보기

위의 마샬링 규칙은 두 형식을 나란히 놓고 보는 것이 확인하기 가장 쉽습니다. 무료 DynamoDB JSON 변환기가 평범한 JSON을 타입이 지정된 { S: … } 형태로, 그리고 그 반대로 바꿔 주므로, 보내기 전에 marshall()이 무엇을 만들어 냈을지 확인할 수 있습니다.

여러분의 테이블을 상대로 항목을 쓰고 편집하려면 — 속성별 폼, 타입 선택기, 결과를 SDK v3 코드로 다시 복사하기 — DynoTable을 다운로드하세요.

관련 가이드

참고 자료

2026-07-28에 Node v24.18.0에서 @aws-sdk/client-dynamodb 3.1095.0과 @aws-sdk/util-dynamodb 3.996.7로, 포트 9000의 DynamoDB Local(amazon/dynamodb-local)을 상대로 재현했습니다. 오류 문자열, 객체 형태, 용량 수치는 캡처한 출력을 그대로 옮긴 것입니다.

Console 없이 DynamoDB 작업하기

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

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