소개
재시도한 주문 제출은 기존 금액을 덮어쓰면 안 됩니다. 오래된 결제 수정 요청도 더 최신 버전을 덮어쓰면 안 됩니다. 실제 조건부 API 요청을 보내 중복과 오래된 버전이 거부되는 모습을 관찰하고, 유효한 새 주문은 여전히 성공함을 입증합니다.
타입이 지정된 항목과 수정 표현식을 포함한 주문용 DynamoDB 테이블 생성 실습을 먼저 완료하세요. 이 새 단원은 이전 테이블이나 자격 증명을 재사용하지 않습니다. /home/labex/project, 공식 AWS CLI, AWS View를 사용하세요.
인증 시험 관련 주제
| 인증 | 시험 태스크 | 실습 내용 |
|---|---|---|
| Developer – Associate (DVA-C02) | 태스크 1.3, 태스크 1.1 | DynamoDB 조건부 쓰기와 낙관적 동시성 제어. |
실습 개요

첫 주문 생성 보호
이 단계에서는 기본 키가 사용되지 않을 때만 주문을 생성합니다. 조건 없는 PutItem은 해당 키의 전체 항목을 교체합니다. 조건은 쓰기와 함께 현재 상태를 원자적으로 확인하므로, 별도의 읽기 후 쓰기 확인은 필요하지 않습니다. 별도로 확인하면 경쟁 상태가 발생할 수 있습니다.
준비된 일회성 연결을 사용해 주문 ID를 String 파티션 키로 사용하는 독립 테이블을 생성하세요.
cd /home/labex/project
aws sts get-caller-identity
aws dynamodb create-table \
--table-name labex-d04-orders \
--attribute-definitions AttributeName=order_id,AttributeType=S \
--key-schema AttributeName=order_id,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
aws dynamodb wait table-exists --table-name labex-d04-orders
일반적인 API 요청 파일을 작성하세요. 따옴표로 감싼 heredoc은 JSON을 그대로 기록합니다. file://를 사용하면 CLI가 큰 요청 데이터를 셸 따옴표로 감싸지 않고 로드할 수 있습니다. attribute_not_exists(order_id)는 해당 키를 가진 항목이 없을 때만 쓰기를 허용합니다. version: 1 필드는 3단계에서 수정을 보호하는 데 사용할 애플리케이션 값입니다. DynamoDB가 자동으로 증가시키지는 않습니다.
cat > create-order.json <<'JSON'
{
"TableName": "labex-d04-orders",
"Item": {
"order_id": {
"S": "O100"
},
"amount": {
"N": "145"
},
"status": {
"S": "NEW"
},
"version": {
"N": "1"
}
},
"ConditionExpression": "attribute_not_exists(order_id)"
}
JSON
aws dynamodb put-item --cli-input-json file://create-order.json
aws dynamodb get-item \
--table-name labex-d04-orders \
--key '{"order_id":{"S":"O100"}}' \
--consistent-read
O100에는 amount 145, NEW, version 1이 있습니다. 이 새 키는 부재 조건을 만족합니다. 위쪽 AWS View에서 O100을 펼쳐 실제 항목을 검사하세요.
새 주문을 막지 않으면서 중복 거부
이 단계에서는 다른 금액으로 같은 주문 ID를 다시 제출합니다. 안전하지 않은 덮어쓰기는 O100을 999로 변경할 것입니다. 조건은 이를 거부하고 145를 보존해야 합니다. 거부된 중복은 오류 응답이며, 자동으로 성공한 멱등 애플리케이션 응답이 되는 것은 아닙니다. 애플리케이션은 그 오류의 처리 방식을 결정해야 합니다.
생성 요청을 재시도해도 이미 수락한 주문 금액을 교체해서는 안 됩니다. 항목이 없다는 조건은 쓰기 시점에 이 결정을 보호합니다. 읽기 필터는 반환 항목을 바꾸지만 쓰기 조건은 변경 자체를 허용할지 결정합니다. 경쟁하는 각 요청에 조건이 필요합니다.
같은 키와 부재 조건을 가진 경쟁 요청을 작성하세요.
cat > duplicate-order.json <<'JSON'
{
"TableName": "labex-d04-orders",
"Item": {
"order_id": {
"S": "O100"
},
"amount": {
"N": "999"
},
"status": {
"S": "NEW"
},
"version": {
"N": "1"
}
},
"ConditionExpression": "attribute_not_exists(order_id)"
}
JSON
같은 키로 쓰기를 반복하세요. || true는 이 예상된 실패 후에도 터미널 작업을 계속할 수 있게 합니다. ConditionalCheckFailedException을 확인하세요. 연결 또는 권한 오류에는 다른 진단이 필요합니다.
aws dynamodb put-item --cli-input-json file://duplicate-order.json || true
aws dynamodb get-item \
--table-name labex-d04-orders \
--key '{"order_id":{"S":"O100"}}' \
--consistent-read
ConditionalCheckFailedException을 확인한 뒤, amount 145와 version 1이 변경되지 않았는지 확인하세요. 같은 보호 조건이 모든 쓰기를 거부하는 것이 아니라 다른 주문 ID는 허용함을 입증하세요.
cat > second-order.json <<'JSON'
{
"TableName": "labex-d04-orders",
"Item": {
"order_id": {
"S": "O101"
},
"amount": {
"N": "200"
},
"status": {
"S": "NEW"
},
"version": {
"N": "1"
}
},
"ConditionExpression": "attribute_not_exists(order_id)"
}
JSON
aws dynamodb put-item --cli-input-json file://second-order.json
aws dynamodb get-item \
--table-name labex-d04-orders \
--key '{"order_id":{"S":"O101"}}' \
--consistent-read
O101은 amount 200, NEW, version 1로 생성됩니다. AWS View에는 두 주문이 표시되며 O100의 amount는 145로 유지됩니다. 이 작업들은 키로 지정한 레코드의 의도하지 않은 덮어쓰기를 방지합니다. 여러 항목의 트랜잭션이나 결제 중복 제거를 구현하지는 않습니다.
예상 버전으로 수정 보호
이 단계에서는 읽었던 버전에만 상태 전환을 적용합니다. 낙관적 버전 조건은 더 최신 결정을 조용히 덮어쓰는 대신 오래된 요청을 거부합니다. 쓰기는 원자적으로 version 1을 확인하고 PAID를 설정하며 version을 2로 증가시킵니다.

이 UpdateItem 요청을 작성하고 실행하세요. 표현식 이름 별칭은 속성 이름을 안전하게 처리하고, 타입이 지정된 값은 예상 버전과 다음 버전을 제공합니다.
cat > pay-order.json <<'JSON'
{
"TableName": "labex-d04-orders",
"Key": {
"order_id": {
"S": "O100"
}
},
"UpdateExpression": "SET #state = :paid, #version = :next",
"ConditionExpression": "#version = :expected",
"ExpressionAttributeNames": {
"#state": "status",
"#version": "version"
},
"ExpressionAttributeValues": {
":paid": {
"S": "PAID"
},
":next": {
"N": "2"
},
":expected": {
"N": "1"
}
},
"ReturnValues": "ALL_NEW"
}
JSON
aws dynamodb update-item --cli-input-json file://pay-order.json
ALL_NEW는 amount 145와 함께 PAID 및 version 2를 보여 줍니다. 여전히 version 1을 예상하는 변경되지 않은 요청을 다시 실행하세요.
aws dynamodb update-item --cli-input-json file://pay-order.json || true
aws dynamodb get-item \
--table-name labex-d04-orders \
--key '{"order_id":{"S":"O100"}}' \
--consistent-read
ConditionalCheckFailedException을 확인하세요. 최종 읽기는 PAID, version 2, amount 145를 유지합니다. O101은 NEW/version 1로 남습니다. 운영 환경의 호출자는 오래된 예상 버전으로 끝없이 재시도하는 대신 현재 상태를 다시 읽고 다른 수정이 적절한지 결정해야 합니다.

소유한 주문 테이블만 제거
이 단계에서는 최종 상태를 확인하고 소유한 테이블을 정리합니다. 저장된 JSON 파일은 요청을 설명하지만, 실제 효과는 서비스의 항목이 입증합니다. 테이블이 제거되었다는 증거로 명령 기록이나 오류 로그를 사용하지 마세요.
aws dynamodb scan --table-name labex-d04-orders --consistent-read
O100은 PAID/version 2/amount 145이고 O101은 NEW/version 1/amount 200입니다. 이 테이블만 삭제하고 부재를 기다리세요.
aws dynamodb delete-table --table-name labex-d04-orders
aws dynamodb wait table-not-exists --table-name labex-d04-orders
aws dynamodb list-tables
aws dynamodb get-item \
--table-name labex-d04-reference \
--key '{"id":{"S":"platform"}}' \
--consistent-read
note가 keep unchanged인 참조 테이블만 남습니다. 이 성공한 조회들은 삭제를 연결 실패와 구별합니다. AWS View에서 주문 테이블이 제거됩니다. 일회성 연결은 이 VM과 함께 종료됩니다.
요약
부재 조건으로 새 주문 생성을 보호하고 저장된 금액을 바꾸지 않고 중복이 거부되는 모습을 관찰했으며, 다른 새 키를 허용했습니다. 그런 다음 예상 버전 조건으로 상태 전환 하나를 적용하고 오래된 요청의 재실행을 거부했습니다. 실제 최종 항목 값을 검사한 뒤 소유한 테이블만 삭제했습니다.
자세한 내용은 AWS 공식 문서를 참고하세요: 보호된 쓰기를 위한 조건 표현식.



