스케줄러(Scheduler)를 어떻게 구현하나요?

김상욱·2024년 12월 22일

스케줄러(Scheduler)를 어떻게 구현하나요?

스케줄러(Scheduler는 정해진 시간이나 주기마다 특정 작업을 자동으로 실행하는 기능. 백엔드 개발에서는 데이터 백업, 이메일 발송, 정기 리포트 생성 등 다양한 용도로 활용

1. Spring에서 스케줄링 기능 활성화

Spring에서는 스케줄링을 지원하는 여러 가지 방법을 제공합니다. 가장 기본적이고 많이 사용하는 방법은 @Scheduled 어노테이션을 사용하는 것입니다. 이를 사용하기 위해서는 먼저 스케줄링 기능을 활성화해야 합니다.

설정 방법
  1. Spring Boot 프로젝트 생성
  • Spring Initializer를 사용하여 Spring Boot 프로젝트를 생성
  • 필요한 의존성으로는 Spring Web과 Spring Boot DevTools 등을 선택하면 됩니다.
  1. 스케줄링 활성화
  • 스케줄링을 활성화하려면 @EnableScheduling 어노테이션을 사용해야 합니다.
  • 이 어노테이션은 보통 메인 애플리케이션 클래스에 추가합니다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling  // 스케줄링 활성화
public class SchedulerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SchedulerApplication.class, args);
    }
}

2. 스케줄러 작성

스케줄러는 주기적으로 실행될 작업을 정의한 메서드에 @Scheduled 어노테이션을 붙여서 작성합니다.
ex) 매 5초마다 콘솔에 매시지 출력

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

@Component
public class MyScheduler {

    // 매 5초마다 실행
    @Scheduled(fixedRate = 5000)
    public void printMessage() {
        System.out.println("스케줄러 작동 중: " + System.currentTimeMillis());
    }
}
어노테이션 속성 설명
  • fixedRate : 이전 작업이 시작된 시점부터 지정된 밀리초 후에 다시 실행.
  • fixedDelay : 이전 작업이 완료된 시점부터 지정된 밀리초 후에 다시 실행.
  • cron : 크론 표현식을 사용하여 보다 복잡한 스케줄을 설정할 수 있습니다.
크론 표현식 예제
@Scheduled(cron = "0 0 * * * *") // 매 정시(매 시간 0분 0초)에 실행
public void cronJob() {
    System.out.println("크론 스케줄러 작동 중: " + System.currentTimeMillis());
}

3. 스케줄러의 작동 방식

Spring의 스케줄링은 기본적으로 스레드 풀을 사용하여 작업을 비동기적으로 실행합니다. 기본 스레드 풀의 크기는 1이지만, 필요에 따라 커스터마이징할 수 있습니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.context.annotation.Bean;

@Configuration
@EnableScheduling
public class SchedulerConfig {

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5); // 스레드 풀 크기 설정
        scheduler.setThreadNamePrefix("scheduler-");
        scheduler.initialize();
        return scheduler;
    }
}

4. 스케줄러 사용 시 주의사항

  • 작업 시간 관리 : 작업이 너무 오래 걸리면 스케줄이 밀릴 수 있습니다. 작업의 수행 시간을 고려하여 스케줄 주기를 설정하세요.
  • 예외 처리 : 스케줄러 내에서 예외가 발생하면 다음 스케줄이 실행되지 않을 수 있습니다. 예외 처리를 적절히 해주어야 합니다.
@Scheduled(fixedRate = 5000)
public void safePrintMessage() {
    try {
        // 작업 로직
        System.out.println("안전한 스케줄러 작동 중: " + System.currentTimeMillis());
        // 예외 발생 가능성 있는 코드
    } catch (Exception e) {
        // 예외 처리 로직
        e.printStackTrace();
    }
}

5. 고급 스케줄링 : Quartz 사용

Spring의 기본 스케줄링 기능 외에도 더 복잡한 스케줄링이 필요한 경우 Quartz와 같은 라이브러리를 사용할 수 있습니다. Quartz는 클러스터링, 지속성, 복잡한 트리거 등을 지원합니다. 그러나 신입 개발자라면 기본 @Scheduled 기능을 먼저 익히는 것이 좋습니다.

6. 실제 프로젝트에서의 활용 예

ex) 매일 자정에 데이터베이스 백업

@Component
public class BackupScheduler {

    @Scheduled(cron = "0 0 0 * * *") // 매일 자정에 실행
    public void backupDatabase() {
        // 데이터베이스 백업 로직
        System.out.println("데이터베이스 백업 시작: " + LocalDateTime.now());
        // 백업 작업 수행
    }
}

매 10분마다 외부 API 호출

@Component
public class ApiScheduler {

    @Scheduled(fixedRate = 600000) // 매 10분(600,000 밀리초)마다 실행
    public void callExternalApi() {
        // 외부 API 호출 로직
        System.out.println("외부 API 호출: " + LocalDateTime.now());
        // API 호출 코드
    }
}

신입 Java/Spring 백엔드 개발자로서 스케줄러(Scheduler)를 실습해보는 것은 매우 유익한 경험이 될 것입니다. 실제로 작동하는 프로젝트를 통해 스케줄링 개념을 이해하고, 이를 응용할 수 있는 능력을 키울 수 있습니다. 아래에 몇 가지 실습 아이디어와 함께 구현 방법을 단계별로 설명드리겠습니다.

1. 콘솔 로그 메시지 스케줄러

프로젝트 개요

  • 목표: 일정 주기마다 콘솔에 메시지를 출력하는 간단한 스케줄러 구현
  • 학습 포인트: @Scheduled 어노테이션 사용법, 기본 스케줄링 설정

구현 단계

  1. Spring Boot 프로젝트 생성

    • Spring Initializr를 사용하여 새로운 Spring Boot 프로젝트를 생성합니다.
    • 필요한 의존성: Spring Web
  2. 스케줄링 활성화

    • 메인 애플리케이션 클래스에 @EnableScheduling 어노테이션 추가
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    @SpringBootApplication
    @EnableScheduling
    public class SchedulerDemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(SchedulerDemoApplication.class, args);
        }
    }
  3. 스케줄러 컴포넌트 작성

    • @Scheduled 어노테이션을 사용하여 주기적으로 실행될 메서드 작성
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ConsoleLoggerScheduler {
    
        @Scheduled(fixedRate = 5000) // 5초마다 실행
        public void logMessage() {
            System.out.println("스케줄러 실행: " + System.currentTimeMillis());
        }
    }
  4. 애플리케이션 실행 및 확인

    • 애플리케이션을 실행하고 콘솔에 5초마다 메시지가 출력되는지 확인합니다.

확장 아이디어

  • 로그 메시지에 더 많은 정보를 추가 (예: 현재 사용자 수, 서버 상태 등)
  • 다른 주기 (fixedDelay 또는 cron)로 변경해보기

2. 이메일 발송 스케줄러

프로젝트 개요

  • 목표: 일정 시간마다 이메일을 발송하는 스케줄러 구현
  • 학습 포인트: 이메일 발송 설정, @Scheduled와 연계

구현 단계

  1. Spring Boot 프로젝트 생성

    • Spring Initializr에서 Spring Web, Spring Boot Starter Mail 의존성을 추가하여 프로젝트 생성
  2. 메일 설정 추가

    • application.properties 파일에 이메일 서버 설정 추가
    spring.mail.host=smtp.gmail.com
    spring.mail.port=587
    spring.mail.username=your_email@gmail.com
    spring.mail.password=your_password
    spring.mail.properties.mail.smtp.auth=true
    spring.mail.properties.mail.smtp.starttls.enable=true
  3. 스케줄러 컴포넌트 작성

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.mail.SimpleMailMessage;
    import org.springframework.mail.javamail.JavaMailSender;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    @Component
    public class EmailScheduler {
    
        @Autowired
        private JavaMailSender mailSender;
    
        @Scheduled(cron = "0 0 9 * * *") // 매일 오전 9시에 실행
        public void sendDailyEmail() {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setTo("recipient@example.com");
            message.setSubject("일일 보고");
            message.setText("안녕하세요,\n오늘의 보고서를 확인해주세요.");
            mailSender.send(message);
            System.out.println("이메일 발송 완료: " + System.currentTimeMillis());
        }
    }
  4. 애플리케이션 실행 및 확인

    • 애플리케이션을 실행하고 설정된 시간에 이메일이 발송되는지 확인합니다.

확장 아이디어

  • 이메일 내용에 동적으로 데이터 추가 (예: 데이터베이스에서 조회한 정보)
  • HTML 형식의 이메일 발송

3. 데이터베이스 백업 스케줄러

프로젝트 개요

  • 목표: 일정 주기마다 데이터베이스를 백업하는 스케줄러 구현
  • 학습 포인트: 외부 명령어 실행, 파일 처리

구현 단계

  1. Spring Boot 프로젝트 생성

    • Spring Web, Spring Data JPA 등의 의존성을 추가
  2. 백업 스케줄러 컴포넌트 작성

    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    @Component
    public class DatabaseBackupScheduler {
    
        private static final String BACKUP_PATH = "/path/to/backup/";
        private static final String DB_NAME = "your_db_name";
        private static final String DB_USER = "your_db_user";
        private static final String DB_PASSWORD = "your_db_password";
    
        @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행
        public void backupDatabase() {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            String timestamp = LocalDateTime.now().format(formatter);
            String backupFile = BACKUP_PATH + DB_NAME + "_" + timestamp + ".sql";
    
            String command = String.format("mysqldump -u%s -p%s %s -r %s", DB_USER, DB_PASSWORD, DB_NAME, backupFile);
    
            try {
                Process process = Runtime.getRuntime().exec(command);
                int processComplete = process.waitFor();
                if (processComplete == 0) {
                    System.out.println("데이터베이스 백업 성공: " + backupFile);
                } else {
                    System.out.println("데이터베이스 백업 실패");
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    주의: 실제 프로젝트에서는 데이터베이스 비밀번호를 코드에 직접 작성하지 않고, 환경 변수나 보안 설정을 통해 관리해야 합니다.

  3. 애플리케이션 실행 및 확인

    • 설정된 시간에 데이터베이스 백업이 정상적으로 이루어지는지 확인합니다.

확장 아이디어

  • 백업 파일을 외부 스토리지 (예: AWS S3)에 업로드
  • 백업 파일 관리 (예: 오래된 백업 삭제)

4. 외부 API 호출 및 데이터 저장 스케줄러

프로젝트 개요

  • 목표: 일정 주기마다 외부 API를 호출하고 데이터를 데이터베이스에 저장
  • 학습 포인트: REST API 호출, 데이터베이스 연동, @Scheduled 활용

구현 단계

  1. Spring Boot 프로젝트 생성

    • Spring Web, Spring Data JPA, H2 Database (또는 원하는 DB) 의존성 추가
  2. 엔티티 및 레포지토리 생성

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    
    @Entity
    public class ApiData {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String data;
        private LocalDateTime timestamp;
    
        // Getters and Setters
    }
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface ApiDataRepository extends JpaRepository<ApiData, Long> {
    }
  3. API 호출 스케줄러 컴포넌트 작성

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    import org.springframework.web.client.RestTemplate;
    
    import java.time.LocalDateTime;
    
    @Component
    public class ApiCallScheduler {
    
        @Autowired
        private ApiDataRepository apiDataRepository;
    
        private RestTemplate restTemplate = new RestTemplate();
    
        @Scheduled(fixedRate = 600000) // 매 10분마다 실행
        public void callExternalApi() {
            String apiUrl = "https://api.example.com/data"; // 실제 API URL로 변경
            try {
                String response = restTemplate.getForObject(apiUrl, String.class);
                ApiData apiData = new ApiData();
                apiData.setData(response);
                apiData.setTimestamp(LocalDateTime.now());
                apiDataRepository.save(apiData);
                System.out.println("API 데이터 저장 완료: " + response);
            } catch (Exception e) {
                System.out.println("API 호출 실패: " + e.getMessage());
            }
        }
    }
  4. 애플리케이션 실행 및 확인

    • 애플리케이션을 실행하고 데이터베이스에 API 데이터가 저장되는지 확인합니다.

확장 아이디어

  • 외부 API의 응답을 파싱하여 구조화된 데이터 저장 (예: JSON -> 객체)
  • API 호출 실패 시 재시도 로직 추가

5. 날씨 데이터 수집 스케줄러

프로젝트 개요

  • 목표: 일정 주기마다 날씨 정보를 외부 API에서 가져와 데이터베이스에 저장
  • 학습 포인트: REST API 통신, 데이터 파싱, 스케줄링

구현 단계

  1. Spring Boot 프로젝트 생성

    • Spring Web, Spring Data JPA, H2 Database, Jackson 등의 의존성 추가
  2. 엔티티 및 레포지토리 생성

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    
    @Entity
    public class WeatherData {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String location;
        private String description;
        private Double temperature;
        private LocalDateTime timestamp;
    
        // Getters and Setters
    }
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface WeatherDataRepository extends JpaRepository<WeatherData, Long> {
    }
  3. 날씨 API 호출 스케줄러 컴포넌트 작성

    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    import org.springframework.web.client.RestTemplate;
    
    import java.time.LocalDateTime;
    
    @Component
    public class WeatherDataScheduler {
    
        @Autowired
        private WeatherDataRepository weatherDataRepository;
    
        private RestTemplate restTemplate = new RestTemplate();
        private ObjectMapper objectMapper = new ObjectMapper();
    
        private static final String API_KEY = "your_api_key"; // 실제 API 키로 변경
        private static final String LOCATION = "Seoul";
        private static final String API_URL = "http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION + "&appid=" + API_KEY + "&units=metric";
    
        @Scheduled(cron = "0 0 * * * *") // 매 정시마다 실행
        public void fetchWeatherData() {
            try {
                String response = restTemplate.getForObject(API_URL, String.class);
                JsonNode root = objectMapper.readTree(response);
                String description = root.path("weather").get(0).path("description").asText();
                Double temperature = root.path("main").path("temp").asDouble();
    
                WeatherData weatherData = new WeatherData();
                weatherData.setLocation(LOCATION);
                weatherData.setDescription(description);
                weatherData.setTemperature(temperature);
                weatherData.setTimestamp(LocalDateTime.now());
    
                weatherDataRepository.save(weatherData);
                System.out.println("날씨 데이터 저장 완료: " + description + ", " + temperature + "°C");
            } catch (Exception e) {
                System.out.println("날씨 데이터 수집 실패: " + e.getMessage());
            }
        }
    }
  4. 애플리케이션 실행 및 확인

    • 애플리케이션을 실행하고 데이터베이스에 날씨 데이터가 저장되는지 확인합니다.

확장 아이디어

  • 여러 지역의 날씨 데이터 수집
  • 수집된 데이터를 기반으로 간단한 리포트 생성
  • 프론트엔드와 연동하여 실시간 날씨 정보 제공

추가 팁 및 권장 사항

  1. 버전 관리 사용

    • Git을 사용하여 프로젝트의 변경 사항을 관리하고, GitHub에 저장소를 만들어 포트폴리오로 활용하세요.
  2. 문서화

    • 프로젝트에 대한 README 파일을 작성하여 프로젝트의 목적, 기능, 사용법 등을 명확히 기록하세요.
  3. 에러 핸들링 강화

    • 스케줄러 내에서 발생할 수 있는 다양한 예외 상황을 고려하고, 적절한 예외 처리 로직을 추가하세요.
  4. 로깅 활용

    • SLF4J와 같은 로깅 프레임워크를 사용하여 로그를 체계적으로 관리하세요.
  5. 테스트 작성

    • 스케줄러의 동작을 검증할 수 있는 단위 테스트나 통합 테스트를 작성하여 코드의 신뢰성을 높이세요.
  6. 보안 고려

    • 민감한 정보 (예: API 키, 데이터베이스 비밀번호)는 환경 변수나 application.properties@Value를 사용하여 관리하고, 코드에 직접 노출하지 않도록 주의하세요.

결론

위의 실습 프로젝트들은 스케줄러의 기본 개념을 이해하고, 이를 실제 애플리케이션에 적용해보는 데 큰 도움이 될 것입니다. 각 프로젝트를 구현하면서 발생하는 문제를 해결하는 과정에서 실무에서 필요한 문제 해결 능력과 코딩 실력을 향상시킬 수 있습니다. 또한, 이러한 프로젝트를 포트폴리오에 포함시키면 취업 준비 시 큰 강점이 될 것입니다. 꾸준히 실습을 진행하고, 다양한 기능을 추가해보며 자신만의 프로젝트를 완성해보세요. 화이팅입니다!

0개의 댓글