Configuring and Running a Job

2025. 7. 17. 00:13·Book/Spring Batch docs

❐ 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
  • 기본적으로 ‎`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
'Book/Spring Batch docs' 카테고리의 다른 글
  • ItemReaders and ItemWriters
  • Configuring a Step
  • The Domain Language of Batch
  • Spring Batch Architecture
gilbert9172
gilbert9172
gilbert9172 님의 블로그 입니다.
  • gilbert9172
    バックエンド
    gilbert9172
  • 전체
    오늘
    어제
    • All Categories (207)
      • 우테코 7기 (21)
        • 1주차 (8)
        • 2주차 (5)
        • 3주차 (6)
      • Langauge (6)
        • Java (3)
        • Kotlin (3)
      • Back-End (13)
        • SpringBoot (1)
        • Trouble Shooting (0)
        • Setup & Configuration (1)
        • SQL (3)
        • Redis (8)
      • Architecture (6)
        • Multi Module (1)
        • DDD (5)
      • CS (30)
        • Data Structure (6)
        • Operating System (0)
        • Network (12)
        • Database (10)
        • Design Pattern (2)
      • Algorithm (78)
        • 내용 정리 (18)
        • 문제풀이 (60)
      • DevOps (6)
        • AWS (5)
        • Git (1)
      • Front-End (1)
        • Trouble Shooting (1)
      • Project (6)
        • 페이스콕 (6)
      • Book (39)
        • 친절한 SQL 튜닝 (9)
        • 데이터 중심 애플리케이션 설계 (14)
        • 이벤트 기반 마이크로서비스 구축 (6)
        • Spring Batch docs (10)
        • Quartz docs (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    greedy
    Back-Tracking
    binarysearch
    Two-Pointer
    sliding-window
    부분단조성
    오블완
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
gilbert9172
Configuring and Running a Job
상단으로

티스토리툴바