Theme:

매일 밤 수백만 건의 데이터를 집계해야 한다면, 이걸 API 하나로 처리할 수 있을까요?

실시간 API는 요청-응답 구조에 최적화되어 있습니다. 하지만 대량 데이터를 읽고, 가공하고, 저장하는 작업은 전혀 다른 패러다임이 필요합니다. Spring Batch는 바로 이런 대량 데이터 처리를 위해 설계된 배치 프레임워크입니다.

Spring Batch란

Spring Batch는 엔터프라이즈 환경에서 대량 데이터를 안정적으로 처리하기 위한 경량 배치 프레임워크입니다. 로깅, 트랜잭션 관리, 작업 재시작, 스킵, 리소스 관리 등 배치 처리에 필요한 핵심 기능을 제공합니다.

핵심 특징을 정리하면 이렇습니다.

  • **청크 기반 처리 **: 대량 데이터를 작은 단위(chunk)로 나눠 트랜잭션 안에서 처리
  • ** 재시작 가능 **: 실패한 지점부터 다시 시작할 수 있음
  • ** 메타데이터 관리 **: 모든 실행 이력을 데이터베이스에 기록
  • ** 확장성 **: 멀티 스레드, 파티셔닝, 원격 청킹 지원

왜 필요한가

일반적인 서비스 로직으로 대량 데이터를 처리하면 여러 문제가 생깁니다.

  • 메모리 부족: 수백만 건을 한 번에 조회하면 OOM 발생
  • 트랜잭션 관리: 중간에 실패하면 어디서부터 다시 해야 할지 알 수 없음
  • 모니터링: 어디까지 처리됐는지 추적이 어려움
  • 재처리: 실패한 건만 다시 처리하는 로직을 직접 구현해야 함

Spring Batch는 이런 문제를 프레임워크 레벨에서 해결해줍니다.

핵심 아키텍처

Spring Batch의 아키텍처는 크게 세 가지 계층으로 나뉩니다.

Job — 배치 작업의 최상위 단위

Job은 하나의 배치 작업 전체를 나타냅니다. 하나 이상의 Step으로 구성되며, 실행 순서와 흐름을 정의합니다.

JAVA
@Configuration
public class MyJobConfig {

    @Bean
    public Job myJob(JobRepository jobRepository, Step step1, Step step2) {
        return new JobBuilder("myJob", jobRepository)
                .start(step1)
                .next(step2)
                .build();
    }
}

Step — 작업의 실행 단위

Step은 Job을 구성하는 독립적인 처리 단위입니다. 각 Step은 자체 트랜잭션을 갖고, 독립적으로 성공/실패 상태를 가집니다.

Step은 두 가지 방식으로 구현할 수 있습니다.

  • **Chunk 기반 **: 읽기 → 가공 → 쓰기를 반복 (대량 데이터 처리에 적합)
  • **Tasklet 기반 **: 단일 작업을 한 번에 수행 (파일 삭제, 초기화 등)

JobLauncher — 실행기

JobLauncher는 Job을 실행하는 인터페이스입니다. Job과 JobParameters를 받아 실행하고, JobExecution을 반환합니다.

JAVA
@Component
public class BatchRunner {

    private final JobLauncher jobLauncher;
    private final Job myJob;

    // 생성자 주입 생략

    public void run() throws Exception {
        JobParameters params = new JobParametersBuilder()
                .addString("date", "2026-03-19")
                .toJobParameters();

        JobExecution execution = jobLauncher.run(myJob, params);
        System.out.println("배치 상태: " + execution.getStatus());
    }
}

JobRepository와 메타데이터

JobRepository는 Spring Batch의 핵심 인프라입니다. 모든 배치 실행 정보를 데이터베이스에 저장합니다.

메타데이터 테이블 구조

테이블역할
BATCH_JOB_INSTANCEJob 이름 + JobParameters 조합으로 식별되는 논리적 작업 단위
BATCH_JOB_EXECUTIONJobInstance의 각 실행 시도 (상태, 시작/종료 시간)
BATCH_JOB_EXECUTION_PARAMS실행 시 전달된 파라미터
BATCH_STEP_EXECUTIONStep별 실행 정보 (읽기/쓰기/스킵 횟수 등)
BATCH_JOB_EXECUTION_CONTEXTJob 레벨 ExecutionContext
BATCH_STEP_EXECUTION_CONTEXTStep 레벨 ExecutionContext

JobInstance와 JobExecution의 관계

이 관계를 이해하는 것이 중요합니다.

  • JobInstance: Job 이름 + JobParameters로 식별되는 논리적 인스턴스
  • JobExecution: JobInstance의 실제 실행 시도

같은 JobParameters로 Job을 실행하면 같은 JobInstance를 참조합니다. 이전 실행이 COMPLETED면 다시 실행되지 않고, FAILED면 새로운 JobExecution이 생성되어 재시작됩니다.

PLAINTEXT
Job("월간정산") + Params("2026-03")
  └─ JobInstance (id=1)
       ├─ JobExecution (id=1) → FAILED  (첫 번째 시도)
       └─ JobExecution (id=2) → COMPLETED (재시작)

Spring Boot에서 설정하기

Spring Boot 3.x에서는 @EnableBatchProcessing을 명시적으로 사용하지 않아도 자동 구성됩니다.

JAVA
// application.yml
spring:
  batch:
    job:
      enabled: false  // 애플리케이션 시작 시 자동 실행 방지
    jdbc:
      initialize-schema: always  // 메타데이터 테이블 자동 생성
  datasource:
    url: jdbc:mysql://localhost:3306/batch_db
    username: root
    password: password

job.enabled: false로 설정하면 원하는 시점에 JobLauncher로 직접 실행할 수 있습니다. 스케줄러나 REST API를 통해 트리거하는 것이 일반적입니다.

JAVA
@RestController
public class BatchController {

    private final JobLauncher jobLauncher;
    private final Job settlementJob;

    @PostMapping("/batch/settlement")
    public String runSettlement(@RequestParam String date) throws Exception {
        JobParameters params = new JobParametersBuilder()
                .addString("targetDate", date)
                .addLong("timestamp", System.currentTimeMillis()) // 고유성 보장
                .toJobParameters();

        jobLauncher.run(settlementJob, params);
        return "배치 시작됨";
    }
}

실행 흐름 정리

전체 실행 흐름을 한눈에 보면 이렇습니다.

  1. JobLauncher 가 Job + JobParameters를 받아 실행
  2. JobRepository 에서 기존 JobInstance 확인 (동일 파라미터 여부)
  3. JobExecution 생성 → 상태를 STARTED로 기록
  4. Job 내부의 Step 들이 순서대로 실행
  5. 각 Step 실행 결과가 StepExecution 으로 기록
  6. 모든 Step 완료 후 JobExecution 상태를 COMPLETED/FAILED로 업데이트

실무에서의 활용

Spring Batch가 실무에서 주로 쓰이는 시나리오입니다.

  • **일일/월간 정산 **: 거래 데이터 집계 → 정산 데이터 생성
  • ** 데이터 마이그레이션 **: 레거시 DB → 신규 DB로 대량 데이터 이관
  • ** 리포트 생성 **: 대량 데이터 조회 → CSV/Excel 파일 생성
  • ** 알림 발송 **: 조건에 맞는 사용자 조회 → 이메일/SMS 발송
  • ** 데이터 정리 **: 오래된 로그, 만료된 토큰 삭제

주의할 점

1. 동일한 JobParameters로 완료된 Job을 다시 실행하면 실행되지 않는다

Spring Batch는 Job 이름 + JobParameters 조합으로 JobInstance를 식별합니다. 이미 COMPLETED된 동일 조합은 재실행을 거부합니다. 매번 실행해야 하는 Job이라면 JobParameters에 timestamp 같은 고유값을 포함하여 매 실행마다 새 JobInstance가 생성되도록 해야 합니다.

2. spring.batch.job.enabled=true(기본값)로 두면 애플리케이션 시작 시 모든 Job이 자동 실행된다

Spring Boot의 기본 설정은 애플리케이션이 시작되면 등록된 모든 Job을 바로 실행합니다. 스케줄러나 REST API로 원하는 시점에 실행하려면 반드시 spring.batch.job.enabled=false로 설정해야 합니다. 이를 모르면 배포할 때마다 배치가 의도치 않게 실행됩니다.

3. 메타데이터 DB를 분리하지 않으면 배치 테이블이 운영 DB에 섞인다

Spring Batch는 BATCH_JOB_INSTANCE, BATCH_STEP_EXECUTION 등 6개의 메타데이터 테이블을 생성합니다. 별도 DataSource를 지정하지 않으면 운영 DB에 이 테이블들이 만들어지고, 배치 실행 이력이 운영 데이터와 같은 DB에 쌓입니다. 메타데이터 전용 DataSource를 분리하는 것을 권장합니다.

정리

  • Spring Batch는 Job → Step → (Chunk/Tasklet) 구조로 대량 데이터를 안정적으로 처리합니다
  • JobRepository 가 모든 실행 메타데이터를 관리하므로 재시작, 모니터링이 가능합니다
  • JobInstance 는 Job + Parameters로 식별되고, JobExecution 은 실제 실행 시도를 나타냅니다
  • Spring Boot 3.x에서는 자동 구성으로 간편하게 시작할 수 있습니다
댓글 로딩 중...