JobParameters. 이름만 들으면 그저 '매개변수' 따위로 들리겠지만, 그 실체는 다르다.
이것은 배치의 운명을 결정짓는 통제 변수다
JobParameters는 배치 작업에 전달되는 입력 값이다.
이 값은 배치가 어떤 조건에서, 어떤 데이터를 다룰지를 결정하는 데 핵심적인 역할을 한다.
예를 들어, 매일 실행되는 배치 작업에서 날짜는 매번 바뀐다.
이럴 때 JobParameters가 없으면 작업을 일일이 수정해야 할 것 이다.
하지만 JobParameters를 사용하면?
동일한 Job을 입력 값만 바꿔서 유연하게 실행할 수 있다.
그런데 여기서 의문이 들 수 있다.
"그냥 -D 옵션으로 프로퍼티로 전달하면 되는거 아닌가?"
그럴 듯하게 들리지만, JobParameters와 프로퍼티는 다른 목적을 가진다.
JobParameters는 단순한 값 전달을 넘어서, 배치의 실행과 제어를 관리하는 핵심 메커니즘이다.
이제 JobParameters가 뭔지 알았으니, 실제로 어떻게 사용하는지 보자.
실제 운영 환경에서는 커맨드 라인을 통해 파라미터를 전달하는 방식이 핵심이다.
왜일까? 대부분의 스케줄러와 자동화 도구들이 커맨드라인 실행을 기본으로 지원하기 때문이다.
그렇다면 가장 먼저 커맨드라인에서 JobParameters를 전달하는 방법부터 살펴보자.
실무에서 가장 흔히 사용되는 시나리오 중 하나는 파일 경로를 JobParameters로 넘기는 경우다.
예를 들어, 파일을 말소시키는 배치를 실행할 떄, 어떤 파일들을 제거할지 타겟을 지정해야 하지 않겠나?
./gradlew bootRun --args='--spring.batch.job.name=dataPRocessingJob inputFilePath=/data/input/users.csv,java.lang.String'
--spring.batch.job.name으로 전달한 Job 이름 뒤애 따라오는 inputFilePath=/data/input/users.csv,java.lang.String을 보라.
이것이 바로 잡 파라미터이다.
각 구성 요소가 정확히 어떤 의미를 가지고 있는지 Spring Batch의 JobParameters 표기법을 살펴보자.
parameterName=parameterValue,parameterType,identificationFlag
java.lang.String, java.lang.Integer와 같은 fully qualified name 사용)String 타입으로 가정한다.true 이면 식별에 사용된다는 의미이다.true로 설정된다.그렇다면 parameterType에는 어떤 값이 올 수 있을까?
커맨드 라인 문자열로 전달된 JobParameters는 Spring Batch의 DefaultJobParametersConverter라는 컴포넌트를 통해 적절한 타입으로 변환된다.
내부적으로 Spring의 DefaultConversionService를 사용하기 떄문에, Integer, Boolean과 같은 기본 타입은 물론 그 외 다양한 타입으로 변환을 지원하다.
더불어 자체적으로 LocalDate, LocalDateTime과 같은 시간 관련 타입 변환도 지원한다.
즉, 대부분의 use case에 해당하는 타입은 다 지원한다고 봐도 된다.
먼저 가장 기본이 되는 정수형과 문자열 타입의 파라미터를 전달하는 법을 알아보자.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class SystemTerminatorConfig {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
@Bean
public Job processTerminatorJob(Step terminationStep) {
return new JobBuilder("processTerminatorJob", jobRepository)
.start(terminationStep)
.build();
}
@Bean
public Step terminationStep(Tasklet terminatorTasklet) {
return new StepBuilder("terminatorStep", jobRepository)
.tasklet(terminatorTasklet, transactionManager)
.build();
}
@Bean
@StepScope
public Tasklet terminatorTasklet(@Value("#{jobParameters['terminatorId']}") String terminatorId,
@Value("#{jobParameters['targetCount']}") Integer targetCount) {
return (contribution, chunkContext) -> {
log.info("시스템 종결자 정보:");
log.info("ID: {}", terminatorId);
log.info("제거 대상 수: {}", targetCount);
log.info("⚡ SYSTEM TERMINATOR {} 작전을 개시합니다.", terminatorId);
log.info("☠️ {}개의 프로세스를 종료합니다.", targetCount);
for (int i = 1; i <= targetCount; i++) {
log.info("💀 프로세스 {} 종료 완료!", i);
}
log.info("🎯 임무 완료: 모든 대상 프로세스가 종료되었습니다.");
return RepeatStatus.FINISHED;
};
}
}
terminatorTasklet() 메서드를 보자.
뒤에서 다시 살펴보겠지만 @Value 애노테이션과 #{jobParameters['parameterName']} 표현식으로 JobParameter를 주입받을 수 있다.
해당 메서드에서는 두 가지 타입의 파라미터를 전달 받는다.
그럼 이제 이 코드에 직접 파라미터를 전달해보자.
다음과 같이 커맨드라인으로 파라미터를 전달하면
./gradlew bootRun --args='--spring.batch.job.name=processTerminatorJob terminatorId=KILL-9,java.lang.String targetCount=5,java.lang.Integer'
다음과 같은 실행 결과가 출력될 것이다.
2025-12-30T17:36:03.059+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 시스템 종결자 정보:
2025-12-30T17:36:03.059+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : ID: KILL-9
2025-12-30T17:36:03.059+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 제거 대상 수: 5
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : ⚡ SYSTEM TERMINATOR KILL-9 작전을 개시합니다.
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : ☠️ 5개의 프로세스를 종료합니다.
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 💀 프로세스 1 종료 완료!
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 💀 프로세스 2 종료 완료!
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 💀 프로세스 3 종료 완료!
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 💀 프로세스 4 종료 완료!
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 💀 프로세스 5 종료 완료!
2025-12-30T17:36:03.060+09:00 INFO 89338 --- [ main] c.b.b.j.SystemTerminatorConfig : 🎯 임무 완료: 모든 대상 프로세스가 종료되었습니다.
...
025-12-30T17:36:03.062+09:00 INFO 89338 --- [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=processTerminatorJob]] completed with the following parameters: [{JobParameter{name='terminatorId', value=KILL-9, type=class java.lang.String, identifying=true},JobParameter{name='targetCount', value=5, type=class java.lang.Integer, identifying=true}}] and the following status: [COMPLETED] in 75ms
마지막 로그를 살펴보면, Spring Batch가 출력한 Job 실행 결과에서 우리가 전달한 terminatorId와 targetCount 파라미터가 각각 String과 Integer 타입으로 올바르게 전달된 것을 확인할 수 있다.
앞선 예제와 같이 @Value를 사용해 잡 파라미터를 전달받으려면 @StepScope와 같은 특별한 어노테이션을 선언해줘야 한다.
Spring Batch의 Scope에 대한 자세한 내용은 뒤에서 다룰 예정이다.
대규모 시스템 침투 작전과 같은 복잡한 배치 작업에서는 여러 개의 Job 파라미터를 효율적으로 관리해야 할 떄가 있다.
이런 경우 별도의 클래스를 만들어 파라미터를 관리하면 코드의 구조화와 재사용성을 높일 수 있다.
이번 예제에서는 시스템 침투 작전의 파라미터들을 하나의 POJO로 통합 관리하는 방법을 알아보자.
@Data
@StepScope
@Component
public class SystemInfiltrationParameters {
// 필드 주입
private final String operationCommander;
@Value("#{jobParameters[missionName]}")
private String missionName;
private int securityLevel;
// 생성자 주입
public SystemInfiltrationParameters(@Value("#{jobParameters[operationCommander]}") String operationCommander) {
this.operationCommander = operationCommander;
}
// setter 주입
@Value("#{jobParameters[securityLevel]}")
public void setSecurityLevel(int securityLevel) {
this.securityLevel = securityLevel;
}
}
이 작전 파라미터 클래스의 핵심 포인트를 살펴보자
@Value("#{jobParameters[...]}") 어노테이션을 사용해 다양한 방식으로 Job 파라미터를 주입받을 수 있다.
Spring Batch의 기본 파라미터 표기법에는 한계가 있다.
예를 들어, 다음과 같은 파라미터를 보자.
infiltrationTargets=판교_서버실,안산_데이터센터,java.lang.String
파라미터 값에 이처럼 쉼표(,)가 포함되면 어떻게 될까?
이 경우 Spring Batch는 파라미터 타입을 '안산_데이터센터'로 오해하게 될 것이다.
실제로 이 파라미터로 Job을 실행하면 에러가 발생한다.
Caused by: java.lang.ClassNotFoundException: 안산_데이터센터
이런 모호한 상황에선 어떯게 해야 할까?
이를 위해 Spring Batch 5부터는 JSON 기반의 파라미터 표기법을 새롭게 제공한다.
앞서 살펴본 파라미터를 JOSN 표기법으로 작성하면 다음과 같다.
infiltrationTargets='{"value": "판교_서버실,안산_데이터센터", "type": "java.lang.String"}'
먼저, 다음의 의존성을 추가한다. org.springframework.boot:spring-boot-starter-json
그리고 Configuration 클래스에 JsonJobParametersConverter를 빈으로 등록한다.
@Bean
public JobParametersConverter jobParametersConverter() {
return new JsonJobParametersConverter();
}
JsonJobParametersConverter는 지금까지 사용한 DefaultJobParametersConverter를 계승한 클래스로,
내부적으로 ObjectMapper를 사용해 JSON 형태의 파라미터 표기를 해석한다.
참고로 Gradle bootRun 태스크로 실행할 떄는 JSON 문자열 내의 큰 따옴표를 이스케이프 처리해야 한다.
./gradlew bootRun --args="--spring.batch.job.name=terminatorJob infiltrationTargets='{\"value\":\"판교서버실,안산데이터센터\",\"type\":\"java.lang.String\"}'"
그런데 우리가 입력한 커맨드라인 파라미터가 어떻게 Spring Batch의 Job으로 전달되는 걸까?
그 과정을 간단히 살펴보자.
Sprig Batch를 Spring Boot 3과 함께 사용하면 애플리케이션이 시작될 때 JobLauncherApplicationRunner라는 특별한 컴포넌트가 자동으로 동작한다.
이 컴포넌트는 Spring Boot가 제공하는 ApplicationRunner의 한 종류로,
커맨드라인으로 전달된 Spring Batch Job 파라미터를 해석하고 이를 바탕으로 실제 Job을 실행하는 역할을 맡고있다.
JobLauncherApplicationRunner는 다음과 같은 처리 과정을 거친다.
--spring.batch.job.name을 지정하지 않는 경우 검증 실패.--spring.batch.job.name을 생략할 수 있다.key=value 형식의 인자들을 JobParameters로 변환한다.지금까지는 커맨드라인을 통해 Job 파라미터를 전달하는 방법을 알아보았따.
하지만 실무에서는 커맨드라인 실행 외에도 다양한 방식으로 배치를 실행해야 할 떄가 있다.
예를 들어,
이런 상황에서는 프로그래밍 방식으로 JobParameters를 생성하고 전달하는 방법이 필요하다.
프로그래밍 방식으로 JobParameters를 생성/전달하려면 JobParametersBuilder라는 컴포넌트가 필요하다.
앞서 본 커맨드라인 예제와 비슷하게 파일 경로를 파라미터로 지정하는 방법을 코드로 구현해보자.
JobParameters jobParameters = new JobParametersBuilder()
.addJobParameter("inputFilePath", "/data/input/users.csv", String.class)
.toJobParameters();
jobLauncher.run(dataProcessingJob, jobParameters);
코드를 자세히 살펴보자.
JobParametersBuilder는 체이닝 방식의 API를 제공한다.
addJobParameters() 메서드를 통해 key-value 형태로 파라미터를 추가할 수 있다.
또한 위 메서드는 파라미터의 이름, 값, 타입을 순서대로 받는다. identifying 변수를 받는 메서드도 제공한다.
동일하게 생략할경우 기본값은 true이다.
지금까지 우리는 커맨드라인과 프로그래밍 방식으로 JobParameters를 배치 잡에 전달하는 법을 알아냈다.
그리고 @StepScope와 @Value를 사용해 Job 구성 코드에서 파라미터를 참조하는 방법도 배웠다.
그러나 @Value를 사용하는 방법 외에도 JobParameters에 접근할 수 있는 또 다른 방식이 있다.
이번에는 JobParameters에 직접 접근하는 방법을 알아보자.
다양한 상황에서 유연하게 JobParameters를 다룰 수 있을 것이다.
JobParameters에 직접 접근하려면, 먼저 JobParameters가 어디에 저장되고 관리되는지를 이해해야 한다.
Spring Batch에서는 JobExecution이라는 녀석이 Job의 실행 정보를 쥐고 있다.
결국 스텝 안에서 JobParameters를 찾아내려면 이 JobExecution을 통해야 한다는 거다.
JobExecution 이란?
Job의 실행과 관련된 모든 정보를 담고 있는 객체다.
Job 실행 시점에 생성되어 잡의 실행 상태, JobParameters, 실행 결과 등을 포함한다.
아래의 Tasklet 예제를 통해 JobExecution으로부터 JobParameters를 가져오는 방법을 살펴보자.
@Slf4j
@Component
public class SystemDestructionTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
JobParameters jobParameters = chunkContext.getStepContext()
.getStepExecution()
.getJobParameters();
String targetSystem = jobParameters.getString("system.target");
long destructionLevel = jobParameters.getLong("system.destruction.level");
log.info("타겟 시스템: {}", targetSystem);
log.info("파괴 레벨: {}", destructionLevel);
return RepeatStatus.FINISHED;
}
}
StepExecution은 스텝 실행에 관한 정볼르 담고 있는 객체이면서, 내부적으로 부모 Job의 JobExecution을 참조한다.
즉 StepExecution의 getJobParameters() 메서드를 호출하면, 부모 JobExecution으로부터 파라미터를 가져온다.
이런 계층 구조를 전부 이해할 필요는 없다.
중요한 건 JobExecution이든 StepExecution이든 이를 통해 프로그래밍 바익으로 JobParameters에 접근할 수 있다는 사실만 기억하자.
자, 이번 절에서 다룰 Job 파라미터의 마지막 챕터다.
바로 파라미터 검증이다.
JobParametersValidator를 사용하면 잘못된 파라미터가 들어오는 순간 즉시 차단할 수 있다.
단순하다. 단 하나의 메서드만 가지고 있다.
pubic interface JobParametersValidator {
void validate(@Nullable JobParameters parameters) throws InvalidJobParametersException;
}
빠르게 구현 예제를 훑어보자. 먼저 직접 JobParametersValidator를 구현한 코드를 살펴보자.
@Component
public class SystemDestructionValidator implements JobParametersValidator {
@Override
public void validate(JobParameters parameters) throws InvalidJobParametersException {
if (parameters == null) {
throw new InvalidJobParametersException("파라미터가 NULL입니다");
}
Long destructionPower = parameters.getLong("destructionPower");
if (destructionPower == null) {
throw new InvalidJobParametersException("desturctionPower 파라미터는 필수 값 입니다");
}
if (destructionPower > 9) {
throw new InvalidJobParametersException("파괴력 수준이 허용치를 초과했습니다!");
}
}
}
위와 같은 커스텀 Validator를 사용하려면, JobBuilder의 validator() 메서드를 통해 구성해준다.
Job 구성에서 이 JobParametersValidator를 주입받아 사용하면 된다.
그러나 파라미터 검증을 위해 매번 JobParametersValidator를 구현하는 건 너무 번거롭다.
다행히 Spring Batch는 이미 DefaultJobParametersValidator라는 기본 구현체를 제공한다.
단순히 파라미터의 존재 여부만 확인하면 될 떄는 이걸 사용하면 된다.
간단히 예제로 살펴보고 마무리하자.
@Bean
public Job systemDestructionJob(
JobRepository jobRepository,
Step systemDestructionStep
) {
return new JobBuilder("systemDestructionJob", jobRepository)
.validator(new DefaultJobParametersValidator(
new String[]{"destructionPower"}, // 필수 파라미터
new String[]{"targetSystem"} // 선택적 파라미터
))
.start(systemDestructionStep)
.build();
}