Spring boot 3 기반의 Spring batch 5 프로젝트를 생성spring batch 를 위한 설정법 알아보기Job 의 수동실행 및 테스트 코드 작성법필자 개발환경:
- IDE:
IntelliJ IDEA 2024.2.1 (Ultimate Edition)- OS :
Ubuntu 22.04 (via WSL2)- DB :
PostgreSQL 16 (via Docker)- JDK :
temurin 21- Build Tool :
Maven- 그외:
Spring Boot 3.3.3,Spring Batch Core 5.1.2
Spring boot 3 기반의 프로젝트를 생성해봅시다.
https://start.spring.io/ 에 접속해서 아래와 같은 dependencies 를 갖는
프로젝트를 하나 생성하고, 자신이 사용하는 IDE 를 통해서 여시기 바랍니다.

이후 spring boot 기본 프로젝트를 열면 src/main/resources/ 경로에 있는
application.properties 파일을 아래와 같이 편집합니다.
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driver-class-name=org.postgresql.Driver
spring.batch.jdbc.initialize-schema=always
spring.batch.jdbc.platform=postgresql
spring.batch.job.enabled=false
yaml 사용시에는 아래처럼
spring: datasource: url: jdbc:postgresql://localhost:5432/postgres username: postgres password: postgres batch: jdbc: initialize-schema: always platform: postgresql job: enabled: false
각 설정의 의미
(spring datasource 부분은 여러분 환경에 맞게 수정하세요!)
spring.batch.jdbc.initialize-schema=always:
always 로 하면 처음 한번 생성, 이후 기존 생성 테이블을 계속 사용never 를 지정하면, 미리 자신이 해당 스키마 테이블들을 수동으로 생성해놔야 합니다.spring-batch-core jar 내부에 있습니다.spring.batch.jdbc.platform=postgresql:
위에서 말한 initialize-schema 설정에 의해서 메타 정보 테이블을 생성할 때
사용하게 될 스크립트를 지정하는 옵션입니다. 옵션값은 schema-<옵션값>.sql 처럼 사용되어서 실제 테이블 생성 SQL 파일을 지정하게 됩니다. sql 파일들은 모두 spring-batch-core jar 내부에 있습니다.
spring.batch.job.enabled=false: Job 이 Bean 으로 등록되어 있으면 spring boot 실행시 자동으로Job 도 실행합니다. 이를 방지하는 설정입니다.spring batch 4.x 버전을 사용하시던 분들이라면
Execution Context 의 정보가 json 형태로 저장되던 것을 기억할 겁니다.
하지만 5 버전에서는 Base64 형태로 컨텍스트 정보가 저장이 되는데요.
만약에 이전처럼 json 으로 저장하고 싶다면 아래처럼 설정해주세요.
주의: 아래와 같이 jackson 라이브러리를 미리 세팅해야 됩니다.
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency>
@SpringBootApplication
public class SpringBatchPlaygroundApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchPlaygroundApplication.class, args);
}
// **** 아래처럼 빈 하나를 등록하면 끝입니다. ****
@Bean
public ExecutionContextSerializer executionContextSerializer() {
return new Jackson2ExecutionContextStringSerializer();
}
}
목차는 spring boot 3.3.x 이하 버전을 사용할 때 나오는 WARNING 로그를
없애기 위한 설정입니다. spring boot 3.4.x 이상의 버전을 사용하시는
분들은 이 목차를 읽지말고 넘어가시면 됩니다.
Spring batch 에서 사용되는 JobRegistry 을 위해 제공되는
Spring boot 3 의 자동설정에는 현재 약간 문제가 있습니다.
이를 해결하기 위해서는 다음과 같이 2개의 Bean 을 등록하면 됩니다.
BeanDefinitionRegistryPostProcessor :JobRegistryBeanPostProcessor 제거JobRegistrySmartInitializingSingleton :아래 코드처럼 작성하면 됩니다.
package coding.toast.batch;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringBatchStudyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchStudyApplication.class, args);
}
// 1. jobRegistryBeanPostProcessor 비활성화
@Bean
public static BeanDefinitionRegistryPostProcessor jobRegistryBeanPostProcessorRemover() {
return registry -> registry.removeBeanDefinition("jobRegistryBeanPostProcessor");
}
// 2. jobRegistryBeanPostProcessor 를 대체할 JobRegistrySmartInitializingSingleton 빈 등록
@Bean
public JobRegistrySmartInitializingSingleton
jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) {
return new JobRegistrySmartInitializingSingleton(jobRegistry);
}
}
여기서부터 조금 설명이 길어지는데, 그냥 프로젝트 설정만 빠르게 하실분들은
다음 목차로 바로 이동하시면 됩니다. 그냥 에러가 왜나는지에 대한 설명입니다.
먼저 jobRegistryBeanPostProcessor 를 제거하지 안 하고
프로젝트를 실행하면 아래와 같이 많은 WARN 문구가 나옵니다.

이는 BPP(BeanPostProcessor) 와 일반 Bean 의 등록 순서 문제입니다.
(이 문제가 뭔지 궁금하면 이 링크를 참고하세요)
이와 관련해서는 깃헙 이슈에도 문의 글이 한번 올라왔었죠.
그리고 해당 글에서는 이에 대한 해결법으로 문제를 일으키는 BPP 를 제거하라고 합니다.
@Bean
public static BeanDefinitionRegistryPostProcessor jobRegistryBeanPostProcessorRemover() {
return registry ->
registry.removeBeanDefinition("jobRegistryBeanPostProcessor");
}
그런데 어떤 분들은 이렇게 하니 기존에 JobRegistry 기능을 사용하는 부분에서
에러가 난다고 하더군요. 관련 설정을 지웠으니 어찌보면 당연합니다.
그러면 WARN 이 보여도 억지로 jobRegistryBeanPostProcessor 를 써야할까요?
다행히 JobRegistry 의 설정 방법에는 jobRegistryBeanPostProcessor 외에도
JobRegistrySmartInitializingSingleton 를 통해서도 가능합니다.
그래서 이 부분 또한 코드에 추가했던 거죠.
@Bean
public JobRegistrySmartInitializingSingleton
jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) {
return new JobRegistrySmartInitializingSingleton(jobRegistry);
}
이제 제가 위에서 말한 2개의 Bean 을 등록한 이유가 이해가 되죠?
이렇게 설정하면 WARN 로그가 나오지 않고, JobRegistry 도 평상시처럼
사용할 수 있게 됩니다.
참고로 이 부분은
spring batch 5.2부터 고쳐질 것이라고 합니다.
하지만 현재 기준spring boot release버전인3.3.3에서는
spring batch core버전이5.1.2이므로 안탑깝지만 위처럼 수동으로 문제를 해결해야 합니다.
Spring boot 3.4.x 부터 batch core 5.2.x 를 사용될 예정입니다.
https://github.com/spring-projects/spring-boot/releases/tag/v3.4.0-M3
잘 설정됐는지 확인하기 위해서 Spring Boot 프로젝트를 실행해봅시다.
위처럼 로그에 WARN 로그가 없으면 일단 1차적으로 OK 입니다.
2번째로는 DB 에 Spring Batch 가 메타정보를 저장하는 용도의 테이블들과
사용되는 시퀀스들이 생성된 것이 확인되면 성공한 겁니다.
프로젝트 정상 구동은 확인으니, 이제 간단한 Job, Step 을 하나 생성하고
곧바로 구동을 시켜보겠습니다.
먼저 설정 클래스 하나 생성하고, 안에 Job, Step Bean 을 하나씩 생성해줍니다.
package coding.toast.batch.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Slf4j
@Configuration
public class MyJobConfiguration {
@Bean("job-first")
public Job job1(JobRepository jobRepository,
PlatformTransactionManager platformTransactionManager) {
return new JobBuilder("job-first", jobRepository)
.start(step1(jobRepository, platformTransactionManager))
.build();
}
@Bean("step-first")
public Step step1(JobRepository jobRepository,
PlatformTransactionManager platformTransactionManager) {
return new StepBuilder("step-first", jobRepository)
.tasklet((contribution, chunkContext) -> {
log.info("step1 executed");
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
}
해당 Job 을 수동으로 실행시키는 법은 간단합니다.
JobLauncher 와 실행시키고자 하는 Job 을 DI 받고 사용하면 끝입니다.
아래 코드 처럼 말이죠.
package coding.toast.batch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
// ... 나머지 import 생략 ...
@SpringBootApplication
public class SpringBatchStudyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchStudyApplication.class, args);
}
// ... 중간 생략 ...
// 간단한 테스트를 위해 프로젝트 실행할 때 자동으로 Job Launcher 실행시키기
@Bean
public CommandLineRunner commandLineRunner(JobLauncher jobLauncher,
@Qualifier("job-first") Job job) {
return args -> {
JobParameters jobParameters = new JobParametersBuilder().toJobParameters();
jobLauncher.run(job, jobParameters);
};
}
}
주의사항
이렇게 하면 Job 을 바로 실행할 수 있습니다.
다만 여기서 알아야 할 점은 JobParameter 의 세팅을 바꾸지 않는 이상
한번 실행 성공한 Job 은 재실행되지 않는다는 점입니다.
개발시에는 여러번 Job 을 실행해서 테스트를 해야 되는 경우가 많은데,
이러한 동작 방식은 약간 걸리적 거리죠.
이런 경우에는 spring-batch-test 에서 제공하는 API 사용하는 게 좋습니다.
그 방법은 바로 다음 목차에서 보겠습니다.
프로젝트가 계속 발전하다 보면 여러개의 Job 이 생성되고,
이를 여러번 돌려보면서 테스트를 해보고 싶은 경우가 오는데요.
이때 사용하면 편한게 spring-batch-test 의 JobLauncherTestUtils 입니다.
package coding.toast.batch;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.*;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@SpringBatchTest
public class SimpleLogBatchTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@BeforeEach
public void setup(@Qualifier("job-first") Job job1) {
this.jobLauncherTestUtils.setJob(job1);
}
@Test
public void testMyJob() throws Exception {
// given
// 무조건 적으로 Job 을 재실행하도록 랜덤함 JobParameter 값 하나를 추가한 JobParamBuilder 생성
JobParametersBuilder uniqueJobParametersBuilder = this.jobLauncherTestUtils.getUniqueJobParametersBuilder();
// 필요하다면 여기서 parameter builder 에 필요한 파라미터 정보 추가
JobParameters jobParameters = uniqueJobParametersBuilder.toJobParameters();
// when
JobExecution jobExecution = this.jobLauncherTestUtils.launchJob(jobParameters);
// then
Assertions.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
}
}
getUniqueJobParametersBuilder 를 통해서 매번 유니크한 Long 타입의프로젝트 구현 시, batch 전용 db 스키마를 생성하고 사용하고 싶을 수 있습니다.
그럴 때는 아래처럼 하면 됩니다.
먼저 application.properties 를 아래처럼 수정합니다.
spring.batch.jdbc.initialize-schema=always
spring.batch.jdbc.platform=postgresql
spring.batch.job.enabled=false
# 시스템 전반에서 사용되는 datasource 설정값
spring.datasource.global.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.global.username=postgres
spring.datasource.global.password=postgres
spring.datasource.global.driver-class-name=org.postgresql.Driver
# 스프링 배치용 datasource 설정값
spring.datasource.batch.url=jdbc:postgresql://localhost:5432/postgres?currentSchema=spring_batch
spring.datasource.batch.username=postgres
spring.datasource.batch.password=postgres
spring.datasource.batch.driver-class-name=org.postgresql.Driver
currentSchema=spring_batch 부분이 postgres jdbc url 이 제공하는 옵션입니다. 이렇게 함으로서 dataSource 의 default 스키마를 변경할 수 있습니다.이후 아래와 같이 @Configuration 설정 클래스를 생성해줍니다.
package me.dailycode.springbatch.datasource;
import org.springframework.boot.autoconfigure.batch.BatchDataSource;
import org.springframework.boot.autoconfigure.batch.BatchTransactionManager;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.support.JdbcTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfiguration {
@Bean
@ConfigurationProperties("spring.datasource.global")
public DataSourceProperties globalDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource dataSource() {
return globalDataSourceProperties()
.initializeDataSourceBuilder()
.build();
}
@Bean
@Primary
public PlatformTransactionManager transactionManager() {
return new JdbcTransactionManager(dataSource());
}
@Bean
@ConfigurationProperties("spring.datasource.batch")
public DataSourceProperties batchDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@BatchDataSource
public DataSource batchDataSource() {
return batchDataSourceProperties()
.initializeDataSourceBuilder()
.build();
}
@Bean
@BatchTransactionManager
public PlatformTransactionManager batchTransactionManager() {
return new JdbcTransactionManager(batchDataSource());
}
}
만약 default 스키마의 설정을 Configuration 클래스 코드에서 작성하고 싶다면
아래처럼 @BatchDataSource Bean 메소드 내용을 바꾸시면 됩니다.
@Bean
@BatchDataSource
public DataSource batchDataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(batchDataSourceProperties().getDriverClassName());
hikariConfig.setJdbcUrl(batchDataSourceProperties().getUrl());
hikariConfig.setUsername(batchDataSourceProperties().getUsername());
hikariConfig.setPassword(batchDataSourceProperties().getPassword());
hikariConfig.setSchema("spring_batch"); // default 스키마 지정!
return new HikariDataSource(hikariConfig);
}
이게 필요한 이유는 모든 DB 관련 jdbc 구현체가
postgresql jdbc
처럼currentSchema옵션을 주는 게 아니기 때문입니다.