안녕하세요.
Spring Batch를 처음 공부할 때 가장 먼저 마주치지만, 은근히 헷갈리는 개념이 바로 Job과 Step입니다.
“그냥 다 실행하는 거 아니야?”라고 생각하실 수 있지만, 둘은 엄연히 다른 역할과 책임을 가지고 있습니다. 오늘은 비유를 통해 이 둘의 차이를 완벽하게 정리해 드립니다.

1. 직관적인 비유: 요리 과정
가장 쉬운 이해를 위해 ‘라면 끓이기’에 비유해 보겠습니다.
- Job: “라면 끓이기”라는 전체 프로젝트(목표)입니다.
- Step: 그 목표를 달성하기 위한 세부 단계들입니다.
- 물 끓이기 (Step 1)
- 면과 스프 넣기 (Step 2)
- 계란 풀기 (Step 3)
즉, Job은 Step들의 모음집(Container)입니다. Job 혼자서는 아무런 구체적인 일도 하지 않습니다. 일은 Step이 다 합니다.
2. 기술적인 차이점 (Technical Differences)
| 특징 | Job (작업) | Step (단계) |
|---|---|---|
| 정의 | 배치 처리의 전체 실행 단위 | Job 내부의 독립적인 실행 단계 |
| 구성 | 1개 이상의 Step 포함 | ItemReader, Processor, Writer 포함 (또는 Tasklet) |
| 실행 주체 | JobLauncher에 의해 실행됨 | Job에 의해 순차적/조건부 실행됨 |
| 트랜잭션 | 트랜잭션을 직접 관리하지 않음 | 자체적인 트랜잭션 관리를 수행함 |
| 메타 데이터 | JobInstance, JobExecution | StepExecution |
3. 코드에서의 관계
코드를 보면 관계가 더 명확해집니다.
// Job 설정
@Bean
public Job ramenJob() {
return new JobBuilder("ramenJob", jobRepository)
.start(boilWaterStep()) // Step 1 시작
.next(addNoodleStep()) // Step 1 성공 시 Step 2 실행
.next(addEggStep()) // Step 2 성공 시 Step 3 실행
.build();
}
// Step 설정
@Bean
public Step boilWaterStep() {
return new StepBuilder("boilWaterStep", jobRepository)
.tasklet((contribution, chunkContext) -> {
System.out.println("보글보글 물이 끓습니다.");
return RepeatStatus.FINISHED;
}, transactionManager)
.build();
}

핵심 포인트
- Job은 흐름을 제어합니다:
start(),next(),on(),to()등의 메서드를 통해 Step의 순서를 정하거나, 실패 시 분기 처리(Flow Control)를 담당합니다. - Step은 로직을 수행합니다: 실제 데이터를 읽고 쓰는 비즈니스 로직은 모두 Step 안에 정의됩니다.
4. 왜 굳이 나누었을까?
그냥 Job 안에 모든 코드를 다 넣으면 편하지 않을까요? 왜 Step으로 쪼갰을까요?
- 재사용성: ‘이메일 전송 Step’을 잘 만들어두면, A Job에서도 쓰고 B Job에서도 가져다 쓸 수 있습니다.
- 격리성 (Isolation): Step 1은 성공했는데 Step 2에서 실패했다면? Step 1은 이미 커밋되었으므로 롤백할 필요가 없습니다. 트랜잭션을 단계별로 격리하여 사고 범위를 줄일 수 있습니다.
- 유지보수: 1000줄짜리 통짜 코드보다는, 100줄짜리 Step 10개가 훨씬 디버깅하기 쉽습니다.
5. 결론
- Job = 숲 (Forest)
- Step = 나무 (Tree)
숲을 보며 전체 길을 설계하는 것이 Job이고, 그 길 위에서 묵묵히 나무를 심고 가꾸는 것이 Step입니다. 이 구조를 이해하고 나면 배치 설계를 훨씬 더 유연하게 할 수 있습니다.





