Spring @Scheduled 동작 조건과 반복 실행 문제 해결 가이드

@Scheduled가 동작하지 않는 이유와 설정 조건

Spring의 @Scheduled는 단순히 어노테이션을 붙이는 것만으로는 동작하지 않습니다. 다음과 같은 조건이 반드시 충족되어야 합니다.

✅ 필수 설정

  • @EnableScheduling 어노테이션 선언 필수
  • Bean으로 등록된 클래스에 @Scheduled가 있어야 함
  • Spring 컨텍스트가 살아 있어야 함 (즉, 애플리케이션이 실행 중이고 종료되지 않아야 함)
  • spring.batch.job.enabled=false 설정 (Spring Boot가 Job을 자동 실행하지 않도록 설정)
  • 메서드는 파라미터가 없어야 함public void run() 형태여야 함

💥 예외 사례

아래와 같이 파라미터가 있는 메서드는 @Scheduled에서 예외가 발생합니다:


@Scheduled(cron = "0 0 * * * ?")
public void runSampleJob(Job job) { // ❌ 오류 발생

✅ 올바른 예시


@Component
@EnableScheduling
public class SampleScheduler {

    @Scheduled(cron = "0 0 * * * ?")
    public void runSampleJob() {
        // your logic
    }
}

cron 표현식 정리

표현식설명
*/2 * * * * ?매 2초마다 실행 (6자리: 초 분 시 일 월 요일)
0 0/2 * * * ?✅ 매 2분마다 1회 실행
0 0 1 * * ?매일 새벽 1시 실행
0 0 0 * * ?매일 자정 실행

@Scheduled가 루프처럼 반복 실행되는 이유

❗ 잘못된 cron 사용

*/2 * * * * ?는 매 2초마다 실행을 의미합니다. “2분마다”로 잘못 이해하면 루프처럼 실행되는 문제가 발생합니다.

❗ JobParameters 중복

Spring Batch는 동일한 Job 이름과 동일한 JobParameters로는 중복 실행을 허용하지 않습니다. 동일 파라미터로 실행할 경우 예외가 발생하고, 이를 스케줄러가 계속 재시도하게 되면 루프처럼 보일 수 있습니다.


해결 방법

1. JobParameters에 timestamp 추가


JobParametersBuilder builder = new JobParametersBuilder();
builder.addString("key", "value");
builder.addLong("timestamp", System.currentTimeMillis());
JobParameters parameters = builder.toJobParameters();

2. 실행 중 Job 중복 방지


boolean isRunning = jobExplorer.findRunningJobExecutions(job.getName())
                               .stream()
                               .anyMatch(JobExecution::isRunning);
if (isRunning) {
    log.warn("Job is already running. Skipping this schedule.");
    return;
}

Sample 통합 코드 예시


@Component
@EnableScheduling
@RequiredArgsConstructor
public class SampleJobRunner {

    private final ApplicationContext applicationContext;
    private final JobLauncher jobLauncher;
    private final JobExplorer jobExplorer;
    private final ApplicationArguments applicationArguments;

    @Scheduled(cron = "0 0/2 * * * ?") // 매 2분마다
    public void runSampleJob() {
        try {
            Job sample = applicationContext.getBean("sample", Job.class);

            boolean isRunning = jobExplorer.findRunningJobExecutions(sample.getName())
                                           .stream()
                                           .anyMatch(JobExecution::isRunning);
            if (isRunning) {
                log.warn("Job is already running. Skipping this execution.");
                return;
            }

            JobParameters jobParameter = JobParametersUtils.convertArgsToBuilder(applicationArguments)
                    .addLong("timestamp", System.currentTimeMillis())
                    .toJobParameters();

            jobLauncher.run(sample, jobParameter);

        } catch (Exception e) {
            log.error("스케쥴링 오류 발생", e);
        }
    }
}

결론

Spring의 @Scheduled 기능은 간단해 보이지만, cron 표현식의 정확한 해석, Spring Batch와의 연동에서 발생할 수 있는 JobParameters 중복 문제, 그리고 Job 중복 실행 방지 전략까지 실무에서는 반드시 종합적으로 고려되어야 합니다.

이 글에서는 @Scheduled를 사용할 때 반드시 필요한 설정부터 반복 실행 문제 해결, Job 실행 안정성 확보를 위한 팁까지 모두 정리했습니다. 반복 실행 이슈나 실행 안 되는 문제를 겪고 있다면 이 가이드를 바탕으로 점검해 보시기 바랍니다.

관련 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다