Spring Boot 애플리케이션에서 주기적인 작업을 자동화하는 가장 간단하고 강력한 방법, 바로 Spring Scheduler와 @Scheduled 어노테이션을 활용하여 작업해 보았습니다. API를 호출할 때만 데이터를 수집하는 것이 아니라, 매주 또는 매일 정해진 시간에 자동으로 데이터를 수집하고 싶을 때가 있죠? 최근 제가 진행한 '주기적인 뉴스 데이터 수집' 프로젝트를 예시로 어떻게 스케줄러를 적용하는지 단계별로 보여드리겠습니다.
Spring Scheduler는 특정 시간에 또는 주기적으로 특정 메소드를 실행하도록 예약하는 기능입니다. Spring 프레임워크에 내장되어 있어 별도의 라이브러리 추가 없이 바로 사용할 수 있습니다.
가장 먼저 해야 할 일은 Spring Boot 애플리케이션에 스케줄링 기능이 켜져 있음을 알려주는 것입니다. 보통 메인 애플리케이션 클래스에 @EnableScheduling 어노테이션을 추가하여 활성화합니다.
@SpringBootApplication
@EnableScheduling // 스케줄링 기능을 활성화합니다.
public class SimpleApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleApplication.class, args);
}
}
이 어노테이션이 없으면 @Scheduled가 붙은 메소드는 스케줄링되지 않고 일반 메소드처럼 동작합니다.
스케줄링할 메소드 위에 @Scheduled 어노테이션을 붙여줍니다. 이 어노테이션은 다양한 속성을 통해 실행 주기를 설정할 수 있습니다.
1. cron: 가장 유연하고 강력한 방법입니다. Cron 표현식을 사용하여 매우 구체적인 실행 시간을 예약할 수 있습니다.
2.fixedRate: 이전 작업의 시작 시간을 기준으로, 정해진 시간(밀리초)마다 작업을 실행합니다.
3.fixedDelay: 이전 작업이 끝난 시간을 기준으로, 정해진 시간(밀리초) 후에 다음 작업을 실행합니다.
4.initialDelay: 스케줄링된 작업이 처음 실행되기 전의 대기 시간(밀리초)을 설정합니다. fixedRate나 fixedDelay와 함께 사용됩니다.
처음 작성했던 코드는 다음과 같습니다.
NewsApiController.java (수정 전)
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class NewsApiController {
private final NewsService newsService;
@GetMapping("/newsCollect")
public void collectNews() throws JsonProcessingException {
// ... 네이버 API를 통해 뉴스를 수집하는 로직 ...
newsService.insertNews(responseBody);
}
@GetMapping("/news")
public List<News> getAllNews() {
return newsService.getAllNews();
}
// ... API 호출 관련 private 메서드들 ...
}
/api/newsCollect 라는 API 엔드포인트를 호출해야만 네이버 뉴스 API에서 '날씨' 관련 뉴스를 가져오는 collectNews() 메서드가 실행되는 구조였습니다.
이 방식은
이번 목표는 스프링 스케줄러를 활용하여 collectNews() 로직을 매주 월요일 00시에 자동으로 실행하는 것입니다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling // 스케줄링 기능을 활성화합니다!
public class SimpleApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleApplication.class, args);
}
}
스케줄링 관련 로직을 컨트롤러에서 분리하여 별도의 클래스로 만드는 것이 좋습니다. 이는 코드의 역할을 명확히 나누어 유지보수를 쉽게 만듭니다. scheduler 패키지를 만들고 NewsCollectScheduler 클래스를 생성한 뒤, 기존 NewsApiController에 있던 뉴스 수집 로직을 그대로 옮겨옵니다.
package com.ccp.simple.scheduler;
@Component
@RequiredArgsConstructor
public class NewsCollectScheduler {
private final NewsService newsService;
/**
* 매주 월요일 00:00에 실행
*/
@Scheduled(cron = "0 0 0 * * MON")
public void collectNews() throws JsonProcessingException {
// ... NewsApiController에서 가져온 뉴스 수집 로직 ...
}
// ... API 호출 관련 private 메서드들 ...
}
새로 만든 collectNews() 메서드 위에 @Scheduled 어노테이션을 추가하여 실행 주기를 설정합니다.
@Scheduled(cron = "0 0 0 * * MON")
public void collectNews() { ... }
이제 스케줄러가 뉴스 수집을 담당하므로, NewsApiController는 본연의 임무인 '수집된 뉴스 데이터를 제공하는 API' 역할만 수행하도록 코드를 정리합니다.
package com.ccp.simple.controller;
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class NewsApiController {
private final NewsService newsService;
//뉴스 수집
@GetMapping("/news")
public List<News> getAllNews() {
return newsService.getAllNews();
}
}
Spring Scheduler를 사용하면 @EnableScheduling과 @Scheduled 단 두 개의 어노테이션만으로 복잡한 주기적 작업을 손쉽게 구현할 수 있습니다.