스프링 배치(Spring Batch)는 데이터 처리 중 예외가 발생했을 때 자동으로 재시도하거나 실패를 건너뛰는 로직을 설정할 수 있는 강력한 오류 처리 기능을 제공합니다. 이 중에서도 재시도(Retry)는 특정 작업이 실패했을 때 일정 횟수만큼 다시 시도해보는 기능입니다.
배치 시스템에서 외부 시스템 연동(API, DB, 파일 I/O 등)이나 비즈니스 로직에서 일시적인 오류가 발생할 수 있습니다.
예를 들어:
스프링 배치에서 재시도는 주로 아래 위치에 설정할 수 있습니다:
Step 설정에서 faultTolerant()를 통해 재시도 및 건너뛰기 설정이 가능합니다.
@Bean
public Step retryStep() {
return stepBuilderFactory.get("retryStep")
.<InputType, OutputType>chunk(10)
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.faultTolerant()
.retry(MyTemporaryException.class)
.retryLimit(3)
.build();
}
Spring Batch는 내부적으로 재시도 기능을 구현할 때 Spring Retry를 사용하며, 그 중심에 있는 핵심 컴포넌트가 바로 RetryTemplate입니다.
RetryTemplate은 재시도 로직을 캡슐화한 템플릿으로, 실패 시 조건을 기반으로 재시도를 수행하고, 최대 횟수 초과 시 최종 예외를 던지는 방식으로 동작합니다.
RetryTemplate retryTemplate = new RetryTemplate();
// 정책 설정
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3));
retryTemplate.setBackOffPolicy(new FixedBackOffPolicy());
// 실행
String result = retryTemplate.execute(
context -> {
// 재시도 대상 로직
return someUnstableRemoteCall();
},
context -> {
// 재시도 실패 시 대체 로직
return "fallback-value";
}
);
Spring Batch에서 ItemProcessor는 read로부터 읽어온 데이터를 가공/변환하는 중간 단계입니다.
이 가공 과정에서 예외가 발생할 수 있고, 이를 재시도(Retry)할 수 있도록 설정할 수 있습니다.
재시도가 필요한 예시 상황
재시도 설정 방법
StepBuilder에서 .faultTolerant()와 .retry(), .retryLimit()을 사용하여 재시도 정책을 지정할 수 있습니다.
@Bean
public Step processorRetryStep() {
return stepBuilderFactory.get("processorRetryStep")
.<String, String>chunk(10)
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.faultTolerant()
.retry(ApiTemporaryException.class)
.retryLimit(3)
.build();
}
주의할 점
ItemWriter는 Spring Batch의 Step 내에서 데이터를 외부 시스템(파일, DB, API 등)에 기록하는 역할을 합니다.
이 과정에서 네트워크 오류, DB deadlock, 일시적인 실패 등으로 인해 예외가 발생할 수 있는데, 이런 경우 재시도 기능이 매우 유용합니다.
재시도가 필요한 상황
재시도 설정 방법
@Bean
public Step writerRetryStep() {
return stepBuilderFactory.get("writerRetryStep")
.<String, String>chunk(10)
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.faultTolerant()
.retry(DatabaseTemporaryException.class)
.retryLimit(3)
.build();
}
특징
이유 1: Reader는 상태가 있는 작업이다
ItemReader는 내부적으로 상태를 가집니다. 예를 들어:
이유 2: Spring Batch는 Reader의 예외를 Step 실패로 간주
Spring Batch의 기본 동작 방식은:
이유 3: 재시도 하려면 ExecutionContext 관리가 복잡해짐
Reader는 보통 상태를 ExecutionContext에 저장하고 재시작 시점에 복원합니다.
하지만 재시도를 위해서는 read 전에 상태를 저장해야 함 → 이는 성능, 구현 복잡도 모두 악화시킴.