
배치 처리(Batch Processing)는 대용량 데이터를 사용자 개입 없이 일괄로 처리하는 방식이다.
조금 더 풀어서 말하면, 배치 처리는 다음과 같은 특징을 가진다.
배치 처리는 단순히 for문으로 많은 데이터를 반복 처리하는 것이 아니라, 운영 환경에서 대용량 데이터를 안정적으로 처리하기 위한 실행 방식이라고 볼 수 있다.
배치 처리는 일반적인 웹 API 처리와 다르다. 웹 API는 사용자의 요청에 즉시 응답해야 한다. 예를 들어 사용자가 상품 상세 페이지에 들어오면 서버는 상품 정보를 조회해서 빠르게 응답해야 한다. 반면, 배치 처리는 사용자가 직접 기다리는 작업이 아니라 정산, 통계 집계, 만료 쿠폰 삭제, 대량 알림 발송, 데이터 마이그레이션처럼 정해진 시점에 대량의 데이터를 한 번에 처리하는 경우에 이용되는 방식이다.
정리하자면, 웹 API는 “빠른 응답” 이 중요하고, 배치는 “안정적인 대량 처리” 가 중요하다.
Spring Batch는 스프링 프레임워크 기반의 배치 처리 프레임워크다. 대용량 데이터를 안정적으로 처리하기 위해 필요한 기능들을 제공한다. Spring Batch가 제공하는 대표적인 기능은 다음과 같다.
재시작 기능: 배치 작업은 오래 걸릴 수 있다. 작업 도중 서버가 중단되거나 예외가 발생할 수 있다. Spring Batch는 작업이 어디까지 진행되었는지 데이터베이스에 기록한다. 그래서 작업이 실패하더라도 실행 이력을 바탕으로 다시 실행하거나, 실패 지점을 추적할 수 있다.
Chunk 단위 처리: 100만 건의 데이터를 한 번에 메모리에 올리면 위험하다. Spring Batch는 데이터를 일정 개수씩 나누어 읽고 처리할 수 있다. 예를 들어 200만 건을 한 번에 처리하는 것이 아니라, 1000건씩 읽고, 처리하고, 저장하는 식으로 작업할 수 있다. 이렇게 일정 단위로 나누어 처리하는 방식을 Chunk 지향 처리라고 한다.
Skip/Retry 처리: 운영 데이터는 항상 완전하지 않다. 특정 데이터 하나 때문에 예외가 발생할 수 있다. Spring Batch는 특정 예외가 발생했을 때 해당 데이터만 건너뛰거나, 일정 횟수만큼 재시도하는 기능을 제공한다. 이를 통해 일부 데이터 오류 때문에 전체 작업이 중단되는 상황을 줄일 수 있다.
메타 데이터 관리: Spring Batch는 Job과 Step의 실행 이력을 데이터베이스에 기록한다. 예를 들어, 어떤 Job이 실행되었는지, 어떤 파라미터로 실행됐는지, 언제 시작되고 언제 종료됐는지, 성공했는지 실패했는지, 몇 건을 읽고 쓰고, 몇 건을 건너뛰었는지 등과 같은 정보들을 저장한다.
Spring Batch는 크게 3개의 레이어(Application, Batch Core, Batch Infrastructure)로 구성된다.

Application Layer: 업무 로직이나 서비스, 혹은 잡 구조화와 관련된 내용이 포함된다. 월말 정산 배치를 만든다고 가정해보면 아래와 같은 내용들이 포함될 것이다.Batch Core Layer: 배치 작업을 실행하고 제어하는 핵심 기능을 제공한다. 대표적으로 JobLauncher, Job, Step, JobRepository가 이 레이어에 존재한다. Application Layer에서 개발자가 Job과 Step을 정의하면, Batch Core Layer가 그 Job과 Step을 실행하고 제어한다.
Batch Infrastructure Layer: 배치 처리에 필요한 공통 기반 기능을 제공한다. 예를 들어 CSV 파일을 읽는 FlatFileItemReader, 데이터베이스에서 데이터를 읽는 JdbcPagingItemReader, 데이터를 파일이나 DB에 쓰는 ItemWriter, 재시도 처리를 위한 RetryTemplate, 실행 이력을 저장하는 메타데이터 저장소 등이 이 레이어에 존재한다. 개발자는 Infrastructure Layer에서 제공하는 구현체를 가져와 Application Layer에서 조합하고, Batch Core Layer가 이를 실행하는 구조로 이해할 수 있다.
Spring Batch의 전체 작업 흐름에 대해 살펴보면 아래 그림과 같다.

Triggering System은 Batch Job 실행을 시작시키는 외부 시스템이다. Spring Batch 자체가 특정 시간에 Job을 자동으로 실행해주는 스케줄러는 아니다. 실제로 Job을 언제 실행할지는 외부 트리거가 결정한다.
예를 들면 다음과 같은 것들이 Triggering System이 될 수 있다.
예를 들어 매월 말 정산 배치를 실행한다면, 스케줄러가 정해진 시간에 Batch Job을 실행하도록 트리거할 수 있다.
매월 말 00:00
↓
정산 Batch Job 실행
즉, Triggering System은 “언제 배치를 실행할 것인가?” 를 담당한다.
JobLauncher는 Job을 실행하는 진입점이다. Job을 정의했다고 해서 자동으로 실행되는 것은 아니다. 정의된 Job을 실제로 실행하려면 JobLauncher를 통해 실행 요청을 보내야 한다.
jobLauncher.run(job, jobParameters);
JobLauncher는 실행할 Job과 해당 Job에 전달할 JobParameters를 받아서 배치 작업을 시작한다. 쉽게 말하면 JobLauncher는 “이 Job을 이 파라미터로 실행해줘” 와 같은 요청을 처리한다.
JobParameters는 Job을 실행할 때 함께 전달하는 파라미터다. 단순히 Job 내부에서 사용할 값이라고만 생각하면 안 된다. Spring Batch에서 JobParameters는 JobInstance를 식별하는 중요한 기준이 된다.
Spring Batch는 “Job 이름 + JobParameters” 조합으로 하나의 JobInstance를 구분한다. 예를 들어 다음과 같은 Job이 있다고 해보자.
Job 이름: settlementJob
JobParameters: settlementMonth=2026-06
이 경우 Spring Batch는 이것을 2026년 6월 정산 작업이라는 하나의 논리적인 실행 단위로 보는 것이다.
Job은 하나의 배치 작업 전체를 의미한다. 예를 들어 다음과 같은 작업은 각각 하나의 Job이 될 수 있다.
즉, Job은 “무엇을 처리할 것인가?” 에 대한 가장 큰 단위다.
JobInstance는 Job 이름과 JobParameters 조합으로 식별되는 논리적인 실행 단위다. 예를 들어 다음과 같이 실행했다고 해보자.
Job 이름: settlementJob
JobParameters: settlementMonth=2026-06
그러면 Spring Batch는 이 조합을 하나의 JobInstance로 본다.
settlementJob + settlementMonth=2026-06
= 하나의 JobInstance
이 JobInstance는 2026년 6월 정산 작업이라는 논리적인 배치 작업을 의미한다. 중요한 점은 이미 성공적으로 완료된 JobInstance는 같은 파라미터로 다시 실행할 수 없다는 것이다. 왜냐하면 같은 데이터를 같은 조건으로 다시 처리하면 중복 정산, 중복 지급, 중복 알림 같은 문제가 발생할 수 있기 때문이다.
따라서 Spring Batch는 완료된 JobInstance의 동일 파라미터 재실행을 막는다. 새로운 JobInstance를 만들고 싶다면 새로운 파라미터를 사용해야 한다.
JobExecution은 JobInstance를 실제로 실행한 한 번의 시도다. JobInstance가 “어떤 작업인가?” 를 나타낸다면, JobExecution은 “그 작업을 실제로 실행한 한 번의 기록” 이라고 볼 수 있다.
예를 들어 2026년 6월 정산 JobInstance가 있다고 해보자. 이 작업을 실행했는데 중간에 실패했다면, JobInstance는 그대로 유지된다. 하지만 실행 시도인 JobExecution은 하나 생성된다.
이후 같은 JobInstance를 다시 실행하면 어떻게 될까? 논리적인 작업은 여전히 2026년 6월 정산 작업이므로 JobInstance는 기존 것을 재사용한다.
하지만 실행 시도는 새롭게 발생한 것이므로 JobExecution은 새로 생성된다. 정리하면 다음과 같다.
JobInstance: 2026년 6월 정산 작업
JobExecution 1: 첫 번째 실행 시도 - 실패
JobExecution 2: 두 번째 실행 시도 - 성공
즉, 하나의 JobInstance는 여러 개의 JobExecution을 가질 수 있다. JobExecution에는 보통 다음과 같은 정보가 기록된다.
Step은 Job을 구성하는 독립적인 실행 단계다. 하나의 Job은 하나 이상의 Step으로 구성되어 있다.
예를 들어 월말 정산 Job은 다음과 같이 여러 Step으로 나눌 수 있다.
월말 정산 Job
├─ Step 1. 정산 대상 판매자 조회
├─ Step 2. 판매자별 정산 금액 계산
├─ Step 3. 정산 결과 저장
└─ Step 4. 정산 완료 알림 발송
각 Step은 독립적인 실행 단위다. 앞의 Step이 성공해야 다음 Step이 실행되도록 구성할 수도 있고, 조건에 따라 다른 Step으로 분기할 수도 있다.
StepExecution은 JobExecution 안에서 각각의 Step이 실행된 정보를 의미한다.
JobExecution이 Job의 실행 시도라면, StepExecution은 Step의 실행 시도다. 하나의 Job 안에 Step이 여러 개 있으면, 각 Step마다 StepExecution이 생성된다.
예를 들어 월말 정산 Job에 Step이 3개 있다면 다음과 같이 볼 수 있다.
JobExecution
├─ StepExecution 1. 정산 대상 조회 Step 실행 정보
├─ StepExecution 2. 정산 금액 계산 Step 실행 정보
└─ StepExecution 3. 정산 결과 저장 Step 실행 정보
StepExecution에는 해당 Step의 상태와 처리 건수 등이 기록된다.
예를 들면 다음과 같은 정보가 저장될 수 있다.
ExecutionContext는 JobExecution 또는 StepExecution 범위에서 사용할 수 있는 key-value 형태의 상태 저장 공간이다.
쉽게 말하면 배치 실행 중 유지해야 하는 상태를 저장하는 공간이다. 예를 들어 다음과 같은 정보를 저장할 수 있다.
Spring Batch는 ExecutionContext를 메타데이터 테이블에 저장한다. 그래서 배치 작업이 중간에 실패했다가 재시작될 때, 이전 실행에서 저장해둔 ExecutionContext 값을 다시 읽어와 이어서 처리할 수 있다.
즉, ExecutionContext는 재시작 기능과 밀접하게 연결된 개념이다.
ItemReader는 데이터를 읽는 역할을 한다. 읽어오는 대상은 다양할 수 있다.
ItemReader는 데이터를 한 건씩 읽어서 다음 단계로 넘긴다. 예를 들어 CSV 파일에서 데이터를 읽는다면 FlatFileItemReader를 사용할 수 있고, 데이터베이스에서 페이징 방식으로 데이터를 읽는다면 JdbcPagingItemReader를 사용할 수 있다.
ItemReader의 중요한 특징은 읽을 데이터가 더 이상 없을 때 null을 반환한다는 것이다. Spring Batch는 이 null 반환을 보고 더 이상 처리할 데이터가 없다고 판단한다.
ItemProcessor는 ItemReader가 읽은 데이터를 가공하거나 검증하는 역할을 한다. 예를 들어 정산 배치라면 ItemProcessor에서 다음과 같은 작업을 할 수 있다.
ItemProcessor는 필수는 아니다. 데이터를 읽은 그대로 저장해도 된다면 ItemProcessor 없이 ItemReader와 ItemWriter만으로 Step을 구성할 수 있다.
하지만 실제 비즈니스 로직에서는 데이터를 가공하거나 검증해야 하는 경우가 많기 때문에 자주 사용된다. 또한 ItemProcessor에서 null을 반환하면 해당 아이템은 이후 Writer로 전달되지 않는다.
즉, 특정 조건에 맞지 않는 데이터를 필터링할 때 사용할 수 있다.
ItemWriter는 처리된 데이터를 저장하는 역할을 한다. ItemProcessor를 거친 데이터는 Chunk 단위로 모이고, 정해진 개수만큼 쌓이면 ItemWriter가 한 번에 저장한다.
저장 대상은 다양할 수 있다.
예를 들어 정산 배치라면 ItemWriter는 계산된 정산 결과를 정산 테이블에 저장할 수 있다.
ItemWriter는 Chunk 단위로 데이터를 전달받기 때문에, 한 건씩 저장하기보다는 여러 건을 모아서 저장하는 구조에 가깝다.
Data Source는 배치 작업이 읽거나 쓰는 외부 데이터 저장소를 의미한다. 대표적으로 다음과 같은 것들이 있다.
Spring Batch의 ItemReader는 Data Source에서 데이터를 읽고, ItemWriter는 처리 결과를 Data Source에 쓴다.
정산 배치를 예로 들면 다음과 같이 볼 수 있다.
Database에서 주문 데이터를 읽는다.
↓
정산 금액을 계산한다.
↓
Database에 정산 결과를 저장한다.
즉, Data Source는 배치 작업의 입력이자 출력 대상이 될 수 있다.
JobRepository는 Spring Batch의 실행 메타데이터를 저장하고 관리하는 저장소다.
여기서 중요한 점은 JobRepository가 비즈니스 데이터를 저장하는 저장소가 아니라는 것이다. 예를 들어 정산 결과 자체는 정산 테이블에 저장된다. 반면 JobRepository는 다음과 같은 배치 실행 이력을 저장한다.
Spring Batch는 이 정보를 메타데이터 테이블에 저장한다. 대표적인 테이블은 다음과 같다.
| 테이블 | 설명 |
|---|---|
| BATCH_JOB_INSTANCE | JobInstance 정보 저장 |
| BATCH_JOB_EXECUTION | JobExecution 정보 저장 |
| BATCH_JOB_EXECUTION_PARAMS | JobParameters 정보 저장 |
| BATCH_STEP_EXECUTION | StepExecution 정보 저장 |
| BATCH_JOB_EXECUTION_CONTEXT | JobExecutionContext 저장 |
| BATCH_STEP_EXECUTION_CONTEXT | StepExecutionContext 저장 |
JobRepository가 중요한 이유는 재시작과 추적 때문이다. 배치 작업이 중간에 실패해도, Spring Batch는 JobRepository에 저장된 실행 이력을 바탕으로 어떤 Job이 어떤 상태였는지 확인할 수 있다.
JobRepository는 크게 3가지 시점에서 동작한다.
Job이 시작되기 전: 같은 이름과 파라미터로 조합으로 이전에 실행된 이력이 존재하는지 조회한다.
Job이 실행되는 중: Step이 처리한 건수, 현재 실행 상태 같은 Spring Batch에서 참조할 수 있는 진행 정보를 주기적으로 업데이트한다.
Job이 종료: 최종 상태를 기록한다. (COMPLETED, FAILED 등)
마지막으로 지금까지 나온 용어를 하나의 흐름으로 정리하면 다음과 같다.
1. Triggering System이 배치 실행을 시작한다.
2. JobLauncher가 Job과 JobParameters를 받아 Job을 실행한다.
3. Spring Batch는 Job 이름과 JobParameters로 JobInstance를 식별한다.
4. JobInstance의 실제 실행 시도로 JobExecution이 생성된다.
5. Job은 하나 이상의 Step을 실행한다.
6. 각 Step 실행 정보는 StepExecution으로 기록된다.
7. Step은 Tasklet 또는 Chunk 방식으로 실행된다.
8. Chunk 방식에서는 ItemReader, ItemProcessor, ItemWriter가 Read → Process → Write 흐름으로 데이터를 처리한다.
9. ItemReader와 ItemWriter는 File, Queue, Database 같은 Data Source와 데이터를 주고받는다.
10. JobRepository는 이 모든 실행 이력을 메타데이터 테이블에 저장한다.
<참고 자료>
Spring Batch Tutorial: Batch Processing Made Easy with Spring
Spring Batch, 제대로 이해하기 (1) - 개념이해