본 글은
Spring 6.0.8
Spring Boot 3.0.6
Spring batch 5.0.1
으로 작성되었습니다.
스프링 배치에 대한 전반적인 지식이 부족하신 분은 스프링 배치 이론을 참고해 주세요.
이 글은 어느 정도 스프링에 익숙한 분들을 위한 글입니다.
본 글에서는 Job, Step 작성 및 실행까지만 다룹니다. 각 소스코드에 대한 설명은 없습니다.
각 메서드 및 소스코드에 대한 설명을 다음 편에서 하나씩 진행하도록 하겠습니다.
Spring Initializr에 방문하여 Spring Batch를 추가하고 그 외 필요한 것들을 추가하여 다운로드합니다.
해당 배치에서 접속할 DataSource 설정을 합니다. 스프링 배치 이론에 있는 메타테이블이 생성되어있어야 합니다.
기존 스프링 배치 4.x 버전에서는 @EnableBatchProcessing 어노테이션을 사용했으나,
사용하지 않아도 됩니다. 대신 설정을 변경해야 하면 DefaultBatchConfiguration 클래스를 상속받아 사용합니다.
기본으로 사용하면 딱히 변경할 점이 없어 다루지 않습니다.
@EnableBatchProcessing 어노테이션은 추가하지 않습니다.
https://github.com/spring-projects/spring-batch/issues/4232
EnableBatchProcessing is no longer required. I tried your sample
with Spring Boot 3.0.0-RC2 (Spring Batch 5.0.0-RC2) by removing
@EnableBatchProcessing and the sample works as expected.
클래스 2개를 생성합니다. 앞서 이론 편에서 설명한 Step에 대한 Chunk, Tasklet 방식의 Job 설정을 만들었습니다.
Tasklet 방식 TaskletBatch.java
Tasklet 방식은 이론 편에서 설명했듯이
Step 안에서 수행될 단일 작업을 정의합니다. 즉, Step 실행 시 하나의 Tasklet을 실행하고,
Tasklet이 모두 완료되면 Step이 종료됩니다.
아래 코드에서는 Job 1개, Step 1개, Tasklet 1개로 구성됩니다.
Job 실행 시 단순히 "step1 start"라는 로그를 찍습니다.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class TaskletBatch {
private final JobRepository jobRepository;
private final PlatformTransactionManager platformTransactionManager;
@Bean("taskletJob")
public Job taskletJob() {
return new JobBuilder("taskletJob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(taskletStep())
.build();
}
@Bean("taskletStep")
@JobScope
public Step taskletStep() {
return new StepBuilder("taskletStep", jobRepository)
.tasklet((a, b) -> {
log.info("step1 start");
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
}
아래는 Chunk 방식으로 작성한 ChunkBatch.java입니다.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class ChunkBatch {
private final JobRepository jobRepository;
private final PlatformTransactionManager platformTransactionManager;
@Bean("chunkJob")
public Job chunkJob() {
return new JobBuilder("chunkJob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(chunkStep())
.build();
}
@Bean("chunkStep")
@JobScope
public Step chunkStep() {
return new StepBuilder("chunkStep", jobRepository)
.<String, Integer>chunk(3, platformTransactionManager)
.reader(new ListItemReader<>(List.of("1", "2", "3", "4", "5")))
.processor(Integer::parseInt)
.writer(item -> log.info(String.valueOf(item)))
.build();
}
}
실행 방법
스프링 배치를 실행하는 방법은 여러 가지가 있습니다. ex) Controller를 통해, @Scheduled 어노테이션을 통해, java -jar
그중 빌드된. jar 파일을 직접 실행하는 방법에 대해 다루겠습니다.
기본적으로 빌드 후 java -jar 명령어를 통해 실행시키려면 귀찮기 때문에 IntelliJ CE 기준으로 실행방법을 작성합니다.
상단 실행 -> 구성편집에서 아래와 같이 애플리케이션을 추가합니다.
하나의 Job Configuration 만 작성한 경우 아무런 프로그램 인수 값을 넣지 않아도 정상적으로 배치가 실행됩니다.
하지만 현재 2개 (Tasklet, Chunk)의 Job Configuration을 작성하였고, Spring Batch에서는 어떤 Job을 실행할지 알 수가 없습니다.
프로그램 인수 및 application.yml 파일을 수정하지 않으면 다음과 같은 에러를 마주하게 됩니다.
프로그램 인수로 어떤 Job을 실행해야 하는지 알려줄 수 있습니다.
다음과 같이 job.name을 프로그램 인수로 추가합니다.
application.yml 파일에 다음과 같이 추가합니다.
프로그램 인수로 받은 job.name 이 있으면 해당 값이 없으면 NONE을 주기 때문에 에러가 발생하지 않고 아무런 Job이 실행되지 않습니다.
spring:
batch:
job:
names: ${job.name:NONE}
이제 다시 실행을 해보면? 동일한 Job name must be specified in case of multiple jobs 에러가 발생합니다.
구글링 하면 찾을 수 있는 많은 자료에서 spring.batch.job.names 값을 추가하라고 되어있으나,
아마도 5.x 버전부터 변경된 것 같습니다.
에러가 발생한 곳을 찾아가 보면
다음과 같은 조건이 있습니다.
jobName을 언제 넣어주는 걸까요? setJobName() 메서드를 어디서 사용하는지 찾아가 봤습니다.
properties에서 불러옵니다. job name 은 BatchProperties의 Job Class 안에 존재하였습니다.
jobNames 값을 Job의 getName으로 불러오는 걸 보면
spring.batch.job.names 값이 아닌 spring.batch.job.name을 추가해야 하는 걸 알 수 있습니다.
따라서 application.yml에 추가한 값을 다음과 같이 수정해 줍니다.
spring:
batch:
job:
name: ${job.name:NONE}
그 후 다시 실행해보면?
입력한 Job 이름이 chunkJob인 Job에 대해 정상적으로 동작합니다.
다음 글에서는 Job, Step에 대해 다루겠습니다.
긴 글 읽어주셔서 감사합니다. 😛
'백엔드 > Spring Batch' 카테고리의 다른 글
Spring Batch 이론 (1) | 2023.04.23 |
---|---|
스프링 배치 5.x 알아보기 (0) | 2023.04.23 |