기준: DEA-C01 시험 가이드오류 제보
지금까지 스트리밍 처리(Day1), 신뢰성(Day2), CDC 복제(Day3)를 개별 기술로 다뤘다. 오늘은 한 발 물러서서 이것들을 하나의 시스템으로 엮는 아키텍처 패턴을 본다. 데이터 엔지니어링에서 "어떤 서비스를 쓸까"보다 더 중요한 질문은 "이 조각들을 어떤 모양으로 배치할까"다. 같은 부품도 배치에 따라 견고한 파이프라인이 되기도, 깨지기 쉬운 스파게티가 되기도 한다.
오늘은 두 축을 다룬다. 첫째, 배치와 스트림을 함께 다루는 처리 아키텍처(Lambda / Kappa). 둘째, 컴포넌트를 느슨하게 연결하는 이벤트 기반 수집(EventBridge / SQS / SNS).
Lambda 아키텍처(AWS Lambda 서비스와는 무관한 이름이다)는 같은 데이터를 두 경로로 흘려보내는 패턴이다.
┌─→ [Batch Layer] ──→ [Batch View] ─┐
[Raw Data] ──→ 분기 ──→ │ ├─→ [Serving Layer] → 쿼리
└─→ [Speed Layer] ──→ [Realtime View]┘
| 레이어 | 역할 | 특성 |
|---|---|---|
| Batch Layer | 전체 데이터를 주기적으로 재처리 | 정확하지만 느림(완전성) |
| Speed Layer | 최근 데이터를 실시간 처리 | 빠르지만 근사치(저지연) |
| Serving Layer | 두 뷰를 합쳐 쿼리에 응답 | 사용자에게 통합된 답 |
발상은 이렇다. 실시간 경로(Speed Layer)는 "방금 들어온 데이터"를 빠르게 근사 집계해 즉시 보여준다. 동시에 배치 경로(Batch Layer)는 전체 데이터를 정확히 재처리해 정답을 만든다. 시간이 지나면 배치 결과가 실시간 근사치를 덮어쓴다. "최신은 빠르게, 과거는 정확하게"를 둘 다 잡는 것이다.
AWS로 구현하면 대략 이렇다.
Batch Layer : S3(raw) → Glue/EMR Spark → S3(batch view) → Athena/Redshift
Speed Layer : Kinesis → Managed Flink → DynamoDB/OpenSearch(realtime view)
Serving Layer: Athena/Redshift + 실시간 뷰를 합쳐 응답
💡 관련 이론: Lambda 아키텍처는 Nathan Marz(Apache Storm 창시자)가 2011년경 제안했다. 핵심 통찰은 "배치 레이어가 모든 실수를 바로잡는다(human fault-tolerance)"는 점이다. 실시간 코드에 버그가 있어도, 원본 데이터를 보존하고 배치로 전체 재처리하면 결국 정답으로 수렴한다. 불변 원본 데이터(immutable master dataset) + 재계산이 이 아키텍처의 안전망이다.
⚠️ 함정: Lambda 아키텍처의 가장 큰 비용은 같은 로직을 두 번 구현한다는 것이다. 배치(Spark)와 실시간(Flink)에 동일한 집계 로직을 따로 짜야 하고, 둘이 미묘하게 어긋나면 디버깅이 지옥이 된다. 이 중복을 없애려는 대안이 Kappa 아키텍처다 — 배치 레이어를 없애고 스트림 하나로 통합하되, 재처리가 필요하면 스트림을 처음부터 다시 재생(replay)한다.
[Raw Data] ──→ [Stream(Kinesis/Kafka)] ──→ [Stream Processor(Flink)] ──→ [Serving]
│
(재처리 필요 시 오프셋을 처음으로 되돌려 replay)
Kappa는 "모든 것은 스트림이다. 배치는 그저 경계가 있는 스트림일 뿐"이라는 관점이다. 로직이 하나뿐이므로 유지보수가 단순하다. 단, 전체 재처리를 하려면 스트림에 데이터가 충분히 길게 보존되어 있어야 한다(Kinesis 최대 365일 보존, Kafka 무제한 가능). 보존 기간을 넘은 과거 데이터를 재처리해야 하면 결국 S3 같은 저장소가 다시 필요해진다.
| 구분 | Lambda | Kappa |
|---|---|---|
| 경로 | 배치 + 스트림 이중 | 스트림 단일 |
| 로직 중복 | 있음(두 번 구현) | 없음 |
| 재처리 | 배치로 전체 재계산 | 스트림 replay |
| 단순성 | 낮음 | 높음 |
| 적합 | 무거운 배치 분석 병행 | 실시간 중심, 단순화 우선 |
아키텍처를 견고하게 만드는 또 다른 축은 컴포넌트 간 결합도를 낮추는 것이다. 프로듀서가 컨슈머를 직접 호출하면(tight coupling), 컨슈머가 죽으면 프로듀서도 막힌다. 그 사이에 메시징 계층을 두면 둘이 독립적으로 움직인다(loose coupling). AWS의 세 도구 — SQS, SNS, EventBridge — 의 역할 구분이 핵심이다.
| 서비스 | 모델 | 핵심 특성 |
|---|---|---|
| SQS | 큐 (1:1, point-to-point) | 한 메시지를 한 컨슈머가 처리, 버퍼링·재시도 |
| SNS | 펍/섭 (1:N, fan-out) | 한 메시지를 다수 구독자에게 동시 전달 |
| EventBridge | 이벤트 버스 (라우팅) | 콘텐츠 기반 규칙으로 이벤트를 선별 라우팅 |
SQS : Producer → [Queue] → Consumer (버퍼 + 부하 평준화)
SNS : Producer → [Topic] → 구독자 A,B,C (동시 팬아웃)
EventBridge: 다수 소스 → [Bus] → 규칙 매칭 → 다수 타깃 (스키마 인식 라우팅)
세 도구는 경쟁이 아니라 조합된다. 가장 유명한 패턴이 SNS → SQS 팬아웃이다.
┌─→ [SQS 큐 A] → 결제 처리 Lambda
[주문 SNS Topic] ──→ ├─→ [SQS 큐 B] → 재고 차감 Lambda
└─→ [SQS 큐 C] → 분석 적재 Lambda
주문 이벤트 하나가 SNS로 발행되면, 세 SQS 큐로 각각 복제되어 들어간다. 각 컨슈머는 자기 큐에서 독립적으로 소비하며, 한 컨슈머가 느려도(또는 죽어도) 다른 컨슈머와 SNS 발행 자체에는 영향이 없다. SNS의 팬아웃과 SQS의 버퍼링·재시도(+DLQ)를 결합한 것이다.
왜 SNS만 쓰지 않고 SNS→SQS로 큐를 끼울까? SNS는 메시지를 즉시 푸시하고 실패하면 제한된 재시도 후 버린다(또는 SNS DLQ로). 컨슈머가 일시적으로 다운되면 그동안의 메시지를 잃을 수 있다. SQS를 끼우면 메시지가 큐에 안전하게 쌓여 컨슈머가 복구된 뒤 처리할 수 있다. 즉 SNS는 "동시 분배"를, SQS는 "내구성 있는 버퍼"를 담당한다.
EventBridge는 SNS보다 한 단계 더 똑똑한 라우팅을 한다. 이벤트의 내용을 보고 규칙(rule)에 매칭되는 것만 타깃으로 보낸다.
// EventBridge Rule: orders 중 금액 1000 초과 건만 fraud-check로 라우팅
{
"source": ["myapp.orders"],
"detail-type": ["OrderPlaced"],
"detail": {
"amount": [{ "numeric": [">", 1000] }]
}
}이 패턴 매칭 덕분에 컨슈머는 "관심 있는 이벤트"만 받는다. AWS 서비스 이벤트(S3 객체 생성, EC2 상태 변화 등)도 EventBridge로 흐르므로, "S3에 파일이 도착하면 Glue Job 시작" 같은 이벤트 기반 파이프라인 트리거에 자주 쓰인다. Schema Registry로 이벤트 구조를 등록·발견할 수 있고, Pipes로 소스(예: SQS, DynamoDB Stream)와 타깃을 변환·필터와 함께 직접 연결할 수도 있다.
| 선택 기준 | 적합한 도구 |
|---|---|
| 단순 버퍼링/부하 평준화, 1:1 | SQS |
| 한 메시지를 여러 구독자에게, 저지연 팬아웃 | SNS (또는 SNS→SQS) |
| 콘텐츠 기반 라우팅, AWS 서비스 이벤트, 스키마 | EventBridge |
🎯 시나리오: 전자상거래에서 "주문 생성" 한 번에 결제·재고·이메일·분석 적재가 모두 일어나야 한다. 구성은 (1) 주문 서비스가 EventBridge에
OrderPlaced이벤트 발행 → (2) 규칙으로 각 도메인(결제/재고/알림/분석)에 라우팅 → (3) 내구성이 중요한 결제·재고는 SQS 큐를 타깃으로 두고 컨슈머가 안전하게 소비, 실패 시 DLQ → (4) 분석은 Firehose→S3로 라우팅. 컴포넌트가 서로를 모르고도 협력하는 느슨한 결합이 핵심이다.
이벤트 기반 아키텍처(EDA)의 핵심 가치는 시간적·공간적 디커플링이다. 프로듀서는 컨슈머가 누구인지, 지금 살아있는지 몰라도 된다(공간적 디커플링). 컨슈머가 잠시 죽어도 메시지가 버퍼에 쌓여 나중에 처리된다(시간적 디커플링). 이것이 시스템 일부의 장애가 전체로 번지지 않게 하는 "장애 격벽(bulkhead)"을 만든다.
오늘의 핵심은 두 가지다. 처리 측면에서 Lambda는 정확성과 저지연을 이중 경로로 둘 다 잡되 로직 중복의 대가를 치르고, Kappa는 스트림 하나로 단순화하되 재처리를 replay에 의존한다. 연결 측면에서 SQS(버퍼·1:1), SNS(팬아웃·1:N), EventBridge(콘텐츠 라우팅)를 적재적소에 조합해 느슨한 결합을 만든다. 특히 SNS→SQS 팬아웃과 EventBridge 라우팅은 시험과 실무 모두에서 자주 쓰는 패턴이다. 내일은 Week 3 전체를 종합 복습한다.
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
같은 데이터를 "최근 값은 실시간 근사치로 빠르게, 전체 값은 배치로 정확하게" 둘 다 제공하려는 아키텍처는?
문제 2
Lambda 아키텍처를 운영하다 보니 배치(Spark)와 실시간(Flink)에 같은 집계 로직을 두 번 구현·유지하는 부담이 크다. 로직 중복을 없애는 대안 아키텍처와 그 재처리 방식은?
문제 3
하나의 "주문 생성" 이벤트가 결제·재고·분석 세 컨슈머에게 동시에 전달되어야 하고, 각 컨슈머는 일시적으로 다운되어도 메시지를 잃지 않아야 한다. 가장 적절한 패턴은?
문제 4
"S3 버킷에 새 객체가 생성되면 특정 Glue Job을 트리거"하고, 나아가 이벤트 내용(접두사, 크기 등)에 따라 다른 타깃으로 선별 라우팅하고 싶다. 가장 적합한 서비스는?
문제 5
SNS 단독 대신 SNS→SQS로 큐를 끼우는 주된 이유는?