Framework/Spring

[Batch] Spring Batch Scope의 개념과 Job Parameter 사용 방법

Joonfluence 2024. 11. 27. 00:14

Spring Batch Scope란

Spring Batch에서 Scope란 개념이 존재합니다. Scope는 빈의 생성 및 관리와 관련된 범위를 정의하는 개념으로, _작업(job) 실행 과정에서 특정 컨텍스트나 상태를 유지해야 하는 경우에 사용_됩니다.

Spring Batch에선 모든 빈을 처음에 한 번에 생성하지 않고, 특정 Scope 내에 있는 빈을 실행 시점에 생성합니다. 이렇게 하는 이유는 배치 애플리케이션을 효율적으로 동작시키기 위함입니다. Scope를 활용하여 실행 시점에 빈을 생성하는 것이, 그렇지 않았을 때보다, 어떤 면에서 더 효율적일까요?

Spring Batch에서 실행 시점에 빈을 생성하는 이유

1. 동적 데이터 및 실행 환경의 요구 처리

배치 작업은 실행 시점마다 다른 JobParameters(Spring Batch에서 배치 작업을 실행할 때 전달하는 읽기 전용 메타데이터)나 StepExecutionContext(Spring Batch에서 각 Step의 실행 중에 생성되고 관리되는 컨텍스트 객체)를 필요로 합니다. 예를 들어, @JobScope나 @StepScope 빈은 실행 시점에 파라미터를 주입받아야 적절히 동작합니다. 실행 시점에 빈을 생성하여 실행 파라미터를 기반으로 빈을 동적으로 구성합니다.

@Bean
@StepScope
public FlatFileItemReader<MyData> reader(@Value("#{jobParameters['filePath']}") String filePath) {
    FlatFileItemReader<MyData> reader = new FlatFileItemReader<>();
    reader.setResource(new FileSystemResource(filePath));
    return reader;
}
  • Scope를 통해 파일 경로(filePath)를 실행 시점에만 주입할 수 있습니다.
  • 모든 빈을 사전에 생성하면 filePath를 알 수 없으므로 실행 컨텍스트에 맞는 유연한 동작이 불가능합니다.

2. 리소스 사용 최적화

Spring Batch는 배치 처리에서 대량의 데이터를 다루는 경우가 많아 메모리 사용이 중요합니다. 모든 빈을 미리 생성하면, 사용하지 않는 빈까지 메모리에 로드되며 불필요한 리소스를 소비합니다. Scope는 필요한 빈만 실행 시점에 생성하여 메모리 소비를 최소화합니다.

예시) 특정 Step에서만 사용하는 Processor 또는 Writer

  • 모든 Step에서 동일한 빈이 필요하지 않을 수 있습니다.
  • 특정 Step에만 유효한 빈은 해당 Scope 안에서만 생성되므로 불필요한 메모리 낭비를 방지합니다.

3. 애플리케이션 초기화 속도 개선

  • 배치 작업에서 수백 개 이상의 Step이나 Job을 정의하는 경우, 모든 빈을 초기화하려면 상당한 시간이 걸릴 수 있습니다.
  • Scope는 실행 시점에 필요한 빈만 초기화하므로, 애플리케이션의 초기화 속도를 크게 개선합니다.

예시) 수백 개의 데이터 변환 로직이 각 Step에서 필요하지만, 일부 Step은 실행되지 않는 경우

  • 실행되지 않는 Step의 빈을 미리 생성할 필요가 없으므로 초기화가 빠릅니다.

4. 실행 컨텍스트의 독립성과 상태 관리

Scope는 실행 시점마다 독립된 빈을 생성하므로, 여러 Job 또는 Step 실행 간에 데이터 충돌을 방지합니다. 단일 애플리케이션 컨텍스트에서 동시 실행되는 배치 작업에서 이러한 분리는 필수적입니다.

예시) 여러 Step이 병렬로 실행될 때 ItemReader나 ItemWriter가 다른 데이터 소스를 처리

  • 각 Step에 독립적으로 빈을 생성하여 데이터 상태를 충돌 없이 유지합니다.

5. 빈의 재구성을 통한 유연한 개발 지원

Scope를 사용하면 빈이 특정 Step 또는 Job의 실행 컨텍스트에 따라 다르게 동작하도록 재구성할 수 있습니다. 실행 시점에 빈을 생성하지 않으면, 이러한 유연성을 확보하기 어렵습니다.

예시) 여러 Step에서 같은 ItemProcessor를 사용하지만, Step마다 로직이 약간 다른 경우

@Bean
@StepScope
public ItemProcessor<MyData, ProcessedData> processor(
    @Value("#{stepExecutionContext['processingType']}") String processingType) {
    return item -> {
        if ("TYPE_A".equals(processingType)) {
            item.setProcessedField("Processed with TYPE_A");
        } else {
            item.setProcessedField("Processed with TYPE_B");
        }
        return item;
    };
}
  • 실행 시점마다 processingType에 따라 동적으로 로직이 달라집니다.

6. 테스트 및 유지보수 용이성

Scope를 활용하면 빈의 생성 시점과 컨텍스트가 명확히 분리되므로, 특정 Step 또는 Job에 대해 개별적으로 테스트하거나 유지보수하기가 쉽습니다. 미리 빈을 생성하면 실행 시점에 발생하는 로직의 특성을 테스트하기 어렵습니다.

Job Parameter 사용법

Spring Batch의 경우 외부 혹은 내부에서 파라미터를 받아 여러 Batch 컴포넌트에서 사용할 수 있게 지원하고 있습니다.
이 파라미터를 Job Parameter라고 합니다. Job Parameter를 사용하기 위해선 항상 Spring Batch 전용 Scope를 선언해야만 하는데요. 크게 @StepScope와 @JobScope 2가지가 있습니다. 사용법은 간단한데, 아래와 같이 SpEL로 선언해서 사용하시면 됩니다.

@Value("#{jobParameters[파라미터명]}")

@JobScope는 Step 선언문에서 사용 가능하고, @StepScope는 Tasklet이나 ItemReader, ItemWriter, ItemProcessor에서 사용할 수 있습니다.

@StepScope

  • Step의 실행 범위에서만 빈이 생성되고 사용할 수 있도록 스코프를 제한합니다.
  • Step 실행 중 값이 동적으로 주입되어야 하는 경우 유용합니다.
  • 주로 Step 내부에서 실행되는 ItemReader, ItemProcessor, ItemWriter와 같은 컴포넌트에서 사용됩니다.
@StepScope
@Component
class SampleTasklet(
    private val sampleCommandService: SampleCommandService,
) : Tasklet {
    override fun execute(
        contribution: StepContribution,
        chunkContext: ChunkContext,
    ): RepeatStatus? {
        sampleCommandService.doSomething()
        return RepeatStatus.FINISHED
    }
}

@JobScope

  • Job의 실행 범위에서만 빈이 생성되고 사용할 수 있도록 스코프를 제한합니다.
  • Job 전체 실행 중 값을 한 번만 주입받고, 여러 Step에서 공유할 수 있는 빈을 정의할 때 유용합니다.
@Configuration
public class JobScopeExampleConfig {

    @Bean
    public Job exampleJob(JobBuilderFactory jobBuilderFactory, Step exampleStep) {
        return jobBuilderFactory.get("exampleJob")
                .start(exampleStep)
                .build();
    }

    @Bean
    public Step exampleStep(StepBuilderFactory stepBuilderFactory, FlatFileItemReader<String> itemReader, ItemWriter<String> itemWriter) {
        return stepBuilderFactory.get("exampleStep")
                .<String, String>chunk(10)
                .reader(itemReader)
                .writer(itemWriter)
                .build();
    }

    @Bean
    @JobScope
    public FlatFileItemReader<String> itemReader(@Value("#{jobParameters['filePath']}") String filePath) {
        FlatFileItemReader<String> reader = new FlatFileItemReader<>();
        reader.setResource(new FileSystemResource(filePath));
        reader.setLineMapper((line, lineNumber) -> line); // 간단한 LineMapper
        return reader;
    }

    @Bean
    public ItemWriter<String> itemWriter() {
        return items -> items.forEach(System.out::println); // 출력 처리
    }
}

정리 : Spring Batch에서 Scope의 필요성

  1. 실행 시점 데이터 주입: 배치 작업의 실행 컨텍스트(JobParameters, StepExecutionContext)에 맞는 동적 데이터를 처리하기 위함 입니다.
  2. 리소스 최적화: 불필요한 빈 초기화를 방지하여 메모리 및 CPU 사용량을 절감.
  3. 실행 속도 개선: 애플리케이션 초기화 시점을 최적화.
  4. 독립성 보장: 동시 실행되는 Job 또는 Step 간의 데이터 충돌 방지.
  5. 유연성 확보: 실행 컨텍스트에 따라 빈을 동적으로 재구성 가능.
반응형