AWS Certified Developer - Associate (DVA-C02) #4 Domain 1-3 AWS 서비스로 개발: DynamoDB 개발

6 분 소요

#3 API Gateway에서 관문을 정리했으니, 이번에는 그 뒤에 오는 데이터입니다. 서버리스 애플리케이션의 기본 데이터베이스는 DynamoDB입니다. SAA가 “언제 DynamoDB를 고르는가"를 물었다면, DVA는 한 단계 더 들어가 **“키를 어떻게 설계하고, 쓰기 충돌을 어떻게 막고, 변경을 어떻게 흘려보내는가”**를 코드 수준으로 묻습니다.

키 설계: 파티션 키와 정렬 키 #

DynamoDB의 모든 테이블은 **기본 키(Primary Key)**로 항목을 식별합니다.

키 구성의미
파티션 키만파티션 키 값이 곧 항목의 고유 식별자
파티션 키 + 정렬 키같은 파티션 키 안에서 정렬 키로 여러 항목 정렬,범위 조회

파티션 키는 데이터가 물리적으로 분산되는 기준입니다. 특정 키에 트래픽이 몰리면(핫 파티션) 그 파티션만 스로틀링됩니다. 그래서 시험은 “고르게 분산되는 파티션 키"를 좋은 설계로 봅니다. 카디널리티가 높고 접근이 고른 속성을 파티션 키로 골라야 합니다.

Query vs Scan #

  • Query. 파티션 키로 항목을 찾고, 정렬 키로 범위를 좁힙니다. 효율적입니다.
  • Scan. 테이블 전체를 읽습니다. 느리고 비쌉니다.

시험 원칙: Scan은 피하고 Query를 쓴다. “특정 속성으로 자주 조회해야 하는데 그게 키가 아니다"라면 답은 Scan이 아니라 **인덱스(GSI)**입니다.

LSI vs GSI #

키가 아닌 속성으로 조회하려면 보조 인덱스가 필요합니다. 두 종류의 차이는 시험 단골입니다.

구분LSI(로컬 보조 인덱스)GSI(글로벌 보조 인덱스)
파티션 키테이블과 동일다른 속성 가능
정렬 키다른 속성다른 속성
생성 시점테이블 생성 시에만언제든 추가,삭제
일관성강력한 일관성 가능최종 일관성만
용량테이블 용량 공유자체 용량

핵심 한 줄: LSI는 테이블 만들 때만, 같은 파티션 키. GSI는 언제든, 다른 파티션 키. 대부분의 “다른 속성으로 조회” 요구는 유연한 GSI가 답입니다. LSI는 같은 파티션 키 안에서 다른 정렬 기준이 필요하고 강력한 일관성이 필요할 때만 고려합니다.

읽기 일관성 #

DynamoDB 읽기는 두 가지 일관성을 제공합니다.

  • 최종 일관성 읽기(기본). 가장 최근 쓰기가 아직 반영되지 않았을 수 있음. 비용이 절반.
  • 강력한 일관성 읽기. 항상 최신 값. 비용 2배, GSI에서는 불가.

용량 모드와 스로틀링 #

모드적합한 경우
온디맨드(On-Demand)트래픽이 예측 불가,간헐적. 용량 관리 불필요
프로비저닝(Provisioned)트래픽이 예측 가능. RCU/WCU를 미리 지정, Auto Scaling 가능

프로비저닝 모드에서 용량을 초과하면 ProvisionedThroughputExceededException(스로틀링)이 납니다. SDK는 기본적으로 지수 백오프로 자동 재시도합니다. 짧은 순간의 버스트는 적응형 용량(adaptive capacity)이 일부 흡수합니다. “스로틀링이 난다"의 답은 보통 용량 상향, 키 분산 개선, 지수 백오프 재시도입니다.

조건부 쓰기와 낙관적 잠금 #

DVA에서 가장 중요한 개발 패턴입니다. **조건부 쓰기(Conditional Write)**는 조건이 참일 때만 쓰기를 수행합니다.

  • 항목이 없을 때만 생성. attribute_not_exists(PK) 조건으로 중복 생성을 막습니다(멱등성에 활용).
  • 낙관적 잠금(Optimistic Locking). 항목에 version 속성을 두고, “내가 읽은 버전과 같을 때만 갱신"이라는 조건으로 씁니다. 두 클라이언트가 동시에 같은 항목을 고치면 한쪽만 성공하고 다른 쪽은 **ConditionalCheckFailedException**을 받아 재시도합니다.
UpdateItem
  SET stock = stock - 1, version = version + 1
  ConditionExpression: version = :expectedVersion
  • 원자적 카운터(Atomic Counter). SET views = views + 1처럼 조건 없이 증가시킵니다. 충돌 검증이 필요 없는 단순 카운트에 적합합니다.

낙관적 잠금 = 조건부 쓰기 + 버전 속성. 동시 수정 충돌을 다루는 문항의 정답입니다.

DynamoDB Streams #

테이블의 항목 변경(생성,수정,삭제)을 시간 순서로 캡처하는 변경 로그입니다.

  • Lambda를 이벤트 소스 매핑으로 연결해 변경에 반응합니다(예: 새 주문이 들어오면 알림 발송).
  • 스트림 레코드에는 변경 전/후 이미지를 담을 수 있습니다(StreamViewType).
  • 글로벌 테이블(다중 리전 복제)의 기반이기도 합니다.

“DynamoDB에 항목이 추가되면 자동으로 후속 처리를 하라"의 답은 Streams + Lambda입니다.

TTL #

항목에 만료 시각(epoch) 속성을 지정하면, DynamoDB가 만료된 항목을 자동 삭제합니다. 세션,임시 데이터 정리에 쓰며, 삭제는 즉시가 아니라 백그라운드에서 이뤄집니다(보통 48시간 이내). TTL 삭제도 Streams로 캡처됩니다.

DAX #

**DynamoDB Accelerator(DAX)**는 DynamoDB 전용 인메모리 캐시로, 읽기 응답을 마이크로초 수준으로 낮춥니다.

  • 읽기가 매우 많고 지연에 민감한 워크로드에 적합합니다.
  • 애플리케이션 코드 변경이 거의 없습니다(DAX 클라이언트로 교체).
  • 일반 캐시(ElastiCache)와의 구분이 함정입니다. “DynamoDB 읽기를 마이크로초로"는 DAX, “범용 인메모리 캐시"는 ElastiCache입니다.

시험 출제 패턴 #

  • “키가 아닌 속성으로 자주 조회해야 한다.” → GSI(Scan 아님).
  • “테이블 생성 후에 인덱스를 추가하려 한다.” → GSI(LSI는 생성 시에만).
  • “두 사용자가 동시에 같은 항목을 수정한다. 충돌을 막으려면?” → 조건부 쓰기 + version(낙관적 잠금).
  • “같은 주문이 두 번 들어와도 한 번만 생성하려면?” → attribute_not_exists 조건부 쓰기.
  • “항목이 추가되면 자동으로 후속 작업.” → DynamoDB Streams + Lambda.
  • ProvisionedThroughputExceededException이 난다.” → 용량 상향,키 분산,지수 백오프.
  • “DynamoDB 읽기 지연을 마이크로초로.” → DAX.
  • “세션 데이터를 일정 시간 후 자동 삭제.” → TTL.

정리 #

이번 글에서 잡은 것:

  • 파티션 키는 분산 기준. 고른 분산 = 좋은 키. 조회는 Scan 아닌 Query
  • LSI(생성 시,같은 파티션 키,강력한 일관성) vs GSI(언제든,다른 파티션 키,자체 용량,최종 일관성)
  • 조건부 쓰기 + version = 낙관적 잠금, attribute_not_exists는 멱등 생성
  • Streams + Lambda로 변경에 반응, TTL로 자동 만료
  • 스로틀링은 용량,키 분산,지수 백오프로, 읽기 지연은 DAX

다음: Domain 1-4 메시징과 이벤트 #

서버리스는 컴포넌트를 느슨하게 잇는 비동기 메시징으로 확장됩니다. #5 메시징과 이벤트에서는 SQS(표준 vs FIFO), SNS, 둘을 결합한 팬아웃, EventBridge의 이벤트 라우팅, 그리고 Step Functions의 워크플로 오케스트레이션을 정리하겠습니다.

X