기준: DVA-C02 시험 가이드오류 제보
SQS와 SNS는 메시지를 "처리하면 사라지는" 것으로 다룬다. 소비자가 가져가면 큐에서 지워지고, 발행되면 푸시되고 잊힌다. 그런데 어떤 데이터는 본질적으로 다르다. 사용자 클릭 스트림, IoT 센서 측정값, 애플리케이션 로그는 끊임없이 흐르고, 여러 시스템이 같은 데이터를 각자 다른 목적으로 동시에 읽고 싶어 하며, 어제 데이터를 오늘 새 알고리즘으로 다시 처리하고 싶기도 하다. Amazon Kinesis는 이런 스트리밍 데이터를 위해, 메시지를 소비 후 버리는 대신 로그에 추가만 하고 일정 기간 보존하는 전혀 다른 모델을 택했다.
DVA-C02에서 Kinesis는 "SQS·SNS와 무엇이 다른가"를 묻는 비교 시나리오로 나오기 쉽다. 샤드 처리량 숫자, Data Streams와 Firehose의 역할 구분, Enhanced Fan-Out 같은 디테일도 출제 범위에 든다. 이번 글은 Kinesis가 왜 "추가 전용 로그(append-only log)"라는 구조를 택했는지, 샤드와 파티션 키가 어떻게 순서와 확장을 동시에 푸는지, Data Streams와 Firehose가 갈라지는 이유, Kinesis가 SQS와 근본적으로 다른 지점이 무엇인지를 다룬다.
Kinesis Data Streams의 데이터 모델을 한 단어로 줄이면 로그(log) 다. 데이터베이스나 큐가 아니라, 시간 순서대로 레코드를 끝에 추가만 하고 중간을 수정하지 않는 불변(immutable) 시퀀스다. 생산자는 로그의 끝에 레코드를 붙이고, 소비자는 로그의 어느 지점부터든 자기가 원하는 위치(시퀀스 번호)에서 읽기 시작한다. 읽어도 데이터는 사라지지 않고, 보존 기간(기본 24시간, 최대 365일) 동안 그 자리에 남아 있다.
이 단순한 구조가 SQS로는 불가능한 세 가지를 가능하게 한다.
"로그를 데이터 시스템의 중심에 둔다"는 발상은 Kinesis만의 것이 아니라 분산 시스템의 큰 흐름이다. Jay Kreps(Kafka 공동 창시자)의 글 "The Log: What every software engineer should know about real-time data's unifying abstraction"(2013)이 이 사상을 정리했다. 데이터베이스의 복제도, 메시징도, 이벤트 소싱도 결국 "추가 전용 로그"라는 하나의 추상화로 통합된다는 것이다. 이 아이디어의 뿌리는 데이터베이스의 WAL(Write-Ahead Log) 이다. DB는 변경을 데이터 파일에 쓰기 전에 로그에 먼저 기록하는데, 이 로그만 있으면 어떤 상태든 재구성할 수 있다. Kinesis와 Kafka는 이 "로그가 진실의 원천"이라는 통찰을 메시징 시스템 자체로 만든 것이다.
Kinesis와 SQS의 차이는 "소비가 데이터를 파괴하느냐"로 압축된다.
DeleteMessage로 소비가 곧 삭제다(파괴적 읽기).하나의 로그를 한 줄로만 두면 처리량이 그 한 줄의 천장에 묶인다(SQS FIFO에서 본 문제와 같다). Kinesis는 스트림을 여러 샤드(shard) 로 쪼개 병렬화한다. 각 샤드는 독립된 로그이고, 고유한 처리량 한도를 가진다.
| 동작 | 샤드당 한도 |
|---|---|
| 쓰기 | 1 MB/s 또는 1,000 records/s |
| 읽기 (Classic) | 2 MB/s 또는 5 GetRecords 호출/s (모든 소비자 공유) |
| 읽기 (Enhanced Fan-Out) | 2 MB/s 소비자별 전용 |
| 레코드 최대 크기 | 1 MB |
| 보존 | 24시간 ~ 365일 |
여기서 핵심은 파티션 키(partition key) 다. 생산자가 레코드를 보낼 때 파티션 키를 지정하면, Kinesis는 그 키를 해시해서 어느 샤드로 갈지 결정한다. 같은 파티션 키를 가진 레코드는 항상 같은 샤드로 가고, 한 샤드 안에서는 순서가 보존된다. 그래서 "주문 ID를 파티션 키로 쓰면 같은 주문의 이벤트들은 순서대로, 다른 주문들은 여러 샤드에 분산되어 병렬로" 처리된다. SQS FIFO의 MessageGroupId와 같은 발상이다.
import boto3, json
kinesis = boto3.client('kinesis')
kinesis.put_record(
StreamName='clickstream',
Data=json.dumps({'userId': 'U123', 'page': '/checkout'}),
PartitionKey='U123' # 이 사용자의 이벤트는 같은 샤드 → 순서 보존
)⚠️ 함정: 파티션 키 설계를 잘못하면 핫 샤드(hot shard) 가 생긴다. 예를 들어 모든 레코드에 같은 파티션 키를 주면 전부 한 샤드로 몰려, 다른 샤드들이 놀고 있는데도 그 한 샤드가 1MB/s에서 막혀
ProvisionedThroughputExceededException이 난다. 시험에서 "특정 샤드만 스로틀링된다"는 증상은 편향된 파티션 키를 가리키는 단서다. 해결은 키가 고르게 분산되도록(카디널리티 높게) 설계하는 것이다. DynamoDB의 핫 파티션 문제와 같은 구조로, 둘 다 "키 해시로 분산하는 시스템"의 공통 함정이다.
클래식 읽기에서는 "2 MB/s를 모든 소비자가 공유"한다. 소비자가 3개면 각자 약 0.67 MB/s밖에 못 읽고, 모두 GetRecords를 폴링하므로 지연이 200ms~수초까지 늘어난다. Enhanced Fan-Out(EFO) 은 각 소비자에게 2 MB/s 전용 파이프를 HTTP/2 푸시로 제공한다. 소비자가 늘어도 서로 처리량을 빼앗지 않고 지연도 약 70ms로 떨어진다. 대가는 비용(소비자-샤드 시간당 + 데이터 전송량)이다. "여러 소비자가 같은 스트림을 낮은 지연으로 읽어야 한다"는 시나리오의 답이 EFO다. 풀(클래식)에서 푸시(EFO)로의 전환은 SQS 폴링 대 SNS 푸시의 대비와 같은 맥락이다.
Kinesis는 단일 서비스가 아니라 패밀리다. 시험에서 중요한 구분은 Data Streams와 Data Firehose다.
Data Streams는 방금 본 샤드 기반 로그다. 소비자(Lambda, KCL 앱, EFO 소비자)가 직접 데이터를 읽어 실시간 처리하며, 재처리·다중 소비·낮은 지연이 필요할 때 쓴다. 샤드를 직접 관리하거나(Provisioned) On-Demand로 자동 확장한다.
Firehose는 완전히 다른 목적이다. 데이터를 버퍼링했다가 목적지로 자동 전달하는 서버리스 ETL 파이프라인이다. 소비자 코드도, 샤드 관리도 없다. S3·Redshift·OpenSearch·Splunk로 데이터를 흘려보내며, 중간에 Lambda로 변환하거나 JSON을 Parquet로 포맷 변환할 수 있다.
| 구분 | Data Streams | Firehose |
|---|---|---|
| 목적 | 실시간 처리(직접 소비) | 목적지로 자동 전달(ETL) |
| 관리 | 샤드 관리 또는 On-Demand | 완전 관리형, 서버리스 |
| 소비자 | 사용자가 작성(Lambda/KCL) | 없음(목적지로 자동) |
| 지연 | 실시간(~200ms, EFO 70ms) | 최소 60초(버퍼링) |
| 재처리 | 가능(보존 기간 내) | 불가(통과형) |
| 보존 | 24시간~365일 | 없음(통과만) |
결정 기준은 "데이터를 내가 코드로 처리해야 하나, 아니면 그냥 S3/Redshift로 보내기만 하면 되나?"다. 전자는 Data Streams, 후자는 Firehose다.
[클릭 이벤트/IoT/로그]
│
▼
[Kinesis Data Streams] ──> Lambda (실시간 알림·이상 탐지)
│ └──> KCL 앱 (복잡한 상태 기반 처리)
▼
[Kinesis Data Firehose] ──버퍼링(60s/1MB)──> S3 (데이터 레이크, Parquet)
└──> Redshift / OpenSearch
두 서비스를 함께 쓰는 구성이 있다. 실시간 분석은 Data Streams + Lambda로 즉시 처리하면서, 동시에 같은 스트림을 Firehose가 구독해 모든 원본 데이터를 S3에 아카이브한다(나중에 Athena로 분석). 앞서 본 "다중 독립 소비" 덕분에 한 스트림을 실시간 소비자와 아카이브 소비자가 동시에, 서로 간섭 없이 읽는다. CloudWatch Logs를 Subscription Filter로 Firehose에 흘려 S3에 장기 보관하는 것도 같은 ETL 패턴이다.
⚠️ 함정: "실시간"이라는 단어에 속으면 안 된다. Firehose는 최소 60초 버퍼링이라 진짜 실시간이 아니다(Near Real-Time). "1초 이내 즉시 처리"가 필요하면 Data Streams를 직접 소비해야 하고, Firehose는 "어차피 S3에 모아 배치 분석할 거라 1분 지연이 괜찮은" 경우에 쓴다. 시험에서 "초저지연 실시간 처리"면 Data Streams, "S3로 자동 적재(서버리스, 코드 최소)"면 Firehose가 답이다.
이 세 서비스의 선택은 시나리오 문제로 묻기 쉬운 주제다. 표면적으로 다 "메시지를 옮기는" 것 같지만 모델이 근본적으로 다르다.
| 시나리오 | 선택 | 이유 |
|---|---|---|
| 비동기 작업 큐(한 작업 = 한 처리) | SQS | 포인트-투-포인트, 소비 후 삭제 |
| 순서·정확히 1회 처리 | SQS FIFO | 그룹 내 순서 + dedup |
| 한 이벤트 → 여러 구독자 동시 | SNS | 발행-구독 푸시 |
| 실시간 스트림 + 재처리 + 다중 소비 | Kinesis Data Streams | 보존되는 로그 |
| 스트림 → S3/Redshift 자동 적재 | Firehose | 서버리스 ETL |
| 초당 수백만 이벤트, 변동 큰 트래픽 | Kinesis On-Demand | 자동 확장(200 MB/s) |
| 다중 모바일 플랫폼 푸시 알림 | SNS Mobile Push | APNs/FCM 추상화 |
Kinesis와 SQS의 본질적 차이를 다시 정리하면 다음과 같다.
BisectBatchOnFunctionError, MaximumRetryAttempts, DestinationConfig(실패 레코드를 SQS/SNS로) 같은 옵션을 둔다. 모델의 차이가 실패 처리 방식의 차이로 직결된다.Kinesis를 관통하는 한 문장은 "메시지를 소비 후 버리는 대신, 추가 전용 로그에 보존해 여러 소비자가 각자의 위치에서 반복해 읽게 한다"이다. 이 로그 모델이 SQS로는 불가능한 다중 독립 소비·재처리·순서 보존을 가능하게 하고, 그 대가로 소비자가 자기 위치(체크포인트)를 책임진다. 샤드는 로그를 병렬화하고 파티션 키는 순서와 분산을 동시에 푼다(핫 샤드는 그 함정). Data Streams는 직접 처리, Firehose는 자동 전달로 역할이 갈리며, 둘을 함께 쓰면 실시간 처리와 아카이브를 한 스트림에서 동시에 얻는다. SQS·SNS·Kinesis의 선택은 "소비가 데이터를 파괴하느냐(SQS), 1:N으로 푸시하느냐(SNS), 보존되는 로그로 다중 소비·재처리하느냐(Kinesis)"라는 모델의 차이로 귀결된다.
다음 글에서는 이런 개별 서비스들을 하나의 흐름으로 엮어 오케스트레이션하는 Step Functions와, 다중 데이터 소스를 단일 GraphQL로 묶는 AppSync로 넘어간다.
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
실시간 클릭스트림을 (1) Lambda로 즉시 이상 탐지하면서, 동시에 (2) 모든 원본을 S3에 아카이브하려 한다. 한 데이터 소스로 둘 다 만족시키는 구성은?
문제 2
Kinesis 스트림에서 특정 샤드만 ProvisionedThroughputExceededException이 발생한다. 다른 샤드는 한가하다. 가장 가능성 높은 원인은?
문제 3
세 개의 소비자가 같은 Kinesis 스트림을 각각 낮은 지연(수십 ms)으로 읽어야 하며, 서로 처리량을 빼앗으면 안 된다. 적절한 기능은?
문제 4
로그 데이터를 코드 작성 없이 S3에 자동 적재하되, 1분 정도의 지연은 허용된다. JSON을 Parquet로 변환해 저장하려 한다. 적절한 서비스는?
문제 5
Kinesis Data Streams와 SQS의 가장 본질적인 차이는?
문제 6
Firehose의 지연 특성에 대한 설명으로 옳은 것은?
문제 7
Lambda가 Kinesis 스트림을 소비하던 중 한 레코드가 처리에 계속 실패한다. 그 뒤 레코드들이 처리되지 않고 막힌다. 이 현상과 대응으로 옳은 것은?