Spring Batch 마스터 노트 시리즈 8편 완결편. Spring Boot 3.x · Spring Batch 5 · Java 17의 결정적 변화 — @EnableBatchProcessing 제거와 자동 설정 대체, JobBuilder/StepBuilder 직접 생성, chunk(size, transactionManager) 시그니처, jakarta.* 네임스페이스, mysql-connector-j 변경까지 마이그레이션 체크리스트 9단계로.
이 글은 Spring Batch 마스터 노트 시리즈의 마지막 편이자 여덟 번째 편입니다. 1~7편이 Spring Batch 4.x 기준 코드였다면, 마지막은 Spring Batch 5로 마이그레이션 하는 자리.
Spring Boot 3.x는 Spring Framework 6.x · Java 17 · Jakarta EE 9+를 기반으로 합니다. 이 변화에 따라 Spring Batch 5도 API가 일부 변경됐어요. @EnableBatchProcessing 의 의미가 뒤집히고, JobBuilder/StepBuilder 생성 방식이 바뀌었습니다. 운영 중인 4.x 코드를 5.x로 옮기는 9단계 체크리스트로 마무리.
처음 마이그레이션이 어렵게 느껴지는 이유
이유는 두 가지예요.
첫째, 변경점이 한 곳이 아니라 여러 곳에 흩어져 있습니다. @EnableBatchProcessing 제거 + JobBuilder 생성 방식 변경 + chunk 시그니처 + jakarta 네임스페이스 + MySQL 커넥터 + Java 버전 — 한 가지 빠뜨리면 빌드가 안 되거나 런타임 오류.
둘째, @EnableBatchProcessing 의미가 뒤집혔어요. 4.x에서는 "이 어노테이션이 배치 빈을 만들어 줘서 필수"였는데, 5.x에서는 "이 어노테이션이 자동 설정을 막으니 절대 쓰지 마세요"가 됐습니다. 정확히 반대.
해결법은 한 가지예요. 9단계 체크리스트 를 차례대로 따라가세요. 한 단계씩 밟으면 어렵지 않아요.
주요 변경사항 한눈에
| 구분 | Spring Batch 4.x | Spring Batch 5.x |
|---|---|---|
| 배치 활성화 | @EnableBatchProcessing 필수 |
자동 설정으로 불필요 |
| JobBuilder | JobBuilderFactory.get("name") |
new JobBuilder("name", jobRepository) |
| StepBuilder | StepBuilderFactory.get("name") |
new StepBuilder("name", jobRepository) |
| chunk 설정 | .<I,O>chunk(size) |
.<I,O>chunk(size, transactionManager) |
| Java 버전 | 8+ | 17+ |
| Spring Boot | 2.x | 3.x |
| Spring Framework | 5.x | 6.x |
| EE 네임스페이스 | javax.* |
jakarta.* |
| DB 드라이버 | mysql-connector-java |
mysql-connector-j |
@EnableBatchProcessing — 의미가 뒤집힌 어노테이션
4.x에서의 역할
다음 빈들을 자동 제공:
JobBuilderFactoryStepBuilderFactoryJobLauncherJobRepositoryJobExplorerPlatformTransactionManager
// 4.x — 필수
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
}
5.x — 제거해야
Spring Boot 3.x 자동 설정이 자동으로 배치 인프라 제공.
// 5.x — @EnableBatchProcessing 제거!
@Configuration
public class BatchConfiguration {
// JobBuilderFactory, StepBuilderFactory 사용 안 함
// → JobBuilder, StepBuilder 직접 생성
}
여기서 정말 중요한 시험 함정 — 5.x에서 @EnableBatchProcessing을 남겨 두면 자동 설정이 비활성화됩니다. 빌드는 되지만 의도치 않은 동작이 발생할 수 있어요. 반드시 제거.
JobBuilder/StepBuilder — 직접 생성
4.x 방식
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job myJob() {
return jobBuilderFactory.get("myJob")
.start(myStep())
.build();
}
@Bean
public Step myStep() {
return stepBuilderFactory.get("myStep")
.<Product, Product>chunk(10)
.reader(reader())
.writer(writer())
.build();
}
5.x 방식
@Bean
public Job myJob(JobRepository jobRepository, Step myStep) {
return new JobBuilder("myJob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(myStep)
.build();
}
@Bean
public Step myStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("myStep", jobRepository)
.<Product, Product>chunk(10, transactionManager)
.reader(reader())
.writer(writer())
.build();
}
메서드 파라미터로 의존성 주입
@Configuration
public class BatchConfiguration {
// JobRepository와 TransactionManager는 Spring Boot 자동 설정으로 생성됨
@Bean
public Step csvStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager) {
return new StepBuilder("csvStep", jobRepository)
.<Product, Product>chunk(10, transactionManager)
.reader(csvReader())
.processor(productProcessor())
.writer(dbWriter())
.build();
}
@Bean
public Job csvJob(JobRepository jobRepository, Step csvStep) {
return new JobBuilder("csvJob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(csvStep)
.build();
}
}
@Autowired 필드 선언 불필요 — 메서드 파라미터로 받음. 더 명시적이고 테스트에도 유리.
Tasklet Step — TransactionManager 추가
4.x
@Bean
public Step taskletStep() {
return stepBuilderFactory.get("taskletStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("Task 실행");
return RepeatStatus.FINISHED;
})
.build();
}
5.x
@Bean
public Step taskletStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager) {
return new StepBuilder("taskletStep", jobRepository)
.tasklet((contribution, chunkContext) -> {
System.out.println("Task 실행");
return RepeatStatus.FINISHED;
}, transactionManager) // transactionManager 추가
.build();
}
Conditional Flow — 큰 변화 없음
4.x
@Bean
public Job conditionalJob() {
return jobBuilderFactory.get("conditionalJob")
.start(step1())
.on("COMPLETED").to(step2())
.on("FAILED").fail()
.from(step2())
.on("*").end()
.end()
.build();
}
5.x
@Bean
public Job conditionalJob(JobRepository jobRepository,
Step step1, Step step2) {
return new JobBuilder("conditionalJob", jobRepository)
.start(step1)
.on("COMPLETED").to(step2)
.on("FAILED").fail()
.from(step2)
.on("*").end()
.end()
.build();
}
흐름 제어 메서드(on/to/from/end) 자체는 동일. JobBuilder 생성 방식만 변경.
Jakarta EE 네임스페이스
Java EE → Jakarta EE 브랜드 변경. 패키지 javax.* → jakarta.*.
| 변경 전 (javax) | 변경 후 (jakarta) |
|---|---|
javax.validation.* |
jakarta.validation.* |
javax.validation.constraints.* |
jakarta.validation.constraints.* |
javax.persistence.* |
jakarta.persistence.* |
javax.transaction.* |
jakarta.transaction.* |
javax.servlet.* |
jakarta.servlet.* |
javax.annotation.* |
jakarta.annotation.* |
Validation 어노테이션 변경
4.x
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;
public class Product {
@NotNull
private Integer productId;
@NotBlank
private String productName;
@Min(0)
private Double productPrice;
}
5.x
import jakarta.validation.constraints.NotBlank; // javax → jakarta
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Max;
public class Product {
@NotNull
private Integer productId;
@NotBlank
private String productName;
@Min(0)
private Double productPrice;
}
여기서 시험 함정이 하나 있어요. JSR-303 Validation 어노테이션 도 패키지가 바뀝니다. BeanValidatingItemProcessor를 쓰는 도메인 모델 모두 import 변경 필요.
pom.xml 변경
Spring Boot 버전
<!-- 4.x / 2.x -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.x</version>
</parent>
<!-- 5.x / 3.x -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
</parent>
MySQL Connector
<!-- 구버전 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 신버전 -->
<dependency>
<groupId>com.mysql</groupId> <!-- 그룹 ID 변경 -->
<artifactId>mysql-connector-j</artifactId> <!-- 아티팩트 ID 변경 -->
<scope>runtime</scope>
</dependency>
Java 버전
<!-- 4.x -->
<properties>
<java.version>11</java.version>
</properties>
<!-- 5.x -->
<properties>
<java.version>17</java.version> <!-- 최소 17 필수 -->
</properties>
전체 마이그레이션 예시
Before (4.x)
import javax.validation.constraints.NotBlank;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
@Configuration
@EnableBatchProcessing // 4.x 필수
public class BatchConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private DataSource dataSource;
@Bean
public FlatFileItemReader<Product> csvReader() {
// ...
}
@Bean
public Step csvStep() {
return stepBuilderFactory.get("csv-step")
.<Product, Product>chunk(10)
.reader(csvReader())
.processor(processor())
.writer(writer())
.build();
}
@Bean
public Job csvJob() {
return jobBuilderFactory.get("csv-job")
.incrementer(new RunIdIncrementer())
.start(csvStep())
.build();
}
}
After (5.x)
import jakarta.validation.constraints.NotBlank; // javax → jakarta
// @EnableBatchProcessing 제거
// JobBuilderFactory, StepBuilderFactory import 불필요
@Configuration
public class BatchConfiguration {
// Factory 필드 선언 불필요 — 메서드 파라미터
@Autowired
private DataSource dataSource;
@Bean
public FlatFileItemReader<Product> csvReader() {
// 동일한 설정
}
@Bean
public Step csvStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager) {
return new StepBuilder("csv-step", jobRepository)
.<Product, Product>chunk(10, transactionManager)
.reader(csvReader())
.processor(processor())
.writer(writer())
.build();
}
@Bean
public Job csvJob(JobRepository jobRepository, Step csvStep) {
return new JobBuilder("csv-job", jobRepository)
.incrementer(new RunIdIncrementer())
.start(csvStep)
.build();
}
}
Tasklet Step 마이그레이션
// 4.x
@Bean
public Step initStep() {
return stepBuilderFactory.get("initStep")
.tasklet((contribution, chunkContext) -> RepeatStatus.FINISHED)
.build();
}
// 5.x
@Bean
public Step initStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager) {
return new StepBuilder("initStep", jobRepository)
.tasklet((contribution, chunkContext) -> RepeatStatus.FINISHED,
transactionManager)
.build();
}
Job 파라미터 — 큰 변화 없음
// 4.x · 5.x 모두 동일
JobParameters params = new JobParametersBuilder()
.addString("reportDate", "2026-01-01")
.addLong("startTime", System.currentTimeMillis())
.toJobParameters();
// @StepScope · @Value도 동일
@Bean
@StepScope
public FlatFileItemReader<Product> csvReader(
@Value("#{jobParameters['inputFile']}") String inputFile) {
FlatFileItemReader<Product> reader = new FlatFileItemReader<>();
reader.setResource(new FileSystemResource(inputFile));
return reader;
}
마이그레이션 9단계 체크리스트
1. Spring Boot 버전 업그레이드
pom.xml의spring-boot-starter-parent버전을3.x.x로- Java 버전 최소 17
2. MySQL Connector 의존성 변경
mysql:mysql-connector-java→com.mysql:mysql-connector-j
3. @EnableBatchProcessing 제거
@Configuration클래스에서 어노테이션 제거
4. JobBuilderFactory 제거
@Autowired JobBuilderFactory필드 제거- 메서드 파라미터로
JobRepository받기 jobBuilderFactory.get("name")→new JobBuilder("name", jobRepository)
5. StepBuilderFactory 제거
@Autowired StepBuilderFactory필드 제거- 메서드 파라미터로
JobRepository와PlatformTransactionManager받기 stepBuilderFactory.get("name")→new StepBuilder("name", jobRepository)
6. chunk() 메서드에 TransactionManager 추가
.<I,O>chunk(size)→.<I,O>chunk(size, transactionManager)
7. Tasklet step에 TransactionManager 추가
.tasklet(tasklet)→.tasklet(tasklet, transactionManager)
8. javax → jakarta
import javax.validation.*→import jakarta.validation.*import javax.persistence.*→import jakarta.persistence.*- 기타
javax.*확인·변경
9. 빌드 및 테스트
- 컴파일 오류 수정
- 단위 테스트
- 통합 테스트
흔한 마이그레이션 오류
'Class not found: javax.validation.constraints.NotBlank'
원인: Boot 3.x로 업그레이드했지만 javax.validation 사용 중.
해결: import javax.* → import jakarta.*.
'No qualifying bean of type JobBuilderFactory'
원인: 5.x에서 JobBuilderFactory 제거됨.
해결: JobBuilder 직접 생성.
'chunk method missing transactionManager parameter'
원인: 5.x chunk() 시그니처 변경.
해결: chunk(size) → chunk(size, transactionManager).
'could not find mysql-connector-java'
원인: 아티팩트 ID 변경.
해결: mysql-connector-java → mysql-connector-j.
'@EnableBatchProcessing disables auto-configuration'
원인: 5.x에서 이 어노테이션이 자동 설정 차단. 해결: 어노테이션 제거.
Spring Batch 5의 새 기능
DefaultBatchConfiguration 커스터마이징
배치 인프라 커스터마이징.
@Configuration
public class CustomBatchConfiguration extends DefaultBatchConfiguration {
@Autowired
private DataSource dataSource;
@Override
protected DataSource getDataSource() {
return dataSource;
}
// PlatformTransactionManager, JobRepository 등 오버라이드 가능
}
Observability 통합
Spring Micrometer + 분산 추적. BatchMetrics로 Job/Step 메트릭 자동 수집.
버전 호환성 표
| 구분 | Spring Boot 2.x | Spring Boot 3.x |
|---|---|---|
| Spring Batch | 4.x | 5.x |
| Spring Framework | 5.x | 6.x |
| Java 최소 | 8 | 17 |
| Jakarta EE | javax.* |
jakarta.* |
| @EnableBatchProcessing | 필수 | 사용 안 함 |
| JobBuilderFactory | 사용 | Deprecated |
| StepBuilderFactory | 사용 | Deprecated |
| chunk() | chunk(size) |
chunk(size, txManager) |
| MySQL Driver | mysql-connector-java |
mysql-connector-j |
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 8편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- Spring Batch 5 = Spring Boot 3.x · Java 17 · Jakarta EE 9+
- @EnableBatchProcessing 의미가 뒤집힘 — 4.x 필수 / 5.x 제거
- 5.x에서 자동 설정으로 배치 인프라 제공
- JobBuilderFactory·StepBuilderFactory 제거 — Builder 직접 생성
new JobBuilder("name", jobRepository)new StepBuilder("name", jobRepository)- 메서드 파라미터로
JobRepository·PlatformTransactionManager받음 @Autowired필드 선언 불필요- chunk(size) → chunk(size, transactionManager)
- tasklet(tasklet) → tasklet(tasklet, transactionManager)
- 조건부 흐름 메서드(
on/to/from/end) 동일 javax.*→jakarta.*모든 EE 네임스페이스javax.validation.*→jakarta.validation.*(JSR-303)javax.persistence.*→jakarta.persistence.*(JPA)- Spring Boot 버전 —
3.x.x - Java 최소 — 17
mysql-connector-java→mysql-connector-j- 그룹 ID도
mysql→com.mysql - JobParameters · @StepScope · @Value — 변화 없음
- 9단계 체크리스트 — Spring Boot → MySQL → @EnableBatchProcessing 제거 → Factory 제거 → chunk → tasklet → jakarta → 테스트
- DefaultBatchConfiguration 커스터마이징 가능
- Spring Micrometer로 Observability 통합
시리즈 마무리 — 8편 전체 요약
여기까지 오신 분이 있다면 진심으로 축하드립니다. 시리즈 전체에서 잡아 둔 핵심을 마지막으로 정리합니다.
- 1편 — 입문 — Job·Step·Chunk·JobRepository 기초 모델
- 2편 — Job 설정 —
@EnableBatchProcessing·JobBuilder·조건부 흐름 - 3편 — 청크 처리 — Reader/Processor/Writer 패턴, 변환·필터링·검증
- 4편 — ItemReader — FlatFile/JdbcCursor/JdbcPaging 선택 가이드
- 5편 — ItemWriter — FlatFile/JdbcBatch/Composite, Bulk INSERT
- 6편 — Job Flow — 6가지 리스너·ExecutionContext·PromotionListener
- 7편 — 오류 처리 — Skip·Retry·Restart, SkipPolicy·SkipListener
- 8편 — Spring Batch 5 (현재 글) — 마이그레이션 9단계 체크리스트
이 8편이 Spring Batch의 거의 모든 영역을 다룹니다. 다음 단계로 가신다면 Remote Partitioning(분산 처리), Spring Cloud Data Flow와 통합, Kubernetes 배치 같은 주제가 자연스러운 확장이에요.
시리즈 다른 편
- 1편 — Spring Batch 입문 (Job·Step·Chunk 모델)
- 2편 — Spring Batch Job 설정 (Tasklet과 Chunk Step)
- 3편 — 청크 처리 (Reader·Processor·Writer 패턴)
- 4편 — ItemReader 마스터 (CSV·JdbcCursor·Paging)
- 5편 — ItemWriter 마스터 (FlatFile·JdbcBatch·Composite)
- 6편 — Job Flow와 리스너 (조건부·병렬·ExecutionContext)
- 7편 — 오류 처리 (Skip·Retry·SkipPolicy)
- 8편 — Spring Batch 5 마이그레이션 (현재 글, 시리즈 완결)
공식 문서: Spring Batch 5 Migration Guide와 Spring Boot 3 Migration에서 추가 자료를 볼 수 있어요.
Spring Batch는 한 번에 정복되는 도구가 아니에요. 매번 새 데이터·새 비즈니스 요구·새 함정과 만나며 평생 다듬어 가는 영역입니다. 시리즈 끝까지 읽어 주셔서 감사합니다.