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 updateGenre도 Awards도 없던 항목에 대해 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.message는Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year로 설정됩니다.문서 클라이언트는 또 다른 절충입니다.
@aws-sdk/lib-dynamodb는 네이티브 JS 값을 받고 응답을 언마셜링해 주지만, 그 문자열 안전성을 대가로 치릅니다.@aws-sdk/util-dynamodb의marshall({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을 다운로드하세요.
관련 가이드
- DynamoDB 업데이트 표현식 —
SET,ADD,REMOVE,DELETE와 관용구. - ReturnValues 이해하기 — 각
ReturnValues옵션이 주는 것. - "Attribute name is a reserved keyword" — 여기서 별칭 맵이 선택 사항이 아닌 이유.
- "Invalid UpdateExpression" 문법 오류 — 흔한 SET/ADD 문법 실수 해독.
참고 자료
- UpdateItem — Amazon DynamoDB API Reference
- UpdateItemCommand — AWS SDK for JavaScript v3 Reference
- Update expressions — Amazon DynamoDB Developer Guide
위에 링크된 공식 AWS 문서를 기준으로 2026-07-28에 마지막으로 검증했습니다.