📌 Spring Batch에 대한 자세한 개념들은 아래 포스팅을 참고해주세요.
<스프링 배치(Spring batch)란?>
1. build.gradle
에 의존성 추가
2. application.yml
에 DB 스키마 생성 전략 설정
➜ DB 스키마 생성 SQL을 항상 실행
➜ RDBMS 설정이 되어 있을 경우, 내장 DB보다 우선적으로 실행됨
3. 애플리케이션 시작 클래스에 @EnableBatchProcessing
, @EnableScheduling
애너테이션 붙이기
4. YataBatchConfig
클래스 작성
( 우리 프로젝트에서는 Yata라는 게시물의 마감 상태 변경을 위해 Spring Batch를 사용하기 때문에 YataBatchConfig
를 작성하였다. )
@Configuration // Spring에 의해 구성 클래스로 인식
// Spring Batch의 모든 Job은 @Configuration으로 등록해서 사용해야 함
@Slf4j
@EnableBatchProcessing // Spring Batch 기능을 사용할 수 있도록 활성화
public class YataBatchConfig {
public final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final JpaYataRepository yataRepository;
private final JpaYataRequestRepository yataRequestRepository;
public YataBatchConfig(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, JpaYataRepository yataRepository, JpaYataRequestRepository yataRequestRepository) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
this.yataRepository = yataRepository;
this.yataRequestRepository = yataRequestRepository;
}
@Bean
public Job yataJob() {
return jobBuilderFactory.get("yataJob")
.start(yataStep()).next(yataRequestStep())
.build();
} ⠀
⠀
@Bean
public Step yataStep() {
return stepBuilderFactory.get("yataStep")
.tasklet((contribution, chunkContext) -> {
yataRepository.updateYataOverDepartureTime();
return RepeatStatus.FINISHED;
})
.build();
}
⠀
public Step yataRequestStep() {
return stepBuilderFactory.get("yataRequestStep")
.tasklet((contribution, chunkContext) -> {
yataRequestRepository.updateExpiredYataRequest();
return RepeatStatus.FINISHED;
})
.build();
}
}
✔️
JobBuilderFactory
/StepBuilderFactory
- Spring Batch에서 제공하는 빌더 클래스
- 각각 Job과 Step을 생성하는 데에 사용
✔️
yataJob()
- 생성한 스텝들을 어떤 순서로 실행시킬지 묶어서 하나의 Job으로 만듦
✔️
yataStep()
- tasklet 기반으로 StepBuilderFactory를 사용하여 스텝을 생성
- yataRepository.updateYataOverDepartureTime(); 작업을 수행
- tasklet의 execute 메서드로 RepeatStatus.FINISHED를 사용하여 종료
✔️
yataRequestStep()
- tasklet 기반으로 StepBuilderFactory를 사용하여 스텝을 생성
- yataRequestRepository.updateExpiredYataRequest(); 작업을 수행
- tasklet의 execute 메서드로 RepeatStatus.FINISHED를 사용하여 종료
❗ 보통 Chunk 방식을 대다수 사용하지만,
우리 프로젝트에서는 비교적 간단한 Step들을 정의하고 있기 때문에 Tasklet 방식을 사용함 !
5. YataBatchScheduler
클래스 작성
@Component // 컴포넌트로 등록
@Slf4j
public class YataBatchScheduler {
private final JobLauncher jobLauncher;
private final YataBatchConfig yataBatchConfig;
⠀
public YataBatchScheduler(JobLauncher jobLauncher, YataBatchConfig yataBatchConfig) {
this.jobLauncher = jobLauncher;
this.yataBatchConfig = yataBatchConfig;
}
⠀
// 10분 마다 실행
//@Scheduled(cron = "0 0/10 * * * *")
// 1분 마다 실행
@Scheduled(fixedDelay = 1000 * 60 * 20) // 20분 마다 실행
public void runExpiredYataBatch() {
Map<String, JobParameter> jobParameter = new HashMap<>();
jobParameter.put("yataJobExpire", new JobParameter(System.currentTimeMillis()));
JobParameters jobParameters = new JobParameters(jobParameter);
⠀
try {
jobLauncher.run(yataBatchConfig.yataJob(), jobParameters);
log.info("YataBatchScheduler runExpiredYataBatch");
} catch (Exception e) {
e.printStackTrace();
log.error("YataBatchScheduler Error :{}", e.getMessage());
}
}
}
✔️
@Scheduled(fixedDelay = 1000 * 60 * 20)
- 작업이 실행될 시간의 단위를 정하는 애너테이션
- 위는 해당 작업이 모두 끝난 후 20 후에 작업 다시 실행
- fixedDelay = 해당 작업이 끝난 시점부터 시간을 셈
- fixedRate = 해당 작업의 시작 시점부터 시간을 셈
⠀
Ex. 만약@Scheduled(fixedRate = 1000 * 60 * 20)
라고 한다면,
해당 작업이 시작한 시점부터 20분 후에 작업이 다시 실행되는 것
✔️
runExpiredYataBatch()
@Scheduled
를 사용하여 스케줄링- 작업 실행 시간마다 새로운 JobParameters를 생성하여 파라미터로 전달하고,
jobLauncher를 사용하여 잡 실행- 배치 작업이 정상적으로 실행되면 "YataBatchScheduler runExpiredYataBatch"라는 로그를 남기고, 예외가 발생한다면 해당 예외를 출력하고 에러 메시지를 로그에 남김
✔️
jobLauncher.run()
- 해당 Job(yataBatchConfig의
yataJob()
)을 실행하고, 앞서 설정한 JobParameters를 전달
여기까지 우리 프로젝트에 적용하고 서버를 돌린 후에 postman으로 Test를 해보면,
콘솔창에서 아래와 같이 스케줄러가 정상적으로 작동되는 것을 알 수 있고 !
우리 프로젝트에서도 마감된 게시물을 자동으로 20분마다 한꺼번에 신청마감
상태로 처리하게 되는 것을 알 수 있다 !