Spring Batch 입문 — Job·Step·Chunk 모델

2026-05-03확률과 통계 마스터 노트

Spring Batch 마스터 노트 시리즈 1편. 야간·주말에 수백만 건을 처리하는 배치의 토대 — 온라인 처리와 결정적으로 다른 자동화 모델, Job·Step·Chunk·Tasklet 4단어, JobInstance vs JobExecution의 헷갈리는 차이, JobRepository 6개 메타데이터 테이블, Batch Status vs Exit Status까지 한 흐름으로.

이 글은 Spring Batch 마스터 노트 시리즈의 첫 번째 편입니다. 야간 2시에 수백만 건의 거래를 정산하고, 매월 1일에 청구서를 한꺼번에 발행하고, 매일 새벽 데이터 웨어하우스에 ETL을 돌리는 — 이런 작업은 사용자가 화면에서 버튼 누르는 방식으로는 절대 못 합니다. 배치 처리 의 영역이에요.

이 시리즈는 8편으로 Spring Batch의 거의 모든 영역을 다룹니다. 1편의 목표는 단순해요. Job·Step·Chunk·JobRepository 네 단어를 손에 잡히게 만드는 것. 이 네 개념이 잡혀야 2편 Job 설정, 3편 청크 처리가 자연스럽게 따라옵니다.

📚 학습 노트

이 시리즈는 Spring Batch 공식 문서, Spring Boot 가이드, 여러 Java 백엔드 학습 자료를 참고해 한국어 학습 노트로 풀어쓴 자료입니다.

읽으면서 IDE에 spring-boot-starter-batch 의존성을 추가하고 직접 Job을 띄워 보면 본문이 머리에 훨씬 잘 박혀요. 30분이면 첫 Tasklet Step이 콘솔에 로그를 찍는 걸 볼 수 있습니다.

처음 배치 처리가 어렵게 느껴지는 이유

이유는 두 가지예요.

첫째, 온라인 처리(웹·API) 와 사고방식이 완전히 다릅니다. 웹 요청은 사용자 클릭 → 즉시 응답이 핵심이지만, 배치는 스케줄 → 자동 실행 → 결과만 기록이에요. 사용자가 없는 환경에서 오류를 어떻게 처리할지, 실패한 Job을 어떻게 재시작할지가 첫 단계에서 막힙니다.

둘째, Spring Batch의 도메인 모델이 복잡합니다. Job·Step·Chunk·Tasklet·JobInstance·JobExecution·JobRepository — 비슷한 이름들이 한꺼번에 들어와요. 특히 JobInstance와 JobExecution의 차이가 헷갈리면 재시작 동작 전체가 안 보입니다.

해결법은 한 가지예요. 모든 배치 = "정해진 시간에 자동으로 + 데이터를 변환해서 + 어딘가에 저장하는 작업" 한 줄로 줄이세요. Spring Batch는 이 작업의 공통 뼈대(재시작·트랜잭션·로깅·모니터링) 를 다 만들어 둔 도구입니다. 비즈니스 로직(무엇을 읽고·어떻게 변환하고·어디에 쓸지)에만 집중하면 돼요.

배치 처리란 — 온라인과 결정적으로 다른 모델

구분 온라인 처리 배치 처리
트리거 사용자 요청 스케줄·이벤트·수동
응답 시간 즉시 (ms~s) 수분~수시간
데이터 규모 소량 (개별 레코드) 대량 (수백만 레코드)
실행 방식 지속 대기 일회성·주기적
사용자 개입 있음 없음 (자동화)
오류 처리 즉시 사용자 알림 로그·스킵·재시도

배치가 필요한 자리:

  • 매일 새벽 수백만 건 정산
  • 외부 시스템 CSV → DB 일괄 적재
  • 월말 보고서·청구서 일괄 생성
  • 데이터 마이그레이션 (일회성 대량 변환)
  • 시스템 간 데이터 동기화

여기서 시험 함정이 하나 있어요. "배치 = 느린 처리"가 아닙니다. 배치는 응답이 즉시 필요 없을 뿐이지 처리 속도 자체는 매우 빨라요. 청크 단위 트랜잭션·병렬 처리·Bulk INSERT 같은 최적화로 단건 처리보다 수십 배 빠릅니다.

배치 처리 사용 사례

ETL — 가장 전형적인 패턴

[CSV 파일] --읽기--> [Spring Batch] --변환/검증--> [데이터베이스]
[레거시 DB] --읽기--> [Spring Batch] --마이그레이션--> [신규 DB]
[외부 API] --읽기--> [Spring Batch] --정제--> [데이터 웨어하우스]

Extract(추출) → Transform(변환) → Load(적재) 세 단계. 1편의 출발점이자 5편 이커머스에서 본격적으로 다룬 패턴이에요.

금융 처리 — 정확성이 생명

은행·카드사·보험사의 핵심 인프라. 이자 계산·청구서·정산이 매일 야간 배치로 돌아갑니다. 이 영역에서는 Spring Batch의 재시작 기능과 감사 추적이 결정적이에요. 잘못된 정산은 회사 평판을 깎고 법적 문제로도 이어질 수 있으니까요.

보고서 생성

[데이터베이스] → [배치 Job] → [집계 처리] → [보고서 파일] → [이메일 발송]

매일·매주·매월 시점에 경영진에게 자동 발송되는 보고서. 복잡한 집계 쿼리를 업무 시간 외에 돌려 시스템 부하 분산.

재고 관리

이커머스·물류·제조에서 창고·POS·ERP 시스템 간 재고를 주기적으로 동기화. 재고 부족 알림·자동 발주 트리거도 배치로.

Spring Batch — 공통 뼈대를 만들어 둔 프레임워크

Spring Batch가 제공하는 5가지 핵심 가치:

  • 재시작 가능성 — 실패한 Job을 중단 지점부터 재실행
  • 스킵 기능 — 오류 레코드를 건너뛰고 나머지 계속 처리
  • 청크 처리 — 대량 데이터를 묶음 단위로 메모리 효율 처리
  • 병렬 처리 — 여러 Step 동시 실행으로 시간 단축
  • 모니터링 — 모든 실행 내역이 DB에 자동 기록

이 5가지를 직접 짜려면 수개월. Spring Batch는 이 뼈대를 다 만들어 두고 비즈니스 로직만 채우면 됩니다.

Job — 배치 작업 전체

Job 은 배치 처리의 최상위 개념. 하나 이상의 Step으로 구성되며, Step들의 실행 순서·흐름을 정의합니다.

@Bean
public Job myJob() {
    return jobBuilderFactory.get("myJob")
        .start(step1())
        .next(step2())
        .build();
}

여기서 시험 함정이 하나 있어요. Job은 재사용 가능한 설정 단위입니다. 같은 Job을 다른 JobParameters로 여러 번 실행할 수 있어요. "오늘 날짜로 일일 보고서", "어제 날짜로 일일 보고서"처럼 같은 Job을 다른 파라미터로 매일 실행합니다.

Step — Job의 독립 처리 단계

Step은 Job의 한 처리 단계예요. 자신의 트랜잭션 경계를 가지는 완결된 작업 단위.

두 가지 유형으로 나뉩니다.

Tasklet 기반 Step — 단순 작업

파일 삭제, 이메일 발송, 외부 API 호출 같은 단순 작업.

@Bean
public Step taskletStep() {
    return stepBuilderFactory.get("taskletStep")
        .tasklet((contribution, chunkContext) -> {
            System.out.println("단순 작업 수행");
            return RepeatStatus.FINISHED;
        })
        .build();
}

Chunk 기반 Step — 대량 데이터 처리

ItemReader → ItemProcessor → ItemWriter 패턴으로 대량 데이터를 처리. 이 시리즈의 가장 핵심.

@Bean
public Step chunkStep() {
    return stepBuilderFactory.get("chunkStep")
        .<Product, Product>chunk(10)  // 청크 크기 10
        .reader(itemReader())
        .processor(itemProcessor())
        .writer(itemWriter())
        .build();
}

3편에서 자세히 다룹니다.

Chunk — 트랜잭션 단위의 묶음

Chunk는 한 트랜잭션으로 처리되는 데이터의 묶음입니다.

[ItemReader] → item → item → item → ... (chunk 크기만큼)
                                         ↓
                                   [ItemProcessor] (각 아이템 처리)
                                         ↓
                                   [ItemWriter] (묶음으로 한 번에 쓰기)
                                         ↓
                                   [Commit Transaction]

청크 크기 선택은 trade-off예요.

청크 크기 메모리 트랜잭션 실패 시 재처리
1 최소 매우 많음 1개
10 낮음 많음 최대 10개
100 중간 보통 최대 100개
1000 높음 적음 최대 1000개

여기서 정말 중요한 시험 함정 — 청크 크기를 너무 키우면 메모리 폭발 + 실패 시 재처리 범위 확대예요. 너무 작으면 트랜잭션 오버헤드로 성능 저하. 일반적으로 10~1000 사이, 시작은 100 정도가 무난합니다.

Tasklet — 단순 작업의 인터페이스

public interface Tasklet {
    @Nullable
    RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception;
}

RepeatStatus.FINISHED 반환 = 작업 종료. CONTINUABLE 반환 = 같은 작업 반복 실행.

여기서 시험 함정이 하나 있어요. RepeatStatus.CONTINUABLE을 잘못 반환하면 무한 루프예요. 종료 조건이 명확하지 않으면 항상 FINISHED를 반환하는 게 안전합니다.

JobRepository — 메타데이터 저장소

JobRepository는 배치 처리의 모든 메타데이터를 저장하는 컴포넌트예요. Job 실행 이력·Step 실행 이력·실행 상태·시간 — 모든 정보를 DB에 영속화.

자동 관리되는 6개 테이블:

BATCH_JOB_INSTANCE         — Job 인스턴스
BATCH_JOB_EXECUTION        — Job 실행 정보
BATCH_JOB_EXECUTION_PARAMS — 실행 파라미터
BATCH_JOB_EXECUTION_CONTEXT — 실행 컨텍스트
BATCH_STEP_EXECUTION       — Step 실행 정보
BATCH_STEP_EXECUTION_CONTEXT — Step 실행 컨텍스트

이 메타데이터 덕분에 재시작·이중 실행 방지·모니터링 이 가능해집니다.

JobLauncher — Job 실행자

public interface JobLauncher {
    JobExecution run(Job job, JobParameters parameters);
}

기본 동기 실행. 비동기 실행도 설정 가능.

여기서 시험 함정이 하나 있어요. Spring Boot는 기본적으로 앱 시작 시 모든 Job을 자동 실행합니다. REST API로 Job을 제어하려면 spring.batch.job.enabled=false로 자동 실행을 끄세요. 안 끄면 앱 시작할 때마다 의도치 않게 Job이 돌아요.

JobInstance vs JobExecution — 가장 헷갈리는 두 개념

이 둘의 차이를 이해하는 게 Spring Batch의 핵심.

JobInstance — 논리적 실행 단위

Job 이름 + JobParameters 의 조합으로 식별. "오늘 날짜로 실행한 일일 보고서 Job"이 하나의 JobInstance.

같은 JobParameters로 실행하면 같은 JobInstance를 참조해요. 이미 성공 완료된 JobInstance는 재실행 불가 — 이중 처리 방지.

JobExecution — 실제 실행 시도

JobInstance의 실행 시도. 하나의 JobInstance가 여러 JobExecution을 가질 수 있어요 (실패 후 재시도).

JobInstance (날짜=2026-01-01, 보고서=일일)
├── JobExecution #1: 00:00 실행 → FAILED (네트워크 오류)
├── JobExecution #2: 01:00 재실행 → FAILED (DB 오류)
└── JobExecution #3: 02:00 재실행 → COMPLETED ✓

JobInstance (날짜=2026-01-02, 보고서=일일)
└── JobExecution #4: 00:00 실행 → COMPLETED

여기서 정말 중요한 시험 함정 — 이미 COMPLETED 된 JobInstance에 같은 파라미터로 재실행 시도하면 JobInstanceAlreadyCompleteException 예외가 발생합니다. 개발 중에는 RunIdIncrementer로 매번 새 JobInstance를 만들거나, time 파라미터에 System.currentTimeMillis()를 추가해서 회피.

@Bean
public Job myJob() {
    return jobBuilderFactory.get("myJob")
        .incrementer(new RunIdIncrementer())  // 자동 run.id 증가
        .start(myStep())
        .build();
}

JobParameters — 실행 식별자

JobParameters params = new JobParametersBuilder()
    .addString("reportDate", "2026-01-01")
    .addLong("time", System.currentTimeMillis())
    .toJobParameters();
jobLauncher.run(myJob, params);

타입 — String, Long, Double, Date.

JobParameters가 JobInstance를 식별하니, 운영에서는 날짜·배치 ID 같은 의미 있는 파라미터를 사용합니다.

StepExecution — Step 실행 정보

각 Step 실행마다 생성되는 객체. readCount·writeCount·skipCount·filterCount 등을 추적.

public ExitStatus afterStep(StepExecution stepExecution) {
    long readCount = stepExecution.getReadCount();
    long writeCount = stepExecution.getWriteCount();
    long skipCount = stepExecution.getSkipCount();
    System.out.println("읽음: " + readCount + ", 씀: " + writeCount + ", 스킵: " + skipCount);
    return ExitStatus.COMPLETED;
}

Batch Status vs Exit Status — 두 가지 상태

자주 헷갈리는 두 상태 시스템.

Batch Status — 프레임워크 내부

상태 의미
COMPLETED 성공 완료
STARTING 시작 중
STARTED 실행 중
STOPPING 중지 요청
STOPPED 중지 (재시작 가능)
FAILED 실패 (재시작 가능)
ABANDONED 포기 (재시작 불가)
UNKNOWN 알 수 없음

Spring Batch가 자동 관리. 개발자가 직접 변경 어려움.

Exit Status — 흐름 제어용

기본 — COMPLETED, FAILED, STOPPED, NOOP, UNKNOWN.

개발자가 커스텀 ExitStatus 반환 가능. Job Flow의 .on("EXIT_CODE") 조건에 사용.

public ExitStatus afterStep(StepExecution stepExecution) {
    if (stepExecution.getWriteCount() == 0) {
        return new ExitStatus("NO_DATA");  // 커스텀 상태
    }
    return ExitStatus.COMPLETED;
}

여기서 정말 중요한 시험 함정 — .on("COMPLETED")는 Batch Status가 아닌 Exit Status를 참조합니다. 두 시스템이 같은 이름을 쓰지만 의미가 다르니, 조건부 흐름 설정 시 항상 Exit Status 기준으로 생각하세요.

H2 vs MySQL — 메타데이터 DB 선택

H2 (개발용)

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
spring.batch.jdbc.initialize-schema=always

여기서 시험 함정이 하나 있어요. H2는 인메모리 DB라 앱 재시작하면 모든 배치 이력이 사라져요. 개발/테스트 전용. 운영에는 절대 X.

MySQL (운영용)

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
spring.datasource.url=jdbc:mysql://localhost:3306/batch_schema
spring.datasource.username=batch_user
spring.datasource.password=batch_user
spring.batch.jdbc.initialize-schema=always

운영에서는 initialize-schema=never로 두고 별도 마이그레이션 스크립트 사용 — 기존 배치 이력 삭제 위험 회피.

시험 직전 한 번 더 — 자주 헷갈리는 함정 모음

여기까지가 1편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.

  • 배치 처리 = 사용자 개입 없이 대량 데이터 자동 처리
  • 온라인(즉시) vs 배치(스케줄·자동·대량) — 사고방식 자체가 다름
  • 배치 사용 사례 — ETL / 금융 정산 / 보고서 / 재고 관리
  • Spring Batch 5가지 가치 — 재시작·스킵·청크·병렬·모니터링
  • Job = 최상위 단위, 하나 이상의 Step으로 구성
  • Step = 독립 처리 단계, Tasklet 또는 Chunk 유형
  • Tasklet = 단순 작업 (RepeatStatus.FINISHED 반환)
  • CONTINUABLE 반환은 무한 루프 위험
  • Chunk = 트랜잭션 단위 묶음, 일반적으로 10~1000
  • 청크 크기 trade-off — 메모리 vs 트랜잭션 vs 재처리 범위
  • JobRepository = 메타데이터 저장소, 6개 테이블 자동 관리
  • BATCH_JOB_INSTANCE / EXECUTION / PARAMS / STEP_EXECUTION 등
  • JobLauncher = Job 실행자, 기본 동기
  • Spring Boot 앱 시작 시 자동 실행 = spring.batch.job.enabled=false로 끄기
  • JobInstance = Job + Parameters 논리적 단위
  • JobExecution = JobInstance의 실제 실행 시도
  • 같은 파라미터 재실행 = JobInstanceAlreadyCompleteException
  • RunIdIncrementer = 매번 새 JobInstance 생성
  • StepExecution = readCount/writeCount/skipCount 추적
  • Batch Status = 프레임워크 내부 상태
  • Exit Status = 흐름 제어용 상태
  • .on("COMPLETED")는 Exit Status (Batch Status X)
  • 커스텀 ExitStatus = new ExitStatus("CUSTOM_CODE")
  • H2 = 인메모리, 개발용 / MySQL = 운영용
  • 운영 initialize-schema=never로 이력 보호

시리즈 다른 편

같은 시리즈의 다른 글들도 같은 톤으로 묶어 정리되어 있어요. 1편 모델이 잡히면 2편 Job 설정부터 본격 코드입니다.

공식 문서: Spring Batch ReferenceSpring Boot Batch Starter에서 더 깊이 갈 수 있어요.

다음 글(2편)에서는 @EnableBatchProcessing·JobBuilderFactory·StepBuilderFactory·REST API로 Job 실행·조건부 흐름·JobExecutionDecider까지 — Spring Batch 4.x 기준 Job/Step 설정의 본격을 풀어 갑니다.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!