
서비스를 개발하다 보면 주기적으로 동일한 작업을 처리해야 하는 경우가 생긴다.
예를 들어 사용자가 어떤 유료 서비스를 구독하고 있고 매달 결제가 일어난다고 했을 때
매일 특정 시간에 결제일이 된 고객의 카드 결제가 일어나도록 해야한다. 이런 주기적인 반복 작업을 태스트(Task) 또는 배치(Batch) 라고 부른다.
#npm
$ npm i -s @nestjs/schedule @types/cron
#yarn
$ yarn add @nestjs/schedule @types/cron
...
@Module({
imports:[
ScheduleModule.forRoot(),
]
})
...
Nest에서 테스크 스케줄링을 선언하는 방법은 3가지가 있다.
@Cron('* * * * * *', { name: 'Test' })
handleCron() {
this.logger.log('Task Called');
}
크론 잡 방식은 @Cron데코레이터를 이용해 매서드를 태스크로 구현하는 방식이다.
첫번째 인자는 태스크의 반복주기를 뜻하며, cron 패턴을 따른다.
공백으로 구분된 여섯개의 값을 가지는 문자열을 입력받는데, 각 자리별 의미는 다음과 같다.
* * * * * *
| | | | | |
| | | | | day of week (요일, 0-7의 값을 가짐. 0과 7은 일요일)
| | | | month (월, 0-12의 값을 가짐. 0과 12는 12월)
| | | day of month (날, 1-31의 값을 가짐)
| | hour (시간, 0-23의 값을 가짐)
| minute (분, 0-59의 값을 가짐)
second (초, 0-59의 값을 가짐, 선택사항)
패턴의 예시는 다음과 같다.

1초마다 로그가 잘 나오는지 확인해보자.

만약 특정 시간에 한번만 실행되게 하고싶다면 다음과 같이 Date 객체로 직접 날짜를 넣으면 된다.
//앱이 실행되고 3초후 한번만 실행
@Cron(new Date(Date.now() + 3 * 1000))
@Cron(CronExpression.MONDAY_TO_FRIDAY_AT_1AM)
Nest는 자주 사용할 만한 패턴을 CronExpression열거형으로 제공한다. 예를 들어 매주 월요일부터 금요일까지 새벽 1시에 수행되는 테스크를 만들고 싶다면 위 코드 처럼 쓰면 된다.
미리 지원하는 ENUM 코드는 여기에서 볼 수 있다.

⚠️
timeZone옵션과utcOffset옵션을 함께 사용하면 이상동작을 일으킬 수 있다.
다음 선언 방식은 js에 익숙하다면 아! 할 것이다.
@Interval('intervalTask', 3000)
handleInterval() {
this.logger.log('Task Called by interval');
}
태스크 수행 함수에 @Interval 데코레이터를 사용할 수 있다.
첫번째 인자는 태스크의 이름, 두번째 인자는 타임 아웃 시간(ms)이다.
위 코드는 실행된 후 3초 후에 실행되고 3초마다 반복된다.
@Timeout('timeoutTask', 5000)
handleTimeout() {
this.logger.log('Task Called by Timeout');
}
타임아웃 선언방식은 앱이 실행된 후 태스크를 단 한번만 수행한다.
@Timeout데코레이터를 사용하고 인자는 인터벌과 동일하다.
지금 까진 앱이 동작하면 실행되는 스케쥴링이 실행되는 상대적으로 정적인 태스크를 등록했다.
이번엔 특정 조건을 만나면 실행되는 동적인 스케쥴링을 구현 해보겠다.
constructor(
...
private readonly schedulerRegistry: SchedulerRegistry,
...
) {
this.subscribe();
}
subscribe() {
const name = 'subscribe';
const job = new CronJob(`5 * * * * *`, () => {
this.logger.verbose('결제가 완료 되었습니다.');
});
this.schedulerRegistry.addCronJob(name, job);
}
@Post('/subscribe/start')
start() {
const job = this.schedulerRegistry.getCronJob('subscribe');
job.start();
this.logger.log('구독시작!');
}
@Post('/subscribe/end')
end() {
const job = this.schedulerRegistry.getCronJob('subscribe');
job.stop();
this.logger.log('구독종료!');
}
위 코드는 특정 라우터에 접근하면 구독/취소를 하며 동적으로 스케쥴러가 시작/종료된다.

매분 5초마다 태스크가 잘 실행되는걸 확인 할 수 있다.
SchedulerRegistry의 핵심 메서드는 다음과 같다.