DynamoDB Streams
DynamoDB Streams는 변경 데이터 캡처(CDC) 로그입니다: 테이블의 모든 삽입, 업데이트, 삭제가 순서대로 캡처되어, 반응할 수 있는 레코드 스트림이 됩니다. 테이블을 폴링하지 않고 이벤트 소스로 바꾸는 방법입니다.
감사 로그 시나리오에서는 민감한 이벤트가 도착하는 순간 반응하고 싶을 것입니다 — 누군가 송장을 내보내거나 관리자 역할을 부여할 때 알림을 발생시키되, 타이머로 테이블을 스캔하지 않고서 말입니다. Streams는 그것의 푸시 측면입니다.
DynamoDB Streams는 어떻게 작동하나요?
DynamoDB Streams는 테이블의 모든 삽입, 업데이트, 삭제를 시간순으로 정렬되고 중복이 제거된 레코드 로그로 캡처하며, 최대 24시간 동안 보존됩니다. StreamViewType으로 각 레코드가 담을 내용을 선택하고(키, 새 이미지, 이전 이미지, 또는 둘 다), Lambda 트리거로 스트림을 소비하여 폴링 없이 항목 변경에 반응할 수 있습니다.
- Streams는 항목 수준 변경을 캡처합니다 — 시간순으로 정렬되고 중복이 제거된 로그로, 최대 24시간 보존됩니다.
- 각 레코드가 무엇을 담을지 선택합니다 —
StreamViewType을 통해: 키만, 새 이미지, 이전 이미지, 또는 이전과 새 이미지 모두. - 레코드는 파티션 키 내에서 정렬되며, 스트림은 테이블과 같은 방식으로 샤딩됩니다.
- 네이티브 소비자는 Lambda입니다 — 새 레코드 배치마다 실행되는 트리거이며, 더 풍부한 팬아웃을 위한 대안으로 Kinesis Data Streams가 있습니다.
문제: 폴링 없이 반응하기
"role.granted 이벤트가 쓰여지면 알려줘"가 필요합니다. 순진한 접근은 매분 새
이벤트를 스캔하는 예약 작업입니다 — 이는 매번 최근 파티션 전체를 읽고, 용량을
소모하며, 항상 최소 1분은 늦습니다.
실제로 원하는 것은 푸시입니다: DynamoDB가 항목이 변경되는 순간 알려주는 것입니다. 그것이 바로 Streams가 제공하는 것이며, 변경 레코드가 당신이 찾아다니는 대신 코드로 전달됩니다.
Streams의 작동 방식
AWS 문서에 따르면, DynamoDB Streams는 네이티브 Lambda 통합과 함께 "최대 24시간 동안 중복이 제거되고 시간순으로 정렬된 변경 로그"를 저장합니다 (change data capture for DynamoDB). 각 레코드는 하나의 항목 수준 수정을 기술합니다.
스트림을 활성화할 때 **StreamViewType**을 고르며, 이는 각 레코드가 변경된
항목을 얼마나 담을지 제어합니다:
| StreamViewType | each record contains |
|---|---|
| KEYS_ONLY | only the key attributes of the changed item |
| NEW_IMAGE | the entire item as it looks after the change |
| OLD_IMAGE | the entire item as it looked before the change |
| NEW_AND_OLD_IMAGES | both the before and after images |
레코드는 각 파티션 키 내에서 정렬되며 스트림은 테이블과 같은 파티션 구조에 따라 샤딩됩니다. 보존 기간은 24시간입니다 — Streams는 반응 버퍼이지 영구 기록이 아닙니다. 내구성 있는 기록을 위해서는 이벤트 자체를 저장합니다(바로 우리의 audit-log 테이블이 이미 그렇습니다).
네이티브 소비자는 Lambda 트리거입니다: DynamoDB가 새 스트림 레코드가 도착하면 그 배치와 함께 함수를 호출합니다.
실전 예시: 민감한 감사 이벤트에 알림 보내기
audit-log 테이블에 NEW_IMAGE로 스트림을 붙이면, 각 레코드가 전체 새 이벤트를
담습니다. Lambda가 배치를 소비하고 중요한 레코드만 전달합니다:
| stream record (NEW_IMAGE) | consumer action | ||
|---|---|---|---|
| TENANT#acme | EVENT#…#a2 | action=invoice.export | send to SIEM |
| TENANT#globex EVENT#…#b9 action=role.granted | page on-call | ||
| TENANT#acme | EVENT#…#a1 | action=login.success | ignore |
함수는 테이블을 전혀 건드리지 않습니다 — 스트림이 건네주는 것에만 순전히 반응합니다. 폴링도 스캔도 없으며, 쓰기 후 수 초 이내에 알림이 발생합니다. 레코드는 파티션 키별로 정렬되므로, 한 테넌트의 모든 이벤트는 쓰여진 순서대로 도착합니다.
이는 또한 다운스트림 사본을 유지하는 표준 방법이기도 합니다: 스트림 소비자가 각 이벤트를 전문(full-text) 감사 검색을 위해 OpenSearch로 투영하거나, 카운트를 집계할 수 있습니다 — 모두 같은 변경 로그에서 파생됩니다.
DynoTable에서 해보기
스트림 소비자를 연결하기 전에, Lambda가 받게 될 항목의 정확한 형태를 알아야
합니다 — 어떤 속성이 존재하는지, 중첩된 맵과 리스트가 어떻게 보이는지,
NEW_IMAGE 레코드가 실제로 무엇을 담을지.
샘플 항목을 일반 JSON과 스트림 레코드가 사용하는 속성-값 형태 사이에서 변환하려면,
DynamoDB JSON Converter가 브라우저에서
처리해줍니다. 그리고 DynoTable에서는 전체 항목을 — DynamoDB-JSON 형태를 포함해 —
직접 확인할 수 있으므로, 필드 형태를 추측하는 대신 실제 데이터에 맞춰 NEW_IMAGE
레코드를 모델링할 수 있습니다.

소비자를 로컬에서 테스트한다면, 테이블을 DynamoDB Local에 대고 실행한 뒤 같은 방식으로 검사하세요 — DynamoDB Local에 연결하기를 참고하세요.
함정과 다음 단계
- 24시간은 백로그가 아닙니다. 소비자가 하루 동안 다운되면 레코드는 만료되어 사라집니다. Streams는 거의 실시간 반응을 위한 것이지 내구성 있는 재생을 위한 것이 아닙니다 — 기록을 위해서는 이벤트 자체를 보관하세요.
- 필요한 가장 작은
StreamViewType을 고르세요.NEW_AND_OLD_IMAGES는 payload를 두 배로 만듭니다. 항목을 다시 읽으러 갈 키만 필요하다면KEYS_ONLY가 더 저렴합니다. - 정렬은 파티션 키별이지 전역이 아닙니다. 서로 다른 두 테넌트의 이벤트는 서로 간에 정렬 보장이 없습니다 — 오직 한 테넌트의 파티션 내에서만 있습니다.
- TTL 삭제는 스트림 레코드로 나타납니다 — 시스템 속성 마커와 함께이며, 이것이 만료되는 항목을 아카이브하는 방법입니다 — DynamoDB TTL을 참고하세요.
Streams는 감사 로그를 이벤트 소스로 바꿉니다. 다음 운영 관심사는 항목 수명의 반대편 끝 — 오래된 이벤트를 자동으로 만료시키는 것이며, DynamoDB TTL이 그 역할을 합니다.
Lambda 코드를 한 줄 쓰기 전에 스트림 소비자가 받게 될 정확한 항목 형태를 검사하려면 DynoTable을 다운로드하세요.


