❐ Description
- Job 객체는 단순히 여러 Step을 담는 컨테이너처럼 보일 수 있지만, 사실 많은 설정 옵션들을 알고 있어야 함
- Job을 어떻게 실행할지, 실행 중에 메타데이터를 어떻게 저장할지도 여러 가지를 고려해야 함
- 이 장에서는 Job의 다양한 설정 방법과 실행 시 고려해야 할 사항들을 설명함
❐ 1. Configuring a Job
@Bean
public Job footballJob(JobRepository jobRepository) {
return new JobBuilder("footballJob", jobRepository)
.start(playerLoad())
.next(gameLoad())
.next(playerSummarization())
.build();
}
- 세 개의 Step 인스턴스로 구성된하나의 Job
- playerLoad → gameLoad → playerSummarization
- 이러한 Job 빌더는 다양한 요소도 포함할 수 있음
- 병렬 처리(Split), 선언형 흐름 제어(Decision), 흐름 정의의 외부화(Flow)와 같은
- Job(그리고 일반적으로 그 안의 모든 Step)은 JobRepository를 필요
- 이 JobRepository의 설정은 Java 기반 설정(Java Configuration)을 통해 처리
🌀 1-1. Restartability
기본 설명
- 배치 잡을 실행할 때 중요한 이슈 중 하나는 Job이 재시작될 때의 동작 방식
- 이미 JobExecution이 존재한다면, 이 Job을 다시 실행하는 것은 재시작으로 간주
- 이론적으로는 모든 Job이 이전에 중단된 지점에서 다시 시작할 수 있어야 함
- 하지만 현실적으로는 그렇게 할 수 없는 경우도 있음
- 그런 경우, 개발자가 새로운 JobInstance를 생성하도록 책임지고 처리
- 하지만 Spring Batch는 이런 상황을 도와줄 수 있는 기능도 제공
- 예를 들어, Job이 절대 재시작되면 안 되고, 항상 새로운 JobInstance로 실행되어야 한다면,
- 해당 Job의 restartable 속성을 false로 설정하면 됨
Java sample
@Bean
public Job footballJob(JobRepository jobRepository) {
return new JobBuilder("footballJob", jobRepository)
.preventRestart()
...
.build();
}
- restartable을 false로 설정하는 것은 "이 Job은 다시 시작할 수 없다" 는 뜻
- restartable이 false인 Job을 다시 시작하려고 하면 JobRestartException이 발생
Job job = new SimpleJob();
job.setRestartable(false); // 재시작 불가능 설정
JobParameters jobParameters = new JobParameters();
// 첫 번째 실행 생성 및 저장
JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters);
jobRepository.saveOrUpdate(firstExecution);
// 두 번째 실행 시도
try {
jobRepository.createJobExecution(job, jobParameters); // 같은 JobInstance로 실행 시도
fail(); // 예외가 발생하지 않으면 테스트 실패
} catch (JobRestartException e) {
// 예외가 발생하면 정상 처리
}
- 재시작이 불가능한 Job에 대해 첫 번째 JobExecution 생성은 문제 없이 작동
- 하지만 두 번째 시도에서는 JobRestartException이 발생
🌀 1-2. Intercepting Job Execution
- 배치 작업(Job) 실행 중 다양한 이벤트에 대해 알림을 받고, 그 시점에 맞춰 커스텀 코드를 실행할 수 있음.
- `SimpleJob` 은 적절한 시점에 `JobListener` 를 호출하여 이 기능을 제공
- Job 실행 전후에 특정 작업을 하고 싶을 때, `JobExecutionListener`를 구현해서 리스너로 등록하면 됨.
Interface : JobExecutionListener
public interface JobExecutionListener {
// job 실행 전 호출
void beforeJob(JobExecution jobExecution);
// job 실행 후 호출
void afterJob(JobExecution jobExecution);
}
리스너 설정하기
@Bean
public Job footballJob(JobRepository jobRepository) {
return new JobBuilder("footballJob", jobRepository)
.listener(sampleListener()) // here
...
.build();
}
afterJob 메서드의 동작
- `afterJob` 메서드는 Job의 성공/실패와 상관없이 항상 호출됨.
- Job의 성공 여부는 `JobExecution`의 상태로 확인할 수 있음.
public void afterJob(JobExecution jobExecution){
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
// 작업 성공
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
// 작업 실패
}
}
🌀 1-3. Inheriting from a parent job
- 자바의 클래스 상속처럼, 자식 Job은 부모 Job의 요소와 속성을 그대로 물려받을 수 있음.
- 동시에 자신만의 설정을 추가할 수도 있음.
<job id="baseJob" abstract="true">
<listeners>
<listener ref="listenerOne"/>
</listeners>
</job>
<job id="job1" parent="baseJob">
<step id="step1" parent="standaloneStep"/>
<listeners merge="true">
<listener ref="listenerTwo"/>
</listeners>
</job>
- baseJob
- 추상 Job(실행 불가)
- 리스너(listenerOne) 만 정의.
- job1
- baseJob을 상속받는 구체적인 Job
- baseJob의 listenerOne을 상속받음
- 자신만의 listenerTwo를 추가
- step1이라는 Step도 포함
🌀 1-4. JobParametersValidator
설명
- XML로 정의된 Job이든, AbstractJob을 상속받은 Job이든
- 실행 시점에 JobParameter를 검증하는 Validator를 선택적으로 설정할 수 있음.
- Spring Batch에는 DefaultJobParametersValidator라는 기본 구현체가 있음.
- 따라서 필수 파라미터와 선택 파라미터를 쉽게 지정할 수 있음.
- 더 복잡한 검증 조건이 필요하다면, 직접 JobParametersValidator 인터페이스를 구현하면 됨.
Java에서 사용 예시
@Bean
fun job1(
jobRepository: JobRepository,
step1: Step,
parametersValidator: JobParametersValidator
): Job =
JobBuilder("job1", jobRepository)
.validator(parametersValidator)
.start(step1)
.build()
- `validator(parametersValidator)`
- Job 실행 시 전달된 JobParameter를 검증하는 로직을 설정.
- parametersValidator는 DefaultJobParametersValidator를 리턴하거나, 직접 구현
- Job이 실행될 때 필수 파라미터가 없거나 형식이 맞지 않으면 예외 발생.
DefaultJobParametersValidator 사용 예시
@Bean
fun parametersValidator(): JobParametersValidator =
DefaultJobParametersValidator().apply {
setRequiredKeys(arrayOf("date", "filePath"))
setOptionalKeys(arrayOf("mode"))
}
- date와 filePath는 필수
- mode는 선택
❐ 2. Java Configuration
@EnableBatchProcessing
- 배치 구성의 기본을 자동으로 등록함.
- `StepScope`, `JobScope`와 함께 다음 빈들을 자동으로 제공
- jobRepository
- jobLauncher
- jobRegistry
- jobExplorer
- jobOperator
- `StepScope`, `JobScope`와 함께 다음 빈들을 자동으로 제공
- 기본적으로 `dataSource`와 `transactionManager` 빈이 필요하며, 속성으로 커스터마이즈할 수 있음.
DefaultBatchConfiguration (v5.0부터)
- 애노테이션 없이 프로그램적으로 동일한 인프라 빈을 제공
- 이 클래스를 상속하여 필요한 빈 설정을 오버라이드할 수 있음.
중요한 주의사항
- @EnableBatchProcessing과 DefaultBatchConfiguration을 동시에 사용하면 안 됨.
- 둘 중 하나만 선택해야 함.
❐ 3. Configuring a JobRepository
JobRepository
- Batch에서 JobExecution, StepExecution 등 배치 실행 정보를 저장하고 조회(CRUD)하는 핵심 저장소
- 내부적으로는 RDBMS를 사용하며, JobLauncher, Job, Step 등이 이를 사용한다.
- @EnableBatchProcessing을 사용하면 자동으로 JobRepository를 주입해줌. (설정 예시)
🌀 3-1. Transaction Configuration for the JobRepository
➔ JobRepository의 CRUD는 반드시 트랜잭션 내에서 이루어져야 함.
기본적으로...
- namespace 또는 제공된 FactoryBean을 사용하면 트랜잭션이 자동으로 적용됨.
- 이는 실패 후 재시작에 필요한 상태를 포함한 배치 메타데이터가 올바르게 영속화되도록 보장하기 위한 것
- 리포지토리 메서드들이 트랜잭션을 사용하지 않으면, 프레임워크의 동작은 명확하게 정의되지 있지 않음.
property : isolationLevelForCreate
- Job이 시작될 때, `create*()` 메서드들이 호출되며 실행 이력을 생성함.
- 이때 사용되는 트랜잭션 격리 수준(isolation level) 은 기본적으로 SERIALIZABLE
- isolationLevelForCreate 속성을 설정하면 격리수준 변경할 수 있음.
AOP를 통해 수동으로 트랜잭션 적용하기
- JobRepository를 직접 구현하는 경우에 이렇게 쓰면 됨.
@Bean
public TransactionProxyFactoryBean baseProxy() {
TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
Properties transactionAttributes = new Properties();
transactionAttributes.setProperty("*", "PROPAGATION_REQUIRED");
transactionProxyFactoryBean.setTransactionAttributes(transactionAttributes);
transactionProxyFactoryBean.setTarget(jobRepository());
transactionProxyFactoryBean.setTransactionManager(transactionManager());
return transactionProxyFactoryBean;
}
🌀 3-2. Changing the Table Prefix
- JobRepository는 배치 실행 상태(JobExecution, JobInstance 등)를 저장하는 메타데이터 테이블을 사용
- 이 테이블 이름은 기본적으로 모두 접두사 `BATCH_` 로 시작
- 접두사 바꿀 수 있음.
@Configuration
@EnableBatchProcessing(tablePrefix = "SYSTEM.TEST_")
public class MyJobConfiguration {
// job definition
}
🌀 3-3. Non-standard Database Types in a Repository
- 지원 목록에 없는 데이터베이스를 사용할 경우, JobRepositoryFactoryBean을 직접 사용하여 수동 설정
@Bean
public JobRepository jobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setDatabaseType("db2");
factory.setTransactionManager(transactionManager);
return factory.getObject();
}
❐ 4. Configuring a JobLauncher
🌀 4-1. 가장 심플한 방법 : TaskExecutorJobLauncher
예제 코드
@Bean
fun jobLauncher(): JobLauncher =
TaskExecutorJobLauncher().apply {
setJobRepository(jobRepository) // 필수 의존성: jobRepository
afterPropertiesSet()
}
Job Launcher Sequence

- JobExecution을 얻으면 그것을 Job의 execute() 메서드에 넘기고
- 최종적으로 그 JobExecution을 호출자에게 반환
여기서 궁금한점: Job의 execute 메서드는 반환 타입이 void인데 어떻게 exitStatus를 반환하지?
- execute 메서드 자체는 void지만, exitStatus는 JobExecution 객체 내부에서 업데이트됨.
- SimpleJob.java의 doExecute 메소드를 보면 이해할 수 있음.
public class SimpleJob extends AbstractJob {
//...
@Override
protected void doExecute(JobExecution execution) throws JobInterruptedException, JobRestartException, StartLimitExceededException {
StepExecution stepExecution = null;
for (Step step : steps) {
stepExecution = handleStep(step, execution);
if (stepExecution.getStatus() != BatchStatus.COMPLETED) break;
}
if (stepExecution != null) {
if (logger.isDebugEnabled()) {
logger.debug("Upgrading JobExecution status: " + stepExecution);
}
// here!
execution.upgradeStatus(stepExecution.getStatus());
execution.setExitStatus(stepExecution.getExitStatus());
}
}
}
🌀 4-2. 비동기로 설정한 방법 : TaskExecutorJobLauncher
왜 비동기로 설정해야 할까?
- Batch 작업이 HTTP 요청으로 들어온 경우를 생각할 수 있음.
- 기본적으로 HTTP 요청은 작업 이후 응답을 줘야함.
- 이때 Batch 작업이 오래걸린다면? 그만큼 응답 latency가 길어짐
- 이런 문제를 방지하고자 Spring Batch에서는 비동기 실행을 지원함
Configuration
@Bean
fun jobLauncher(): JobLauncher =
TaskExecutorJobLauncher().apply {
setJobRepository(jobRepository())
setTaskExecutor(SimpleAsyncTaskExecutor())
afterPropertiesSet()
}
Asynchronous Job Launcher Sequence

- Client가 JobLauncher.run()을 호출
- JobLauncher는 내부적으로 JobExecution 객체를 만들어서 반환
- 이때 ExitStatus는 아직 결과를 모르기 때문에 UNKNOWN으로 응답
- 실제 배치는 백그라운드 스레드에서 execute()를 통해 실행
- 나중에 작업이 완료되면 ExitStatus가 FINISHED 또는 FAILED로 업데이트돼요.
- Client는 배치 작업의 상태를 확인하기 위해서 별도의 "상태 조회 API"를 호출해야 함.
❐ 5. Running a Job
🌀 5-1. Running Jobs from the Command Line
🌀 5-2. Running Jobs from within a Web Container
❐ 6. Advanced Metadata Usage
'Book > Spring Batch docs' 카테고리의 다른 글
| ItemReaders and ItemWriters (0) | 2025.10.07 |
|---|---|
| Configuring a Step (0) | 2025.10.06 |
| The Domain Language of Batch (0) | 2025.07.07 |
| Spring Batch Architecture (0) | 2025.07.06 |
| Spring Batch Introduction (0) | 2025.07.06 |
