새발블로그

[Spring] Spring Batch + Scheduler 본문

Server/Spring

[Spring] Spring Batch + Scheduler

EUG 2025. 10. 7. 15:50

1. 서론

Banklab에서 Spring Batch와 Scheduler를 붙이면서 정말 많은 삽질을 했다.ㅠㅠ

당시에 하루면 끝날 줄 알았는데 ...3일은 걸렸었던 거 같다.

그냥 레퍼런스대로 하면될 줄 알았는데 아예 모르는 상태로 하니까 어려워서 하루는 거의 공부하는 데 쓴 거 같다

정리내용
초기 설계

 

Spring Batch는 금융 API 데이터를 주기적으로 수집·적재하는 데 필수였고, 스케줄러는 그 배치를 매일 새벽에 돌리는 역할을 맡았다.

나중에 upsert로 변경하였지만 초기에는 delete + insert 구조여서 다음과 같이 설계하고 시작하였다

 

  • 환경: Spring Framework 5.3.37
  • Batch 버전: 4.3.9 (5.x 시도했다가 호환성 문제로 실패 → 결국 4.x 선택)

 

이 글은 내가 겪은 버전 지옥, JobBuilderFactory 에러, Tasklet/Chunk 선택, 스케줄러와 배치 혼동을 정리한 기록이다~~

2. JobBuilderFactory/StepBuilderFactory 삽질

처음엔 이런 코드가 잘 돌았다.

@Bean
public Job savingsRefreshJob() {
    return jobBuilderFactory.get("savingsRefreshJob")
            .start(deleteSavingsStep())
            .next(fetchAndInsertSavingsStep())
            .build();
}

 

그런데 Batch를 5.x로 올리자마자 JobBuilderFactory와 StepBuilderFactory 주입 실패로 애플리케이션이 부팅조차 안 됐다.

알고 보니 Batch 5.x에서 이 팩토리들은 더 이상 빈으로 제공되지 않고, JobRepository와 TransactionManager를 직접 주입해야 한다.

결국 Spring Framework 5.3과 Batch 5.x 조합은 호환성 문제가 계속 생겨서, 다시 4.3.9로 내리면서 해결했다.

 

정리 1: Batch 4 vs 5 차이

  • 4.x: JobBuilderFactory, StepBuilderFactory → @Autowired 가능, @EnableBatchProcessing 자동 설정 OK
  • 5.x: Factory 제거, new JobBuilder("name", jobRepository) 직접 생성 필요, PlatformTransactionManager 필수 인자 전달 필요

 

3. Tasklet vs Chunk: 언제 뭘 쓸까?

  • Tasklet: 단순한 단발성 작업 (예: 테이블 삭제)
  • Chunk: 대용량 데이터, Reader → Processor → Writer 반복 처리 (예: API 데이터 적재)

 

내가 실제로 적용한 패턴은

1. Tasklet으로 기존 데이터 삭제 후 재적재였다.

상품이 데이터적으로 많은 건 아니었기때문에 Tasklet을 적용했다

 

정리 2: Tasklet vs Chunk

  • Tasklet은 단일/원샷 작업, 제어 흐름에 적합
  • Chunk는 데이터 처리 중심, 트랜잭션 단위 관리에 적합
  • 기준: 반복적이면 Chunk, 한 번이면 Tasklet

 

4. 스케줄러 vs 배치

처음엔 “스케줄러 = 배치”라고 착각했는데, 사실은 전혀 다르다.

 

  • 배치: 무엇을 실행할지 정의 (Job/Step)
  • 스케줄러: 언제 실행할지 제어 (Cron 등)

 

정리 3: 배치 vs 스케줄러

  • Spring Batch = 실행 로직 정의
  • Scheduler(@Scheduled, Quartz) = 실행 타이밍 지정
  • 스케줄러는 배치를 JobLauncher로 기동하는 트리거일 뿐

 

5. 실제 스케줄러 코드

Banklab 프로젝트에서는 금융상품 배치를 새벽 시간대에 나눠 실행했다.

 

@Component
public class ProductScheduler {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired @Qualifier("depositRefreshJob")
    private Job depositRefreshJob;

    @Autowired @Qualifier("savingsRefreshJob")
    private Job savingsRefreshJob;

    // 예금 상품 배치 - 매일 오전 2시 실행
    @Scheduled(cron = "0 0 2 * * *")
    public void runDepositBatch() {
        try {
            JobParameters jobParameters = new JobParametersBuilder()
                    .addLong("timestamp", System.currentTimeMillis())
                    .toJobParameters();
            jobLauncher.run(depositRefreshJob, jobParameters);
            log.info("=== 예금 상품 배치 완료 ===");
        } catch (Exception e) {
            log.error("예금 상품 배치 실행 중 오류 발생", e);
        }
    }

    // 적금 상품 배치 - 매일 오전 2시 5분 실행
    @Scheduled(cron = "0 5 2 * * *")
    public void runSavingsBatch() {
        try {
            JobParameters jobParameters = new JobParametersBuilder()
                    .addLong("timestamp", System.currentTimeMillis())
                    .toJobParameters();
            jobLauncher.run(savingsRefreshJob, jobParameters);
            log.info("=== 적금 상품 배치 완료 ===");
        } catch (Exception e) {
            log.error("적금 상품 배치 실행 중 오류 발생", e);
        }
    }
}

 

  • @Scheduled로 실행 시간 지정
  • JobLauncher.run(job, params)로 배치 기동
  • JobParameters에 timestamp 추가해 매번 실행 구분

 

6. Config와 스키마 문제

  • Batch 설정 클래스가 컴포넌트 스캔에 안 잡히면 배치 실행 자체가 안 됨
  • Boot에서는 자동 구성되지만, 멀티 모듈에서는 반드시 @Configuration + @EnableBatchProcessing 포함해야 함
  • 스케줄러를 쓰려면 @EnableScheduling도 루트 설정에 추가 필요

 

여기서 또 하나 크게 헤맸던 게 바로 스키마 문제였다.

Spring Batch는 실행 정보를 관리하기 위해 자체 메타데이터 테이블을 사용한다(BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION 등).

이걸 DB에 미리 생성하지 않으면, 애플리케이션 부팅 단계에서 오류가 발생하거나 Job 실행 시 “Table not found” 에러가 터진다.

나는 처음에 이걸 몰랐다가, 공식 SQL 스크립트(schema-mysql.sql)를 DB에 적용한 뒤에야 정상적으로 Job이 실행됐다.

 

  • 데이터베이스 스키마 및 테이블 관리
  • Spring Batch는 배치 작업을 수행하는 동안 작업의 상태, 메타데이터, 실행 정보 등을 저장
  • MySQL을 데이터베이스로 사용할 때 이 테이블들을 명확하게 정의하고 관리할 필요 존재
  • 테이블
    • BATCH_JOB_INSTANCE: 배치 작업 인스턴스 정보를 저장 (어떤 작업이 실행됐는지)
    • BATCH_JOB_EXECUTION: 배치 작업 실행의 상태 정보 저장 (성공/실패, 시작/종료 시간 등)
    • BATCH_STEP_EXECUTION: 각 Step의 실행 상태 저장
    • BATCH_JOB_EXECUTION_CONTEXT: 배치 작업의 실행 시 사용된 데이터 저장
    • BATCH_STEP_EXECUTION_CONTEXT: 각 Step에서 사용된 임시 데이터 저장

이렇게 정리해놨다

메타테이블 생성은 프로젝트에서 할 수 있다.

 

우리 서비스는 mysql 을 사용했기 때문에 mysql 스키마를 사용했다

 

7. 교훈 & 회고

  • 버전 호환성은 미리 확인하자 (Spring 5.x → Batch 4.x, Spring 6.x → Batch 5.x)
  • Factory 자동주입은 언젠가 사라진다. 빌더 직접 생성법도 익혀야 한다.
  • 스케줄러와 배치는 엄연히 다르다. 역할을 분리해 생각하자.
  • Config와 스키마 설정을 소홀히 하지 말자.
  • 제대로 알고 쓰자..