이번 포스팅에서는 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분에 실행