[Spring Boot] @Scheduled를 이용한 스케줄러 구현
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`에 등록합니다.
해당 클래스를 `Bean`으로 등록 후 애플리케이션을 실행하면 설정한 시간마다 실행되는 모습을 확인할 수 있습니다.
@Scheduled 속성
`@Scheduled` 속성을 이용하여 스케줄 옵션을 다양하게 설정할 수 있습니다.
@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 설정
@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;
}
}
실행되는 Task가 각각 다른 스레드에서 실행되는 모습을 확인할 수 있습니다.
마무리
이번 포스팅에서는 Spring Boot의 `@Scheduled` 어노테이션을 사용하여 주기적인 작업을 자동화하는 방법에 대해 알아보았습니다. `@Scheduled`를 활용하면 복잡한 스케줄링 작업을 손쉽게 관리할 수 있고, 다양한 옵션을 통해 세밀한 제어가 가능합니다.
'Spring' 카테고리의 다른 글
[SpringBoot] AWS SES로 이메일 전송 기능 구현하기 (0) | 2025.01.12 |
---|---|
[SpringBoot] 다양한 동시성 제어 방법 (2) | 2024.12.20 |
[SpringBoot] Prometheus, Grafana를 이용한 모니터링 (0) | 2024.12.10 |
[Spring] Redis를 사용한Session 로그인 구현, Security없이 인증, 인가 구현 (26) | 2024.11.14 |
[Spring] offset, no offset 차이점과 페이지네이션 구현예제 (6) | 2024.10.23 |