반응형
IntelliJ에서 Spring Boot 배치 로컬 테스트 – bootRun --args로 날짜 파라미터 실행하는 방법
운영 환경에서 Apache Airflow로 스케줄링하는 Spring Boot 배치를 로컬에서 테스트할 때, IntelliJ Gradle Run Configuration의 bootRun --args 옵션을 활용하면 Airflow 없이도 원하는 날짜 조건을 그대로 재현할 수 있습니다. 이 글에서는 실제로 동작하는 방법인 bootRun --args='--job_name=... collectYmd=... compYmd=...' 형식을 중심으로 설정 방법과 주의사항을 정리합니다.
목차
1. 운영(Airflow)과 로컬(bootRun) 실행 방식 비교
2. IntelliJ Run Configuration --args 설정 방법
3. --args 파라미터 형식과 규칙
4. Spring Boot 배치 코드에서 파라미터 수신
5. 자주 발생하는 오류와 해결 방법
#1. 운영(Airflow)과 로컬(bootRun) 실행 방식 비교
1) 두 환경의 실행 방식 차이
운영과 로컬은 실행 주체가 다르지만, 날짜를 포함한 파라미터를 args로 전달한다는 구조는 동일합니다. 로컬 테스트의 핵심은 Airflow가 JAR에 넘기는 args를 bootRun에서 그대로 재현하는 것입니다.
| 구분 | 운영 환경 (Airflow) | 로컬 환경 (IntelliJ bootRun) |
|---|---|---|
| 실행 주체 | Airflow Scheduler / DAG | 개발자 (IntelliJ Run Configuration) |
| 실행 명령 | java -jar batch.jar --job_name=... collectYmd=... | bootRun --args='--job_name=... collectYmd=...' |
| 날짜 값 출처 | Airflow 템플릿 변수 ({{ ds_nodash }}) | 개발자가 직접 날짜 입력 |
| 빌드 결과물 | 배포된 JAR 파일 직접 실행 | 소스 기반 즉시 실행 (JAR 빌드 불필요) |
· · · · ·
2) Airflow에서 배치를 실행하는 구조
운영에서 Airflow는 보통 아래와 같이 BashOperator로 JAR를 실행하면서 날짜 파라미터를 전달합니다. 로컬 테스트 시 이 구조를 bootRun --args로 동일하게 재현합니다.
# Airflow DAG 예시 (운영 환경)
run_batch = BashOperator(
task_id='run_spring_batch',
bash_command="""
java -jar /app/batch.jar \
--job_name=job_TranRconcl \
collectYmd={{ ds_nodash }} \
compYmd={{ macros.ds_add(ds, -1) | replace('-', '') }}
""",
)
# ds_nodash 예시: 20260228 / macros.ds_add(ds, -1): 전일 날짜
run_batch = BashOperator(
task_id='run_spring_batch',
bash_command="""
java -jar /app/batch.jar \
--job_name=job_TranRconcl \
collectYmd={{ ds_nodash }} \
compYmd={{ macros.ds_add(ds, -1) | replace('-', '') }}
""",
)
# ds_nodash 예시: 20260228 / macros.ds_add(ds, -1): 전일 날짜
#2. IntelliJ Run Configuration --args 설정 방법
1) Gradle Run Configuration 생성 및 --args 입력
IntelliJ에서 아래 순서로 Run Configuration을 생성하고 --args 옵션을 입력합니다.
② 좌측 상단 + 버튼 클릭 → Gradle 선택
③ Name 입력란에 식별하기 쉬운 이름 입력 (예: admin-ba [bootRun])
④ Gradle project에 해당 모듈 선택 (예: admin-ba)
⑤ Run 입력란에 아래 형식으로 입력
(1) Run Configuration 생성 순서
① 상단 메뉴 Run → Edit Configurations 클릭② 좌측 상단 + 버튼 클릭 → Gradle 선택
③ Name 입력란에 식별하기 쉬운 이름 입력 (예: admin-ba [bootRun])
④ Gradle project에 해당 모듈 선택 (예: admin-ba)
⑤ Run 입력란에 아래 형식으로 입력
bootRun --args='--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227'
⑥ Apply → OK 클릭 후 상단 Run 버튼(▶)으로 실행
⑦ 날짜를 바꿀 때는 Run 입력란의 날짜 숫자만 수정 후 재실행
⑦ 날짜를 바꿀 때는 Run 입력란의 날짜 숫자만 수정 후 재실행
· · · · ·
2) 날짜별 Configuration을 여러 개 저장하는 방법
과거 날짜 재처리 검증처럼 날짜를 바꿔가며 반복 테스트할 때는 Configuration을 여러 개 저장해 두면 편리합니다.
| Configuration 이름 | Run 입력값 (--args 부분) | 용도 |
|---|---|---|
| bootRun-0228 | bootRun --args='--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227' | 2/28 배치 테스트 |
| bootRun-0301 | bootRun --args='--job_name=job_TranRconcl collectYmd=20260301 compYmd=20260228' | 3/1 배치 테스트 |
| bootRun-reprocess | bootRun --args='--job_name=job_TranRconcl collectYmd=20260101 compYmd=20251231' | 특정 과거 날짜 재처리 |
#3. --args 파라미터 형식과 규칙
1) --args 파라미터 작성 형식
bootRun --args 안에 들어가는 파라미터는 Spring Boot가 실행될 때 main 메서드의 args 배열로 전달됩니다. 파라미터 종류에 따라 앞에 --를 붙이는 규칙이 다릅니다.
| 파라미터 종류 | 형식 | 예시 |
|---|---|---|
| Spring 옵션 인수 (Spring properties) | --key=value | --job_name=job_TranRconcl |
| 비옵션 인수 (Non-option args) | key=value (-- 없음) | collectYmd=20260228 |
| Spring 환경 설정 | --spring.profiles.active=... | --spring.profiles.active=local |
· · · · ·
2) 실제 동작하는 --args 형식 예시
사진에서 확인된 실제 동작 형식입니다. --args 내부를 작은따옴표(' ')로 감싸고, 여러 파라미터는 공백으로 구분합니다.
// 기본 형식 (실제 동작 확인)
bootRun --args='--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227'
// 로컬 프로파일 추가 시
bootRun --args='--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227 --spring.profiles.active=local'
// 터미널에서 직접 실행할 경우 (Windows는 작은따옴표 대신 큰따옴표)
./gradlew bootRun --args="--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227"
bootRun --args='--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227'
// 로컬 프로파일 추가 시
bootRun --args='--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227 --spring.profiles.active=local'
// 터미널에서 직접 실행할 경우 (Windows는 작은따옴표 대신 큰따옴표)
./gradlew bootRun --args="--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227"
· · · · ·
3) build.gradle 별도 설정 없이 동작하는 이유
--args는 Gradle Spring Boot 플러그인이 기본 제공하는 옵션입니다. build.gradle에 별도 bootRun 블록을 추가하지 않아도 IntelliJ Run Configuration의 Run 입력란에서 바로 사용할 수 있습니다.
단, Spring Boot Gradle 플러그인 2.2.0 이상에서만 --args 옵션이 지원됩니다. 그 이하 버전에서는 build.gradle에서 args를 직접 설정해야 합니다.
단, Spring Boot Gradle 플러그인 2.2.0 이상에서만 --args 옵션이 지원됩니다. 그 이하 버전에서는 build.gradle에서 args를 직접 설정해야 합니다.
#4. Spring Boot 배치 코드에서 파라미터 수신
1) ApplicationRunner에서 파라미터 수신 및 Job 실행
--args로 전달된 파라미터는 ApplicationArguments를 통해 수신합니다. --key=value 형식은 getOptionValues()로, key=value 형식(비옵션 인수)은 getNonOptionArgs()로 꺼냅니다.
@Component
@RequiredArgsConstructor
public class BatchJobRunner implements ApplicationRunner {
private final JobLauncher jobLauncher;
private final Job sampleJob;
@Override
public void run(ApplicationArguments args) throws Exception {
// 비옵션 인수(key=value) 파싱: collectYmd=20260228 compYmd=20260227
Map<String, String> params = new HashMap<>();
for (String arg : args.getNonOptionArgs()) {
String[] kv = arg.split("=", 2);
if (kv.length == 2) params.put(kv[0], kv[1]);
}
String collectYmd = params.getOrDefault("collectYmd",
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
String compYmd = params.getOrDefault("compYmd",
LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyyMMdd")));
JobParameters jobParams = new JobParametersBuilder()
.addString("collectYmd", collectYmd)
.addString("compYmd", compYmd)
// 로컬 재실행 허용을 위해 run.id 추가
.addLong("run.id", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(sampleJob, jobParams);
}
}
@RequiredArgsConstructor
public class BatchJobRunner implements ApplicationRunner {
private final JobLauncher jobLauncher;
private final Job sampleJob;
@Override
public void run(ApplicationArguments args) throws Exception {
// 비옵션 인수(key=value) 파싱: collectYmd=20260228 compYmd=20260227
Map<String, String> params = new HashMap<>();
for (String arg : args.getNonOptionArgs()) {
String[] kv = arg.split("=", 2);
if (kv.length == 2) params.put(kv[0], kv[1]);
}
String collectYmd = params.getOrDefault("collectYmd",
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
String compYmd = params.getOrDefault("compYmd",
LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyyMMdd")));
JobParameters jobParams = new JobParametersBuilder()
.addString("collectYmd", collectYmd)
.addString("compYmd", compYmd)
// 로컬 재실행 허용을 위해 run.id 추가
.addLong("run.id", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(sampleJob, jobParams);
}
}
· · · · ·
2) Step에서 날짜 파라미터 사용
Step 내에서는 반드시 @StepScope를 선언한 뒤 @Value("#{jobParameters['collectYmd']}")로 날짜를 주입받습니다.
@StepScope
@Bean
public Tasklet reconcileTasklet(
@Value("#{jobParameters['collectYmd']}") String collectYmd,
@Value("#{jobParameters['compYmd']}") String compYmd) {
return (contribution, chunkContext) -> {
System.out.println("수집 기준일: " + collectYmd);
System.out.println("비교 기준일: " + compYmd);
return RepeatStatus.FINISHED;
};
}
@Bean
public Tasklet reconcileTasklet(
@Value("#{jobParameters['collectYmd']}") String collectYmd,
@Value("#{jobParameters['compYmd']}") String compYmd) {
return (contribution, chunkContext) -> {
System.out.println("수집 기준일: " + collectYmd);
System.out.println("비교 기준일: " + compYmd);
return RepeatStatus.FINISHED;
};
}
#5. 자주 발생하는 오류와 해결 방법
1) 오류 유형별 원인과 해결
| 오류 / 증상 | 원인 | 해결 방법 |
|---|---|---|
| 파라미터 값이 null로 수신됨 | key=value 형식을 getOptionValues()로 받으려 함 | 비옵션 인수는 getNonOptionArgs()로 파싱 |
| JobInstanceAlreadyCompleteException | 동일 JobParameters로 완료된 Job 재실행 시도 | JobParametersBuilder에 run.id(currentTimeMillis) 추가 |
| Unrecognized option: --args | Spring Boot Gradle 플러그인 2.2.0 미만 | 플러그인 버전 업그레이드 또는 build.gradle args 방식으로 변경 |
| 파라미터가 잘못 분리됨 | --args 내부 공백 처리 오류 | --args='...' 작은따옴표로 전체를 감싸고 내부는 공백으로만 구분 |
| @StepScope 빈 생성 오류 | @StepScope 누락 또는 @Bean 메서드에서 직접 호출 | @StepScope + @Bean 함께 선언, 직접 호출하지 않고 Spring이 주입하도록 설정 |
· · · · ·
2) --args 형식별 수신 방법 정리
--job_name=...처럼 앞에 --가 붙으면 Spring이 옵션 인수로 처리하고, collectYmd=...처럼 --가 없으면 비옵션 인수로 처리합니다. 수신 방법이 다르므로 주의가 필요합니다.
// --args='--job_name=xxx collectYmd=20260228 compYmd=20260227' 기준
// 1) 옵션 인수 수신 (앞에 -- 있음)
String jobName = args.getOptionValues("job_name").get(0);
// → "job_TranRconcl"
// 2) 비옵션 인수 수신 (앞에 -- 없음: key=value)
List<String> nonOptions = args.getNonOptionArgs();
// → ["collectYmd=20260228", "compYmd=20260227"]
// split("=", 2)으로 key/value 분리하여 사용
// 1) 옵션 인수 수신 (앞에 -- 있음)
String jobName = args.getOptionValues("job_name").get(0);
// → "job_TranRconcl"
// 2) 비옵션 인수 수신 (앞에 -- 없음: key=value)
List<String> nonOptions = args.getNonOptionArgs();
// → ["collectYmd=20260228", "compYmd=20260227"]
// split("=", 2)으로 key/value 분리하여 사용
마무리
Airflow 없이 IntelliJ에서 Spring Boot 배치를 로컬 테스트할 때 가장 간단하고 확실한 방법은 Gradle Run Configuration의 Run 입력란에 아래 형식을 직접 입력하는 것입니다.
bootRun --args='--job_name=job_TranRconcl collectYmd=20260228 compYmd=20260227'
--job_name처럼 -- 접두사가 붙은 옵션 인수는 getOptionValues()로, collectYmd처럼 -- 없는 비옵션 인수는 getNonOptionArgs()를 파싱하여 수신합니다. 날짜별로 Run Configuration을 저장해 두면 과거 날짜 재처리 시나리오도 클릭 한 번으로 빠르게 검증할 수 있습니다.
긴 글 읽어주셔서 감사합니다.
끝.

끝.

반응형
'Dev & Tech > Web' 카테고리의 다른 글
| [Web] Apache Airflow DAG 사용법 및 서버 연동 방법 (0) | 2025.10.31 |
|---|---|
| [Web] Apache Airflow 워크플로우 자동화 방법 (0) | 2025.10.30 |
| [Web] Apache Kafka 완벽 가이드 (0) | 2025.10.24 |
| [Web] Spring Boot 3.4 + AWS 환경 Tomcat vs JBoss WildFly 성능 비교 분석 (3) | 2025.04.07 |
| [Web] JEUS DB Connection Leak 완벽 해결 방법 (0) | 2024.07.24 |