Spring 프레임워크에서 제공하는 @Scheduled어노테이션은 주기적으로 실행해야 하는 작업 데이터 정리, 알림 전송, 백업 등의 작업을 자동화하는 도구입니다.

이번 포스팅에서는 @Scheduled어노테이션에 대한 개념과 이를 활용한 주기적인 작업 실행 예제를 다뤄보도록 하겠습니다.

 

@Scheduled 란?

  • `@Scheduled`는 Spring에서 제공하는 스케줄링 작업을 정의하는 어노테이션입니다. 이를 통해 특정 메서드를 주기적으로 실행하거나, 예약된 시간에 실행하도록 설정할 수 있습니다.
  • Spring에서 `@Scheduled` 어노테이션은 기본적으로 `싱글 스레드`에서 실행되며, 만약 두 개 이상의 `@Scheduled` 작업이 정의되어 있어도, 별도의 `병렬 스케줄링 설정`을 하지 않으면 순차적으로 실행됩니다. 즉, 여러 작업이 동시에 실행되지 않고, 하나씩 차례로 실행되는 것입니다.
  • `@Scheduled` 어노테이션을 활성화하려면 애플리케이션에 `@EnableScheduling`을 추가해야 합니다. 이 어노테이션을 사용하면 `@Scheduled` 어노테이션이 붙은 메서드들이 `자동`으로 실행됩니다.

 

사용 방법

스케줄링 활성화

@EnableScheduling
@SpringBootApplication
public class SchedulerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SchedulerApplication.class, args);
    }
}

`@Scheduled`사용을 위해 `Application 클래스`에서 `@EnableScheduling`을 설정합니다.

 

@Scheduled 어노테이션 적용

@Slf4j
@Component
public class SchedulerTask {

    @Scheduled(fixedDelay = 1000) // 1초마다 실행
    public void run() {
        log.info("Scheduler 실행");
    }
}

`@Component`를 사용하여 해당 스케줄러를 `Spring Bean`에 등록합니다.

 

Scheduler 실행 이미지

해당 클래스를 `Bean`으로 등록 후 애플리케이션을 실행하면 설정한 시간마다 실행되는 모습을 확인할 수 있습니다.

 

@Scheduled 속성

`@Scheduled` 속성을 이용하여 스케줄 옵션을 다양하게 설정할 수 있습니다.

@Scheduled cron 속성 설명 이미지

@Scheduled(cron = "0 0 7 * * ?") // 매일 07시에 실행
public void run() {
	log.info("Scheduler 실행");
}
  • cron
    • 크론`cron` 정규표현식을 사용해 원하는 시간대나 요일에 작업을 실행합니다.
  • cron 필드
    • `필드 명` : `값의 허용 범위` : `허용된 특수 문자`
    • 초`Seconds` : `0 ~ 59` : `, - * /`
    • 분 `Minutes` : `0 ~ 59` : `, - * /`
    • 시 `Hours` : `0 ~ 23` : `, - * /`
    • 일 `Day` : `1 ~ 31` : `, - * ? / L W`
    • 월 `Month` : `1 ~ 12 또는 JAN ~ DEC` : `, - * /`
    • 요일 `Week` : `0 ~ 6 또는 SUN ~ SAT` : `, - * ? L #`
  • 특수 문자
    • `*` : 모든 값을 의미
    • `?` : 특정한 값이 없음을 의미
    • `-` : 범위를 나타낼 때 사용 → `월요일부터 수요일까지 - MON-WED`
    • `,` : 특정 값을 여러 개 나열할 때 사용 → `월,수,금 - MON,WED,FRI`
    • `/` : 시작 시간 /단위 → `0분부터 매 5분 - 0/5`
    • `L` : 일에서 사용하면 마지막 일, 요일에서 사용하면 마지막 요일(토요일)
    • `W` : 가장 가까운 평일 → `15일에서 가장 가까운 평일 - 15W`
    • `#` : 몇째 주의 무슨 요일을 표현 → `2번째주 수요일 - 3#2`

 

@Scheduled(fixedDelay = 10000) // 이전 작업 종료 후 10초 뒤 실행
public void run1() {
	log.info("Scheduler 실행1");
}

@Scheduled(fixedDelayString = "10000") // 문자열
public void run2() {
    log.info("Scheduler 실행2");
}
  • fixedDelay
    • ms 단위
    • 이전 Task의 종료 시점으로부터 정의된 시간만큼 지난 후 Task를 실행합니다. `이전 작업이 종료된 시점 기준으로 일정 시간 뒤 실행`
    • `fixedDelayString` fixedDelay와 동일하지만, property의 값을 String으로 입력합니다.

 

@Scheduled(fixedRate = 10000) // 10초 간격으로 실행
public void run1() {
    log.info("Scheduler 실행1");
}

@Scheduled(fixedRateString = "10000") // 문자열
public void run2() {
    log.info("Scheduler 실행2");
}
  • fixedRate
    • ms 단위
    • 이전 Task의 시작 지점으로부터 정의된 시간만큼 지난 후 Task를 실행합니다. `이전 작업이 시작된 시점 기준으로 일정 간격으로 실행`
    • `fixedRateString` fixedRate와 동일하지만, property의 값을 String으로 입력합니다.

 

@Scheduled(fixedRate = 10000, initialDelay = 1000) // 1초 대기 후 시작, 10초 간격 실행
public void run() {
    log.info("Scheduler 실행");
}
  • initialDelay
    • 스케줄러에서 메서드가 등록되자마자 수행하는 것이 아닌, 초기 지연시간을 설정합니다. `작업이 시작되기 전 초기 대기 시간을 설정`
    • `initialDelayString` initialDelay와 동일하지만, property의 값을 String으로 입력합니다.

 

@Scheduled(zone = "Asia/Seoul", cron = "0 0 7 * * ?") // 타임존 서울, 매일 07시에 실행
public void run() {
    log.info("Scheduler 실행");
}
  • zone
    • time zone 설정
    • default : 서버의 time zone

 

Thread Pool 설정

Thread Pool 설정 예시 이미지

 

@Scheduled(zone = "Asia/Seoul", fixedDelay = 1000) // 타임존 서울, 이전 작업이 끝난 후 1초마다 실행
public void run1() {
    log.info("Scheduler 실행1 : {}", Thread.currentThread().getName());
}

@Scheduled(zone = "Asia/Seoul", fixedDelay = 1000) // 타임존 서울, 이전 작업이 끝난 후 1초마다 실행
public void run2() {
    log.info("Scheduler 실행2 : {}", Thread.currentThread().getName());
}

실행중인 스레드 이미지

앞서 설명한 것처럼 `@Scheduled` 어노테이션은 기본적으로 `싱글 스레드`에서 실행됩니다. `하나의 스레드에서 여러개의 Task가 실행`될 경우 `run1`이 종료되어야만 `run2`가 실행되는 구조입니다.

 

만약 작업 중 `Slow Query` 또는 `긴 시간을 소모하는 연산`과 같은 성능 저하 요인이 존재한다면

`첫 번째 Task의 종료 시간 지연` → `두 번째 Task의 시작 지연` → `결과적으로 모든 스케줄링 작업에 지연 발생`

 

이러한 문제를 해결하기 위해 `Thread Pool`을 설정하여 여러 스레드에서 작업을 병렬로 실행할 수 있도록 구성할 수 있습니다.

 

Config 설정

@Configuration
public class SchedulerConfig {

    @Bean
    public ThreadPoolTaskScheduler configureScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(5); // 스레드 풀 크기
        threadPoolTaskScheduler.setThreadGroupName("tao scheduler thread pool"); // 스레드 그룹명
        threadPoolTaskScheduler.setThreadNamePrefix("tao-scheduler-"); // 스레드명 접두사
        threadPoolTaskScheduler.initialize(); // 스케줄러 초기화
        return threadPoolTaskScheduler;
    }
}

 

Thread Pool 이미지

실행되는 Task가 각각 다른 스레드에서 실행되는 모습을 확인할 수 있습니다.

 

마무리

이번 포스팅에서는 Spring Boot의 `@Scheduled` 어노테이션을 사용하여 주기적인 작업을 자동화하는 방법에 대해 알아보았습니다. `@Scheduled`를 활용하면 복잡한 스케줄링 작업을 손쉽게 관리할 수 있고, 다양한 옵션을 통해 세밀한 제어가 가능합니다.