Spring 배울때 한번 배웠고 프로젝트에서도 사용했지만, 복습차원에서 한번 더 쓰면서 공부한다
Spring Batch
대량의 데이터를 처리하고 관리하기 위한 엔터프라이즈급 애플리케이션을 구축하기 위한 경량 배치 프레임워크
@Scheduled 애너테이션을 활용하여 스케쥴러를 구성하여 배치를 동작시키는 방법을 사용해본 적이 있다
Batch
특정 주기마다 데이터를 처리하는 작업. 정산이나 통계 혹은 로깅관리에 쓰이며 매주, 매월마다 한 번 실행되게끔 한다
배치작업은 Spring Framework를 이용하지 않고 순수한 Java언어로도 만들 수 있다. 애초에 Spring 특성상 Spring을 벗겨내도 순수한 자바언어로 동작하게끔 할 수 있다. 그래도 프레임워크를 활용하면 훨씬 간단하게 배치 프로그램을 만들 수 있겠지?
Spring Batch는 어디에 사용되는가?
주로 데이터 마이그레이션, 주기적 데이터 처리에 많이 사용된다. 하나의 시스템에서 다른 시스템으로 데이터를 이동하거나 형식을 변경할 때 또는 매일, 매주 정해진 시간처럼 일정한 주기마다 실행되어야 하는 작업을 자동화할 때 사용함
배치에 사용되는 용어들에 대해
| Job | 배치 처리의 전체 실행 단위 실제 객체를 의미한다 |
| Step | Job 내의 독립된 작업 단위 Job은 최소한 1개를 Step을 가져야한다 Tasklet 단일 작업 또는 Chunk 기반 처리(읽기, 처리, 쓰기)를 수행함 |
| JobLauncher | Job을 실행하는 객체 |
| ItemReader ItemProcessor ItemWritter |
Chunk 기반 처리에서 데이터를 읽고, 가공하고, 쓰는 역할을 담당 |
기본적인 배치 사용하기
1. 의존성 추가
배치가 잘 돌아가나 확인만 해볼것이다.

build.gradle에 잘 추가되었는지 확인한다
* Lombok, Spring JPA도 추가해주자. 사실 JPA가 없으면 돌아가질 않는다.
# lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
#Spring jpa
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
밑에서 h2를 사용할거니까 h2에 대한 의존성도 추가해주자
#h2
runtimeOnly 'com.h2database:h2'
추가적으로 configuration-processor을 annotationProcessor 형태로 등록할 수 있다고 한다.
이걸 추가해주면 Lombok처럼 Spring Batch에 도움이 되는 Annotation들을 지원해준다고 한다.
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
2. yml파일 설정
application.yml 파일에 배치 관련 설정을 해줄 수 있다고 한다. 좀 길게 적었는데..
여기에서 중요한 점은, 배치처리에 대한 연습이나 실전 테스트를 할땐 반드시 DB가 필요하다.
h2 기본 설정을 하던지, 자기 PC 내부 DB를 활용하던지(MySQL, PostgresSQL, 등 cmd로 접근 가능한 DB)
어느것이든 하나의 DB와 연결시켜주면 Batch처리에 대한 활용이 가능하다.

Postgre 세팅도 해두긴 했는데, 최대한 간단하게 테스트하기 위해 h2를 사용했다.
이후 main 메서드를 가진 Spring 클래스에 @EnableBatchProcessing 애너테이션을 명시해주면 Batch 사용 준비가 끝난다.

3. Batch 처리하기
나는 content/config/ContentConfig.java의 형태로 패키지와 클래스를 만들었다.

그리고 이 ContentConfig 클래스를 Bean으로 등록해야한다. @Configuration 애너테이션을 붙여주자.

그리고 JobBuilderFactory와 StepBuilderFactory 타입의 필드를 만들어보려고 했는데, Spring Batch에서 지원을 종료했다고 한다. 정확하게 말하면 저 두 클래스가 '사라졌다'고 보는게 좋겠다.
3.2.5 버전의 Spring Boot 기준으로 사라졌다.
링크
https://multifrontgarden.tistory.com/310
https://github.com/spring-projects/spring-batch/issues/4188
그래서 JobBuilder를 사용해보는데 얘도 제거될 예정이라고 한다..

그래서 조사해보니, Spring Batch 5.2.4 API와 관련된 자료가 있었다.
batch 전반에 관한 패키지는 여기
https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/package-summary.html
Configuration에 관한건 DefaultBatchConfiguration을 상속받아서 사용하면 되겠는데?
참고할만한 링크
https://alwayspr.tistory.com/49
https://cwangg897.tistory.com/214
@Configuration을 붙여서 클래스를 Bean으로 만들어줬으니 @EnableBatchProcessing을 붙여줘도 되는데
Spring Batch 5.x 버전부터는 이 애너테이션이 필요없어졌다고 한다.
또한 @EnableBatchProcessing 애너테이션을 선언할 경우 autoConfiguration에 의해 자동으로 Bean들이 등록되지 못한다.
출처 : https://velog.io/@hoyo1744/SpringBoot-3.x-Spring-Batch-5.x
그래서 DefaultBatchConfiguration 상속만 해준다.

이걸 상속해주지 않아도 배치처리는 할 수 있다고 하는데, 상속해주면 간편하게 오버라이드 할 수는 있더라.
작성한 Config 내용이다.
읽기전에 정리
| Job | 스프링배치 작업을 정의하는 클래스 |
| JobBuilder | Job 객체를 생성하기 위한 Builder 클래스 |
| Step | 스프링 배치 작업 단계를 정의하는 클래스 |
| StepBuilder | Step 객체를 생성하기 위한 빌더 클래스 |
| Tasklet | 스프링 배치 작업에서 실행될 Tasklet을 정의하는 클래스 |
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Slf4j
@Configuration
public class ContentConfig extends DefaultBatchConfiguration {
@Bean
public Job helloJob(JobRepository jobRepository, Step helloStep) {
return new JobBuilder("helloJob", jobRepository)
.start(helloStep)
.build();
}
@Bean
public Step helloStep(JobRepository jobRepository,
Tasklet helloTasklet,
PlatformTransactionManager platformTransactionManager) {
return new StepBuilder("helloTasklet", jobRepository)
.tasklet(helloTasklet, platformTransactionManager)
.build();
}
@Bean
public Tasklet helloTasklet() {
return ((contribution, chunkContext) -> {
log.info("helloTasklet");
System.out.println("Hello World Spring Batch");
return RepeatStatus.FINISHED;
});
}
}
이후에 실행해보면 Hello World가 콘솔에 출력된다고 하는데..
난 출력이 안됐다.. 왜지?
로그도 제대로 출력되지 않았다.. Batch에 대한건 잘 실행된것 같은데..

☆원인을 찾았음★
Spring Boot 3.x + Spring Batch 5에서 BatchAutoConfiguration은 아래처럼 생겨먹었다.
@AutoConfiguration
@ConditionalOnMissingBean(
value = { DefaultBatchConfiguration.class },
annotation = { EnableBatchProcessing.class }
)
public class BatchAutoConfiguration {
}
위 BatchAutoConfiguration에서 BatchDataSourceScriptDatabaseInitializer를 만들어서
schema-h2.sql을 실행한 다음 BATCH에 해당하는 테이블을 자동으로 생성해준다.
여기서가 중요한데,
DefaultBatchConfiguration 타입의 빈이 하나라도 있으면 BatchAutoConfiguration 전체가 비활성되어버린다고 한다.
위에서 내가 적은 ContentConfig를 다시 보자.
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration; // import
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Slf4j
@Configuration
public class ContentConfig extends DefaultBatchConfiguration { // DefaultBatchConfiguration 상속
// ...
}
지금 내가 작성한 ContentConfig는 DefaultBatchConfiguration을 상속하고 있다.
그래서 스프링은 ContentConfig를 DefaultBatchConfiguration 타입의 Bean으로 인식하기 때문에
@ConditionalOnMissingBean(DefaultBatchConfiguration.class)의 조건이 깨진다.
따라서 BatchAutoConfiguration이 돌지 않고 그 안에 있는 BatchDataSourceScriptDatabaseInitializer도 만들어지지 않는다.
이러면 내가 application.yml에 작성한 spring.batch.jdbc.initialize-schema: always도 완전히 무시된다.
테이블을 자동으로 만들어주지 않는다는 소리임
그래서 내가 postgre를 통해 cmd로 DB를 만들어두건, H2를 쓰건 테이블이 자동으로 생성되지 않았던 것이다.
그래서 내가 컨트롤러로 요청을 하면 BATCH_JOB_INSTANCE를 찾다가 계속 뻗어버리는것임
덩달아 ContentConfig는 현재 DefaultBatchConfiguration을 상속하고 있기 때문에 안에 적힌 메서드는 완전히 무시된다.
해결법은 무엇인가??????
저 상속만 지워주면 된다..

이 상태에서 application.yml에 batch.job.enabled: true로 맞춰주고 실행해보면


내가 ContentConfig 내 helloTasklet에 작성한 출력문이 로그에 출력된다.
특정 메서드가 호출되었을때 배치를 시작하게끔 하기
우선 application.yml의 batch.job.enabled: false로 바꿔준다
바로 위에 적혀있으니 따로 첨부는 하지 않겠다.
그리고 요청이 들어왔을 때 배치처리를 하게끔 해야하니까 컨트롤러, 서비스를 각각 만들어주자
BatchController.java

BatchService.java

이 서비스를 통해 요청에 따른 Spring Batch Job을 실제로 실행시킬 수 있다.
이건 내가 실습을 통해 다시 사용했을때 로직의 흐름을 적어두기로 하겠다. 나는 페르마가 좋더라..
아무튼 두 클래스를 작성하고 다시 실행하면,
application.yml에 job.enabled: false로 바꿨기 때문에 배치가 자동으로 실행되지 않는다.
실행시키고 싶다면 Controller에 맵핑된 주소가 호출되어야한다.
나는 localhost:8080/batch를 주소에 입력했다.
호출하기 전 로그

그리고 localhost:8080/batch를 입력해서 호출하고..

로그를 확인하면..

잘 작동한다!
복습할때 편의를 위해 코드를 밑에 남긴다
build.gradle의 의존성
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.yml
server:
port: 8080
spring:
application:
name: testbatch2
threads:
virtual:
enabled: true
batch:
jdbc:
initialize-schema: always
job:
enabled: false
datasource:
url: jdbc:h2:mem:testbatch2
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
defer-datasource-initialization: true
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
default_batch_fetch_size: 100
format_sql: true
highlight_sql: true
use_sql_comments: true
h2:
console:
enabled: true
path: /h2-console
logging:
level:
com.ll.sbb20240111: DEBUG
org.hibernate.SQL: DEBUG
org.hibernate.orm.jdbc.bind: TRACE
org.hibernate.orm.jdbc.extract: TRACE
org.springframework.transaction.interceptor: TRACE
content/ContentConfig.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Slf4j
@Configuration
public class ContentConfig {
@Bean
public Job helloJob(JobRepository jobRepository, Step helloStep) {
return new JobBuilder("helloJob", jobRepository)
.start(helloStep)
.build();
}
@Bean
public Step helloStep(JobRepository jobRepository,
Tasklet helloTasklet,
PlatformTransactionManager platformTransactionManager) {
return new StepBuilder("helloTasklet", jobRepository)
.tasklet(helloTasklet, platformTransactionManager)
.build();
}
@Bean
public Tasklet helloTasklet() {
return ((contribution, chunkContext) -> {
log.info("helloTasklet");
System.out.println("Hello World Spring Batch");
return RepeatStatus.FINISHED;
});
}
}
content/BatchController.java
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequiredArgsConstructor
public class BatchController {
private final BatchService batchService;
@GetMapping("/batch")
@ResponseBody
public String simple() {
batchService.runSimpleJob();
return "runSimpleJob OK";
}
}
content/BatchService.java
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class BatchService {
private final JobLauncher jobLauncher;
private final Job simpleJob;
public void runSimpleJob() {
try {
JobParameters jobParameters = new JobParametersBuilder().toJobParameters();
jobLauncher.run(simpleJob, jobParameters);
} catch(Exception e) {
e.printStackTrace();
}
}
}
'Playlist > Spring Batch' 카테고리의 다른 글
| BatchConfig (0) | 2025.12.17 |
|---|---|
| SportContentTasklet (0) | 2025.12.17 |
| TvSeriesTasklet (0) | 2025.12.17 |
| MovieTasklet (0) | 2025.12.17 |
| ContentScheduler (0) | 2025.12.17 |