본문 바로가기

SpringBoot

[Spring Batch] JobParameters와 Scope

Spring Batch에서 단순히 로직을 짜는 것만큼이나 중요한 것이 바로 "외부에서 값을 어떻게 주입받고, 어떻게 관리할 것인가?" 입니다. 이 메커니즘을 제대로 이해하지 못하면, 매번 코드를 수정해서 배포해야 하는 끔찍한 상황이 벌어집니다.

오늘은 배치의 입력값을 다루는 JobParameters와, 배치의 생명주기를 관리하는 JobScope/StepScope에 대해 완벽하게 정리해 봅니다.

 

오늘은 배치의 입력값을 다루는 JobParameters와, 배치의 생명주기를 관리하는 JobScope/StepScope에 대해 완벽하게 정리해 봅시다.

 


 

1. JobParameters: 배치의 입력값

JobParameters는 배치 작업 실행 시 외부에서 주입하는 파라미터입니다. 단순한 Java Main Arguments나 -D 옵션과는 차원이 다릅니다.

  • 역할:
    1. 동적 제어: 날짜, 파일 경로 등 실행 시점마다 변하는 값을 주입.
    2. Job Instance 식별: (중요 ⭐) **"같은 Job이라도 파라미터가 다르면 다른 실행이다"**라고 인식하게 해주는 식별자 역할.

✅ 1-1. 커맨드라인으로 전달하기 (기본)

실무에서는 주로 젠킨스(Jenkins)나 에어플로우(Airflow) 같은 스케줄러에서 커맨드라인으로 파라미터를 던집니다.

표기법: 파라미터명=값,타입,식별여부

 

# 예시: 2025년 12월 15일자 데이터를 처리하라
java -jar my-batch.jar --spring.batch.job.name=dailyJob targetDate=2024-12-15,java.time.LocalDate

 

 

  • targetDate: 파라미터 키
  • 2024-01-01: 파라미터 값
  • java.time.LocalDate: 타입 (생략 시 String)

 

✅ 1-2. 코드로 주입받기 (@Value)

주입받은 파라미터는 SpEL(Spring Expression Language)을 사용하여 간편하게 꺼내 쓸 수 있습니다. 단, 반드시 @StepScope나 @JobScope가 필요합니다. (이유는 뒤에서 설명!)

 

 

@Bean
@StepScope // 필수! 이게 없으면 실행 시점에 값을 못 받아옴
public Tasklet dailyTasklet(
        @Value("#{jobParameters['targetDate']}") LocalDate targetDate,
        @Value("#{jobParameters['retryCount']}") Integer retryCount
) {
    return (contribution, chunkContext) -> {
        log.info("처리 대상 날짜: {}", targetDate);
        log.info("재시도 횟수: {}", retryCount);
        return RepeatStatus.FINISHED;
    };
}

 

✅ 1-3. 쉼표(,)가 포함된 파라미터 (JSON 표기법)

targets=서울,부산 처럼 값 안에 쉼표가 들어가면 파싱 에러가 납니다. 이럴 땐 JSON 방식을 쓰면 깔끔합니다.

 

targets='{"value": "서울,부산", "type": "java.lang.String"}'

 

(Spring Batch 5부터 지원하며, JsonJobParametersConverter 설정이 필요할 수 있습니다.)

 


2. JobScope & StepScope: 배치의 특별한 생명주기

 

Spring의 기본 빈 스코프는 Singleton(앱 뜰 때 생성)입니다. 하지만 배치에서는 "Job이 실행될 때" 혹은 "Step이 실행될 때" 빈이 만들어져야만 하는 경우가 있습니다.

바로 JobParameters를 늦게(Late Binding) 받기 위해서입니다.

 

✅ 왜 필요한가요?

  1. Late Binding (지연 할당): 앱 구동 시점에는 targetDate가 뭔지 모릅니다. Job이 실제로 실행되는 순간에 파라미터를 받아와야 하므로, 빈 생성 시점을 뒤로 미루는 것입니다.
  2. Thread Safety (동시성 보장): 동일한 Job이 동시에 여러 번 실행될 때, 각 실행(Thread)마다 서로 다른 Step/Tasklet 인스턴스를 생성해 줍니다. (서로 간섭 X)

✅ 사용 범위 및 주의사항 

  • @JobScope: Job 실행 시 생성 ~ 종료 시 소멸. (Step 빈에 사용)
  • @StepScope: Step 실행 시 생성 ~ 종료 시 소멸. (Tasklet, Reader, Writer, Processor 빈에 사용)

[주의] Step 빈에는 @StepScope를 쓰지 마세요!

// 잘못된 예: Step에 @StepScope를 걸면 안 됨
@Bean
@StepScope 
public Step myStep() { ... } 

// 올바른 예: Tasklet이나 Reader 같은 하위 컴포넌트에 거세요
@Bean
public Step myStep() {
    return new StepBuilder(...)
            .tasklet(myTasklet(), transactionManager)
            .build();
}

@Bean
@StepScope // 여기에 걸어야 함!
public Tasklet myTasklet(...) { ... }

 

(Step은 프레임워크가 메타데이터 관리를 위해 미리 접근해야 하므로, StepScope를 걸면 실행 시점에 꼬일 수 있습니다.)

 


3. ExecutionContext: 배치의 기억 저장소 🧠

 

JobParameters가 "시작할 때 받은 명령서"라면, ExecutionContext는 "작업 도중 적어두는 메모장"입니다.

  • 용도:
    • Step 간 데이터 공유 (Step A 결과 → Step B 입력)
    • 실패 후 재시작 시 상태 복원: (예: "100번째 줄까지 읽었음" 저장해두면, 재시작 시 101번째부터 수행)
// Step 1: 메모장에 데이터 저장
chunkContext.getStepContext().getStepExecution().getJobExecution()
        .getExecutionContext().put("myKey", "중요한 데이터");

// Step 2: 메모장에서 데이터 꺼내기 (@Value 활용)
@Value("#{jobExecutionContext['myKey']}") String myData

 


 

@StepScope를 Step 빈에 붙이지 말라

개념 역할 특징
JobParameters 배치의 입력값 (Read-Only) 잡 인스턴스를 식별하는 기준이 됨
JobScope / StepScope 빈의 생성 시점 지연 JobParameters를 주입받기 위해 필수
ExecutionContext 배치의 상태 저장소 (R/W) 재시작 시 복구 지원, Step 간 데이터 공유