07. Backup Batch - 3
- -
이번 포스트에서는 Job을 실행시켜보자.
JobOperator
기본
JobOperator는 JobOperatorFactoryBean에 의해 공급되며 기본 빈으로 제공 되기 때문에 쉽게 주입받아서 사용할 수 있다.
public class JobOperatorFactoryBean implements FactoryBean<JobOperator>,
ApplicationContextAware, InitializingBean {
...
}
JobOperator의 start()를 호출하면 JobExecution을 만들고 여기에 JobParameters를 저장해서 Job을 실행하게 된다. Job이 종료된 후 JobExecution에는 Job의 종료 상태(ExitStatus)가 포함되서 반환된다. (그림에는 FINISHED가 있지만 실제는 COMPLETED이다.)

만약 HTTP 요청에 의해서 동작하는 경우는 시간이 오래 걸릴 수 있기 때문에 비동기로 응답하는 것이 유리하다. 이를 위해 SimpleAsyncTaskExecutor를 TaskExecutor로 설정한 JobOperatorFactoryBean을 구성하고 이를 통해 JobOperator를 구할 수 있다.
@Bean
public JobOperatorFactoryBean customJobOperatorFactory(JobRepository jobRepository) {
JobOperatorFactoryBean bean = new JobOperatorFactoryBean();
bean.setJobRepository(jobRepository);
bean.setTaskExecutor(new SimpleAsyncTaskExecutor());
return bean;
}
이 경우 execute가 마무리 되기 전에 응답하고 JobExecution의 상태값은 STARTED가 된다.(그림은 UNKNOWN이라고 되어있지만.)

@Controller를 통한 Job 실행
이제 @Controller에서 JobOperator를 통해 paymentBackupJob을 실행해보자.
@RestController
@RequestMapping("/batch")
public class BatchController {
private final JobOperator jobOperator;
private final Job paymentBackupJob;
// 생성자 주입
public BatchController(JobOperator jobOperator,
@Qualifier("paymentBackupJob") Job paymentBackupJob) {
this.jobOperator = jobOperator;
this.paymentBackupJob = paymentBackupJob;
}
@GetMapping("/payment-backup")
public Map<String, Object> runPointJob(@RequestParam String yearMonth) {
try {
JobParameters params = new JobParametersBuilder()
.addString("yearMonth", yearMonth).toJobParameters();
JobExecution execution = jobOperator.start(paymentBackupJob, params);
long jobId = execution.getJobInstance().getId();
return Map.of(
"jobId", jobId,
"executionId", execution.getId(),
"status", execution.getStatus().toString()
);
} catch (Exception e) {
e.printStackTrace();
return Map.of("error", e.getMessage());
}
}
}
클라이언트 작성
index.html 에서 간단한 rest 요청으로 batch를 호출해보자.
<fieldset>
<legend>backup batch(db)</legend>
<input type="month" id="backup-month" min="2005-05" max="2006-02" value="2005-05">
<button id="backup-btn">backup</button>
<br>
<output id="backup-output"></output>
</fieldset>
<script type="text/javascript">
document.querySelector("#backup-btn").addEventListener("click", async ()=>{
const yearMonth = document.querySelector("#backup-month").value;
let result;
try{
const response = await fetch("/batch/payment-backup?yearMonth="+yearMonth);
result = await response.text();
}catch(error){
result = error;
}finally{
document.querySelector("#backup-output").value = result;
}
});
</script>
실행 결과 확인
backup을 실행시켜 보면 처리가 잘 된 것을 확인할 수 있다.

로그를 살펴봐도 1156건의 데이터가 잘 backup 된 것을 확인할 수 있다.
Payment 백업 배치 완료!
========================================
대상 월: 2005-05
읽은 건수: 1156건
저장 건수: 1156건
백업 확인: 1156건
제외 건수: 0건 (paymentId null)
========================================
만약 동일한 월(2005-05)로 다시 한번 시도하면 어떻게 될까?

Batch JobInstance는 JobParameters로 구분되는데 이미 해당 데이터를 이용한 Job을 처리가 완료 되었기 때문에 중복되서 실행되지는 않는다.
Scheduler를 이용한 Job 실행
스케쥴링
일반적으로 Job은 대용량의 데이터를 처리하기 때문에 시스템 사용이 적은 시간 등에 주기적으로 실행하는 것이 일반적이다. 따라서 스케쥴러를 이용하는데 다행스럽게도 Spring에서는 Scheduler를 내장하고 있다.
먼저 내장 Scheduler를 사용하기 위해서는 @Configuration에 @EnableScheduling 설정이 필요하다.
@SpringBootApplication(exclude = {MybatisAutoConfiguration.class})
@EnableScheduling
public class SpringBatchApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchApplication.class, args);
}
}
Spring Batch와 Scheduler는 직접적인 관계가 없다.
@Scheduler
@EnableScheduling을 설정했다면 @Scheduler를 사용할 수 있다.
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Scheduled {
// 음수면 해당 옵션 사용 안함
long fixedRate() default -1; // 이전 작업이 끝나고 나서 지연시간(밀리초) 후 실행
long fixedDelay() default -1; // 일정 간격(밀리초)으로 작업을 실행 (이전에 실행 시작 시점 기준)
long initialDelay() default -1; // 첫 실행 전에 지연시간(밀리초) 설정
// 위에서 사용되는 값의 단위를 지정하는 Enum 값
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
String cron() default ""; // 크론 표현식으로 스케줄 실행 주기 지정 (ex: 매일 9시에)
}
@Scheduler는 간단한 설정을 위해서는 fixedXXX 등을 이용하지만 일반적으로는 cron 식을 이용하는 cron을 사용한다.
cron 식이란 "초 분 시 일 월 요일 [년]"의 6가지 필드로 시간/주기를 표현한다. 년을 포함하면 7개지만 년은 거의 사용되지 않는다. 기본적으로 초와 분은 0~59, 시는 0~23, 일은 1~31, 월은 1~12, 요일은 0~7(0 = 일요일)의 의미를 갖는다.
@Scheduled(fixedDelay = 20000) // 20초 간격으로 실행 - 이전 작업이 끝난 후 기준
@Scheduled(fixedRate = 20000) // 20초 간격으로 실행 - 이전 작업이 시작 후 기준
@Scheduled(cron = "0 0 1 * * *") //매일 새벽 01시
cron식은 필드에 여러 가지 특수문자를 사용할 수도 있다.
| 문자 | 설명 | 예시 |
| * | 모든 값 | 0 0 10 * * * : 매일 오전 10시 0분 0초에 실행 |
| - | 범위 | 0 30 9 ? * MON-FRI : 월-금 오전 9시 30분 0초 |
| , | 목록 | MON,WED,FRI : (월요일, 수요일, 금요일) |
| / | 증분(단계) | 0 0/5 * * * ? : 매 5분마다 (예: 0분, 5분, 10분 …) |
| ? | 특정 값 없음. 일/요일 필드 중 하나만 지정하고 싶을 때 다른 하나에 사용 * 보다 명확한 의도 전달(동작은 동일) |
0 0 10 ? * MON : 매주 월요일 오전 10시 |
| L | 마지막(Last). 일에서는 '월의 마지막 날', 요일에서는 '토요일' 의미 |
0 0 12 L * ? : 매월 마지막 날 정오 |
| W | 가장 가까운 평일(Weekday). 15W: 15일이 평일이면 당일, 토요일이면 14일, 일요일이면 16일 |
0 0 10 15W * ? (매월 15일에 가장 가까운 평일 오전 10시) |
| # | N번째 요일. 요일 필드에만 사용. WED#2는 '월의 두 번째 수요일' |
0 0 10 ? * MON#2 (매월 두 번째 월요일 오전 10시) |
@Scheduler를 통한 Job 수행
매월 1일 1시가 되면 지난달의 backup을 수행하도록 스케쥴을 잡아보자.
@Component
@Slf4j
public class PaymentBatchScheduler {
private final JobOperator jobOperator;
private final Job paymentBackupJob;
public PaymentBatchScheduler(JobOperator jobOperator,
@Qualifier("paymentBackupJob") Job paymentBackupJob) {
this.jobOperator = jobOperator;
this.paymentBackupJob = paymentBackupJob;
}
@Scheduled(cron = "0 0 1 1 * *")
public void runMonthlyBackup() {
String targetYearMonth = YearMonth.now()
.minusMonths(1) // 한 달 전으로 이동
.format(DateTimeFormatter.ofPattern("yyyy-MM")); // "YYYY-MM" 형식으로 포매팅
try {
JobParameters params = new JobParametersBuilder()
.addString("yearMonth", targetYearMonth).toJobParameters();
JobExecution execution = jobOperator.start(paymentBackupJob, params);
long jobId = execution.getJobInstance().getId();
log.debug("{} Payment Backup Job Finished.{} - {}"
,targetYearMonth, jobId, execution.getStatus());
} catch (Exception e) {
log.error("monthly backup job fail", e);
}
}
}
'Spring Batch' 카테고리의 다른 글
| 09. Backup Batch - 5 (0) | 2026.01.09 |
|---|---|
| 08. Backup Batch - 4 (0) | 2026.01.08 |
| 06. Backup Batch - 2 (0) | 2026.01.06 |
| 05. Backup Batch - 1 (0) | 2026.01.05 |
| 04. Project 구성 (1) | 2026.01.04 |
소중한 공감 감사합니다