Node.js의 DynamoDB UpdateItem (AWS SDK v3)

저수준 v3 클라이언트는 양방향 모두 DynamoDB JSON으로 말합니다. 즉 여러분이 보내는 모든 숫자와 돌려받는 모든 숫자가 문자열입니다. 이것은 흠이 아닙니다. 숫자 타입이 double뿐인 언어에서 38자리 DynamoDB 숫자가 살아남는 유일한 방법입니다. 그리고 버그가 사는 곳이기도 합니다.

코드

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

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

const command = new UpdateItemCommand({
  TableName: 'Music',
  Key: {
    Artist: {S: 'Arturo Sandoval'},
    SongTitle: {S: 'Cubano Chant'}
  },
  UpdateExpression: 'SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2',
  ExpressionAttributeNames: {
    '#upd0': 'Genre',
    '#upd1': 'Year',
    '#upd2': 'Awards'
  },
  ExpressionAttributeValues: {
    ':updValue0': {S: 'Latin Jazz'},
    ':updValue1': {N: '1994'},
    ':updValue2': {N: '1'}
  },
  ReturnValues: 'ALL_NEW'
});

const response = await client.send(command);
console.log(response.Attributes); // the item after the update

GenreAwards도 없던 항목에 대해 response.Attributes는 이렇게 돌아옵니다:

{"Artist":{"S":"Arturo Sandoval"},"Awards":{"N":"1"},"Genre":{"S":"Latin Jazz"},"Year":{"N":"1994"},"SongTitle":{"S":"Cubano Chant"}}

typeof response.Attributes.Awards.N"string"이므로 response.Attributes.Awards.N + 1"11"로 평가됩니다. 아무것도 예외를 던지지 않고, 아무것도 경고하지 않으며, 잘못된 숫자가 다음 쓰기로 들어갑니다. 경계에서 파싱하세요: Number(response.Attributes.Awards.N).

설명

  • 표현식은 평범한 문자열이고 v3는 그것을 검사하지 않습니다. UpdateItemCommand는 입력 객체의 형태만 검증할 뿐 UpdateExpression 안의 문법은 결코 검증하지 않으므로, 오타 하나가 왕복 한 번과 400 응답이 됩니다. 문법은 업데이트 표현식에 있습니다. ADD #upd2 :updValue2가 원자적 증가이고, ConditionExpression: 'attribute_exists(Artist)'를 추가하면 이 호출이 업서트가 아니라 업데이트 전용이 됩니다.

  • 보통 원하는 것은 ReturnValues: 'UPDATED_NEW'입니다. 같은 업데이트가 {"Awards":{"N":"2"}}만 반환하고 그 외에는 아무것도 반환하지 않습니다. ALL_NEW는 호출마다 항목 전체를 돌려보내는데, 뚱뚱한 항목에서는 카운터 하나를 읽으려고 대역폭을 지불하는 셈입니다.

  • $metadata는 v3의 대역 외 채널입니다: {"httpStatusCode":200,"requestId":"...","attempts":1,"totalRetryDelay":0}. attempts는 "재시도되었나"에 대한 정직한 답이며, 멱등적이지 않은 쓰기가 두 번 실행되었는지 따질 때 중요합니다.

  • ValidationException은 catch할 수 있는 클래스가 아니라 비교할 수 있는 name일 뿐입니다. 별칭이 빠지면 err.name === 'ValidationException'으로 돌아오고 err.messageInvalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year로 설정됩니다.

  • 문서 클라이언트는 또 다른 절충입니다. @aws-sdk/lib-dynamodb는 네이티브 JS 값을 받고 응답을 언마셜링해 주지만, 그 문자열 안전성을 대가로 치릅니다. @aws-sdk/util-dynamodbmarshall({awards: 9007199254740993})은 아예 거부합니다:

    Number 9007199254740992 is greater than Number.MAX_SAFE_INTEGER. Use NumberValue from @aws-sdk/lib-dynamodb.

    그 메시지의 숫자를 자세히 보세요. 리터럴에 적힌 3이 아니라 2로 끝납니다. SDK가 보기도 전에 JavaScript가 이미 반올림한 것입니다. 이 코드의 저수준 클라이언트에는 그런 문제가 있을 수 없습니다. {N: '9007199254740993'}은 전송 구간까지 내내 텍스트이기 때문입니다.

조건 실패가 건네주는 것

입력에 ReturnValuesOnConditionCheckFailure: 'ALL_OLD'를 추가하면 던져진 오류가 여러분을 이긴 항목을 실어 옵니다:

name: ConditionalCheckFailedException | message: "The conditional request failed" | http: 400
err.Item: {"Artist":{"S":"Arturo Sandoval"},"Awards":{"N":"2"},"Year":{"N":"1994"},"SongTitle":{"S":"Cubano Chant"},"Genre":{"S":"Latin Jazz"}}

err.Item은 어느 클라이언트가 던졌든 원시 DynamoDB JSON이며, 공짜입니다. 이것이 없다면 낙관적 동시성 업데이트가 왜 실패했는지 알아내는 정직한 방법은 읽기 비용이 들고 이미 다시 낡았을 수도 있는 후속 GetItem뿐입니다.

DynamoDB JSON 변환기는 그 페이로드를 평범한 JS 객체로, 또 그 반대로 바꿔 주므로 실제 항목에서 픽스처를 만드는 가장 빠른 길입니다. 애초에 그 항목을 라이브 테이블에서 가져오려면 DynoTable을 다운로드하세요.

관련 가이드

참고 자료

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

Console 없이 DynamoDB 작업하기

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

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