Spring Batch 입문 7편. JobRepository 깊이 — 영속화 메커니즘, 3가지 구현체 (JDBC·MongoDB·Resourceless), schema·table prefix·isolation level·serializer·custom 등 운영 핵심 영역까지 풀어쓴 학습 노트.
이 글은 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 에서 원문을 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 2편 — Architecture (Application · Core · Infrastructure)
- 3편 — Domain Language (Job · JobInstance · Step · Item · Chunk)
- 4편 — v6 What's New + Hello Job 5분 hands-on
- 5편 — Batch Infrastructure (@EnableBatchProcessing · Beans)
- 6편 — Configuring a Job (JobBuilder · Validator · Listener)
다음 글: