같은 데이터라도 "언제, 얼마씩" 처리하느냐에 따라 시스템 설계가 완전히 달라진다. 매일 밤 하루치 주문을 모아 정산 리포트를 만드는 것과, 결제가 일어나는 순간 이상거래를 잡아내는 것은 같은 결제 데이터를 다루지만 전혀 다른 기계가 필요하다. 전자가 배치(batch), 후자가 스트리밍(streaming)이다.
오늘은 이 두 처리 패러다임의 본질, 각각이 적합한 사례, 그리고 둘을 가르는 핵심 트레이드오프인 지연(latency)과 처리량(throughput)을 본다. 데이터 엔지니어가 새 파이프라인을 설계할 때 가장 먼저 내리는 판단이 "이건 배치인가 스트림인가"다.
차이는 데이터를 묶음으로 처리하느냐, 도착하는 대로 처리하느냐다.
배치 (Batch)
[····· 모은다 ·····] → 한 번에 처리 → 결과
경계가 있는(bounded) 데이터, 정해진 주기로 실행
스트리밍 (Streaming)
→ 도착 → 처리 → 도착 → 처리 → 도착 → 처리 →
경계가 없는(unbounded) 데이터, 끊임없이 흐름
배치는 "지난 한 시간/하루 동안 쌓인 것"이라는 유한한 묶음을 다룬다. 시작과 끝이 분명하다. 스트리밍은 "지금부터 영원히 들어오는 이벤트"라는 무한한 흐름을 다룬다. 끝이 없으므로, 결과를 내려면 "최근 5분"처럼 시간 창(window)으로 흐름을 잘라야 한다.
구분
배치
스트리밍
데이터
경계 있음(bounded)
경계 없음(unbounded)
실행
주기적(시간/일 단위)
연속적(상시)
지연
분~시간
밀리초~초
처리량
매우 높음
건당 처리
AWS 예시
Glue, EMR, Batch
Kinesis, MSK, Flink
💡 관련 이론: 스트리밍에서 가장 중요한 개념이 윈도잉(windowing)이다. 무한한 흐름에서 집계를 내려면 흐름을 시간 단위로 잘라야 한다. 텀블링 윈도우(겹치지 않는 고정 구간), 슬라이딩 윈도우(겹치며 이동), 세션 윈도우(활동 간격으로 구분)가 대표적이다. 또한 "이벤트가 발생한 시각(event time)"과 "시스템이 받은 시각(processing time)"의 차이를 다루는 것이 스트리밍 설계의 핵심 난제다.
# 배치: 하루치 로그를 모아 한 번에 집계 (Glue/Spark)df = spark.read.parquet("s3://logs/date=2026-06-25/") # 하루치 전체daily = df.groupBy("region").agg(sum("amount"))daily.write.parquet("s3://reports/daily/2026-06-25/")# 스트리밍: 5분 윈도우로 흐르는 이벤트를 집계 (Flink/KDA)events.window(TumblingWindow.of(minutes=5)) \ .key_by("region") \ .aggregate(SumAggregator()) # 끝없이 5분마다 결과 방출
핵심은 "실시간이 비싸다"는 점이다. 스트리밍은 상시 가동되는 인프라와 더 복잡한 운영을 요구한다. 그래서 "정말 실시간이 필요한가, 5분 늦어도 되는가"를 따지는 것이 비용 최적화의 출발점이다. 막연히 "최신 데이터가 좋으니 다 스트리밍"으로 가면 비용과 복잡도가 폭증한다.
마이크로배치(micro-batch)는 둘의 중간 지점이다. Spark Structured Streaming처럼 아주 짧은 주기(수 초)로 작은 배치를 반복 실행해 "준실시간"을 만든다. 진짜 이벤트 단위 스트리밍보다 운영이 단순하면서 초 단위 지연을 달성하므로, 많은 현실 파이프라인이 이 절충안을 택한다.
지연(latency): 데이터가 발생한 뒤 결과가 나오기까지의 시간. 낮을수록 "실시간"에 가깝다.
처리량(throughput): 단위 시간당 처리할 수 있는 데이터의 양. 높을수록 "대용량"에 강하다.
배치 : 지연 높음 ↑ 처리량 매우 높음 ↑↑↑
스트림 : 지연 낮음 ↓ 건당 처리 → 누적 처리량은 낮음
배치는 데이터를 모아 한 번에 처리하므로 건당 오버헤드가 분산되어 처리량이 압도적으로 높다. 대신 모으는 시간만큼 지연이 생긴다. 스트리밍은 이벤트가 오는 즉시 처리하므로 지연이 매우 낮지만, 건마다 처리·전달 비용이 들어 같은 인프라로 낼 수 있는 누적 처리량은 배치보다 낮다.
여기에 정확성(correctness)이라는 축이 더해진다. 스트리밍은 늦게 도착하는 데이터(late-arriving data)나 순서가 뒤섞인 이벤트를 다뤄야 하므로, "정확한 최종 결과"와 "빠른 근사 결과" 사이에서 타협해야 한다. 배치는 모든 데이터가 도착한 뒤 처리하므로 이 문제에서 자유롭다.
이 트레이드오프를 아키텍처 수준에서 푼 고전이 람다 아키텍처(Lambda Architecture)다. 배치 레이어(정확하지만 느림)와 스피드 레이어(빠르지만 근사)를 동시에 돌려 서빙 레이어에서 합친다. 코드를 두 벌 유지해야 하는 부담 때문에, 스트리밍 엔진 하나로 배치까지 처리하는 카파 아키텍처(Kappa Architecture)가 대안으로 등장했다. AWS에서는 Kinesis/MSK + Flink 조합이 카파에 해당한다.
특히 헷갈리기 쉬운 둘을 구분해야 한다. Kinesis Data Streams는 낮은 지연과 다중 소비자가 필요한 진짜 스트리밍용이고, Kinesis Data Firehose는 스트림을 받아 S3·Redshift 등으로 자동 적재하는 "관리형 준실시간 배달부"다. 직접 코드로 소비자를 운영할 필요 없이 목적지에 떨궈주길 원하면 Firehose다.
"실시간"은 사람마다 뜻이 다르다. 시험 지문도 real-time, near real-time, within minutes, daily 를 구분해서 쓴다. 지연 요구를 구간으로 나누면 후보가 좁혀진다.
요구되는 신선도
처리 형태
AWS 구성
근거가 되는 동작
1초 안쪽 반응
이벤트 단위 스트리밍
KDS/MSK + Managed Service for Apache Flink, 또는 Lambda(향상된 팬아웃)
향상된 팬아웃은 레코드를 push 로 받아 전파 지연이 평균 70ms 수준, 공유 처리량은 소비자 1개일 때 평균 200ms 수준
수 초 ~ 1~2분
준실시간 적재
Data Firehose
버퍼(크기 또는 시간)가 차면 한 번에 전달. S3 목적지의 버퍼 간격은 0~900초, 기본 300초
1~수 분
마이크로배치
Glue 스트리밍 ETL
Spark Structured Streaming 기반. 기본 100초 윈도우로 읽고 쓴다
수 분 ~ 시간
이벤트 기반 배치
S3 이벤트 → Lambda/Glue 잡, EventBridge 스케줄
파일이 도착할 때 또는 정해진 간격으로 작은 배치를 실행
하루 단위
스케줄 배치
Glue·EMR·AWS Batch + Step Functions/MWAA
하루치 파티션을 통째로 읽어 한 번에 집계
표의 위로 갈수록 인프라가 상시 가동이고, 아래로 갈수록 실행한 시간만큼만 돈을 낸다.
Lambda 를 KDS 소비자로 붙이면 표준 방식에서는 샤드마다 초당 1회 기본 주기로 폴링한다. "함수니까 즉시"가 아니라 폴링 주기와 배치 윈도우만큼 지연이 생긴다.
Firehose 의 버퍼 간격을 60초 미만으로 낮추면 더 빨리 도착하지만 S3 에 작은 객체가 늘고 PUT 요청 비용이 오른다. 신선도와 파일 크기는 항상 맞바꾸는 관계다.
⚠️ 함정: 지문에 "실시간으로 S3에 적재"라고 쓰여 있어도 요구가 "쌓기"뿐이면 정답은 Firehose 다. KDS 는 소비자 코드를 직접 운영해야 하므로 "운영 부담 최소" 조건과 충돌한다. 반대로 "여러 애플리케이션이 같은 이벤트를 각자 읽는다", "과거 구간을 다시 처리한다"가 나오면 Firehose 만으로는 안 된다.
이벤트 시간(event time) : 사건이 실제로 일어난 시각. 프로듀서가 페이로드 안에 넣어 보내야 한다.
처리 시간(processing time) : 시스템이 그 레코드를 받은 시각. KDS 레코드의 ApproximateArrivalTimestamp 가 이것이다. 스트림 도착 시각이지 사건 발생 시각이 아니다.
모바일 앱이 지하철에서 오프라인으로 쌓아둔 클릭을 20분 뒤 한꺼번에 보내면, 이벤트 시간은 10:00 인데 처리 시간은 10:20 이 된다. 처리 시간 기준으로 5분 윈도우를 자르면 이 클릭은 엉뚱한 윈도우에 들어간다.
이벤트 시간 기준 5분 텀블링 윈도우 [10:00~10:05)
10:01 이벤트 A ─ 10:01 도착 → 정상 집계
10:03 이벤트 B ─ 10:04 도착 → 정상 집계
10:02 이벤트 C ─ 10:21 도착 → 윈도우가 이미 닫혔다면? (늦은 데이터)
스트리밍 엔진은 워터마크(watermark) 로 "이 시각 이전 이벤트는 다 왔다고 본다"는 기준선을 정하고, 허용 지연을 넘긴 이벤트는 버리거나 별도 출력으로 빼낸다.
배치는 이 문제를 기다림으로 해결한다. 자정 직후가 아니라 새벽 2시에 어제 파티션을 처리하면 늦은 이벤트 대부분이 이미 도착해 있다. 그래도 더 늦게 온 것은 해당 날짜 파티션을 통째로 다시 계산(backfill)해 덮어쓴다.
Firehose 의 기본 S3 접두사(YYYY/MM/dd/HH, UTC)는 Firehose 에 도착한 시각 기준이다. 이벤트 시간 기준으로 폴더를 나누려면 동적 파티셔닝으로 레코드 안의 필드를 꺼내 써야 한다.
배치 재처리가 안전한 이유는 "같은 입력 파티션 → 같은 출력 파티션을 덮어쓰기"라는 멱등한 구조 덕분이다. 스트리밍은 장애 후 체크포인트 지점부터 다시 읽기 때문에 일부 레코드가 두 번 처리될 수 있고(at-least-once), 결과 저장소 쪽에서 중복을 흡수하도록 설계해야 한다.
예전 자료의 Kinesis Data Analytics(KDA) 는 현재 Amazon Managed Service for Apache Flink 다. 본문 표의 KDA(Flink) 도 같은 서비스를 가리킨다.
SQL 로만 작성하던 구버전 Kinesis Data Analytics for SQL 은 지원이 종료됐다(신규 생성 차단 후 2026년 1월 27일부터 애플리케이션 삭제). 스트림에 SQL 을 쓰려면 Flink SQL 이나 Managed Service for Apache Flink Studio 노트북을 쓴다.
Kinesis Data Firehose 의 현재 이름은 Amazon Data Firehose 다. 시험 지문과 콘솔에 두 이름이 섞여 나오지만 같은 서비스다.