[아이티센 부트캠프] Spring Boot 6

이언덕·2026년 4월 27일

아이티센 부트캠프

목록 보기
75/115
post-thumbnail

Spring Scheduling(Task)

이 단원은 Spring Scheduling(Task)를 이용해 사용자의 요청 없이도 서버가 정해진 시간 규칙에 따라 메서드를 자동 실행하는 흐름을 이해하는 구간이다.
일반 Controller 메서드는 브라우저 요청이 들어와야 실행되지만, 스케줄링 메서드는 서버가 실행 중이면 설정된 주기에 따라 자동으로 실행된다.


웹 애플리케이션은 보통 사용자의 요청을 기다린다.
예를 들어 사용자가 특정 URL에 접속하면 Controller가 실행되고, 그 결과 화면이 응답된다.
하지만 모든 작업이 사용자 요청으로만 실행되는 것은 아니다.


어떤 작업은 사람이 직접 요청하지 않아도 정해진 시간마다 자동으로 실행되어야 한다.
예를 들어 일정 시간마다 로그를 남기거나, 매일 새벽 데이터를 정리하거나, 주기적으로 상태를 확인하는 작업이 있다.
이런 반복 작업을 처리할 때 사용하는 기능이 Spring Scheduling이다.
스케줄링은 서버가 켜져 있는 동안 정해진 시간 규칙에 맞춰 특정 메서드를 자동 실행하는 기능이다.


스케줄링이 필요한 이유

Scheduling은 일정한 시간 규칙에 따라 작업을 자동으로 실행하는 기능이다.
여기서 작업은 특정 메서드 안에 작성한 코드라고 이해하면 된다.
예를 들어 5초마다 로그를 출력하거나, 매일 밤 오래된 데이터를 정리하거나, 일정 시간마다 서버 상태를 확인하는 작업이 여기에 해당한다.


일반적인 Controller 메서드는 사용자가 요청해야 실행된다.
예를 들어 /hello라는 주소를 만들었다면, 사용자가 브라우저에서 /hello로 접속해야 해당 메서드가 실행된다.
즉, 실행 기준이 사용자 요청이다.


반대로 스케줄링 메서드는 사용자의 요청을 기다리지 않는다.
서버가 실행 중이고 스케줄링 설정이 되어 있다면, 정해진 시간마다 자동으로 실행된다.
즉, 실행 기준이 사용자 요청이 아니라 시간 규칙이다.


두 흐름을 비교하면 이렇다.

  • Controller 메서드는 사용자가 요청해야 실행된다.
  • 스케줄링 메서드는 정해진 시간이 되면 자동으로 실행된다.
  • Controller 메서드는 요청과 응답 흐름에 가깝다.
  • 스케줄링 메서드는 서버 내부의 반복 작업 흐름에 가깝다.

스케줄링이 필요한 이유는 반복 작업을 사람이 직접 실행하지 않고 서버가 자동으로 처리하게 만들기 위해서이다.
반복되는 작업을 매번 사람이 실행하면 실수하기 쉽고, 시간을 계속 신경 써야 한다.
스케줄링을 사용하면 이런 반복 작업을 코드로 정해 두고 자동 실행할 수 있다.


Task와 Scheduled의 관계

Task는 반복적으로 실행할 작업 자체를 의미한다.
어렵게 생각하지 말고, “정해진 시간마다 실행할 메서드”라고 이해하면 된다.
예를 들어 현재 시간을 출력하는 메서드가 있고, 이 메서드를 5초마다 실행하고 싶다면 그 메서드가 하나의 Task가 된다.


@Scheduled는 이 Task를 언제 실행할지 알려 주는 어노테이션이다.
어노테이션은 코드 위에 붙여서 Spring에게 의미를 알려 주는 표시이다.
즉, @Scheduled를 메서드 위에 붙이면 Spring은 이 메서드를 일반 메서드가 아니라 정해진 시간마다 실행해야 하는 메서드로 인식한다.


관계를 정리하면 이렇다.

  • Task는 실행할 작업이다.
  • @Scheduled는 작업을 언제 실행할지 정하는 표시이다.
  • @Scheduled가 붙은 메서드는 개발자가 직접 호출하지 않아도 Spring이 시간 규칙에 따라 실행한다.

여기서 중요한 점은 메서드를 직접 호출하지 않는다는 것이다.
일반 메서드는 보통 다른 코드에서 methodName()처럼 호출해야 실행된다.
하지만 스케줄링 메서드는 @Scheduled 설정에 맞춰 Spring이 자동으로 실행한다.


스케줄링 메서드는 내가 직접 호출하는 메서드가 아니라, Spring이 시간 규칙에 따라 대신 호출해 주는 메서드이다.
이 흐름을 이해해야 뒤에서 보는 fixedRate, fixedDelay, cron 설정도 자연스럽게 이어진다.


스케줄링을 사용하기 위한 기본 조건

스케줄링을 사용하려면 먼저 Spring이 해당 메서드를 관리할 수 있어야 한다.
Spring이 모르는 클래스 안에 @Scheduled를 붙여도 제대로 동작하지 않는다.
그래서 스케줄링 메서드가 들어 있는 클래스는 Spring Bean으로 등록되어야 한다.


Spring Bean은 Spring이 직접 만들고 관리하는 객체를 의미한다.
초보자 기준에서는 “Spring이 알고 있는 객체”라고 이해하면 된다.
@Component를 클래스 위에 붙이면 해당 클래스를 Spring Bean으로 등록할 수 있다.


또 하나 필요한 조건은 스케줄링 기능을 켜는 것이다.
Spring Boot에서는 보통 시작 클래스에 @EnableScheduling을 붙여 스케줄링 기능을 활성화한다.
@EnableScheduling은 @Scheduled가 붙은 메서드를 찾아서 시간 규칙에 따라 실행할 수 있게 해 주는 설정이다.


XML 설정을 사용하는 Spring MVC 환경에서는 <task:annotation-driven/> 같은 설정으로 스케줄링 기능을 활성화할 수 있다.
이 글에서는 Spring Boot 기준의 @EnableScheduling 방식으로 먼저 이해하면 된다.
중요한 흐름은 어떤 방식이든 같다.
스케줄링 기능을 켜야 @Scheduled 메서드가 자동 실행될 수 있다.


@Scheduled가 붙는 메서드 자체에도 기본적으로 지켜야 할 형태가 있다.
스케줄링 메서드는 보통 매개변수 없이 만든다.
외부 요청에서 값을 받는 메서드가 아니라, Spring이 정해진 시간에 자동으로 호출하는 메서드이기 때문이다.
또 화면 이름을 반환하는 Controller 메서드가 아니므로 보통 void로 작성한다.
반환값이 있더라도 스케줄링 실행 흐름에서는 그 값을 화면 응답처럼 사용하지 않는다고 이해하면 된다.


즉, 기본 조건은 이렇게 정리할 수 있다.

  • 스케줄링 기능을 활성화해야 한다.
  • 스케줄링 메서드가 있는 클래스는 Spring Bean이어야 한다.
  • 실행할 메서드 위에 @Scheduled를 붙여야 한다.
  • @Scheduled 안에 실행 주기나 시간 규칙을 작성해야 한다.
  • @Scheduled 메서드는 보통 매개변수 없이 작성한다.
  • @Scheduled 메서드는 보통 반환값 없이 void로 작성한다.

기본 개념예제

아래 예제는 스케줄링 기능을 켜고, 5초마다 현재 시간을 콘솔에 출력하는 가장 기본적인 흐름이다.
먼저 시작 클래스에 @EnableScheduling을 붙인다.

// SchedulingBasicApplication.java
package com.example.springedu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling // 스케줄링 기능을 활성화한다
public class SchedulingBasicApplication {
    public static void main(String[] args) {
        SpringApplication.run(SchedulingBasicApplication.class, args); // 서버를 실행한다
    }
}

@SpringBootApplication은 Spring Boot 애플리케이션의 시작점을 의미한다.
여기에 @EnableScheduling을 추가하면 Spring은 프로젝트 안에서 @Scheduled가 붙은 메서드를 찾아 자동 실행할 준비를 한다.

// SimpleScheduler.java
package com.example.springedu.scheduler;

import java.time.LocalDateTime;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SimpleScheduler {
    @Scheduled(fixedRate = 5000)
    public void printNow() {
        System.out.println("5초마다 실행 : " + LocalDateTime.now()); // 현재 시간을 출력한다
    }
}

SimpleScheduler에는 @Component가 붙어 있다.
그래서 이 클래스는 Spring Bean으로 등록된다.
printNow() 위에는 @Scheduled(fixedRate = 5000)이 붙어 있다.
fixedRate = 5000에서 5000은 기본적으로 밀리초 단위이다.
즉, 1000이 1초이고, 5000은 5초이다.


이 설정은 메서드를 5초마다 실행하라는 의미이다.

// 콘솔 결과 예시
// 5초마다 실행 : 2026-04-27T11:00:00.123
// 5초마다 실행 : 2026-04-27T11:00:05.123
// 5초마다 실행 : 2026-04-27T11:00:10.123

이 예제에서 브라우저 요청은 필요하지 않다.
서버가 실행되면 Spring이 @Scheduled 메서드를 찾아서 5초마다 자동으로 실행한다.
즉, 이 예제의 핵심은 사용자 요청 없이도 서버 내부에서 메서드가 자동 실행된다는 점이다.


예제를 하나씩 확인하고 싶다면 한 번에 하나의 스케줄러 클래스만 실행되도록 두는 것이 좋다.
여러 스케줄러 클래스가 모두 @Component로 등록되어 있으면 서버 실행 시 여러 작업이 동시에 자동 실행되어 콘솔 로그가 섞일 수 있다.


fixedRate와 fixedDelay 차이

@Scheduled에는 반복 실행 기준을 정하는 속성이 있다.
그중 자주 헷갈리는 것이 fixedRate와 fixedDelay이다.
둘 다 일정 시간마다 반복 실행한다는 점은 비슷하다.
하지만 다음 실행 시간을 계산하는 기준이 다르다.


먼저 숫자의 단위를 정확히 알아야 한다.
fixedRate = 3000, fixedDelay = 3000에서 3000은 3000밀리초를 의미한다.
밀리초는 1초를 1000으로 나눈 시간 단위이다.
그래서 3000은 3초이고, 5000은 5초이다.


fixedRate는 이전 작업이 시작된 시점을 기준으로 다음 실행 시간을 계산한다.
예를 들어 fixedRate = 5000이면, 첫 번째 작업이 시작된 시점부터 5초 뒤에 다음 작업을 실행하려고 한다.
즉, 기준은 작업 시작 시간이다.


fixedDelay는 이전 작업이 끝난 시점을 기준으로 다음 실행 시간을 계산한다.
예를 들어 fixedDelay = 5000이면, 첫 번째 작업이 끝난 뒤 5초를 기다렸다가 다음 작업을 실행한다.
즉, 기준은 작업 종료 시간이다.


작업이 2초 걸리고 설정값이 3초라고 생각하면 차이가 더 잘 보인다.
fixedRate = 3000은 작업 시작 시점부터 3초 뒤를 다음 실행 기준으로 본다.
반면 fixedDelay = 3000은 작업이 끝난 뒤 3초를 더 기다린다.
그래서 fixedDelay는 작업 시간까지 포함하면 실제 다음 실행 간격이 더 길어질 수 있다.


차이를 짧게 정리하면 이렇다.

  • fixedRate는 작업 시작 시점을 기준으로 반복한다.
  • fixedDelay는 작업 종료 시점을 기준으로 반복한다.
  • 작업 시간이 짧으면 둘의 차이가 크게 안 보일 수 있다.
  • 작업 시간이 길어지면 다음 실행 시점을 계산하는 방식이 달라진다.

fixedRate와 fixedDelay의 핵심 차이는 다음 실행 시간을 계산할 때 시작 시간을 기준으로 보느냐, 종료 시간을 기준으로 보느냐이다.
초보자는 먼저 이 기준 차이만 정확히 잡으면 된다.

기본 개념예제

아래 예제는 fixedRate와 fixedDelay를 비교하기 위한 기본 예제이다.
두 메서드 모두 3초라는 시간을 사용하지만, 실행 기준이 다르다.

// RateDelayScheduler.java
package com.example.springedu.scheduler;

import java.time.LocalDateTime;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class RateDelayScheduler {
    @Scheduled(fixedRate = 3000)
    public void fixedRateTask() {
        System.out.println("fixedRate 시작 기준 실행 : " + LocalDateTime.now()); // 시작 시점 기준으로 반복된다
    }

    @Scheduled(fixedDelay = 3000)
    public void fixedDelayTask() {
        System.out.println("fixedDelay 종료 기준 실행 : " + LocalDateTime.now()); // 종료 시점 기준으로 반복된다
    }
}

이 코드는 두 개의 스케줄링 메서드를 가지고 있다.
fixedRateTask()는 fixedRate = 3000이므로 시작 시점을 기준으로 3초마다 실행된다.
fixedDelayTask()는 fixedDelay = 3000이므로 이전 실행이 끝난 뒤 3초를 기다렸다가 다시 실행된다.

// 콘솔 결과 예시
// fixedRate 시작 기준 실행 : 2026-04-27T11:10:00.100
// fixedDelay 종료 기준 실행 : 2026-04-27T11:10:00.100
// fixedRate 시작 기준 실행 : 2026-04-27T11:10:03.100
// fixedDelay 종료 기준 실행 : 2026-04-27T11:10:03.100

이 예시처럼 작업이 아주 짧게 끝나면 두 결과가 비슷해 보일 수 있다.
하지만 실제 작업 시간이 길어지면 차이가 보인다.
fixedRate는 시작 시점을 기준으로 다음 실행을 잡고, fixedDelay는 작업이 끝난 뒤 기다리는 방식이기 때문이다.


예를 들어 어떤 작업이 2초 동안 실행되고 fixedDelay = 3000이라면, 다음 실행은 작업이 끝난 뒤 3초 후이다.
즉, 전체 간격은 작업 시간 2초와 대기 시간 3초를 합쳐 약 5초처럼 느껴질 수 있다.
반대로 fixedRate = 3000은 작업 시작 시점을 기준으로 3초마다 실행하려고 한다.


정리하면, 일정한 시작 간격을 중요하게 보면 fixedRate가 어울린다.
작업이 완전히 끝난 뒤 일정 시간 쉬고 다시 실행해야 한다면 fixedDelay가 어울린다.


예제 로그를 깔끔하게 보고 싶다면 SimpleScheduler와 CronScheduler를 잠시 주석 처리하고 RateDelayScheduler만 Spring Bean으로 등록된 상태에서 실행하면 된다.
여러 스케줄러가 동시에 등록되어 있으면 각 메서드가 자기 주기에 맞춰 실행되기 때문에 콘솔에 여러 로그가 함께 출력된다.


cron 표현식은 어떻게 읽는가

cron은 더 세밀한 시간 규칙을 문자열로 작성하는 방식이다.
fixedRate나 fixedDelay는 몇 밀리초마다 반복할지를 정한다.
반면 cron은 “매분 10초에 실행”, “매일 오전 9시에 실행”처럼 시간 조건을 더 구체적으로 작성할 수 있다.


cron 표현식은 여러 칸으로 구성된다.
각 칸은 초, 분, 시, 일, 월, 요일 같은 시간 요소를 의미한다.
칸의 위치가 의미를 가지기 때문에 순서를 잘못 쓰면 원하는 시간에 실행되지 않을 수 있다.


Spring의 @Scheduled에서 자주 사용하는 cron 표현식은 보통 다음 순서로 읽는다.

  • Seconds
  • Minutes
  • Hours
  • Day of Month
  • Month
  • Day of Week

cron 표현식 허용 범위 표

cron 표현식은 Seconds, Minutes, Hours, Day of Month, Month, Day of Week, Years 순서로 읽으며, 각 자리마다 사용할 수 있는 값의 범위가 정해져 있다.
다만 Spring의 @Scheduled에서는 보통 Years를 제외한 6칸 표현식을 사용한다고 이해하면 된다.


예를 들어 10 * * * * *는 다음처럼 읽을 수 있다.

  • 첫 번째 칸 10은 초를 의미한다.
  • 두 번째 칸 *은 모든 분을 의미한다.
  • 세 번째 칸 *은 모든 시간을 의미한다.
  • 네 번째 칸 *은 모든 일을 의미한다.
  • 다섯 번째 칸 *은 모든 월을 의미한다.
  • 여섯 번째 칸 *은 모든 요일을 의미한다.

따라서 10 * * * * *는 매분 10초마다 실행하라는 의미이다.
즉, 11:00:10, 11:01:10, 11:02:10처럼 매분 10초에 실행된다.


cron 표현식에는 특수문자도 자주 사용된다.
특수문자는 단순히 숫자 하나만 쓰는 대신, 반복 범위나 여러 값, 특정 조건을 표현하기 위해 사용한다.

cron 표현식 특수문자 표

cron 표현식의 특수문자는 반복 주기, 범위, 여러 값, 마지막 날, 가장 가까운 평일 같은 실행 조건을 표현할 때 사용한다.


초보자가 먼저 자주 보는 특수문자는 이 정도이다.

  • *는 모든 값을 의미한다.
  • ?는 특정 값을 정하지 않겠다는 의미로 사용한다.
  • -는 범위를 의미한다.
  • ,는 여러 값을 나열할 때 사용한다.
  • /는 일정 간격마다 반복할 때 사용한다.

?는 주로 Day of Month와 Day of Week 자리에서 사용한다.
두 자리 모두 날짜 조건을 의미할 수 있기 때문에, 한쪽만 조건을 주고 다른 한쪽은 ?로 비워 두는 방식으로 사용한다.
예를 들어 특정 날짜 기준으로 실행할 때는 요일 자리를 ?로 둘 수 있고, 특정 요일 기준으로 실행할 때는 날짜 자리를 ?로 둘 수 있다.


예를 들어 0/10 * * * * *는 0초부터 시작해서 10초마다 실행한다는 의미로 읽을 수 있다.
0 0 9 * * *는 매일 9시 0분 0초에 실행한다는 의미이다.


cron은 외우는 문법이 아니라, 각 칸이 어떤 시간 단위를 의미하는지 읽는 방식으로 이해해야 한다.
먼저 자리 순서를 보고, 그다음 각 자리에 들어간 숫자와 특수문자를 해석하면 된다.

기본 개념예제

아래 예제는 매분 10초마다 현재 시간을 출력하는 cron 기본 예제이다.

// CronScheduler.java
package com.example.springedu.scheduler;

import java.time.LocalDateTime;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class CronScheduler {
    @Scheduled(cron = "10 * * * * *")
    public void cronTask() {
        System.out.println("매분 10초에 실행 : " + LocalDateTime.now()); // cron 규칙에 따라 실행된다
    }
}

@Scheduled(cron = "10 * * * * *")는 매분 10초마다 cronTask()를 실행하라는 의미이다.
여기서 첫 번째 10은 초를 의미하고, 나머지 *는 모든 값을 의미한다.
그래서 매분마다 10초가 되는 순간에 메서드가 실행된다.

// 콘솔 결과 예시
// 매분 10초에 실행 : 2026-04-27T11:20:10.001
// 매분 10초에 실행 : 2026-04-27T11:21:10.001
// 매분 10초에 실행 : 2026-04-27T11:22:10.001

이 예제는 fixedRate처럼 단순히 몇 초마다 실행하는 방식과 다르다.
cron은 “매분 10초”처럼 시간의 특정 위치를 지정한다.
즉, cron은 특정 시간 조건을 더 자세히 표현하고 싶을 때 사용한다.


이 예제를 따로 확인하고 싶다면 다른 스케줄러 예제 클래스는 잠시 주석 처리하는 것이 좋다.
여러 스케줄러가 동시에 실행되면 fixedRate, fixedDelay, cron 로그가 한 콘솔에 섞여 보여서 처음에는 흐름을 구분하기 어렵다.


스케줄링 핵심 정리

Spring Scheduling은 서버 내부에서 반복 작업을 자동으로 실행하기 위한 기능이다.
사용자가 직접 요청하지 않아도, 서버가 실행 중이면 정해진 시간 규칙에 따라 메서드가 실행된다.


이 단원에서 중요한 흐름은 다음과 같다.

  • Task는 정해진 시간마다 실행할 작업이다.
  • @Scheduled는 해당 작업을 언제 실행할지 정하는 어노테이션이다.
  • @EnableScheduling은 스케줄링 기능을 활성화한다.
  • XML 설정 환경에서는 <task:annotation-driven/> 같은 설정으로 기능을 활성화할 수 있다.
  • @Component는 스케줄링 클래스가 Spring Bean으로 등록되게 한다.
  • @Scheduled 메서드는 보통 매개변수 없이 작성한다.
  • @Scheduled 메서드는 보통 void로 작성한다.
  • 반환값이 있더라도 스케줄링 흐름에서는 화면 응답처럼 사용하지 않는다.
  • fixedRate와 fixedDelay의 숫자는 기본적으로 밀리초 단위이다.
  • fixedRate는 작업 시작 시점을 기준으로 반복한다.
  • fixedDelay는 작업 종료 시점을 기준으로 반복한다.
  • cron은 초, 분, 시, 일, 월, 요일 같은 시간 조건을 직접 작성하는 방식이다.
  • 여러 스케줄러 클래스가 동시에 Spring Bean으로 등록되어 있으면 콘솔 로그가 섞일 수 있다.

스케줄링의 핵심은 요청 없이 자동 실행되는 메서드를 만들고, 그 실행 시점을 코드로 정하는 것이다.
fixedRate와 fixedDelay는 간단한 반복 주기에 적합하고, cron은 특정 시간 조건을 세밀하게 정할 때 적합하다.




응용예제

1. 스케줄링 자동 실행 확인하기 (SpringeduApplication.java, SpringSchedulerTest.java)

앞에서는 Spring Scheduling이 사용자의 요청 없이 정해진 시간 규칙에 따라 메서드를 자동 실행한다고 정리했다.
이 예제는 그 개념을 실제 콘솔 로그로 확인하는 예제이다.


실제로는 스케줄링 기능 활성화, 스케줄링 클래스의 Spring Bean 등록, @Scheduled 설정, 현재 시간 출력, cron과 fixedDelay 실행 방식 차이까지 함께 보여 준다.
즉, 이 묶음은 서버를 실행하기만 해도 @Scheduled 메서드가 자동으로 반복 실행되는지 확인하는 응용예제이다.


먼저 실행 흐름만 간단히 보면 이렇다.

  • SpringeduApplication.java에서 @EnableScheduling으로 스케줄링 기능을 활성화한다.
  • SpringSchedulerTest.java에 @Component를 붙여 스케줄링 클래스를 Spring Bean으로 등록한다.
  • scheduleRun() 메서드 위에 @Scheduled를 붙여 실행 시간을 지정한다.
  • 메서드 안에서는 현재 시간을 yyyy-MM-dd HH:mm:ss 형식으로 출력한다.
  • 서버를 실행하면 브라우저 요청 없이 콘솔에 스케줄 실행 로그가 반복 출력된다.



이 예제가 같이 보여주는 개념

이 예제에서 가장 먼저 봐야 할 파일은 SpringeduApplication.java이다.
스케줄링은 @Scheduled만 붙인다고 바로 동작하는 것이 아니다.
Spring이 스케줄링 기능을 사용할 수 있도록 먼저 기능을 활성화해야 한다.

// SpringeduApplication.java
package com.example.springedu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling // @Scheduled가 붙은 메서드를 자동 실행할 수 있게 한다
@ComponentScan(basePackages = {"com.example.springedu", "thymeleaf.exam"})
public class SpringeduApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringeduApplication.class, args); // 스프링 부트 서버를 실행한다
    }
}

@EnableScheduling은 스케줄링 기능을 켜는 역할을 한다.
이 설정이 있어야 Spring이 프로젝트 안에서 @Scheduled가 붙은 메서드를 찾아 정해진 시간에 자동 실행할 수 있다.


@ComponentScan은 Spring이 어떤 패키지에서 컴포넌트를 찾을지 정하는 설정이다.
여기서는 com.example.springedu와 thymeleaf.exam 패키지를 스캔 대상으로 지정하고 있다.
스케줄링 클래스인 SpringSchedulerTest는 com.example.springedu.service 패키지에 있으므로 com.example.springedu 스캔 범위 안에 포함된다.


이제 실제 스케줄링 작업을 담은 SpringSchedulerTest.java를 보면 된다.
이 클래스에는 @Component가 붙어 있다.
그래서 Spring이 이 클래스를 직접 관리하는 Spring Bean으로 등록한다.

// SpringSchedulerTest.java
package com.example.springedu.service;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SpringSchedulerTest {
    @Scheduled(cron = "0 30 12 * * 1") // 매주 월요일 12시 30분 0초에 실행한다
    // @Scheduled(fixedDelay = 5000) // 이전 실행이 끝난 뒤 5초 후 다시 실행한다
    public void scheduleRun() {
        Calendar calendar = Calendar.getInstance(); // 현재 날짜와 시간을 가져온다
        SimpleDateFormat dateFormat =
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 출력 형식을 정한다
        System.out.println("**** 스케줄 실행 : " +
                dateFormat.format(calendar.getTime())); // 현재 시간을 콘솔에 출력한다
    }
}

@Component는 이 클래스가 Spring Bean으로 등록되게 만든다.
스케줄링 메서드는 Spring이 자동으로 찾아 실행해야 하므로, 클래스가 Spring Bean으로 등록되어 있어야 한다.


scheduleRun() 메서드 위에는 @Scheduled가 붙어 있다.
이 어노테이션이 붙은 메서드는 개발자가 직접 호출하지 않아도 Spring이 정해진 시간 규칙에 맞춰 자동으로 실행한다.


현재 코드에서는 @Scheduled(cron = "0 30 12 * * 1")가 활성화되어 있다.
이 표현식은 초, 분, 시, 일, 월, 요일 순서로 읽는다.
따라서 0 30 12 * * 1은 매주 월요일 12시 30분 0초에 실행하라는 의미이다.


바로 아래에는 @Scheduled(fixedDelay = 5000)이 주석 처리되어 있다.
fixedDelay = 5000은 이전 작업이 끝난 뒤 5000밀리초, 즉 5초를 기다렸다가 다시 실행하라는 의미이다.
이번 결과 화면처럼 5초마다 로그가 반복 출력되는 모습을 확인하려면 cron 설정을 주석 처리하고 fixedDelay = 5000 설정을 활성화해야 한다.


메서드 안에서는 Calendar와 SimpleDateFormat을 사용한다.
Calendar.getInstance()는 현재 날짜와 시간을 가져온다.
SimpleDateFormat("yyyy-MM-dd HH:mm:ss")는 날짜와 시간을 2026-04-27 12:26:16 같은 형태로 보기 좋게 바꿔 준다.
그리고 System.out.println()으로 콘솔에 현재 시간을 출력한다.


즉, 이 코드의 전체 흐름은 아래처럼 이어진다.

  • 서버가 실행된다.
  • @EnableScheduling 때문에 스케줄링 기능이 켜진다.
  • @Component가 붙은 SpringSchedulerTest가 Spring Bean으로 등록된다.
  • Spring이 @Scheduled가 붙은 scheduleRun() 메서드를 찾는다.
  • 설정된 시간 규칙에 따라 scheduleRun()이 자동 실행된다.
  • 현재 시간이 콘솔에 출력된다.



그래서 결과가 이렇게 나온다

첫 번째 결과는 fixedDelay = 5000 설정으로 스케줄링 메서드가 반복 실행되는 장면이다.
현재 실행 결과에서는 12:26:16, 12:26:21, 12:26:26, 12:26:31처럼 약 5초 간격으로 로그가 출력된다.

스케줄링 5초 간격 실행 결과

콘솔에 **** 스케줄 실행 : 로그가 반복해서 출력된다.
이 결과가 중요한 이유는 브라우저에서 특정 주소를 요청하지 않았는데도 메서드가 자동으로 실행되고 있다는 점이다.
즉, Controller처럼 요청이 들어와야 실행되는 흐름이 아니라, Spring이 시간 규칙에 맞춰 메서드를 실행하는 흐름이다.



두 번째 결과도 같은 흐름을 한 번 더 확인하는 장면이다.
서버가 실행된 뒤 일정 시간이 지나면 scheduleRun() 메서드가 계속 자동 실행되고, 그때마다 현재 시간이 콘솔에 출력된다.

스케줄링 반복 실행 확인

이 결과를 보면 스케줄링 메서드는 한 번만 실행되고 끝나는 메서드가 아니라, 설정된 조건에 따라 계속 반복 실행되는 메서드라는 것을 알 수 있다.
fixedDelay = 5000을 사용하면 이전 실행이 끝난 뒤 5초를 기다렸다가 다시 실행된다.


여기서 주의할 점은 코드와 실행 결과의 설정이 같아야 한다는 것이다.
@Scheduled(cron = "0 30 12 * * 1")가 활성화되어 있으면 매주 월요일 12시 30분 0초에 실행되는 흐름이다.
반면 현재 이미지처럼 5초마다 반복되는 결과를 보려면 아래처럼 설정해야 한다.

// SpringSchedulerTest.java
package com.example.springedu.service;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SpringSchedulerTest {
    // @Scheduled(cron = "0 30 12 * * 1") // 특정 시간 실행을 확인할 때 사용한다
    @Scheduled(fixedDelay = 5000) // 5초 간격 반복 실행을 확인할 때 사용한다
    public void scheduleRun() {
        Calendar calendar = Calendar.getInstance(); // 현재 날짜와 시간을 가져온다
        SimpleDateFormat dateFormat =
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 출력 형식을 정한다
        System.out.println("**** 스케줄 실행 : " +
                dateFormat.format(calendar.getTime())); // 현재 시간을 콘솔에 출력한다
    }
}

이렇게 바꾸면 scheduleRun()은 이전 실행이 끝난 뒤 5초 후 다시 실행된다.
그래서 콘솔에 약 5초 간격으로 **** 스케줄 실행 : 현재시간 로그가 계속 출력된다.


반대로 cron 방식으로 실행하고 싶다면 fixedDelay를 주석 처리하고, cron 설정을 활성화하면 된다.

// SpringSchedulerTest.java
@Component
public class SpringSchedulerTest {
    @Scheduled(cron = "0 30 12 * * 1") // 매주 월요일 12시 30분 0초에 실행한다
    // @Scheduled(fixedDelay = 5000) // 5초 간격 반복 실행은 주석 처리한다
    public void scheduleRun() {
        Calendar calendar = Calendar.getInstance(); // 현재 날짜와 시간을 가져온다
        SimpleDateFormat dateFormat =
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 출력 형식을 정한다
        System.out.println("**** 스케줄 실행 : " +
                dateFormat.format(calendar.getTime())); // 현재 시간을 콘솔에 출력한다
    }
}

이 경우에는 5초마다 실행되는 것이 아니다.
cron 표현식에 적힌 시간 조건이 맞을 때 실행된다.
즉, 0 30 12 * * 1은 매주 월요일 12시 30분 0초가 되었을 때 실행된다.


이 예제의 핵심을 다시 정리하면 이렇다.

  • @EnableScheduling은 스케줄링 기능을 활성화한다.
  • @Component는 스케줄링 클래스를 Spring Bean으로 등록한다.
  • @Scheduled는 메서드를 언제 실행할지 정한다.
  • cron은 특정 시간 조건을 정할 때 사용한다.
  • fixedDelay는 이전 작업이 끝난 뒤 일정 시간 후 다시 실행할 때 사용한다.
  • 현재 결과 화면은 fixedDelay = 5000을 활성화했을 때의 실행 결과이다.
  • 브라우저 요청 없이 콘솔 로그가 반복 출력되면 스케줄링이 정상 동작하는 것이다.

결국 이 응용예제는 스케줄링 메서드가 사용자 요청 없이 서버 내부에서 자동 실행된다는 점을 콘솔 로그로 확인하는 예제이다.
코드에서 중요한 것은 @EnableScheduling, @Component, @Scheduled가 서로 연결되어야 한다는 점이다.

0개의 댓글