이번 포스팅에서는 Spring Boot
에서 @Scheduled
어노테이션을 이용하여 스케쥴러를 구현하는 방법에 대해 작성하고자 합니다.
먼저, @Scheduled
를 사용하기 위해서는 Application 클래스에서 @EnableScheduling
을 설정해주어야합니다.
@EnableScheduling
@SpringBootApplication
public class SchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerApplication.class, args);
}
}
이제 스케쥴러를 구현할 클래스를 생성합니다.
이때 유의할 점은 해당 스케쥴러가 스프링 빈
에 등록되어야 합니다. 저는 @Component
애노테이션을 이용해서 빈에 등록했습니다.
아래 코드는 예시로, 60초마다 장비의 상태를 동기화하는 코드를 작성했습니다.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
@Slf4j
public class SchedulerConfiguration {
private final DeviceService deviceService;
@Scheduled(fixedDelay = 60000)
public void run() {
deviceService.synchronize();
}
}
@Scheduled
속성을 이용하여 스케쥴 옵션을 다양하게 설정할 수 있습니다. 속성에는 크게 fixedDelay
, fixedRate
, initDelay
, cron
등이 있습니다.
fixedRate
: 작업 수행시간과 상관없이 일정 주기마다 메소드를 호출하는 것fixedDelay
는 (작업 수행 시간을 포함하여) 작업을 마친 후부터 주기 타이머가 돌아 메소드를 호출initialDelay
: 스케줄러에서 메소드가 등록되자마자 수행하는 것이 아닌 초기 지연시간을 설정cron
: Cron 표현식을 사용하여 작업을 예약fixedDelay
: milliseconds 단위로, 이전 Task의 종료 시점으로부터 정의된 시간만큼 지난 후 Task를 실행합니다.@Scheduled(fixedDelay = 1000)
public void run() {
log.info("Scheduler 실행");
}
fixedDelayString
: fixedDelay와 같은데 문자열로 값을 표현하겠다는 의미입니다.@Scheduled(fixedDelayString = "1000")
public void run() {
log.info("Scheduler 실행");
}
fixedRate
: milliseconds 단위로, 이전 Task의 시작 시점으로부터 정의된 시간만큼 지난 후 Task를 실행합니다.@Scheduled(fixedRate = 1000)
public void run() {
log.info("Scheduler 실행");
}
fixedRateString
: fixedRate와 같은데 문자열로 값을 표현하겠다는 의미입니다.@Scheduled(fixedRateString = "1000")
public void run() {
log.info("Scheduler 실행");
}
initialDelay
: 스케줄러에서 메소드가 등록되자마자 수행하는 것이 아닌 초기 지연시간을 설정하는 것입니다.@Scheduled(fixedRate = 5000, initialDelay = 3000)
public void run() {
log.info("Scheduler 실행");
}
위와 같이 사용하면 3초의 대기시간(initialDelay) 후에 5초(fixedRate)마다 로그를 출력하는 작업을 스케줄러가 수행해줍니다.
initialDelayString
: 위와 마찬가지로 문자열로 값을 표현하겠다는 의미입니다.cron 표현식은 Scheduling의 정규 표현식을 의미합니다. 크론(cron) 설정은 리눅스의 크론탭(crontab) 기능과 유사하게 설정할 수 있습니다.
리눅스의 크론탭(crontab)
리눅스의 크론탭은 특정 시간에 프로그램을 실행시키기 위해 사용하며, 윈도우에서는 스케줄러와 비슷합니다.
cron의 구성은 아래와 같이 총 6개의 필드로 구성됩니다.
필드 명 | 값의 허용 범위 | 허용된 특수문자 |
---|---|---|
초 (Seconds) | 0 ~ 59 | , - * / |
분 (Minutes) | 0 ~ 59 | , - * / |
시 (Hours) | 0 ~ 23 | , - * / |
일 (Day) | 1 ~ 31 | , - * ? / L W |
월 (Month) | 1 ~ 12 or JAN ~ DEC | , - * / |
요일(Week) | 0 ~ 6 or SUN ~ SAT | , - * ? / L # |
특수 문자 | 설명 | 예제 |
---|---|---|
* | 모든 값 의미 | |
? | 특정한 값이 없음을 의미 | |
- | 범위를 나타낼 때 사용 | 월요일부터 수요일까지 -> MON-WED |
, | 특정 값을 여러 개 나열할 때 사용 | 월,수,금 -> MON,WED,FRI |
/ | 시작 시간 / 단위 | 0분부터 매 5분 -> 0/5 |
L | 일에서 사용하면 마지막 일, 요일에서 사용하면 마지막 요일(토요일) | |
W | 가장 가까운 평일 | 15W -> 15일에서 가장 가까운 평일 |
# | 몇째 주의 무슨 요일을 표현 | 3#2 -> 2번째주 수요일 |
0 0/5 * * * ?
: 매 5분마다 실행0 0 0/1 * * ?
: 매 1시간마다 실행0 0 12 * * ?
: 매일 낮 12시에0 15 10 ? * *
: 매일 오전 10:15분에 실행0 15 10 * * ?
: 매일 오전 10:15분에 실행0 * 14 * * ?
: 매일 오후 2:00에 시작해서 매분마다 실행하고 2:59분에 종료0 0/5 14,18 * * ?
: 매일 오후 2:00에 시작해서 5분마다 실행되어 2:55에 끝나고, 6:00에 시작하여 5분마다 실행되어 6:55에 종료0 0-5 14 * * ?
: 매일 오후 2:00에 시작하여 매분마다 실행하고 오후 2:05분에 종료0 10,44 14 ? 3 WED
: 3월 동안 오후 2:10과 2:44 실행0 15 10 ? * MON-FRI
: 주중 오전 10:15분에0 15 10 15 * ?
: 매달 15일 오전 10:15에0 15 10 L * ?
: 매월 말일 오전 10:15에0 15 10 ? * 6L
: 매월 마지막 금요일 오전 10:15에0 15 10 ? * 6#3
: 매월 3째주 금요일 오전 10:15에애플리케이션을 개발하다 보면 하드 코딩(소스 내에서 관리) 보다는 유연한 설정(환경 설정 파일에서 관리)을 지원하는 것이 필요합니다. application.yml
파일에 아래와 같이 스케쥴러 설정 정보를 추가해줍니다.
schedule:
cron: 0 0 0 * * *
use: true
이제 아래와 같이 코드를 작성하면, 옵션에 따라 스케쥴러를 실행합니다.
@Component
@Slf4j
@RequiredArgsConstructor
public class CronTable {
private final JobService jobService;
@Value("${schedule.use}")
private boolean useSchedule;
@Scheduled(cron = "${schedule.cron}")
public void mainJob() {
try {
if (useSchedule) {
jobService.run();
}
} catch (InterruptedException e) {
log.info("* Thread가 강제 종료되었습니다. Message: {}", e.getMessage());
} catch (Exception e) {
log.info("* Batch 시스템이 예기치 않게 종료되었습니다. Message: {}", e.getMessage());
}
}
}
안녕하세요. 좋은 글 감사합니다.
질문이 생겨서 댓글 남깁니다.
매일 실행 하는 거라면 ?를 안쓰고 *로만 3개를 채워도 될꺼라고 생각 하는데 ? 를 써줘야 하는 이유가 있나요?
0 0 12 * ? : 매일 낮 12시에
0 15 10 ? : 매일 오전 10:15분에 실행
0 15 10 * ? : 매일 오전 10:15분에 실행