Skip to content

Task Scheduling

설명

Github Link

NestJs에서 batch를 제작하는 방법은 여러가지가 있습니다.
그 중 하나는 scheduler를 만드는 것입니다.
단, scheduler를 이용할 떄 여러가지 예상치 못하는 제약이 있습니다.
처음 사용했을 떄는 다른 module에서 import하는 의존성을 알지 못하는 경우도 있고 예전에 적용한 nestjs-cls의 context를 읽지 못하는 경우도 있습니다.
scheduler를 적용하면서 cats를 findAll하는 method에 적용하였고 적용하면서 cls를 Async Local Storage로 모두 교체하였습니다.

설치 및 적용

설치

sh
npm install --save @nestjs/schedule

적용

typescript
// app.module.ts

import { Module } from "@nestjs/common";
import { ScheduleModule } from "@nestjs/schedule";

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

데코레이터 종류

  • Cron: cron은 linux에서 널리 사용하는 스케줄러 입니다. generator를 이용하면 간편합니다.
    • 이름을 지정하여 동적으로 작동할 수 있습니다. Dynamic schedule
    • timezone을 설정할 수 있습니다.
  • Interval: millisecond로 설정하면 해당 시간에 맞춰 반복적으로 실행됩니다.
  • Timeout: 설정한 millisecond만큼 기다린 뒤 1번실행되고 종료 됩니다.

적용

작동하는 것을 보기 위해 아래와 같은 서비스를 만들었습니다.

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

  constructor(private readonly schedulerRegistry: SchedulerRegistry) {}

  @Cron("45 * * * * *", {
    name: "cron-job",
    timeZone: "Asia/Seoul",
  })
  handleCron() {
    this.logger.debug(`Called when the current second is 45`);
  }

  @Cron(CronExpression.EVERY_30_SECONDS, {
    name: "cron-job-every-30-seconds",
    timeZone: "Asia/Seoul",
  })
  handleCronEvery30Seconds() {
    this.logger.debug("Called every 30 seconds");
  }

  @Interval("interval-job", 10000)
  handleInterval() {
    this.logger.debug("Called Interval every 10 seconds");
  }

  @Timeout(60000)
  handleTimeout() {
    this.schedulerRegistry.deleteCronJob("cron-job");
    this.schedulerRegistry.deleteCronJob("cron-job-every-30-seconds");
    this.schedulerRegistry.deleteInterval("interval-job");
    this.logger.debug("Called Timeout after 60 seconds and deleted all jobs");
  }
}

결과는 아래와 같습니다.

sh
backend-1        | [Nest] 509  - 04/24/2024, 4:19:26 AM   DEBUG [CronService] Called Interval every 10 seconds
backend-1        | [Nest] 509  - 04/24/2024, 4:19:30 AM   DEBUG [CronService] Called every 30 seconds
backend-1        | [Nest] 509  - 04/24/2024, 4:19:36 AM   DEBUG [CronService] Called Interval every 10 seconds
backend-1        | [Nest] 509  - 04/24/2024, 4:19:45 AM   DEBUG [CronService] Called when the current second is 45
backend-1        | [Nest] 509  - 04/24/2024, 4:19:46 AM   DEBUG [CronService] Called Interval every 10 seconds
backend-1        | [Nest] 509  - 04/24/2024, 4:19:56 AM   DEBUG [CronService] Called Interval every 10 seconds
backend-1        | [Nest] 509  - 04/24/2024, 4:20:00 AM   DEBUG [CronService] Called every 30 seconds
backend-1        | [Nest] 509  - 04/24/2024, 4:20:06 AM   DEBUG [CronService] Called Timeout after 60 seconds and deleted all jobs

cats controller에 적용

typescript
@Cron('0 0 */1 * * *', {
  name: 'cats-all',
  timeZone: 'Asia/Seoul',
})
@Get()
async findAll() {
  const result = await this.catsService.findAll();
  this.logger.log(result);
  return result;
}

위를 적용하기 위해 cls를 als로 모두 변경하였습니다.
이제 매 정각 시간마다 출력결과가 logd에 찍히게 됩니다.