일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 글또9기
- 오프라인밋업
- 글또후기
- 글또OT
- 검색개발
- SSAFY
- 자바
- 유데미
- 배치
- 글또 다짐
- 글또10기
- 글또 OT 후기
- 개발자
- 백준
- BOJ
- 코드트리x글또
- 알고리즘
- 검색도메인
- 코드트리
- 제네릭
- Git
- 검색 도메인
- 코딩테스트
- 글또
- 자바17
- 스프링 핵심 원리 - 고급
- 자바21
- 글쓰는또라이
- 카프카강의
- 스프링배치 5
- Today
- Total
영주의 개발노트
Spring Batch 5 마이그레이션 트러블슈팅 본문
개요
이전에 소개했던 팀 배치 프로젝트 최신화 과정에서 Spring Boot 버전 업그레이드를 고민하고 있다고 언급하였다. Java 17 업그레이드를 마무리하여 Spring Boot와 Spring Batch 버전도 업그레이드하기로 의사결정하였다.
사실, Java 업그레이드처럼 뚝딱 될 줄 알고 공식문서를 읽지 않고 일정 산정을 하였다. 실제로 넉넉하게 1주일 정도로 생각하였다. 하지만, 버전 변경 후 마법처럼 뚝딱하고 잘 돌아가는 일은 일어나지 않았다.
Spring Batch 5에서는 대대적인 개편이 일어났다. (필자 기준에서는... 👶) Spring Batch 5로 마이그레이션하면서 겪었던 문제 및 해결하는 과정에 대해 소개해보고자 한다.
항목
자세한 마이그레이션 가이드를 확인하고 싶다면 하기 링크를 참고하길 바란다.
1. DefaultBatchConfigurer 삭제
현재 사내에 전사적으로 배치 관련 로그를 관리하는 테이블이 존재하였기에 프로젝트 구성 당시 Spring Batch에서 제공하는 메타 테이블을 굳이 사용할 필요가 없다고 의사결정하였다. 그래서 메타테이블을 저장할 DataSource를 set 하는 메서드를 오버라이드하여 비워두는 방식으로 개발을 진행하였다. 아래 코드는 앞서 설명한 내용을 개발한 코드이다.
import javax.sql.DataSource;
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BatchConfig extends DefaultBatchConfigurer {
@Override
public void setDataSource(DataSource dataSource) {
}
}
Spring Batch 5에서는 DefaultBatchConfigurer 이 삭제되며 위 코드처럼 설정할 수 없게 되었다. 이를 해결하기 위해 여러 방법을 찾아본 결과 2가지 정도로 추릴 수 있었다.
- Spring Batch 메타 테이블 사용
- JobRepository를 커스텀하여 사용
JobRepository를 입맛대로 커스텀하여 메타 테이블을 사용하지 않을 수 있지만, 해당 방식은 내가 인지하지 못한 문제를 야기할 수 있다고 판단하여 Spring Batch 메타 테이블을 사용하는 방향으로 문제를 해결하였다.
2. @EnableBatchProcessing 역할 변경
필자가 Spring Batch를 처음 공부할 때에만 해도 @EnableBatchProcessing은 Spring Batch 애플리케이션이 작동하기 위해 선언되어야 하는 필수 애노테이션이었다. 해당 애노테이션에서 총 4개의 설정 클래스를 실행하여 Spring Batch의 모든 초기화 및 실행 구성이 이루어졌다.
하지만, Spring Batch 5에서는 해당 애노테이션이 필수가 아니게 되었다. 설정 관련 작업을 하기 위해 DefaultBatchConfiguration이라는 클래스가 새로 추가되었다.
또한, @EnableBatchProcessing을 사용하게 된다면 yml, properties 등에서 직접 설정한 값들이 원하는 대로 불러오지 못할 수 있다. (tablePrefix = "XXX" 등의 코드 작성이 필요하다.) 그래서 Spring Batch 마이그레이션을 진행하면서 배치 Job을 구성함에 있어 @EnableBatchProcessing 애노테이션을 모두 제거하였다.
1번에서 언급한 것처럼 Spring Batch 메타 테이블을 사용하려고 보니 데이터가 삽입되지 않고 에러가 났다. 에러 로그를 확인해 보니 PARAMETER_VALUE가 유효하지 않다는 것이었다. 그제서야 공식문서를 찾아보니 Spring Batch 5에서는 메타 테이블 스키마가 변경되었다는 것을 알게 되었다.
만약, 기존에 Spring Batch 5 이전 버전을 사용하고 있고, 메타 테이블을 활용하고 있었다면 스키마 업데이트가 필요하다. 필자의 경우, DB개발팀의 도움을 받아 기존 테이블을 활용하는 타 팀을 고려하여 메타 테이블 관련 새로운 테이블을 생성하여 해당 테이블을 사용하는 방식으로 진행하였다.
4. JobBuilderFactory, StepBuilderFactory 는 들어가고 JobRepository, TransactionManager 는 명시
기존에는 JobBuilderFactory를 통해 Job을 만들고 StepBuilderFactory를 통해 Step을 구성하였다. 하지만, Spring Batch 5에서는 이를 활용할 수 없게 되었다. 기존 구조에서는 공식문서를 읽지 않는 한 JobRepository의 역할이 드러나지 않는다는 이유로 Spring Batch 5에서는 Factory들을 없애고 JobRepository를 노출했다.
기존에는 아래 코드처럼 JobBuilderFactory를 이용하여 Job을 만들었다.
// Sample with v4
@Configuration
@EnableBatchProcessing
public class MyJobConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Bean
public Job myJob(Step step) {
return this.jobBuilderFactory.get("myJob")
.start(step)
.build();
}
}
Spring Batch 5부터는 아래처럼 JobRepository를 명시하고 JobBuilder를 이용해 Job을 구성하면 된다.
// Sample with v5
@Configuration
@EnableBatchProcessing
public class MyJobConfig {
@Bean
public Job myJob(JobRepository jobRepository, Step step) {
return new JobBuilder("myJob", jobRepository)
.start(step)
.build();
}
}
StepBuilderFactory도 마찬가지이다. 이전에는 StepBuilderFactory를 사용하여 Step을 만들었다.
// Sample with v4
@Configuration
@EnableBatchProcessing
public class MyStepConfig {
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Step myStep() {
return this.stepBuilderFactory.get("myStep")
.tasklet(..) // or .chunk()
.build();
}
}
Spring Batch 5에서는 PlatformTranactionManager를 명시해주는 방식으로 변경되었다. transaction manager는 tasklet으로 구성된 step에서만 필수적이다. 다른 step 타입들은 transaction manager를 필요로 하진 않는다.
// Sample with v5
@Configuration
@EnableBatchProcessing
public class MyStepConfig {
@Bean
public Tasklet myTasklet() {
return new MyTasklet();
}
@Bean
public Step myStep(JobRepository jobRepository, Tasklet myTasklet, PlatformTransactionManager transactionManager) {
return new StepBuilder("myStep", jobRepository)
.tasklet(myTasklet, transactionManager) // or .chunk(chunkSize, transactionManager)
.build();
}
}
마무리하며
개발을 하면서 느낀 건 기술은 손 놓고 있으면 어느 순간 따라잡기 힘들 정도로 앞서나가있다는 것이다. 발전하는 기술에 맞춰 나 스스로도 끊임없이 공부를 해야겠다고 이번 프로젝트를 진행하며 다시금 느꼈다. 또한, 릴리즈된 버전에 대한 공식문서를 읽는 습관을 가져야겠다고 생각했다. 공식문서만 제대로 숙지했더라도 삽질하는 시간이 줄어들었을 것 같다. 개발 인생 아자자! ⼒🔋