💡 Spring Boot Quartz - Java 기반의 오픈 소스 작업 '스케줄링 라이브러리'를 의미합니다. 이를 사용하면 특정시간에 작업을 실행하거나 특정 간격으로 작업을 수행할 수 있습니다. -이를 사용하면 시스템의 자동화 및 효율성 향상에 기여하며 백그라운드 작업을 수행하는 서비스, 이메일 발송 스케줄링, 데이터베이스 백업 등에 활용될 수 있습니다.
1. 주요 클래스 및 인터페이스
용어
분류
설명
Job
인터페이스
실행할 작업을 정의하는 인터페이스
JobDetail
인터페이스
실행될 작업을 정의하고 구성하는 인터페이스
JobBuilder
클래스
JobDetail 인스턴스를 생성하는데 사용되는 유틸리티 클래스
JobListener
인터페이스
작업의 생명 주기 동안 발생하는 이벤트를 처리하는 인터페이스
JobDataMap
클래스
작업 실행시 필요한 데이터를 저장하는 맵
Trigger
인터페이스
작업의 실행 시간을 결정하는 인터페이스
CronTrigger
인터페이스
복잡한 실행 스케줄을 정의할 수 있는 Trigger 인터페이스
TriggerBuilder
클래스
Trigger 인스턴스를 생성하는데 사용되는 유틸리티 클래스
SimpleScheduleBuilder
클래스
간단한 실행 스케줄을 정의할 수 있는 클래스
CronScheduleBuilder
클래스
복잡한 실행 스케줄을 cron 표현식으로 정의할 수 있는 클래스
Scheduler
인터페이스
작업과 트리거를 관리하고 실행하는 인터페이스
SchedulerFactory
클래스
Scheduler 인스턴스를 생성하는 클래스
2) Job
💡 Job
- 스케줄러에서 사용되는 Job은 JobDetail 객체를 구성하기 위해 JobBuilder로 구성하며, 해당 JobBuilder에는 Job의 인터페이스를 MyJob이라는 클래스에서 구현체로 ‘수행해야 하는 작업’을 작성하여 Builder를 구성하여 사용합니다.
1. Job
💡 Job - Quarz에서 ‘실행 할 작업을 정의’하는 인터페이스입니다. 실행할 작업은 클래스를 생성하고 해당 Job 인터페이스의 구현체로 작업을 정의하여 사용합니다.
- Job 인터페이스를 구현하여 자신이 실행하고자 하는 작업에 대해서 정의를 할 수 있으며 Quartz의 생명 주기에 따라 주기적으로 실행이 됩니다.
JobDetail jobDetail = JobBuilder
.newJob(MyJob.class) // Job의 구현 클래스 설정
.withIdentity("myJob", "group1") // Job의 identity 설정
.withDescription("This is my job") // Job의 설명 설정
.ofType(MyJob.class) // Job의 구현 클래스 설정
.requestRecovery(true) // Job이 실패한 경우 다시 실행하도록 설정
.storeDurably(true) // Job이 지속적으로 저장되도록 설정
.usingJobData("key", "value") // Job의 data map 설정
.build(); // 설정된 정보를 바탕으로 JobDetail 인스턴스 생성
4. JobListener
💡 JobListener
- 스프링 배치에서 제공하는 인터페이스로 배치 작업의 생명주기 동안 발생하는 이벤트를 처리합니다. 이 인터페이스를 구현함으로써 개발자는 배치 작업이 시작되거나 완료될 때마다 특정 작업을 수행하도록 할 수 있습니다.
- 예를 들어, 작업이 시작될 때 데이터베이스에 로그를 기록하거나, 작업이 완료된 후에 이메일 알림을 보내는 등의 작업을 수행할 수 있습니다. 이러한 기능은 배치 작업의 상태를 모니터링하고, 문제가 발생했을 때 즉시 알아차릴 수 있도록 돕습니다.
메서드
반환 값
설명
getName()
String
JobListener의 이름을 반환합니다.
jobToBeExecuted()
void
Job이 실행되기 직전에 호출되는 메서드로, Job의 실행 준비 상황을 확인하거나 필요한 작업을 수행하는 데 사용할 수 있습니다.
jobExecutionVetoed()
void
다른 JobListener가 Job의 실행을 방해(veto)했을 때 호출되는 메서드입니다.
jobWasExecuted()
void
Job의 실행이 완료된 후에 호출되는 메서드로, Job의 실행 결과를 로깅하거나 후속 작업을 수행하는 데 사용할 수 있습니다.
package com.adjh.testproject;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
/**
* Job 생명주기동안에 발생하는 이벤트를 처리합니다.
*
* @author : jonghoon
* @fileName : MyJobListener
* @since : 2/26/24
*/
public class MyJobListener implements JobListener {
@Override
public String getName() {
return "MyJobListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
System.out.println("Job 실행되기 이전에 수행됩니다.");
}
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
System.out.println("Job 실행이 실패하였을때 수행됩니다.");
}
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
System.out.println("Job 실행 이후에 수행됩니다.");
}
}
💡 사용예시 - 구성한 JobListener를 스케줄러 내에 추가하여 scheduler가 실행되는 동안 Job의 생명주기를 확인 할 수 있습니다.
public static void main(String[] args) throws SchedulerException {
JobDetail jobDetail = JobBuilder
.newJob(MyJob.class) // Job의 구현 클래스 설정
.withIdentity("myJob", "group1") // Job의 identity 설정
.withDescription("This is my job") // Job의 설명 설정
.ofType(MyJob.class) // Job의 구현 클래스 설정
.requestRecovery(true) // Job이 실패한 경우 다시 실행하도록 설정
.storeDurably(true) // Job이 지속적으로 저장되도록 설정
.usingJobData("key", "value") // Job의 data map 설정
.build(); // 설정된 정보를 바탕으로 JobDetail 인스턴스 생성
// Trigger 생성
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever())
.build();
// 스케줄러 생성 및 Job, Trigger 등록
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
MyJobListener myJobListener = new MyJobListener();
scheduler.getListenerManager().addJobListener(myJobListener); // JobListener 등록
scheduler.start();
scheduler.scheduleJob(jobDetail, trigger);
}
5. JobDataMap
💡 JobDataMap - Quartz 스케줄러에서 Job의 상태 데이터를 관리하는 데 사용되는 클래스입니다. 이 클래스는 키와 값의 쌍으로 이루어진 데이터를 저장하는 Map 인터페이스를 확장한 것입니다.
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("key", "value");
JobDetail job = JobBuilder
.newJob(FcmJob.class) // Job 구현 클래스
.withIdentity("fcmSendJob", "fcmGroup") // Job 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Job") // Job 설명
.setJobData(jobDataMap)
.build();
3) Trigger
💡 Trigger
- 스케줄러에서 사용되는 Trigger는 SimpleTrigger와 CronTrigger로 나뉩니다.
- SimpleTrigger는 Trigger로 인스턴스화 해야합니다. 이는 TriggerBuilder를 통해 구성하며 withSchedule() 메서드를 통해서 SimpleScheduleBuilder를 구성합니다. - CronTrigger는 Trigger 혹은 CronTrigger로 인스턴스화해야 합니다. 이는 TriggerBuilder를 통해 구성하며 withSchedule() 메서드를 통해서 CronScheduleBuilder를 구성합니다.
1. Trigger
💡 Trigger
- Quartz의 Trigger는 스케줄링 작업의 실행 시간을 결정하는 역할을 합니다. Trigger는 간단히 말해 작업이 언제 실행될지를 정의하는 것입니다. - Trigger 역시 JobDetail 객체와 비슷하게 TriggerBuilder로 인스턴스를 생성합니다. - Quartz는 SimpleTrigger와 CronTrigger 두 가지 주요한 Trigger를 제공합니다.
1. SimpleTrigger - 특정 시간에 시작하여 주기적으로 작업을 실행하도록 설정할 수 있습니다. 이때 실행 주기와 반복 횟수를 설정해야 합니다
2. CronTrigger - UNIX cron 표현식을 사용하여 작업 실행 스케줄을 정의할 수 있습니다. 복잡한 실행 스케줄을 정의할 수 있으므로, 매주 특정 요일에 실행되거나 매월 특정 날에 실행되는 등의 스케줄을 설정할 수 있습니다.
💡 CronScheduleBuilder 사용예시 - Trigger를 인스턴스화 할때 TriggerBuilder를 사용하여 구성합니다. - TriggerBuilder에서는 cronSchedule을 사용하여 구성하였고 10초마다 반복되는 트리거를 사용하였습니다.
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("fcmSendTrigger", "fcmGroup") // Trigger 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Trigger") // Trigger 설명
.startNow()
.withSchedule(
CronScheduleBuilder
.cronSchedule("0/10 * * * * ?")
)
.build();
2. CronTrigger
💡 CronTrigger - 작업을 주기적으로 실행하도록 설정하는 데 사용됩니다. 이는 UNIX의 cron 표현식을 사용하여 태스크의 실행 스케줄을 정의하며, 이를 통해 매우 복잡한 실행 스케줄을 설정할 수 있습니다. - 예를 들어, 매주 특정 요일에 실행하거나, 매월 특정 날에 실행되는 등의 스케줄을 설정할 수 있습니다.
💡 사용예시 - 10초마다 수행하는 Trigger를 구성하였습니다.
CronTrigger cronTrigger = TriggerBuilder
.newTrigger()
.withIdentity("fcmSendTrigger", "fcmGroup") // Trigger 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Trigger") // Trigger 설명
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();
3. TriggerBuilder
💡 TriggerBuilder
- Quartz 스케줄러에서 트리거를 생성하는 데 사용되는 유틸리티 클래스입니다. 이 클래스를 사용하면 트리거의 다양한 속성을 설정하고, 최종적으로 트리거 인스턴스를 생성할 수 있습니다.
- TriggerBuilder를 사용하여 트리거 이름, 그룹, 연관된 작업, 시작 시간, 종료 시간, 스케줄, 우선순위 등을 설정할 수 있습니다. 또한, JobDataMap을 사용하여 트리거가 실행될 때 필요한 추가 데이터를 제공할 수 있습니다.
TriggerBuilder는 SimpleTriggerBuilder와 CronTriggerBuilder 같은 여러 하위 클래스를 가지고 있습니다 1. SimpleTriggerBuilder - 트리거가 특정 시간에 시작하여 일정한 간격으로 반복 실행되도록 설정할 수 있습니다 2. CronTriggerBuilder - 복잡한 스케줄 패턴을 정의할 수 있습니다.
- 일정을 쉽게 만들 수 있는 도구입니다. 사용자는 특정 일정의 시작시간, 종료시간, 그리고 반복되는 패턴을 설정할 수 있습니다. - 또한, 각 일정에 대한 설명, 위치, 참가자 등의 정보를 추가할 수 있습니다. SimpleScheduleBuilder를 사용하면 복잡한 일정도 간편하게 관리할 수 있습니다.
💡 Scheduler - 스케줄러에서 사용되는 Scheduler는 StdSchedulerFactory를 통해 스케줄러를 가져와서 Scheduler Job을 구성합니다. 또한 Job의 생명주기를 관리하는 JobListener를 등록할 수 있습니다. - Scheduler Job에는 Job과 Trigger가 등록이 됩니다.
1. Scheduler
💡 Scheduler - 여러 작업(Job)을 관리하고 이들을 트리거(Trigger)에 따라 실행합니다. 스케줄러는 작업의 실행 시간, 빈도 등을 제어하며, 필요에 따라 작업을 일시 중지하거나 재개할 수 있습니다. - 또한, 스케줄러는 작업의 실행 상태를 추적하고 작업 완료 후의 후속 작업을 관리하는 등의 역할을 합니다.
메서드
리턴 타입
설명
addCalendar(calName, calendar, replace, updateTriggers)
- Quartz 스케줄러의 가장 일반적인 구현체입니다. 이 클래스는 Scheduler 인스턴스를 생성하고 초기화하는 역할을 합니다. - 설정 파일이나 직접 전달된 Properties 객체를 이용해 스케줄러를 설정할 수 있습니다. 또한, 스케줄러가 사용하는 스레드 풀, JobStore 등의 구성 요소를 관리하고 설정합니다.
메서드 명
반환 타입
설명
getScheduler()
Scheduler
Scheduler 인스턴스를 반환합니다. 만약 아직 스케줄러가 초기화되지 않았다면 초기화를 수행한 후 반환합니다.
getScheduler(String schedName)
Scheduler
주어진 이름에 해당하는 Scheduler 인스턴스를 반환합니다.
getAllSchedulers()
Collection<Scheduler>
현재 팩토리에 의해 생성된 모든 Scheduler 인스턴스들을 반환합니다.
initialize()
void
기본 quartz.properties 파일을 사용해 스케줄러 팩토리를 초기화합니다.
initialize(String filename)
void
주어진 파일 이름의 properties 파일을 사용해 스케줄러 팩토리를 초기화합니다.
initialize(Properties props)
void
주어진 Properties 객체를 사용해 스케줄러 팩토리를 초기화합니다.
5) 환경 구성
1. 의존성 주입
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-quartz:3.2.3' // Spring Boot Quartz
}
2. Job 구성
💡 Job 구성 - Job 인터페이스의 구현체로 구성합니다. 해당 클래스는 실제 데이터 처리를 위한 작업 내용을 명시합니다. - Job 인터페이스에서 제공하는 메서드인 execute() 내에 실제 데이터 처리를 위한 Job을 구성합니다.
package com.adjh.multiflexapi.scheduler.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
/**
* 실제 데이터 처리를 위한 Job을 구성합니다.
*
* @author : lee
* @fileName : MyJob
* @since : 2/28/24
*/
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) {
System.out.println("실제 수행하는 Job 입니다.");
}
}
3. JobListener 구성
💡 JobListener 구성
- JobListener 인터페이스의 구현체로 구성합니다. 해당 클래스에서는 Job이 실행 직전, 실행 완료, 실행중 오류가 발생하였을 때의 생명주기를 관리하는 클래스입니다.
메서드 명
반환 값
설명
getName
String
JobListener의 이름을 반환합니다.
jobToBeExecuted
void
Job이 실행되기 직전에 호출되는 메서드로, Job의 실행 준비 상황을 확인하거나 필요한 작업을 수행하는 데 사용할 수 있습니다.
jobExecutionVetoed
void
다른 JobListener가 Job의 실행을 방해(veto)했을 때 호출되는 메서드입니다.
jobWasExecuted
void
Job의 실행이 완료된 후에 호출되는 메서드로, Job의 실행 결과를 로깅하거나 후속 작업을 수행하는 데 사용할 수 있습니다.
package com.adjh.multiflexapi.scheduler.job;
import org.quartz.JobListener
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Job의 생명주기를 관리합니다.
*
* @author : lee
* @fileName : MyJobListener
* @since : 2/28/24
*/
public class MyJobListener implements JobListener {
@Override
public String getName() {
return "hello";
}
/**
* Job 실행 이전 수행
*
* @param jobExecutionContext
*/
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
System.out.println("[-] Job이 실행되기전 수행됩니다");
}
/**
* Job 실행 취소 시점 수행
*
* @param jobExecutionContext
*/
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
System.out.println("[-] Job이 실행 취소된 시점 수행됩니다.");
}
/**
* Job 실행 완료 시점 수행
*
* @param jobExecutionContext
* @param e
*/
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
System.out.println("[+] Job이 실행 완료된 시점 수행됩니다.");
}
}
4. SchedulerConfig 구성
💡 SchedulerConfig 구성 - 해당 클래스에서는 스케줄러를 구성하는 과정을 정의한 클래스입니다. Job, Trigger, Scheduler 인스턴스를 생성하여 일정 시간마다 수행하도록 구성합니다. - 아래의 예시에서는 트리거에서 10초마다 반복처리 되어 수행되도록 구성되어 있습니다.
메서드
설명
jobProgress
생성자 구성 및 초기 구성을 마치고 @PostConstruct 어노테이션에 따라 수행이 되며 스케줄러를 선택하여 호출하면 스케줄러가 수행이 되는 메서드입니다.
simpleScheduler
SimpleScheduler를 이용하여서 스케줄러를 구성하는 메서드입니다.
cronScheduler
CronScheduler를 이용하여 스케줄러를 구성하는 메서드입니다.
package com.adjh.multiflexapi.scheduler;
import com.adjh.multiflexapi.scheduler.job.MyJob;
import com.adjh.multiflexapi.scheduler.job.MyJobListener;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/**
* 스케줄러의 설정을 관리합니다.
*
* @author : lee
* @fileName : ScheduleConfig
* @since : 2/28/24
*/
@Configuration
public class SchedulerConfig {
private Scheduler scheduler;
public SchedulerConfig(Scheduler scheduler) {
this.scheduler = scheduler;
}
/**
* 스케줄러의 실제 처리 과정을 담당합니다.
*/
@PostConstruct
private void jobProgress() throws SchedulerException {
cronScheduler();
}
/**
* SimpleScheduler 구성 메서드
*
* @throws SchedulerException
*/
private void simpleScheduler() throws SchedulerException {
// [STEP1] Job 생성
JobDetail job = JobBuilder
.newJob(MyJob.class) // Job 구현 클래스
.withIdentity("myJob", "myGroup") // Job 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Job") // Job 설명
.build();
// [STEP2] Trigger 생성
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("myTrigger", "myGroup") // Trigger 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Trigger") // Trigger 설명
.startNow()
.withSchedule(
SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(5)
.repeatForever())
.build();
// [STEP3] 스케줄러 생성 및 Job, Trigger 등록
scheduler = new StdSchedulerFactory().getScheduler();
MyJobListener myJobListener = new MyJobListener();
scheduler.getListenerManager().addJobListener(myJobListener);
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
/**
* CronScheduler 구성 메서드
*/
private void cronScheduler() throws SchedulerException {
// [STEP1] Job 생성
JobDetail job = JobBuilder
.newJob(MyJob.class) // Job 구현 클래스
.withIdentity("myJob", "myGroup") // Job 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Job") // Job 설명
.build();
CronTrigger cronTrigger = TriggerBuilder
.newTrigger()
.withIdentity("fcmSendTrigger", "fcmGroup") // Trigger 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Trigger") // Trigger 설명
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();
// [STEP3] 스케줄러 생성 및 Job, Trigger 등록
scheduler = new StdSchedulerFactory().getScheduler();
MyJobListener myJobListener = new MyJobListener();
scheduler.getListenerManager().addJobListener(myJobListener);
scheduler.start();
scheduler.scheduleJob(job, cronTrigger);
}
}