현재 UOS공지사항의 백엔드 시스템을 nestjs로 변경하는 작업을 진행중이다.
UOS공지사항에서 가장 핵심적인 부분이 공지사항의 스크래핑이라고 볼 수 있다.
공지사항의 스크래핑은 그 특성상 주기적으로 반복해야하는 작업이다.
nestjs에서 특정 서비스 로직을 어떤 방법으로 주기적으로 실행할 수 있는지 정리해보았다.
cron은 작업을 고정된 시간, 날짜, 간격에 주기적으로 실행하는데 사용되는 표준 Unix 유틸리티이다.
nestjs는 node-cron 패키지를 사용하여 cron job을 지원한다. node-cron은 OS레벨에서 작동하는 cron은 아니며 cron의 기능을 모방하는 패키지이다.
Class Decorator를 통한 declarative방식과 nestjs의 SchedulerRegistry API를 통한 dynamic방식으로 cron을 사용할 수 있다.
nestjs의 @Cron
decorator에 첫번째 인수로 표준 cron패턴을 입력한다.
표준 cron패턴은 Asterisk(ex. *
), Range(ex. 1-3, 5
) 그리고 Step(ex. */2
)으로 이루어진다.
cron 패턴 string은 총 6자리로 이루어져 있고 각 자리마다의 의미는 다음과 같다.
* * * * * *
| | | | | |
| | | | | day of week
| | | | month
| | | day of month
| | hour
| minute
second (optional)
만약에 매일 오전 9시부터 오후 5시까지 30분마다 Cron job을 실행하고 싶다면 cron패턴은 다음과 같다.
@Cron('0 */30 9-17 * * *')
크론 패턴을 뒤에서부터 읽어볼때 day of week
과 month
그리고 day of month
가 *
로 표시되어 있으므로 job을 매일 반복적으로 실행하겠다는 의미가 된다.
hour
가 9-17
로 표시되어 있으므로 9시 ~ 17시 사이에 job을 실행하겠다는 의미가 추가된다.
마지막으로 minute
가 */30
로 표시되어 있으므로 매 30분 마다 job을 실행한다.
결과적으로 '0 */30 9-17 * * *'
은 매일 오전 9시부터 오후 5시까지 30분마다 job을 실행한다.
nestjs의 @Cron
데코레이터에 두번째 인수로 추가적인 옵션을 전달할 수 있다.
전달가능한 옵션은 name
timezone
utcOffset
이 있다.
timezone
과 utcOffset
은 cron job의 실행을 위한 시간대 설정에 관한 옵션이다. 선택에 따라 둘 중 하나의 옵션을 전달하면 된다.
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
@Injectable()
export class NotificationService {
@Cron('* * 0 * * *', {
name: 'notifications',
timeZone: 'Europe/Paris',
})
triggerNotifications() {}
}
@nestjs/schedule
모듈의 dynamic API를 통하여 declarative cron job을 관리하거나 새롭게 cron job을 생성할 수 있다.
SchedulerRegistry
API를 통해 CronJob인스턴스에 접근하려면 SchedulerRegistry
를 주입해야한다.
constructor(private schedulerRegistry: SchedulerRegistry){}
새로운 cron job 생성
this.schedulerRegistry.addCronJob(name, new Cron(cronExpression, callback));
cron job 삭제
this.schedulerRegistry.deleteCronJob(name);
cron job 가져오기
const job = this.schedulerRegistry.getCronJob(name)
getCronJob(name)
으로 가져온 cronjob 오브젝트는 다음과 같은 작업을 수행할 수 있다.
stop()
: 계획되어있는 job을 중단시킨다.start()
: 멈춘 job을 재개한다.setTime(time: CronTime)
: job을 잠깐 멈춘 후 새로운 시간을 설정하고 다시 job을 시작한다.lastDate()
: 마지막 job이 실행된 시각을 string으로 반환하는 함수이다.nextDates(count: number)
: 다가오는 job의 실행 시각을 count만큼의 크기를 갖는 배열로 반환한다.특정 task를 일정시간 간격으로 반복적으로 실행하기 위해 사용한다.
interval역시 nestjs에서 declarative, dynamic한 방식으로 사용할 수 있으며 JavaScript의 setInterval()
함수가 기반이 된다.
@Interval('notifications', 10000)
handleInterval() {
this.logger.debug('Called every 10 seconds');
}
const interval = this.schedulerRegistry.getInterval('notifications');
clearInterval(interval);
setInterval(callback, delay, args...)
는 delay 마다 콜백을 주기적으로 실행한다.
만약에 delay를 100ms로 설정했는데 callback 함수의 실행이 100ms보다 오래걸리면 어떻게 될까?
interval은 delay없이 콜백함수를 바로바로 실행한다.
특정 task를 일정 시간이 지난 후에 단 한번 실행하기 위해 사용한다.
@Timeout('notifications', 5000)
handleTimeout() {
this.logger.debug('Called once after 5 seconds');
}
const timeout = this.schedulerRegistry.getTimeout('notifications');
clearTimeout(timeout);
setTimeout(callback, delay, args...)
은 delay 후 callback을 딱 한번 실행한다.
하지만 중첩된 setTimeout을 사용하면 callback을 반복적으로 실행할 수 있다.
setInterval과 중첩된 setTimeout의 차이점이라면 setInterval과 달리 setTimeout은 콜백 사이의 최소 delay를 보장해준다는 것이다.
Documentation | NestJS - A progressive Node.js framework
setTimeout과 setInterval을 이용한 호출 스케줄링
https://www.notion.so/10-setTimeout-setInterval-b71a02545c8f44e6afcb6b685d675cd3