주문 상태용 인덱스 추가

AWSBeginner
지금 연습하기

소개

포장 팀에는 여러 고객의 NEW 주문이 필요합니다. 고객 및 날짜 기본 키로는 기본 테이블 Query 한 번에 이 접근 패턴을 처리할 수 없습니다. 글로벌 보조 인덱스를 추가하고 투영을 선택한 뒤 실제 큐를 쿼리하고, 기본 테이블 쓰기 후 포장된 주문이 인덱스 파티션 사이에서 이동하는 모습을 관찰합니다.

파티션 키와 정렬 키로 주문 쿼리 실습을 먼저 완료하세요. 이 새 VM에는 기본 테이블, 가상 레코드, 참조 테이블, 애플리케이션 틀만 준비되어 있습니다. 인덱스 생성과 조회 설정은 직접 수행합니다. /home/labex/project, 공식 AWS CLI, AWS View를 사용하세요.

인증 시험 관련 주제

인증 시험 태스크 실습 내용
Solutions Architect – Associate (SAA-C03) 태스크 3.3 DynamoDB 보조 인덱스와 속성 프로젝션.
Developer – Associate (DVA-C02) 태스크 1.3 DynamoDB 보조 인덱스와 속성 프로젝션.
Data Engineer – Associate (DEA-C01) 태스크 2.4 기초 실습: DynamoDB 보조 인덱스와 속성 프로젝션.

실습 개요

AWS4 아이콘을 사용하고 draw.io로 내보낸 개념도: GSI는 같은 주문을 상태 및 날짜별로 조회하여 여러 고객의 NEW 주문 목록을 제공합니다. 기본 테이블 쓰기가 이 파생 보기를 갱신합니다.

다른 접근 패턴 추가

이 단계에서는 여러 고객의 NEW 주문이 필요한 포장 큐를 위한 인덱스를 추가합니다. 준비된 테이블은 customer_id와 order_key를 사용합니다. status는 파티션 키가 아니므로 기본 테이블 Query로 상태에 따라 모든 고객을 선택할 수 없습니다. 글로벌 보조 인덱스 (GSI)는 같은 항목을 다른 파티션 키와 정렬 키로 볼 수 있게 합니다.

고객 조회와 포장 대기열은 같은 주문으로 서로 다른 업무 질문에 답합니다. GSI를 사용하면 대기열이 여러 고객의 상태 파티션을 직접 선택할 수 있습니다. 고객 Query에 상태 필터를 추가해도 해당 고객 파티션만 검사하며 대기열에 필요한 별도의 접근 경로를 만들지는 못합니다.

같은 항목에서 선택한 속성을 기본 테이블 키와 상태 인덱스 키로 배열한 모습.

새 접근 패턴에 집중할 수 있도록 주문 테이블과 세 개의 가상 레코드가 독립적으로 준비되어 있습니다. 먼저 검사하세요. 실습에서 다루는 상태 수정 외에는 모든 레코드를 그대로 유지하세요.

cd /home/labex/project
aws dynamodb describe-table --table-name labex-d03-orders --query 'Table.KeySchema'
aws dynamodb scan --table-name labex-d03-orders --consistent-read

status를 파티션 키, order_key를 정렬 키로 사용하는 StatusDateIndex를 추가하세요. 투영은 인덱스가 저장할 속성을 결정합니다. INCLUDE는 amount를 추가합니다. 테이블 키와 인덱스 키는 자동으로 포함됩니다. 키가 아닌 source 속성은 기본 테이블에만 남습니다. AWS에서는 인덱스 스토리지와 쓰기에 비용이 발생합니다.

aws dynamodb update-table \
  --table-name labex-d03-orders \
  --attribute-definitions AttributeName=status,AttributeType=S AttributeName=order_key,AttributeType=S \
  --global-secondary-index-updates '[{"Create":{"IndexName":"StatusDateIndex","KeySchema":[{"AttributeName":"status","KeyType":"HASH"},{"AttributeName":"order_key","KeyType":"RANGE"}],"Projection":{"ProjectionType":"INCLUDE","NonKeyAttributes":["amount"]}}}]'
aws dynamodb describe-table \
  --table-name labex-d03-orders \
  --query 'Table.GlobalSecondaryIndexes[].{Name:IndexName,State:IndexStatus,Keys:KeySchema,Projection:Projection}'

StatusDateIndex가 ACTIVE로 표시될 때만 계속하세요. AWS에서는 생성에 시간이 걸릴 수 있습니다. 인덱스가 아직 생성 중이면 describe 명령을 반복하세요. table-exists waiter는 새 인덱스가 준비되었음을 입증하지 않습니다. 서비스가 인덱스를 보고하면 AWS View의 테이블 카드 안에 표시됩니다.

상태 인덱스 쿼리 및 투영 검사

이 단계에서는 대체 키를 통해 NEW 큐를 읽습니다. --index-name은 GSI를 선택합니다. 키 조건에는 status 동등 조건을 사용합니다. 기본 테이블 스캔에 필터를 적용하면 관련 없는 항목을 먼저 읽게 됩니다.

aws dynamodb query \
  --table-name labex-d03-orders \
  --index-name StatusDateIndex \
  --key-condition-expression '#state = :state' \
  --expression-attribute-names '{"#state":"status"}' \
  --expression-attribute-values '{":state":{"S":"NEW"}}'

인덱스는 amount를 포함한 Ada의 O100과 Bob의 O200을 반환합니다. source 속성은 투영되지 않았으므로 없습니다. 기본 테이블의 추가 속성을 가져오려면 별도의 키 기반 읽기가 필요합니다.

AWS에서 GSI 수정은 비동기이며 GSI 읽기는 최종 일관성을 사용합니다. GSI Query에 --consistent-read를 추가하지 마세요. 해당 요청은 ValidationException으로 거부됩니다. 최근 쓰기가 아직 인덱스에 반영되지 않았다면 기다린 뒤 읽기를 반복하세요. 오래된 결과를 쓰기 실패의 증거로 간주하지 마세요.

이전 실습에서 사용한 제공된 order-lookup 애플리케이션은 일반적인 lookup.json 설정을 읽습니다. 이 인덱스와 NEW 큐를 선택하도록 요청을 작성하세요. 따옴표로 감싼 heredoc은 JSON을 그대로 기록하며, ConsistentRead: false는 인덱스 읽기 선택을 명시합니다.

cat > lookup.json <<'JSON'
{
  "paginate": true,
  "request": {
    "TableName": "labex-d03-orders",
    "IndexName": "StatusDateIndex",
    "KeyConditionExpression": "#state = :state",
    "ExpressionAttributeNames": {
      "#state": "status"
    },
    "ExpressionAttributeValues": {
      ":state": {
        "S": "NEW"
      }
    },
    "ConsistentRead": false
  }
}
JSON
./order-lookup

애플리케이션은 같은 두 주문을 반환합니다. AWS View에서 StatusDateIndex를 펼쳐 실제 투영과 조회 결과를 비교하세요. 상태 큐에서는 두 고객을 모두 포함하는 것이 올바릅니다. 특정 고객만 보는 화면에는 다른 키 패턴이 필요합니다.

주문이 상태 큐 사이에서 이동하는 모습 관찰

이 단계에서는 O100을 포장하고 인덱스 항목이 이동하는 모습을 관찰합니다. GSI는 기본 테이블 쓰기를 따라갑니다. 인덱스에 직접 쓰지는 않습니다. status를 수정하면 기본 키는 그대로 유지되지만 항목의 인덱스 파티션이 바뀝니다.

aws dynamodb update-item \
  --table-name labex-d03-orders \
  --key '{"customer_id":{"S":"ada"},"order_key":{"S":"2026-10-01#O100"}}' \
  --update-expression 'SET #state = :state' \
  --expression-attribute-names '{"#state":"status"}' \
  --expression-attribute-values '{":state":{"S":"PACKED"}}' \
  --return-values ALL_NEW
./order-lookup

이제 NEW 큐에는 Bob의 O200만 있습니다. PACKED를 쿼리해 O100이 서비스에서 사라진 것이 아니라 O101과 함께 표시되는지 확인하세요.

aws dynamodb query \
  --table-name labex-d03-orders \
  --index-name StatusDateIndex \
  --key-condition-expression '#state = :state' \
  --expression-attribute-names '{"#state":"status"}' \
  --expression-attribute-values '{":state":{"S":"PACKED"}}'

PACKED에서 Ada의 O100과 O101을 확인하세요. AWS에서는 인덱스 전파가 보일 때까지 이 읽기를 반복하세요. 수정 응답과 기본 테이블의 일관성 읽기가 즉시 확인할 수 있는 항목 상태의 기준입니다. GSI는 최종 일관성을 가진 파생 뷰입니다. AWS View는 O100의 새 상태와 O200만 포함한 NEW 조회 결과를 보여 줍니다.

활성 인덱스에는 대체 키와 amount가 투영되고 NEW 큐에는 Bob의 주문만 포함됩니다.

소유한 인덱스와 테이블 제거

이 단계에서는 대체 뷰를 제거한 다음 소유한 테이블을 제거합니다. 인덱스 삭제는 기본 테이블 항목을 그대로 유지합니다. 테이블 삭제는 항목과 남아 있는 인덱스를 제거합니다. 소유한 리소스만 이름으로 지정하세요.

aws dynamodb update-table \
  --table-name labex-d03-orders \
  --global-secondary-index-updates '[{"Delete":{"IndexName":"StatusDateIndex"}}]'
aws dynamodb describe-table \
  --table-name labex-d03-orders \
  --query 'Table.GlobalSecondaryIndexes'

인덱스 목록이 비면 계속하세요. AWS에서는 삭제가 진행되는 동안 DescribeTable을 반복하세요. 이 시점에도 테이블에는 세 항목이 남아 있습니다.

aws dynamodb scan --table-name labex-d03-orders --consistent-read --select COUNT
aws dynamodb delete-table --table-name labex-d03-orders
aws dynamodb wait table-not-exists --table-name labex-d03-orders
aws dynamodb list-tables
aws dynamodb get-item \
  --table-name labex-d03-reference \
  --key '{"id":{"S":"platform"}}' \
  --consistent-read

주문 테이블과 그 인덱스는 없고, 참조 note는 keep unchanged로 유지됩니다. 성공한 목록 조회와 참조 읽기는 정리 완료를 서비스 연결이 끊긴 상태와 구별합니다. AWS View에는 관련 없는 참조만 남습니다. 일회성 연결은 VM과 함께 종료됩니다.

요약

여러 고객의 상태 큐를 위한 GSI를 추가하고 투영할 속성을 선택한 뒤, 실제 쿼리에 인덱스 키를 사용했습니다. 기본 테이블의 상태 수정은 기본 키를 변경하지 않고 주문을 인덱스 파티션 사이에서 이동시켰습니다. 즉시 확인 가능한 기본 테이블 상태와 최종적으로 전파되는 인덱스를 구별한 다음, 소유한 인덱스와 테이블만 삭제했습니다.

자세한 내용은 AWS 공식 문서를 참고하세요: 글로벌 보조 인덱스, 프로젝션 및 일관성.