Spring Batch 입문 7편 — JobRepository (영속화 · Schema · Isolation)

2026-05-17Spring Batch 입문에서 운영까지

Spring Batch 입문 7편. JobRepository 깊이 — 영속화 메커니즘, 3가지 구현체 (JDBC·MongoDB·Resourceless), schema·table prefix·isolation level·serializer·custom 등 운영 핵심 영역까지 풀어쓴 학습 노트.

📚 Spring Batch 입문에서 운영까지 · 7편 — JobRepository (영속화 · Schema · Isolation)

이 글은 Spring Batch 입문에서 운영까지 시리즈 48편 중 7편이에요. 5·6편 으로 infrastructure·Job 설정 을 잡았다면, 이번 7편은 모든 영속화의 중심JobRepository.

JobRepository의 역할

JobRepository = 모든 Job·Step·ExecutionContext (Job 실행 중 공유되는 key-value 저장소) 의 실행 상태 영속 저장소.

Job 시작
   ↓
JobInstance·JobExecution·StepExecution·ExecutionContext 생성
   ↓
JobRepository 에 영속 저장 (DB)
   ↓
Job 진행 중 = 매 chunk·step 마다 update
   ↓
Job 종료 = 최종 상태 저장
   ↓
다음 실행·재시작 = JobRepository 에서 read

재시작 보장의 핵심. JobRepository 없으면 Spring Batch 가 기억 없는 실행기 가 됩니다.

3가지 구현체

1. JDBC (운영 표준)

@EnableBatchProcessing
@EnableJdbcJobRepository

RDBMS (PostgreSQL·MySQL·Oracle 등) 에 영속. 모든 운영 환경에서 검증됐고, 직접 SQL 조회가 가능해서 디버깅·모니터링이 편하며, ACID 가 보장되고, 대부분 회사가 이미 운영 중인 DB 를 그대로 쓸 수 있다는 게 장점이에요.

2. MongoDB (v6 신규)

@EnableBatchProcessing
@EnableMongoJobRepository

MongoDB 에 영속. RDBMS 안 쓰는 환경 에서 씁니다.

@Configuration
public class MongoBatchConfig extends MongoDefaultBatchConfiguration {
    @Bean
    public MongoTemplate mongoTemplate(MongoClient client) {
        return new MongoTemplate(client, "batch");
    }
}

3. Resourceless (학습·테스트)

@EnableBatchProcessing

(기본) — 메모리 only. DB 없이 동작.

ResourcelessJobRepository
ResourcelessTransactionManager

운영 환경 절대 X — 재시작 불가, 이력도 사라집니다.

Schema 종합

Spring Batch 가 사용하는 9개 table:

BATCH_JOB_INSTANCE                -- JobInstance (논리적 1회)
BATCH_JOB_EXECUTION               -- JobExecution (물리적 1회)
BATCH_JOB_EXECUTION_PARAMS        -- JobParameters
BATCH_JOB_EXECUTION_CONTEXT       -- Job 의 ExecutionContext
BATCH_STEP_EXECUTION              -- StepExecution
BATCH_STEP_EXECUTION_CONTEXT      -- Step 의 ExecutionContext
BATCH_JOB_SEQ                     -- Job ID sequence
BATCH_JOB_EXECUTION_SEQ           -- JobExecution ID sequence
BATCH_STEP_EXECUTION_SEQ          -- StepExecution ID sequence

자세한 schema = 47편 Meta-Data Schema.

Schema 자동 생성

spring:
  batch:
    jdbc:
      initialize-schema: always       # 매번 (개발만)
      # initialize-schema: never       # 운영 권장
      # initialize-schema: embedded    # H2 같은 embedded 만

운영 환경 = never + DBA (Database Administrator, DB 운영자) 가 수동 적용.

Schema 수동 적용

# schema 파일 위치 (jar 안)
spring-batch-core/org/springframework/batch/core/schema-postgresql.sql
spring-batch-core/org/springframework/batch/core/schema-mysql.sql
spring-batch-core/org/springframework/batch/core/schema-oracle.sql

DBA 에게 이 파일을 운영 DB 에 적용 요청. 또는:

$ jar -xf spring-batch-core-*.jar org/springframework/batch/core/schema-postgresql.sql
$ psql -h db.example.com -U batch_user -d batch_db -f schema-postgresql.sql

JobRepository Bean Override

DefaultBatchConfiguration 상속해 customize:

@Configuration
public class CustomJobRepoConfig extends JdbcDefaultBatchConfiguration {

    @Override
    protected String getTablePrefix() {
        return "MY_BATCH_";
    }

    @Override
    protected Isolation getIsolationLevelForCreate() {
        return Isolation.SERIALIZABLE;     // 기본 = SERIALIZABLE
    }

    @Override
    protected Charset getCharset() {
        return StandardCharsets.UTF_8;
    }

    @Override
    protected ExecutionContextSerializer getExecutionContextSerializer() {
        return new MyCustomSerializer();
    }

    @Override
    protected int getClobTypeToUse() {
        return Types.CLOB;
    }

    @Override
    protected int getMaxVarCharLength() {
        return DEFAULT_MAX_VARCHAR_LENGTH;  // 2500
    }
}

Isolation Level — ISOLATION_SERIALIZABLE

기본 = SERIALIZABLE (가장 엄격).

이유 — 같은 JobInstance 의 동시 실행 차단 (3편). SERIALIZABLE 격리 수준이 동시 createJobExecution 충돌을 DB 레벨에서 차단.

운영 환경에서 가끔 deadlock·성능 이슈 가 나오면:

@Override
protected Isolation getIsolationLevelForCreate() {
    return Isolation.READ_COMMITTED;     // 완화 (애플리케이션이 별도 보장)
}

기본값 유지 권장. 변경 시 애플리케이션 측 동시 실행 차단 별도 구현 필요.

ExecutionContext Serializer

ExecutionContext = key-value 가 DB 의 BLOB (Binary Large Object, 대용량 이진 데이터) 또는 CLOB (Character Large Object, 대용량 문자 데이터) 에 영속. 직렬화 방식:

Default (Jackson)

new Jackson2ExecutionContextStringSerializer()

기본. JSON 직렬화. 사람이 읽을 수 있음 (디버깅 편함).

Java Serialization (옛)

new DefaultExecutionContextSerializer()

Java 객체 직렬화. 호환성 위험에 사람이 못 읽음. 거의 안 씁니다.

Custom

public class MyEncryptedSerializer implements ExecutionContextSerializer {
    @Override
    public void serialize(Map<String, Object> object, OutputStream out) {
        // 암호화 + 직렬화
    }

    @Override
    public Map<String, Object> deserialize(InputStream in) {
        // 복호화 + 역직렬화
    }
}

민감 정보 (예: API key) 가 ExecutionContext 에 들어가는 환경 = 암호화 serializer.

Table Prefix — 다중 Batch

5편에서 본 내용 다시:

spring:
  batch:
    jdbc:
      table-prefix: BATCH_A_     # 기본 BATCH_

또는 코드:

@Override
protected String getTablePrefix() {
    return "MY_BATCH_";
}

같은 DB 에 여러 batch application 공유 시 충돌 방지.

DataSource 와 TransactionManager

JobRepository 가 어느 DB 에 영속할지 = DataSource. 그 위 transaction = PlatformTransactionManager.

기본 = Spring Boot 가 autowire:

@Bean
public DataSource dataSource() { ... }

@Bean
public PlatformTransactionManager transactionManager(DataSource ds) {
    return new JdbcTransactionManager(ds);
}

5편에서 본 Multi-DataSource = batch 메타데이터와 business 데이터 분리.

여기서 시험 함정이 하나 있어요 — JobRepository 의 TransactionManager 와 Step 의 TransactionManager 가 다를 수 있음.

@Bean
public Step myStep(JobRepository repo, PlatformTransactionManager businessTx) {
    return new StepBuilder("myStep", repo)
        .<X, Y>chunk(100, businessTx)     // ← business DB 의 TM
        .reader(...).writer(...)
        .build();
}

JobRepository 의 TM 은 batch metadata DB 의 tx 를 담당하고, Step 의 TM 은 business 작업 (read/write) 의 tx 를 담당합니다. 같은 DB 면 같은 TM 으로 가도 되지만, 다른 DB 면 분리 가 맞아요. 12편 commit-interval 에서 더 깊이 들어갑니다.

JobExplorer — Read-only 조회

@Autowired
private JobExplorer jobExplorer;

// 최근 JobInstance N 개
List<JobInstance> instances = jobExplorer.findJobInstancesByJobName("myJob", 0, 10);

// 특정 instance 의 모든 execution
List<JobExecution> executions = jobExplorer.getJobExecutions(instances.get(0));

// 특정 execution 의 step 들
JobExecution execution = jobExplorer.getJobExecution(executionId);
for (StepExecution step : execution.getStepExecutions()) {
    log.info("{} read={} write={} skip={}",
        step.getStepName(),
        step.getReadCount(),
        step.getWriteCount(),
        step.getSkipCount());
}

JobRepository = write·read 모두, JobExplorer = read 전용 + 더 풍부한 query. 운영 모니터링·대시보드에서 자주.

운영 환경 권장

spring:
  datasource:
    url: jdbc:postgresql://batch-db:5432/batch_meta
    username: ${BATCH_DB_USER}
    password: ${BATCH_DB_PASSWORD}
    hikari:
      maximum-pool-size: 10

  batch:
    jdbc:
      initialize-schema: never           # 운영 = never
      table-prefix: BATCH_
      isolation-level-for-create: ISOLATION_SERIALIZABLE
    job:
      enabled: false

핵심은 네 가지예요. business 와 분리된 별도 batch metadata DB, initialize-schema: never + 수동 schema, isolation = SERIALIZABLE 유지, 그리고 이력·재시작을 보장하기 위한 batch metadata DB 의 정기 백업 정책.

JobRepository 운영 시 자주 보는 자리

Long-running Job 의 lock

같은 JobInstance 가 너무 길게 실행 중 → 새 JobExecution 가 동시 시작 시 lock 충돌. SERIALIZABLE 이라 더 민감.

해결 — Job 분할 (시간·partition 단위) 또는 long transaction 회피.

Cleanup 정책

BATCH_* 테이블이 무한 누적. 운영 = N 일 전 데이터 archive·삭제:

DELETE FROM BATCH_STEP_EXECUTION_CONTEXT WHERE STEP_EXECUTION_ID IN (...);
DELETE FROM BATCH_STEP_EXECUTION WHERE JOB_EXECUTION_ID IN (...);
-- ... cascade

권장 = Spring Cloud Data Flow 의 cleanup job 또는 직접 cleanup batch.

Migration

Spring Batch 버전 업그레이드 시 schema 변경 있을 수 있어요. 공식 migration script:

schema-<db>-migration.sql

upgrade 전 staging 환경 검증.

한계·실무 함정

1. Resourceless 운영 사용

위에서 강조. 운영 절대 X.

2. initialize-schema: always 운영

부팅마다 schema 재시도 → 위험.

3. Isolation 변경

성능 위해 READ_COMMITTED 로 변경 시 동시 실행 차단 깨질 수 있음. 명시적 lock 또는 unique 제약 별도.

4. 큰 ExecutionContext

ExecutionContext 에 수 MB 데이터 박으면 DB BLOB 폭증. 핵심 metadata 만.

5. JobRepository DB 와 Business DB 의 transaction 결합

XA (eXtended Architecture, 분산 트랜잭션 표준) 없이 두 DB 가 같은 transaction 시도 = 분산 트랜잭션 문제. 패턴은 셋으로 나뉩니다. JobRepository 는 별도 simple commit, Business work 은 별도 transaction, 둘은 chunk 의 두 단계로 논리적으로 분리.

6. Custom Serializer 오류

직렬화·역직렬화 비호환 = 재시작 시 ExecutionContext 복원 실패. 변경 시 backward compatibility 확인.

시험 직전 한 번 더 — JobRepository 함정 압축 노트

  • JobRepository = 모든 Job·Step·ExecutionContext 영속 저장소
  • 3가지 구현체 = JDBC (운영 표준) · MongoDB (v6 신규) · Resourceless (학습만)
  • Resourceless = 메모리 only, 운영 절대 X
  • 9개 table = JOB_INSTANCE·JOB_EXECUTION·JOB_EXECUTION_PARAMS·JOB_EXECUTION_CONTEXT·STEP_EXECUTION·STEP_EXECUTION_CONTEXT·JOB_SEQ·JOB_EXECUTION_SEQ·STEP_EXECUTION_SEQ
  • Schema 자동 = initialize-schema: always/never/embedded
  • 운영 = never + 수동 schema (jar 안 schema-<db>.sql)
  • Customize = DefaultBatchConfiguration 상속 + getTablePrefix·getIsolationLevelForCreate·getCharset·getExecutionContextSerializer·getMaxVarCharLength
  • Isolation = SERIALIZABLE (기본, 동시 실행 차단) → 변경 시 별도 보장 필요
  • ExecutionContext Serializer = 기본 Jackson JSON (사람 읽기 가능)
  • 옛 Java Serialization·Custom (암호화 등)
  • Table Prefix = 다중 batch application 분리 (BATCH_A_)
  • JobExplorer = read-only 조회 (모니터링·대시보드)
  • 핵심 메서드 — findJobInstancesByJobName·getJobExecutions·getJobExecution
  • 운영 권장 = 별도 batch metadata DB + initialize-schema never + isolation SERIALIZABLE + 정기 백업
  • JobRepository TM vs Step TM = batch metadata 와 business 가 다른 DB 면 분리
  • 함정 — Resourceless 운영
  • 함정 — initialize-schema: always 운영
  • 함정 — Isolation 완화 시 동시 실행 안전성
  • 함정 — 큰 ExecutionContext (BLOB 폭증)
  • 함정 — JobRepo + Business 한 transaction = XA
  • 함정 — Custom Serializer 비호환 = 재시작 실패

공식 문서: Configuring a JobRepository 에서 원문을 확인할 수 있어요.

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!