본문으로 바로가기

docs.nestjs.com/techniques/task-scheduling

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

공식적으로 제공되는 예시들

github.com/nestjs/nest/tree/master/sample/27-scheduling

 

 

리눅스에서는 cron이라고 해서 시스템 프로그래밍에서 OS 단에서 주기적 실행을 했는데, Node에서는 cron과 같은 기능을 하는 많은 패키지들이 있다. Nest의 @nestjs/schedule 패키지는 node-cron이라는 유명한 패키지를 이용하여 cron을 제공한다.

 

* Cron과 interval을 구별하자

 

Cron : "매 분 n초마다 실행" => 5 * * * * * 이면 1분 5초, 2분 5초 ... 이렇게 실행된다.

interval : "n초마다 실행" => 5초 interval이면 5, 10, 15, 20 ... 이렇게 실행된다.

 

 

설치, AppModule에 import

$ npm install --save @nestjs/schedule
$ npm install --save-dev @types/cron
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';

@Module({
  imports: [
    ScheduleModule.forRoot()
  ],
})
export class AppModule {}

 

 

declarative한 방법으로 cron, interval, timeout

 

cron in a declarative way

 

Service 단에서 선언하고 사용하면 된다.

import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron('45 * * * * *')
  handleCron() {
    this.logger.debug('Called when the current second is 45');
  }
}

 

좀 더 간단한 예시를 들자면 다음과 같다.

// 매분의 5초마다 실행 (1분 5초, 2분 5초... 따라서 간격은 1분)
@Cron('5 * * * * *')
async checkForPayments() {
  console.log(Date.now());
  console.log('checking for payments');
}

 

 

Cron의 예시들을 살펴보고, 잘 모르면 여기를 이용해보자.

crontab.guru/examples.html

 

Cron examples - Crontab.guru

 

crontab.guru

 

 

@Cron 데코레이터에서 사용할 수 있는 패턴들.

 

  • Asterisk (e.g. *) : every
  • Ranges (e.g. 1-3,5) 
  • Steps (e.g. */2)
* * * * * *
| | | | | |
| | | | | day of week
| | | | month
| | | day of month
| | hour
| minute
second (optional)

 

이해를 위한 예시를 들어보면 다음과 같다.

* * * * * * every second
45 * * * * * every minute, on the 45th second
매분 45초마다.
* 45초마다 실행되는 것이 아니라 매 분의 45초에 실행된다.
(1분 45초, 2분 45초... 간격은 1분이 된다)
* 10 * * * * every hour, at the start of the 10th minute
매시, 10분마다. (1시 10분, 2시 10분...)
0 */30 9-17 * * * every 30 minutes between 9am and 5pm
오전9시~오후 5시 사이에 매 30분마다
0 30 11 * * 1-5 Monday to Friday at 11:30am
(평일, 11시 30분에)

 

이런 패턴이 익숙하지 않다면 일반적으로 사용되는 cron 패턴을 enum으로 제공하니 이것을 사용해도 좋다.

예시를 들면 다음과 같다. 매 45초마다 실행하도록 만드는 것이다.

import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron(CronExpression.EVERY_45_SECONDS)
  handleCron() {
    this.logger.debug('Called every 45 seconds');
  }
}

 

정확한 날짜에 딱 한 번 실행하고 싶다면 @Cron에 javascript Date를 제공할 수도 있다.

 

 

@Cron() decorator 추가 옵션들

말은 추가인데, 사실상 써줘야 하는 것들입니다.

name Useful to access and control a cron job after it's been declared.
특히, dynamic한 방법으로 cron을 다루기 위해서는 name 값을 주어야 합니다. 가급적이면 필수적으로 줍시다.
timeZone Specify the timezone for the execution. This will modify the actual time relative to your timezone. If the timezone is invalid, an error is thrown. You can check all timezones available at Moment Timezone website.
utcOffset This allows you to specify the offset of your timezone rather than using the timeZone param.
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class NotificationService {
  @Cron('* * 0 * * *', {
    name: 'notifications',
    timeZone: 'Europe/Paris',
  })
  triggerNotifications() {}
}

 

 

interval in a declarative way

ms 단위로 interval 선언 가능.

이후 dynamic한 방법으로 제어하기 위해서는 name을 줄 것.

@Interval('interval', 10000)
handleInterval() {
  console.log("this function execute every 10 secs)
}

 

timeout in a declarative way

ms 단위로 timeout 선언 가능

이후 dynamic한 방법으로 제어하기 위해서는 name을 줄 것.

// timeout
// This mechanism uses the JavaScript setTimeout() function
@Timeout('timeout', 5000)
handleTimeout() {
  console.log('timeout! after 5 secs');
}

 

 

Dynamic schedule module API

 

The @nestjs/schedule module provides a dynamic API that enables managing declarative cron jobs, timeouts and intervals.

The API also enables creating and managing dynamic cron jobs, timeouts and intervals, where the properties are defined at runtime.

 

dynamic이라는 말에서 알 수 있듯이, cron, inteval, timeout을 선언한 후 정지, 재시작, 재설정, 생성 등등을 제어할 수 있다는 겁니다.

 

 

service의 constructor에 schedulerRegistry를 등록한 다음 사용하자.

constructor(private schedulerRegistry: SchedulerRegistry) {}

 

 

Dynamic cron

 

(1) 기존 cron 제어하기

 

name 값을 준 cron을 아래와 같이 사용할 수 있다.

// 매분의 5초마다 실행 (1분 5초, 2분 5초... 따라서 간격은 1분)
@Cron('5 * * * * *', { name: 'myJob' })
async checkForPayments() {
  console.log('checking for payments');
  const cronJob = this.schedulerRegistry.getCronJob('myJob');
  cronJob.stop(); // 결과적으론 1번만 실행하고 cronJob이 멈추게 됨
}

 

사용할 수 메서드는 다음과 같은 것이 있다.

  • stop() - stops a job that is scheduled to run.
  • start() - restarts a job that has been stopped.
  • setTime(time: CronTime) - stops a job, sets a new time for it, and then starts it
  • lastDate() - returns a string representation of the last date a job executed
  • nextDates(count: number) - returns an array (size count) of moment objects representing upcoming job execution dates.

SchedulerRegistry.addCronJob() : cron 생성

 

기존 cron 을 제어하는 것 외에도 SchedulerRegistry.addCronJob() 메서드를 통해서 cron을 생성할 수도 있다.

addCronJob(name: string, seconds: string) {
  const job = new CronJob(`${seconds} * * * * *`, () => {
    this.logger.warn(`time (${seconds}) for job ${name} to run!`);
  });

  this.scheduler.addCronJob(name, job);
  job.start();

  this.logger.warn(
    `job ${name} added for each minute at ${seconds} seconds!`,
  );
}

 

SchedulerRegistry.deleteCronJob() : cron 삭제

deleteCron(name: string) {
  this.scheduler.deleteCronJob(name);
  this.logger.warn(`job ${name} deleted!`);
}

 

SchedulerRegistry.getCronJobs() : cron 확인하기

getCrons() {
  const jobs = this.scheduler.getCronJobs();
  jobs.forEach((value, key, map) => {
    let next;
    try {
      next = value.nextDates().toDate();
    } catch (e) {
      next = 'error: next fire date is in the past!';
    }
    this.logger.log(`job: ${key} -> next: ${next}`);
  });
}

 

 

Dynamic interval, Dynamic timeout

 

cron과 대동소이하니 직접 참고하자

 

docs.nestjs.com/techniques/task-scheduling#dynamic-intervals

docs.nestjs.com/techniques/task-scheduling#dynamic-timeouts

 

'Node, Nest, Deno > 🦁 Nest.js' 카테고리의 다른 글

Nest 프로젝트 플로우 종합 : gql, jest test, etc  (0) 2021.01.07
Custom param decorators  (0) 2020.11.24
Guard + MetaData  (0) 2020.11.24
Nest Middleware  (0) 2020.11.24
dynamic-modules 만들기  (0) 2020.11.24

darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체