배치 작업은 정해진 로직을 단순히 반복하는 자동 기계가 아니다. 입력값에 따라 처리 대상 날짜, 파일 경로, 쿼리 조건이 달라지는 유연한 시스템이어야 한다. 스프링 배치에서는 이를 JobParameters와 배치 스코프(Scope)를 통해 구현한다.
1. 배치의 생사를 쥐는 변수: JobParameters
JobParameters는 배치 작업에 전달되는 입력 값이다. 단순히 매개변수 역할을 넘어, 배치의 실행과 제어를 관리하는 핵심 메커니즘이다.
🔍 왜 JobParameters인가?
매일 돌아가는 정산 배치가 있을 때, 날짜가 바뀔 때마다 코드를 수정할 수는 없다.

- 유연성: 코드는 그대로 두고 실행 시점에 파라미터(targetDate=2026-01-15)만 바꿔서 전달한다.
- 작업 식별(Identification): 스프링 배치는 Job 이름 + JobParameters 조합으로 개별 작업을 식별한다. 파라미터가 다르면 별개의 'JobInstance'로 취급하여 관리하므로, 특정 날짜의 실패한 작업만 골라 재시작하는 것이 가능해진다.
2. 잡 파라미터 전달과 기본 표기법
커맨드라인에서 파라미터를 전달할 때는 key=value,type,identifying 형식을 따른다.
⌨️ 커맨드라인 실행 예시
./gradlew bootRun --args="--spring.batch.job.name=dailyJob targetDate=2026-01-15,java.lang.String"
- parameterName: Job 내에서 파라미터를 찾을 키 값.
- parameterValue: 실제 값.
- parameterType: java.lang.String, java.time.LocalDate 등 (생략 시 String).
- identificationFlag: 식별자로 사용할지 여부 (기본값 true).
🛠️ 데이터 타입 지원
스프링 배치는 DefaultJobParametersConverter를 통해 다양한 타입 변환을 지원한다.
- 기본 타입: String, Long, Double, Integer 등.
- 시간 타입: LocalDate (yyyy-MM-dd), LocalDateTime (yyyy-MM-ddThh:mm:ss) 등 ISO 표준 지원.
- Enum 타입: 별도 변환 로직 없이 문자열을 바로 Enum 상수로 매핑 가능.
3. 고도화된 파라미터 주입 방식
복잡한 작전 파라미터를 관리할 때는 단순 문자열을 넘어 JSON이나 POJO 방식을 활용할 수 있다.
📦 JSON 기반 표기법 (Spring Batch 5+)
쉼표(,)가 포함된 문자열을 전달할 때 발생하는 모호함을 해결한다.
infiltrationTargets='{"value": "판교_서버실,안산_데이터센터", "type": "java.lang.String"}'
이를 사용하려면 JsonJobParametersConverter를 빈으로 등록해야 한다.
📂 POJO를 활용한 통합 관리
여러 파라미터를 하나의 객체로 묶어 관리하면 코드 재사용성과 가독성이 높아진다. @StepScope와 @Value를 활용해 빈 주입 방식으로 구현한다.

🔍 STEP 1. 파라미터를 담을 POJO 클래스 정의
먼저 스프링 빈으로 등록될 POJO 클래스를 만든다. 여기서 핵심은 @StepScope를 붙여 지연 초기화가 가능하게 만드는 것이다.
@Getter @Setter
@Component
@StepScope // [중요] 실행 시점에 파라미터를 바인딩하기 위해 필수
public class SystemInfiltrationParameters {
@Value("#{jobParameters['missionName']}")
private String missionName;
@Value("#{jobParameters['targetSystem']}")
private String targetSystem;
@Value("#{jobParameters['destructionLevel']}")
private Integer destructionLevel;
}
🔍 STEP 2. 스텝 구성 시 POJO 주입받기
이제 구성 클래스(Job Configuration)에서 위에서 만든 POJO 빈을 주입받아 사용한다. 스프링의 의존성 주입(DI) 기능을 그대로 활용한다.
@Bean
public Step infiltrationStep(
JobRepository jobRepository,
Tasklet infiltrationTasklet // 아래에서 정의한 Tasklet 빈 주입
) {
return new StepBuilder("infiltrationStep", jobRepository)
.tasklet(infiltrationTasklet, transactionManager)
.build();
}
@Bean
@StepScope // POJO를 주입받는 컴포넌트도 같은 스코프여야 함
public Tasklet infiltrationTasklet(SystemInfiltrationParameters params) {
return (contribution, chunkContext) -> {
// [핵심] Getter를 통해 깔끔하게 파라미터 사용
log.info("작전명: {}", params.getMissionName());
log.info("목표 시스템: {}", params.getTargetSystem());
return RepeatStatus.FINISHED;
};
}
🔍 STEP 3. 커맨드라인에서 값 전달
실행 방식은 기존과 동일하다. 스프링 배치가 내부적으로 key 이름을 찾아 POJO의 @Value에 매핑해준다.
./gradlew bootRun --args="--spring.batch.job.name=infiltrationJob missionName=ProjectX targetSystem=Mainframe"
🔍심층 분석: "어느 잡에 들어가는가?"
많은 분이 "이 POJO가 특정 잡 전용인가요?"라고 질문한다. 정답은 **"주입받는 곳이 어디냐에 따라 결정된다"**이다.
- 공용 어댑터: 위에서 만든 SystemInfiltrationParameters 빈은 스프링 컨테이너에 등록된 하나의 빈일 뿐이다.
- 다중 활용: Job A의 스텝에서도 주입받을 수 있고, Job B의 스텝에서도 주입받을 수 있다.
- 독립성: 중요한 점은 @StepScope 덕분에 Job A가 실행될 때와 Job B가 실행될 때 각각 서로 다른 파라미터 인스턴스가 생성되어 값이 섞이지 않는다는 것이다.
4. Scope 이해하기
@JobScope와 @StepScope는 일반적인 싱글톤 빈과는 생명주기 자체가 다르다. 이들은 '지연 초기화(Lazy Initialization)'를 통해 동작한다.

🔍 스코프의 핵심 이점 3가지
- 동시성 안전: 동시에 여러 요청으로 Job이 실행되어도, 각 실행(Execution)마다 별도의 빈 인스턴스가 생성되어 상태가 섞이지 않는다.
- SpEL 주입: 런타임에 결정되는 잡 파라미터를 #{jobParameters['key']}와 같은 표현식으로 깔끔하게 주입받을 수 있다.
- 리소스 효율: 해당 단계(Job/Step)가 종료되면 빈과 함께 의존하던 DB 커넥션 등의 리소스가 즉시 정리된다.
⚠️ 사용 시 주의사항
- 상속 가능성: 프록시 생성을 위해 대상 클래스는 반드시 상속 가능(final 금지)해야 한다.
- Step 빈에 @StepScope 금지: Step 자체에 @StepScope를 달면, Step을 만들기도 전에 스코프를 참조하려다 ScopeNotActiveException이 발생한다. Step 내의 컴포넌트(Tasklet, Reader 등)에만 적용하라.
5. 방어적 설계를 위한 JobParametersValidator
잘못된 파라미터 유입을 실행 즉시 차단하는 안전장치다.
- 커스텀 구현: JobParametersValidator를 구현하여 비즈니스 규칙(예: 파괴력은 1~9 사이)을 검증한다.
- 기본 구현체: 단순 존재 여부만 확인한다면 DefaultJobParametersValidator를 사용하여 필수(Required)와 선택(Optional) 키를 지정한다.
.validator(new DefaultJobParametersValidator(
new String[]{"destructionPower"}, // 필수
new String[]{"targetSystem"} // 선택
))
6. 마무리하며
우리는 이번 과정을 통해 배치의 지능을 담당하는 JobParameters와 리소스의 안전한 격리 구역인 Scope를 알아보았다. 단순히 값을 넘기는 수준을 넘어, 어떻게 시스템이 작업을 식별하고 지연 바인딩을 통해 동적인 로직을 완성하는지 이해하는 것이 핵심이다.
'Spring > Batch' 카테고리의 다른 글
| [Spring Batch] 시스템의 기록을 읽어내는 FlatFileItemReader (0) | 2026.05.09 |
|---|---|
| [Spring Batch] Listener를 통한 전처리 및 후처리 (0) | 2026.05.08 |
| 스프링 배치 스텝(Step)의 두 가지 유형: Tasklet vs Chunk 지향 처리 (0) | 2026.05.07 |
| [Spring Batch] 스프링 배치 핵심 컴포넌트와 Job/Step 구조 (0) | 2026.05.07 |
| [Spring Batch] 스프링 배치 입문: 시스템의 또 다른 축, 배치 처리 이해하기 (1) | 2026.05.06 |