본문 바로가기
Spring/Batch

Spring Batch + Quartz를 활용한 스케줄러 구현하기

by 흑시바 2023. 8. 6.

Spring 배치 작업을 실행하도록 Quartz를 활용해 스케줄러를 구성하는 방법에 대해 알아본다.

해당 포스팅은 스프링 배치나 Quartz에 대해서 깊게 다루지는 않는다. 스프링 배치를 활용해서 스케줄링을 구현할 때 어떻게 구현하는지 참고 용도로 활용하길 바란다.

0. 개발 환경

- JDK 17

- Spring Boot 3.0.1

- Spring Batch 5

- Quartz 2.3.2

1. Dependency(Maven)

spring-boot-starter-batch, spring-boot-starter-quartz 의존성을 추가한다.

 

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2. 배치 작업 생성하기

@Configuration
public class SimpleJobConfig {

    @Bean
    public Job simpleJob(JobRepository jobRepository, Step simpleStep) {
        return new JobBuilder("simpleJob", jobRepository)
                .start(simpleStep)
                .build();
    }

    @Bean
    public Step simpleStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder("simpleStep", jobRepository)
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("ShibaHolic");
                    return RepeatStatus.FINISHED;
                }, transactionManager)
                .build();
    }
}

 

스프링 배치 5부터는 @EnableBatchProcessing 선언이 필요 없으며, 위와 같은 형식으로 배치 작업이 구성된다.

3. 배치 작업을 실행하는 데 사용할 표준 클래스 생성하기

배치 작업을 실행할 때 필요한 정보들을 JobParameter에 추가하고 JobLauncher를 통해 Job을 실행한다.

해당 클래스는 QuartzJobBean을 상속받아서 excuteInternal 메서드를 작성해야 한다.

 

@Getter
@Setter
public class BatchScheduledJob extends QuartzJobBean {

    private String jobName;
    private JobLocator jobLocator;
    private JobLauncher jobLauncher;

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            Job job = this.jobLocator.getJob(this.jobName);
            JobParameters jobParameters = new JobParametersBuilder()
                    .addString("id", String.valueOf(System.currentTimeMillis()))
                    .toJobParameters();
            this.jobLauncher.run(job, jobParameters);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

 

excuteInternal 메서드는 스케줄링된 이벤트가 발생할 때마다 한 번씩 호출되는 메서드이다.

 

JobLocator + JobName을 통해 실행할 Job을 찾고, JobParameter와 함께 JobLauncher.run()으로 실행시킨다. 

해당 클래스는 Bean으로 등록할 필요가 없으며, 필요한 값들은 추후 JobDetail에서 주입받을 것이다.

Setter를 통해서 값이 주입되기 때문에, 반드시 Setter를 구현해야 한다.

 

Job을 실행할 때 필요한 파라미터가 있다면 JobParameters에 add..() 메서드를 활용해서 추가하면 된다.

4. JobDetail, JobTrigger 작성 후 스케줄러에 등록하기

@Configuration
@RequiredArgsConstructor
public class QuartzConfig {

    private final JobLauncher jobLauncher;
    private final JobLocator jobLocator;

    @Bean
    public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
        JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
        jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
        return jobRegistryBeanPostProcessor;
    }

    @Bean
    public JobDetail simpleJobDetail() {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("jobName", "simpleJob");
        jobDataMap.put("jobLauncher", jobLauncher);
        jobDataMap.put("jobLocator", jobLocator);

        return JobBuilder.newJob(BatchScheduledJob.class)
                .withIdentity("simpleJob")
                .setJobData(jobDataMap)
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger simpleJobTrigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                .cronSchedule("0 0 12 * * ?")
                .inTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Seoul")));

        return TriggerBuilder
                .newTrigger()
                .forJob(simpleJobDetail())
                .withIdentity("jobOneTrigger")
                .withSchedule(scheduleBuilder)
                .build();
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
        scheduler.setTriggers(simpleJobTrigger());
        scheduler.setJobDetails(simpleJobDetail());
        return scheduler;
    }
}

 

JobDetail은 실행할 Quartz Job 수행 시에 사용되는 메타데이터다.

 

simpleJobDetail()에서는 이전에 작성한 QuartzJobBean 구현 클래스를 추가하고, 해당 클래스에서 필요한 값들을 JobDataMap()에 담아서 넘겨준다.

 

Trigger는 스케줄러를 어떤 방식으로, 어떤 주기로 작동시킬지 결정한다. SimpleTrigger와 CronTrigger를 지원한다.

 

simpleJobTrigger()는 Cron 방식으로 실행시키기 위해 CronScheduleBuilder를 활용해서 CronSchedule를 넘겨주었다.

 

JobRegistryBeanPostProcessor는 BeanPostProcessor 단계에서 Bean 초기화 시 자동으로 JobRegistry에 Job을 등록시킨다.

 

Schedulerfactorybean은 스프링 컨테이너가 빈 수명 주기의 형식에 맞게 스케줄을 관리하도록 스케줄을 등록한다.

5. 실행

애플리케이션을 실행하면, CronSchedule에 설정한 시간마다 반복해서 해당 Job이 실행되는 것을 확인할 수 있다.

이후 Job이 추가되고 스케줄러에 등록해야 한다면 JobDetail, Trigger를 생성 후 BeanFactory에 등록하는 작업만 반복하면 된다.

댓글