Spring Batch 실습 1 : 환경 설정

Spring Batch Lab
정찬's avatar
Jun 08, 2025
Spring Batch 실습 1 : 환경 설정

개요

 
4번의 포스팅에 걸쳐 Spring Batch 공식문서를 읽으며 공부해보았다.
이번 시간부터는 실제로 배치 시스템을 구성하고, 모니터링까지 할 계획이다.
 

🧩 실습 목표

 
  • Spring Batch 환경을 구축하고 Quartz로 스케줄링 가능하도록 설정
  • 배치 Job을 실행하고 Prometheus + Grafana로 성능을 모니터링
  • 실제 데이터와 유사한 시나리오 기반으로 대량 데이터를 처리하며 실습
 

🛠 환경 구성

 
환경은 배치 시스템과, 모니터링 시스템으로 구성되어 있다.

사용 기술 스택

구성 요소
기술
언어/프레임워크
Java 17, Spring Boot 3.5
배치
Spring Batch
스케줄러
Spring Boot + Quartz
모니터링
Micrometer, Prometheus, Grafana, PushGateway
데이터베이스
MySQL
인프라
Docker, Docker Compose (Prometheus & Grafana)
빌드 도구
Gradle
 
배치와 모니터링을 위한 기술스택이 몇가지 추가되었는데, 어떤 역할을 하는지 알아보자.

Quartz?

스케줄러는 그전까지 선언적 스케줄러를 많이 사용했었다. 하지만 이번 실습에서는 Quartz를 공부하고 도입하기로 결정하였다. 그 이유는 선언적 스케줄러(@Scheduled)보다 더욱 유연하고 강력한 스케줄링 기능을 제공하기 때문이다.
Spring에서 제공하는 @Scheduled는 설정이 간단하고 가벼운 작업에는 적합하지만, 다음과 같은 한계점이 있다:
  • 애플리케이션 실행 중 동적으로 스케줄을 등록하거나 수정하는 기능이 제한적이다.
  • 작업별 상태 관리(예: 실행 이력, 실패 상태 등)를 체계적으로 할 수 없다.
  • 분산 환경이나 클러스터 환경에서의 스케줄 관리가 어렵다.
  • 트리거 기반 스케줄링(Cron 외 이벤트 기반 등)이 제한된다.
 
반면 Quartz는 다음과 같은 장점을 통해 Spring Batch의 배치 실행 요구사항을 더 잘 만족시킨다:
  1. 정교한 트리거 조건 설정
    1. 단순한 Cron 표현식뿐 아니라 시작/종료 시간, 반복 간격, 반복 횟수, 캘린더 예외 처리 등 복잡한 스케줄 요구사항을 만족할 수 있다.
  1. 실행 상태 및 이력 관리
    1. Job의 실행 상태(대기, 실행 중, 완료, 실패 등)를 관리하고, DB에 저장하여 실행 이력을 조회하거나 실패 시 재시도 정책을 쉽게 구현할 수 있다.
  1. 동적 스케줄링 및 제어 가능
    1. 애플리케이션 실행 중에도 Job 추가, 수정, 삭제가 가능하며, REST API나 관리 UI를 통해 실시간으로 제어할 수 있다.
  1. 분산 및 클러스터링 지원
    1. Quartz는 클러스터 모드를 통해 여러 인스턴스 간 Job 중복 실행 없이 스케줄을 공유하고 분산 처리할 수 있어, 대규모 시스템에 적합하다.
  1. Spring Batch와의 높은 호환성
    1. Quartz Job 내에서 Spring Batch Job을 호출하는 구조를 쉽게 구성할 수 있어, 스케줄과 배치를 명확히 분리하면서 유연한 실행 제어가 가능하다.
       
따라서 단순 주기 실행이 아닌 유지보수성과 확장성, 운영 통제력이 중요한 배치 환경에서는 Quartz가 Spring Batch와 맞다고 판단하여 많이 사용되고 있다. 따라서 이번 실습에서도 Quartz를 사용할 계획이다.
 

Micrometer

 
MicrometerJava 애플리케이션의 메트릭(지표)을 수집하고 외부 모니터링 시스템에 전송할 수 있게 해주는 라이브러리이다. 특히 Spring Boot와 매우 잘 통합되며, Prometheus, Grafana, Datadog, New Relic 같은 다양한 모니터링 툴과 연동할 수 있다.
 
로깅을 추상화한 라이브러리인 Slf4J와 비슷하다. Micrometer는 모니터링을 추상화한 라이브러리라고 생각하면 된다.
 

Prometheus

 
Prometheus는 시계열 데이터(time-series data)를 수집하고 쿼리할 수 있도록 설계된 오픈소스 모니터링 시스템이다. 주로 서버, 애플리케이션, 컨테이너 등의 메트릭을 수집하고, 경고(알림) 및 대시보드와 함께 사용할 수 있다.
 
모니터링 시스템은 크게 Pull과 Push 방식으로 나뉜다. Prometheus는 Pull 기반 메트릭 수집 방법으로 각 서비스의 엔드포인트에 주기적으로 요청해서 데이터를 가져온다.
 

Pushgateway

 
Pushgateway는 Prometheus 생태계에서 Push 방식으로 메트릭을 전달할 수 있게 해주는 중간 브릿지 역할을 한다. Prometheus는 기본적으로 Pull 방식(Prometheus가 주기적으로 데이터를 가져감)을 사용하지만, Push가 필요한 상황에서는 Pushgateway를 사용한다.
 
Batch Job은 기본적으로 Prometheus가 직접 수집할 수 없는 짧게 실행되는 특징을 가지고 있다.
따라서 Pushgateway를 사용해서 메트릭을 수집해야 한다.
 

Grafana

 
Grafana메트릭, 로그, 트레이싱 데이터를 시각화하고 모니터링 대시보드를 만들 수 있는 오픈소스 도구이다. Prometheus, Loki, Tempo, Elasticsearch, InfluxDB 등 다양한 데이터 소스를 연결할 수 있다.
 
 

프로젝트 기본 설정

build.gradle

dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-batch' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-quartz' // prometheus 관련 라이브러리 추가 implementation 'io.micrometer:micrometer-registry-prometheus-simpleclient' implementation 'io.prometheus:simpleclient_pushgateway' //Lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' }
 
implementation 'io.micrometer:micrometer-registry-prometheus-simpleclient'
Pull 방식으로 메트릭을 수집하는 상황에는 micrometer-registry-prometheus를 사용하지만, Push방식으로 메트릭을 수집하기 경우에는 simpleclient를 사용해야 한다. 또한 micrometer만 사용해서 push방식이 안되기 때문에, io.micrometer:micrometer-registry-prometheus-simpleclient 종속성을 사용한다. micrometer는 기본적으로 push방식을 지원하지 않는다.
 
 

application.yml

spring: datasource: url: jdbc:mysql://localhost:3306/{mydb} username: {username} password: {password} driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true batch: initialize-schema: always quartz: job-store-type: memory properties: org.quartz.scheduler.instanceName: QuartzScheduler org.quartz.threadPool.threadCount: 5 management: endpoints: web: exposure: include: prometheus
 

Docker

 
docker-compose.yml
 
services: pushgateway: image: prom/pushgateway container_name: 'pushgateway' ports: - '9091:9091' prometheus: image: prom/prometheus container_name: 'prometheus' ports: - '9090:9090' volumes: - ./docker-prometheus.yml:/etc/prometheus/prometheus.yml grafana: image: grafana/grafana container_name: 'grafana' user: "$UID:$GID" ports: - '3000:3000' volumes: - ./grafana-data:/var/lib/grafana depends_on: - prometheus
 
pushgateway, prometheus, grafana를 컨테이너화 해준다.
 
docker-prometheus.yml
 
global: scrape_interval: 5s evaluation_interval: 5s scrape_configs: - job_name: 'batch_server_scrape' honor_labels: true static_configs: - targets: ['host.docker.internal:9091'] # pushgateway
 
  • scrape_interval: Prometheus가 각 타겟에서 메트릭을 얼마나 자주 수집할지 결정
    • 여기선 5초마다 수집
  • evaluation_interval: 규칙(rule)이 있는 경우, 이를 얼마나 자주 평가할지 결정
    • 이 설정은 alerting/recording rules가 있을 때 사용됨
 
scrape_configs 섹션은 Prometheus가 어디서 메트릭을 가져올지 정의하는 곳이다.
  • job_name: 'batch_server_scrape'
    • 이 job은 Prometheus 내부에서 구분되는 논리적 이름이다.
    • 대시보드나 알람 설정 시 이 job 이름을 기준으로 메트릭을 필터링할 수 있다.
  • honor_labels: true
    • PushGateway가 보낸 원래 메트릭 레이블(label) 을 그대로 유지한다.
    • false인 경우, Prometheus가 자체적으로 덮어쓸 수도 있다.
  • static_configs.targets: ['host.docker.internal:9091']
    • Prometheus가 메트릭을 수집할 대상(target)의 주소
    • 여기선 PushGateway가 실행 중인 주소와 포트 (9091) 이다.
    • host.docker.internalDocker 컨테이너 내부에서 호스트 머신의 IP를 의미한다.
      • 실습 환경에서 Prometheus가 Docker 컨테이너에서 실행되고 있기 때문에, 컨테이너 내부에서 호스트 머신(즉, PushGateway가 실행 중인 로컬 환경)의 9091 포트에 접근하기 위해 host.docker.internal 주소를 사용해야 한다.
      •  

📘 시나리오

 
실습에서는 총 두가지 성격의 시나리오를 구성해보았다.
  • 기본적인 배치 시나리오 (기본적인 Spring Batch 구성 실습)
  • 배치 시스템을 구성할 때 주의할 점을 학습하기 위한 용도의 시나리오 (예외 처리, retry 등의 상황 가정)
 
실제 배치 시스템이 작동할 때 주의할점을 알아보기 위한 시나리오들을 GPT에게 추천받아 채택하였다.

 
먼저 기본적인 시나리오이다.
이 시나리오의 구현 목적은 기본적인 배치 시스템을 작성하기 위함이다.

1. 사용자 행동 로그 통계 집계

  • 목적: 유저의 일간 행동 요약 통계를 생성
  • Input: user_logs
  • Output: user_log_stats
  • 학습 포인트: Reader/Processor/Writer 기본 구성, Date 기준 필터링

2. 상품 주문 정보 → 월간 매출 요약 테이블 생성

  • 목적: 월별 상품 판매 집계
  • Input: orders, order_items
  • Output: monthly_sales_summary
  • 학습 포인트: Join 처리, 월 단위 데이터 필터링, JobParameter 사용
 
이번엔 Edge Case들을 가정한 시나리오이다.
 

3. 예외 발생 시 롤백/스킵 정책 실습

  • 목적: 데이터 처리 중 일부 예외 상황을 유도하고 복구 전략 적용
  • 상황 예시: 잘못된 날짜 포맷, NULL 값 등
  • 학습 포인트:
    • skipPolicy, retryPolicy
    • rollbackFor, noRollbackFor
    • Step ExecutionContext 저장 전략

4. 잡 중단 후 재시작 시 재처리 방지

  • 목적: Job 도중 실패 후 재시작 시 중복 처리 없이 이어지도록 구현
  • 조건: JobParameters + JobExecution 상태 관리
  • 학습 포인트:
    • JobRepository 활용
    • @JobScope / @StepScope 사용
    • ID 기반 커서 저장 방식

5. Step 병렬 처리에 따른 문제 확인

  • 목적: 다중 Step 병렬 실행 시 공유 자원 충돌, DB 락 상황 재현
  • 학습 포인트:
    • taskExecutor, throttleLimit
    • 동시성 이슈 & JobExecutionListener 활용

6. Quartz와 JobLauncher 충돌 시나리오

  • 목적: Quartz에서 동일 Job을 동시에 두 번 실행할 경우 문제 재현
  • 학습 포인트:
    • preventRestart = true 설정
    • JobExecutionAlreadyRunningException 다루기
 

마무리

 
지금까지 환경설정을 해보았다. 아직 배치 시스템을 작성하지 않아서, 모니터링이 불가하다.
다음에는 배치 시나리오1번을 설계해보고 모니터링하는 포스팅으로 돌아오겠다.
 
Share article

lushlife99