Spring Batch 입문 3편 — Domain Language (Job · JobInstance · Step · Item · Chunk)

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

Spring Batch 입문 3편. Spring Batch 의 핵심 어휘 — Job·JobInstance·JobParameters·JobExecution·Step·StepExecution·ExecutionContext·ItemReader·ItemProcessor·ItemWriter·Chunk 의 정확한 의미·관계도를 풀어쓴 학습 노트.

📚 Spring Batch 입문에서 운영까지 · 3편 — Domain Language (Job · JobInstance · Step · Item · Chunk)

이 글은 Spring Batch 입문에서 운영까지 시리즈 48편 중 3편이에요. 2편 에서 3계층 아키텍처 를 잡았다면, 이번 3편은 Spring Batch 에서 가장 자주 만나는 어휘 11가지 — Domain Language.

Domain Language가 어렵게 느껴지는 이유

11가지 단어가 한꺼번에 등장해요 — Job · JobInstance · JobParameters · JobExecution · Step · StepExecution · ExecutionContext · ItemReader · ItemProcessor · ItemWriter · Chunk.

가장 헷갈리는 1순위는 Job vs JobInstance vs JobExecution 이에요. 이름은 비슷한데 완전 다른 개념 이라 매번 처음 보는 사람이 막혀요. 다음은 Step vs StepExecution, ExecutionContext 같은 실행 상태 추적 어휘로, 왜 별도 객체가 필요한가 가 잘 안 잡혀요. 마지막으로 Chunk 는 단순 "묶음" 같은데 Spring Batch 의 핵심 처리 단위 라서 11편에서 깊이 다뤄요.

이 글에서 11가지 어휘를 한 그림으로 + 관계도 + 실수하기 쉬운 함정 까지 정리해요.

큰 그림 한 줄

"A Job has one or more steps, each of which has exactly one ItemReader, an optional ItemProcessor, and one ItemWriter. A job is operated (started, stopped, etc) with a JobOperator, and metadata about the currently running process is stored in and restored from a JobRepository."

이걸 ASCII 로:

[JobRepository] ←→ [JobOperator]
                       │
                       │ start/stop
                       ↓
                   [   Job   ]
                       │
                       │ contains
                       ↓
              [ Step ] [ Step ] [ Step ]
                       │
                       │ chunk-oriented 의 경우
                       ↓
   [ ItemReader ] → [ ItemProcessor (옵션) ] → [ ItemWriter ]
                       (chunk 단위로 묶어 처리)

이 그림이 Spring Batch 의 모든 것 이에요. 11가지 어휘는 이 그림 안의 부품 이름.

Job — 전체 batch process 의 컨테이너

Job = batch 프로세스 한 개를 통째로 캡슐화하는 entity (데이터 객체).

이름, Step 들의 순서·정의, restartable (재시작 가능) 여부, 기타 글로벌 속성으로 구성돼요.

코드

@Bean
public Job footballJob(JobRepository jobRepository, ...) {
    return new JobBuilder("footballJob", jobRepository)
        .start(playerLoad())
        .next(gameLoad())
        .next(playerSummarization())
        .build();
}

3개 Step 을 순서대로 실행하는 footballJob. 이 객체가 Job 인터페이스의 인스턴스 예요.

Job 은 템플릿 일 뿐이라 실제 실행은 JobInstance·JobExecution 으로 이어져요.

JobInstance — 논리적 실행 1회

JobInstance = Job 의 논리적 한 번 실행.

"In the case of this job, there is one logical JobInstance per day. For example, there is a January 1st run, a January 2nd run."

비유

Job                     = "월말 결산"          (템플릿)
JobInstance 1월말 결산  = "2026년 1월의 결산"  (논리적 1회)
JobInstance 2월말 결산  = "2026년 2월의 결산"
...

날짜·기간·식별자 별로 별도 JobInstance 가 만들어져요.

함정

"If the January 1st run fails the first time and is run again the next day, it is still the January 1st run."

1월 1일 결산이 실패해서 1월 2일에 재실행 해도 여전히 같은 JobInstance (1월 1일 결산) 예요. 재실행은 새 JobInstance 가 아니라 같은 JobInstance 의 새 JobExecution 으로 잡혀요.

여기서 시험 함정이 하나 있어요 — 같은 JobInstance 는 동시에 여러 번 실행 X. 재시작 의미만 갖는다는 거예요.

JobParameters — JobInstance 의 식별자

JobParameters = Job 실행 시 전달하는 파라미터 집합.

"JobInstance = Job + identifying JobParameters."

코드

JobParameters parameters = new JobParametersBuilder()
    .addString("targetDate", "2026-01-01")
    .addLong("threshold", 1000L)
    .toJobParameters();

JobExecution execution = jobLauncher.run(footballJob, parameters);

targetDate=2026-01-01targetDate=2026-01-02서로 다른 JobInstance 가 돼요.

Identifying vs Non-identifying

new JobParametersBuilder()
    .addString("targetDate", "2026-01-01", true)   // identifying
    .addString("runId", "abc-123", false)           // non-identifying
    .toJobParameters();

Identifying (식별용, 기본값) 은 JobInstance 식별에 기여하고, Non-identifying (비식별) 은 식별에 쓰이지 않고 단순 reference data (참고 값) 로만 쓰여요.

그래서 같은 targetDate다른 runId 라도 같은 JobInstance 가 돼요.

JobExecution — 물리적 실행 1회

JobExecution = JobInstance 의 한 번의 실제 실행.

관계

Job (템플릿)
  ↓ + JobParameters
JobInstance (논리적 1회)
  ↓ 1:N
JobExecution (물리적 실행)
   - 첫 시도 (실패)
   - 재시도 (성공)

JobExecution 은 시작 시각·종료 시각·상태·ExitStatus (종료 코드)·exception 같은 런타임 정보 를 보유해요.

상태 (BatchStatus — 배치 실행 상태값)

상태 의미
STARTING 시작 중
STARTED 실행 중
STOPPING 종료 요청 받음
STOPPED 종료 완료 (재시작 가능)
COMPLETED 정상 완료
FAILED 실패 (재시작 가능)
ABANDONED 포기 (재시작 X)
UNKNOWN 알 수 없음

코드 — Job 실행 결과

JobExecution execution = jobLauncher.run(footballJob, parameters);

System.out.println(execution.getStatus());       // BatchStatus.COMPLETED
System.out.println(execution.getExitStatus());    // ExitStatus.COMPLETED
System.out.println(execution.getStartTime());
System.out.println(execution.getEndTime());

Step — 한 단계의 처리

Step = Job 의 한 단계. 독립적·순차적 단위예요.

두 가지 유형이 있어요.

Chunk-oriented Step

@Bean
public Step playerLoad(JobRepository repo, PlatformTransactionManager tx) {
    return new StepBuilder("playerLoad", repo)
        .<String, Player>chunk(100, tx)
        .reader(playerReader())
        .processor(playerProcessor())
        .writer(playerWriter())
        .build();
}

100 건씩 묶어 Reader → Processor → Writer 로 흘려요. 11~18편에서 깊이 다뤄요.

TaskletStep

@Bean
public Step archive(JobRepository repo, PlatformTransactionManager tx) {
    return new StepBuilder("archive", repo)
        .tasklet(archiveTasklet(), tx)
        .build();
}

임의 작업 한 덩어리 (파일 압축·외부 명령 호출 등) 를 처리해요. 19편에서 깊이 다뤄요.

StepExecution — Step 의 실행 1회

StepExecution = Step 의 한 번 실행. JobExecution 과 같은 구조예요.

JobExecution
  └── StepExecution (playerLoad)
  └── StepExecution (gameLoad)
  └── StepExecution (playerSummarization)

각 StepExecution 에는 시작·종료 시각, 읽은 record (한 건의 데이터) 수 (readCount), 쓴 record 수 (writeCount), skip 한 수 (skipCount), 처리 시간, exception 정보가 담겨요.

운영 모니터링에서 가장 자주 보는 자리 예요.

ExecutionContext — 실행 상태 저장소

ExecutionContext = Job·Step 의 실행 중 상태영속 저장 하는 key-value (키-값) 저장소.

두 종류

JobExecution.executionContext   = Job 전체 공유
StepExecution.executionContext  = 각 Step 별

사용 예제 — 재시작 지원

// Reader 안에서
@Override
public void open(ExecutionContext context) {
    if (context.containsKey("currentItemCount")) {
        this.currentItemCount = context.getInt("currentItemCount");
    }
}

@Override
public void update(ExecutionContext context) {
    context.putInt("currentItemCount", this.currentItemCount);
}

Reader 가 현재 read 진행 상황 을 ExecutionContext 에 저장해 두면 재시작 시 그 지점부터 이어갈 수 있어요.

여기서 정말 중요한 자리 — ExecutionContext 가 JobRepository (DB) 에 영속 돼요. 그래서 재시작 보장 이 가능하고, 직접 짤 필요가 없어요.

ItemReader · ItemProcessor · ItemWriter

Chunk-oriented Step 의 3 컴포넌트 예요.

ItemReader

public interface ItemReader<T> {
    T read() throws Exception;   // 한 번에 한 record
}

데이터 source 에서 한 건씩 read 해요. null 반환은 더 이상 없다는 뜻.

ItemProcessor (옵션)

public interface ItemProcessor<I, O> {
    O process(I item) throws Exception;
}

각 record 를 변환·검증·enrichment (값 보강) 해요. null 반환은 filter (걸러내기) — 이 record skip 이에요.

ItemWriter

public interface ItemWriter<T> {
    void write(Chunk<? extends T> items) throws Exception;   // chunk 단위로 write
}

chunk 통째로 write 해요. Reader 와 다른 점은 건당이 아니라 묶음 처리 라는 거예요.

흐름

Reader.read() × 100 → 100건 모임
   ↓ each
Processor.process() × 100 (옵션)
   ↓
Writer.write(100건) ← 한 번에
   ↓
transaction commit

이게 Chunk-oriented Processing 의 한 chunk 처리예요.

Chunk — 처리 단위

Chunk = N건의 record 묶음. Reader 가 N건 모일 때까지 반복 read, Processor 가 N건 각각 처리, Writer 가 N건 통째로 write 하는 단위예요.

Chunk size

.<String, Player>chunk(100, tx)

100 이 chunk size 예요. commit interval (커밋 주기) 과 동의어 — N건마다 transaction commit 이에요.

권장 값은 상황에 따라 달라요. 소량 record + 큰 객체 면 작은 chunk (10~50), 대량 record + 작은 객체 면 큰 chunk (500~1000+), DB write 는 100~500 이 적당해요 (network·transaction 균형).

자세한 내용은 12편 commit-interval 에서 다뤄요.

관계도 종합

[Job: "월말 결산"]
   │
   │ JobParameters: targetMonth=2026-01
   ↓
[JobInstance: 2026-01 결산]
   │ 1:N (재시도 시 여러 JobExecution)
   ↓
[JobExecution #1: 첫 시도 (FAILED)]
   │ 1:N
   ├── [StepExecution: playerLoad (COMPLETED)]
   │     - readCount: 10000
   │     - writeCount: 9950
   │     - skipCount: 50
   ├── [StepExecution: gameLoad (FAILED)]
   │     - exception
   │
[JobExecution #2: 재시도 (COMPLETED)]
   │
   ├── [StepExecution: playerLoad] ← skip (이미 COMPLETED)
   ├── [StepExecution: gameLoad (COMPLETED)] ← ExecutionContext 부터 재개
   ├── [StepExecution: playerSummarization (COMPLETED)]

이 관계도가 Spring Batch 의 모든 것 이에요. 한 번 머리에 박혀 있으면 Reference 어느 페이지를 읽어도 위치 파악 가능 해요.

JobRepository · JobOperator 위치

JobRepository위 모든 entity (Job·Instance·Execution·StepExecution·ExecutionContext) 의 영속 저장소 (DB) 이고, JobOperatorJob 실행·중지·재시작 제어 인터페이스 (Core 계층) 예요.

둘은 5~10편 (Part 2) 에서 깊이 다뤄요.

자주 헷갈리는 함정 종합

1. Job vs JobInstance vs JobExecution

Job          = 템플릿 (코드 1개)
JobInstance  = 논리적 1회 (parameters 로 식별, N개)
JobExecution = 물리적 1회 (재시도 시 N개)

2. ItemReader 가 chunk 가 아닌 1건씩

이름이 readerchunk 통째 로 받는 것 같지만 한 건씩 read 해요. Spring Batch 가 내부적으로 chunk size 만큼 모음.

3. ItemProcessor null = filter

null 반환은 이 record skip 이에요. 의도적 패턴.

4. ItemWriter 만 chunk

Writer 만 chunk (List) 를 받아요. Reader·Processor 는 건당 처리.

5. ExecutionContext 가 자동 영속

Reader·Writer 가 직접 DB 저장 안 해도 framework 가 JobRepository 에 영속 해 줘요. 단 Job/Step 별 분리 에 유의.

시험 직전 한 번 더 — Spring Batch Domain Language 함정 압축 노트

  • 핵심 어휘 11가지 — Job · JobInstance · JobParameters · JobExecution · Step · StepExecution · ExecutionContext · ItemReader · ItemProcessor · ItemWriter · Chunk
  • 한 줄 모델 — Job → N Step → 각 Step: Reader → Processor (옵션) → Writer
  • Job = 템플릿 (코드 정의)
  • JobInstance = 논리적 1회, Job + identifying JobParameters 로 식별
  • 같은 JobInstance 재실행 = 새 JobExecution 추가 (instance 그대로)
  • 같은 JobInstance = 동시 실행 X
  • JobParametersaddString·addLong·addDate 등, identifying=true/false
  • JobExecution = 물리적 1회, BatchStatus·ExitStatus·시작/종료
  • BatchStatus 8가지 = STARTING·STARTED·STOPPING·STOPPED·COMPLETED·FAILED·ABANDONED·UNKNOWN
  • Step 2가지 = Chunk-oriented (Reader·Processor·Writer) · TaskletStep (임의 작업)
  • StepExecution = Step 의 1회 실행, readCount·writeCount·skipCount
  • ExecutionContext = key-value 영속 저장소, JobRepository (DB) 에 자동 저장
  • ExecutionContext 종류 2 = JobExecution.context (전체 공유) · StepExecution.context (Step 별)
  • 재시작 = ExecutionContext 부터 재개
  • ItemReader = 한 건씩 read, null = 더 없음
  • ItemProcessor (옵션) = 변환·검증·enrichment, null = filter (skip)
  • ItemWriter = chunk 통째로 write
  • Chunk = N건 묶음, chunk(100, tx) 의 N 이 commit interval
  • 권장 chunk size — 큰 객체 10~50, 작은 객체 500~1000+, DB 100~500
  • 함정 — Job vs JobInstance vs JobExecution 혼동
  • 함정 — Reader 가 chunk 가 아닌 1건씩
  • 함정 — Writer 만 chunk
  • 함정 — Processor null = filter

공식 문서: The Domain Language of Batch 에서 원문을 확인할 수 있어요.

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

이전 글:

다음 글:

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

답글 남기기

error: Content is protected !!