다른 DB에서 부서 DB 서버로 데이터를 끌어오기로 했다.
서로 방해되지 않도록 새벽에 한 번씩 실행하기로 해서 스케줄러를 시도했다.
카프카를 통해 실시간 DB 변화를 감지해도 되지만... 테스트 단계로 데이터가 많지 않아 스케줄러로 진행했다.
package com.[프로젝트];
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Scheduled를 사용하기 위해서는 먼저 Application 클래스에서 @EnableScheduling을 설정해주어야 한다. 그래야 스케줄러 어노테이션이 붙은 설정 파일이 실행된다고 한다.
해당 기능이 작동하는 시간은 추후 변경하기 쉽도록 properties에 해뒀다.
yaml을 쓰는 사람은 해당 파일에 작성해두면 된다.

위에 schedule.cron이 해당 시간을 설정한 것이고,
아래의 schedule.lastWorkTimeHour는 그 후에 작업된 데이터만 불러오기 위해 별도로 설정한 값이다.
cron은 Scheduling의 정규 표현식이며, 리눅스의 크론탭 기능과 유사하게 설정할 수 있다고 한다.
(리눅스의 crontab은 특정 시간에 프로그램을 실행시키기 위해 사용하는 것으로, 윈도우의 스케줄러와 비슷하다.)
일단, 설정 유형에는 6가지가 있다. (연도는 생략가능하여 보통 6가지로 많이 쓴다!)

각 필드에 아래 표에 따라 원하는 값 또는 특수문자를 입력하여 시간을 설정하면 된다.
| 필드 명 | 값의 허용 범위 | 허용된 특수문자 |
|---|---|---|
| 초 (Seconds) | 0 ~ 59 | , - * / |
| 분 (Minutes) | 0 ~ 59 | , - * / |
| 시 (Hours) | 0 ~ 23 | , - * / |
| 일 (Day) | 1 ~ 31 | , - * ? / L W |
| 월 (Month) | 1 ~ 12 or JAN ~ DEC | , - * / |
| 요일(Week) | 0 ~ 6 or SUN ~ SAT | , - * ? / L # |
*: 모든 값을 의미?: 특정한 값이 없음을 의미-: 범위를 나타낼 때 사용,: 특정 값을 여러 개 나열할 때 사용/: <시작 시간/단위> 형태로 쓰며, 0분부터 매 5분은 0/5라고 표시L: 일 또는 요일 필드에서 사용하며, 마지막 일이나 요일(토)을 의미W: 가장 가까운 평일을 의미#: 몇 째 주의 무슨 요일인지 표현하며, <요일#주>의 형태로 '3#2'는 2째주의 수요일을 의미
0 0/5 * * * ? : 매 5분마다 실행0 0 0/1 * * ? : 매 1시간마다 실행0 0 12 * * ? : 매일 낮 12시에0 20 11 ? * * : 매일 오전 11:20분에 실행0 20 11 * * ? : 매일 오전 11:20분에 실행0 18 * ? : 매일 오후 6:00에 시작해서 매분마다 실행하고 오후 6:59분에 종료 (해당 한 시간 동안 매분 실행)0 0/5 14,18 * * ? : 매일 오후 2:00에 시작해서 5분마다 실행되어 오후 2:55에 끝나고, 오후 6:00에 시작하여 5분마다 실행되어 오후 6:55에 종료0 0-5 18 * * ? : 매일 오후 6:00에 시작하여 매분마다 실행하고 오후 6:05분에 종료0 10,44 14 ? 3 WED : 3월의 수요일마다 오후 2:10과 2:44 실행0 15 10 ? * MON-FRI : 주중 오전 10:15분에 실행0 15 10 15 * ? : 매달 15일 오전 10:15에 실행0 15 10 L * ? : 매월 말일 오전 10:15에 실행0 15 10 ? * 6L : 매월 마지막 금요일 오전 10:15에 실행0 15 10 ? * 6#3 : 매월 3째주 금요일 오전 10:15에 실행작업 수행시간과 상관없이 일정 주기마다 메소드 호출!
@Scheduled(fixedDelay = 1000)
public void run() {
log.info("Scheduler 실행");
}
miliseconds 단위로 표시하며, 위의 예시는 1초를 의미한다.
이전 Task가 종료되고 1초가 지나면 해당 메소드를 실행하라는 의미가 된다.
fixedDelayString도 있는데... 이건 해당 숫자를 문자열 값으로 표현한다는 의미가 된다.
작업 수행시간을 포함하여 작업을 마친 후부터 주기 타이머가 돌아가도록!
@Scheduled(fixedRate = 1000)
public void run() {
log.info("Scheduler 실행");
}
단위는 동일하며, 이전 Task의 시작 시점부터 시간을 측정하게 된다.
fixedRateString을 사용하면 해당 시간을 문자열로 표현한다는 의미가 된다.
메소드가 등록되지마자 수행하지 않고, 초기 지연시간 설정!
@Scheduled(fixedRate = 5000, initialDelay = 3000)
public void run() {
log.info("Scheduler 실행");
}
메소드가 등록되고 초기 지연시간을 3초 두는 예시이다.
위의 예시로는 메소드가 등록되면 3초의 대기시간을 가진 후에 5초마다 실행하라는 의미가 된다.
마찬가지로 initialDelayString은 문자열 값이 된다.
Scheduler를 사용할 코드를 작성해보면, config로 두고 해당 메소드를 작성해주었다.
작업을 두 가지 진행해야 하므로, 각각의 메소드로 분리해주고
두 메소드를 통합한 최종 본에만 @Scheduled를 달아주었다.
여기서 크론 값은 properties에 놓은 값을 불러와 사용하도록 했다.
이렇게 작성하면 아래의 코드가 새벽 3시에 작동을 하게 된다.
@Configuration
@RequiredArgsConstructor
@Slf4j
public class SchedulerConfig {
private final LabService labService;
@Scheduled(cron = "${schedule.cron}")
public void copyLabToBm() {
try {
log.info("Copy Lab To BM Start!");
copyAndPasteUser();
copyAndPasteNewRecord();
log.info("Scheduled task completed successfully!");
} catch (Exception e) {
log.error(e.getMessage());
}
}
/** user 정보 복사 */
public void copyAndPasteUser() {
try {
log.info("Copy User Start!");
labService.copyAndPasteUser();
log.info("User Paste Task completed successfully!");
} catch (Exception e) {
log.error(e.getMessage());
}
}
/** 활동 기록 정보 복사(전날 새벽 3시 이후의 데이터) */
public void copyAndPasteNewRecord() {
try {
log.info("Copy Record Start!");
labService.copyAndPasteNewRecords();
log.info("Record Paste Task completed successfully!");
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
만약 여기서 user를 먼저 불러온 뒤 기록을 다시 불러오고 싶다면 별개로 cron을 설정할 수도 있다.
@Configuration
@RequiredArgsConstructor
@Slf4j
public class SchedulerConfig {
private final LabService labService;
/** user 정보 복사 */
@Scheduled(cron = "0 0 3 * * ?")
public void copyAndPasteUser() {
try {
log.info("Copy User Start!");
labService.copyAndPasteUser();
log.info("User Paste Task completed successfully!");
} catch (Exception e) {
log.error(e.getMessage());
}
}
/** 활동 기록 정보 복사(전날 새벽 3시 이후의 데이터) */
@Scheduled(cron = "3 0 3 * * ?")
public void copyAndPasteNewRecord() {
try {
log.info("Copy Record Start!");
labService.copyAndPasteNewRecords();
log.info("Record Paste Task completed successfully!");
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
두 번째 메소드는 3초뒤에 실행하도록 크론을 다시 설정해주었다.
만약 어노테이션을 하나만 두려면 Thread.sleep(3000);를 써서 한 메소드에서 처리하게 해도 된다.
🚨 처음에 cron은 동일하게 설정하고 initDelay로 지연시간을 주려했지만! 두 가지는 함께 쓸 수 없다고 한다.
cron만 쓰거나, 아니면 나머지 속성들을 조합해서 쓰거나!!