이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- batch, stream, pipeline은 각각 어떤 문제를 해결하는가?
- 같은 주문 데이터를 batch로 처리할 때와 stream으로 처리할 때 실패 모델은 어떻게 달라지는가?
- checkpoint, retry, idempotency는 batch와 stream에서 각각 어디에 놓이는가?
- 백엔드 서비스가 pipeline 설계에 제공해야 할 정보는 무엇인가?
개요
batch는 정해진 범위를 묶어서 처리한다.
stream은 계속 들어오는 event를 작은 단위로 처리한다.
pipeline은 source, 처리 단계, target, 의존성, 검증을 하나의 실행 흐름으로 묶는다.
세 단어는 도구 이름이 아니라 처리 방식의 차이를 가리킨다.
Spring Batch를 쓰면 항상 Data Engineering이 되는 것도 아니고, Kafka를 쓰면 항상 stream processing이 되는 것도 아니다.
원리
처리 방식은 데이터 도착 방식과 지연 허용치로 정한다.
- 하루 한 번 전날 매출을 재계산하면 batch가 자연스럽다.
- 주문 생성 후 몇 초 안에 검색 색인을 갱신해야 하면 stream이 자연스럽다.
- 결제 완료 후 staging, warehouse, mart, 품질 검증이 순서대로 이어지면 pipeline으로 봐야 한다.
batch와 stream은 경쟁 관계가 아니다.
stream은 빠른 반영을 맡고, batch는 누락 보정과 재계산을 맡는 경우가 많다.
비교
| 구분 | Batch | Stream | Pipeline |
|---|---|---|---|
| 입력 단위 | 날짜 범위, id 범위, 파일 | event, message, log record | 여러 task의 흐름 |
| 지연 | 분, 시간, 일 | ms, 초, 분 | 단계별 SLA |
| 재처리 | 범위 재실행 | offset 또는 event replay | task/DAG 재실행 |
| 상태 | job repository, checkpoint table | offset, consumer state | dependency, run state |
| 주의점 | 긴 실행, lock, 부분 실패 | 중복, 순서, lag | 의존성, 관측성, owner |
Batch 예시
@Bean
Step orderDailyMartStep(JobRepository jobRepository,
PlatformTransactionManager tx,
ItemReader<OrderRow> reader,
ItemProcessor<OrderRow, DailyOrderFact> processor,
ItemWriter<DailyOrderFact> writer) {
return new StepBuilder("orderDailyMartStep", jobRepository)
.<OrderRow, DailyOrderFact>chunk(1000, tx)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}batch에서는 chunk 크기, transaction 범위, 재시작 지점이 중요하다.
실패 후 같은 날짜를 다시 돌렸을 때 결과가 덮어쓰기 가능한지도 봐야 한다.
Stream 예시
@KafkaListener(topics = "order.events", groupId = "order-search-indexer")
public void handle(OrderPlacedEvent event) {
if (processedEventRepository.existsById(event.eventId())) {
return;
}
searchIndex.upsert(event.orderId(), event.toDocument());
processedEventRepository.save(new ProcessedEvent(event.eventId()));
}stream에서는 중복 event와 재전달을 기본 전제로 둔다.
consumer가 처리 후 죽거나 offset commit 전에 죽어도 결과가 깨지지 않아야 한다.
Pipeline 예시
extract_orders
-> load_order_staging
-> build_fact_order_daily
-> validate_fact_order_daily
-> publish_sales_dashboardpipeline은 task 목록이 아니라 의존성 그래프다.
앞 단계의 성공 기준이 애매하면 뒤 단계가 성공해도 전체 데이터는 틀릴 수 있다.
판단 축
- 반복 계산과 대량 보정이 핵심이면 batch로 시작한다.
- 낮은 지연과 지속 반영이 핵심이면 stream을 검토한다.
- 여러 처리 단계가 의존성을 가지면 pipeline으로 관리한다.
- batch든 stream이든 같은 입력을 다시 처리해도 안전해야 한다.
- pipeline 성공은 task exit code뿐 아니라 품질 검증까지 포함한다.
Checkpoint와 재처리
batch checkpoint는 보통 날짜, id range, file name, page cursor, job execution id다.
stream checkpoint는 topic, partition, offset, event id, consumer group이다.
pipeline checkpoint는 DAG run id, task instance id, target partition, validation result다.
재처리 문서에는 “어디서 다시 시작할 수 있는가”와 “어떤 target을 지우거나 덮어써야 하는가”가 같이 있어야 한다.
실패 모델
- batch가 90% 처리 후 실패했는데 어떤 chunk까지 반영됐는지 모른다.
- stream consumer가 같은 event를 두 번 받아 외부 API를 두 번 호출한다.
- pipeline 중간 task가 성공했지만 target partition이 비어 있다.
- batch 보정이 stream이 만든 최신 결과를 오래된 값으로 덮어쓴다.
- stream lag를 줄이려고 consumer를 늘렸는데 partition key 때문에 병렬성이 늘지 않는다.
운영 지표
job_duration_seconds: batch 실행 시간이 예상 범위를 넘는지 본다.job_processed_count: 입력 범위 대비 처리 건수다.consumer_lag: stream 처리 지연이다.duplicate_event_count: 중복 event 처리 방어가 작동하는지 본다.pipeline_task_failed_count: pipeline 단계별 실패 수다.validation_failed_count: 처리 성공과 데이터 성공을 분리한다.
개인 프로젝트 기준
처음부터 Airflow와 Kafka를 모두 붙이지 않아도 된다.
Spring Scheduler, Spring Batch, 단순 event table만으로도 batch, stream, pipeline 개념을 연습할 수 있다.
중요한 것은 실행 이력과 재처리 단위를 남기는 것이다.
기업 운영 기준
기업 환경에서는 batch window, stream SLA, pipeline owner가 명확해야 한다.
같은 데이터에 stream과 batch가 모두 쓰이면 우선순위와 보정 규칙을 정해야 한다.
예를 들어 stream은 near-real-time 화면을 갱신하고, batch는 D+1 확정 지표를 만든다고 합의한다.
실전 팁
- batch는
from,to,job_run_id를 로그와 target에 남긴다. - stream은 event id와 idempotency 저장소를 먼저 설계한다.
- pipeline은 task 성공 조건과 데이터 검증 조건을 분리한다.
- 지연 요구가 명확하지 않으면 stream부터 도입하지 않는다.
- 재처리 runbook 없이 처리 방식을 선택하지 않는다.
위험 신호!
- “Kafka를 쓰니까 실시간이다”라고만 설명한다.
- batch가 실패하면 전체 기간을 다시 돌리는 방법뿐이다.
- stream consumer가 offset commit과 DB write 순서를 설명하지 못한다.
- pipeline DAG가 성공했는데 mart row count가 0이어도 알림이 없다.
- batch와 stream이 같은 target을 서로 다른 규칙으로 갱신한다.
확인 질문
확인 질문
- 일별 매출 리포트가 틀렸을 때 batch와 stream 중 무엇을 먼저 의심해야 하는가?
- 지연 화면이면 stream lag를 먼저 보고, 확정 리포트면 batch range, mart build, 품질 검증을 먼저 본다.
- stream consumer가 같은 event를 두 번 받으면 어떤 방어가 필요한가?
- event id 기반 처리 이력이나 idempotent upsert가 필요하다.
- pipeline 성공을 task 성공만으로 보면 안 되는 이유는 무엇인가?
- task는 끝났어도 target row 수, freshness, 합계 검증이 실패할 수 있기 때문이다.
참고 문서
- Spring Batch Reference
- Apache Kafka Documentation
- Apache Airflow Documentation
- Martin Kleppmann, Designing Data-Intensive Applications