이 글에서 얻는 것
- 배치 작업을 “대충 스케줄러로 돌리는 코드”가 아니라, 재시작/재처리/정합성을 가진 운영 가능한 작업으로 설계할 수 있습니다.
- Spring Batch의 핵심 개념(Job/Step/Chunk, JobRepository, restartability)을 실무 관점으로 이해합니다.
- 스케줄링에서 가장 흔한 사고(중복 실행, 실패 후 재실행, 멱등성 붕괴)를 예방할 기준이 생깁니다.
0) 배치는 “많이 처리하는 API”가 아니다
배치는 보통 다음 특성이 있습니다.
- 데이터가 크고(수십만~수천만), 실행 시간이 길 수 있음
- 실패가 “일상”이며, 실패 후 재시작/재처리가 필요
- 같은 데이터를 두 번 처리하면 큰 사고(중복 정산/중복 발송)로 이어질 수 있음
그래서 배치는 “성공/실패”만 있는 게 아니라 어디까지 처리했는지가 핵심입니다.
1) Spring Batch 핵심 구조(큰 그림)
- Job: 하나의 배치 실행 단위(예: “어제 주문 집계”)
- Step: Job을 구성하는 단계(예: “읽기 → 처리 → 쓰기”)
- Chunk: 일정 개수 단위로 읽고/처리하고/쓰기 + 커밋하는 단위
- ItemReader/Processor/Writer: 역할을 분리해 파이프라인처럼 구성
2) Chunk 방식이 중요한 이유: “커밋 단위”를 통제한다
Chunk 기반 처리에서는 보통:
- N개 읽고 → N개 처리하고 → N개 쓰고 → 한 번 커밋
즉, Chunk 크기는 성능/정합성의 중요한 레버입니다.
- 너무 작으면: 커밋/IO 오버헤드가 커짐
- 너무 크면: 실패 시 롤백 비용이 커지고, 메모리 부담이 커짐
3) 재시작(restartability)과 JobRepository
Spring Batch는 실행 상태를 JobRepository에 저장해서, 실패했을 때 “어디부터 다시” 시작할 수 있게 합니다.
운영 관점에서의 체크:
- JobRepository DB는 배치의 “상태 저장소”입니다(백업/마이그레이션/정리 정책 필요).
- 같은 파라미터로 Job을 다시 실행할지(재실행) 정책을 명확히 해야 합니다.
4) 실패 처리: retry/skip/재처리(멱등성 포함)
배치에서 실패는 세 종류로 나눠서 생각하면 정리가 쉽습니다.
- 일시적 실패(네트워크/외부 API) → Retry + Backoff
- 일부 데이터만 문제(오염 데이터) → Skip + 리포트
- 코드/스키마 문제 → 빠르게 실패시키고 원인 수정 후 재실행
그리고 무엇보다 중요한 건 멱등성입니다.
- “같은 데이터가 두 번 처리되면” 어떻게 되는가?
- 중복 처리 방지 키(유니크 키/처리 플래그/아웃박스)를 어떻게 둘 것인가?
5) 스케줄링: 단일 인스턴스라면 쉽고, 다중 인스턴스면 어려워진다
5-1) 간단 주기: @Scheduled
@Scheduled(cron = "0 0 * * * *")
public void runHourly() { ... }
단일 인스턴스(또는 배치 전용 서버)라면 단순하고 좋습니다.
5-2) 다중 인스턴스/클러스터: “중복 실행”을 막아야 한다
같은 코드를 여러 서버가 실행하면, 스케줄러가 여러 번 발화해서 배치가 중복 실행될 수 있습니다. 해결은 보통 “단일화” 또는 “락”입니다.
선택지(대표):
- 한 곳에서만 실행되도록 배치 실행을 분리(배치 전용 워커)
- DB 락/분산 락(ShedLock 등)으로 “한 번만” 실행
- Quartz 클러스터(운영 복잡도는 상승)
- 인프라에서 스케줄링(Kubernetes CronJob 등)으로 책임을 넘김
6) 실무 팁: 성능과 안정성을 함께 올리는 포인트
- Reader/Writer에서 N+1/비효율 쿼리가 나지 않게 주의(페이징, 커서, 배치 업데이트)
- 외부 API 호출이 있으면 “재시도/타임아웃/서킷브레이커”를 반드시 포함
- 배치 실행 로그/메트릭(처리량, 소요 시간, 실패 수)을 남겨 “느려짐”을 빨리 감지
연습(추천)
- Chunk 크기를 100/1000/5000으로 바꿔보며 처리 시간/DB 부하가 어떻게 변하는지 관찰해보기
- 일부 데이터만 실패하도록 만들고 skip/retry를 적용해 “전체 실패 없이” 처리하는 흐름을 만들어보기
- 동일 배치를 2개 인스턴스에서 동시에 실행했을 때 중복 실행이 나는지 확인하고, 락으로 막아보기
💬 댓글