Schedule

장현욱(Artlogy)·2022년 11월 17일
0

Nest.js

목록 보기
14/18
post-thumbnail

스케쥴러

서비스를 개발하다 보면 주기적으로 동일한 작업을 처리해야 하는 경우가 생긴다.
예를 들어 사용자가 어떤 유료 서비스를 구독하고 있고 매달 결제가 일어난다고 했을 때
매일 특정 시간에 결제일이 된 고객의 카드 결제가 일어나도록 해야한다. 이런 주기적인 반복 작업을 태스트(Task) 또는 배치(Batch) 라고 부른다.

패키지 설치

#npm
$ npm i -s @nestjs/schedule @types/cron
#yarn
$ yarn add @nestjs/schedule @types/cron

설정

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

테스크 스케줄링

Nest에서 테스크 스케줄링을 선언하는 방법은 3가지가 있다.

CronJob

  @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의 핵심 메서드는 다음과 같다.

  • stop() - 실행이 예약된 작업을 중지
  • start() - 중지된 작업을 다시 시작
  • setTime(time: CronTime) - 현재 작업을 중지하고 새로운 시간을 설정하여 다시 시작
  • lastDate() - 작업이 마지막으로 실행된 날짜를 반환
  • nextDates(count: number) - 예정된 작업의 실행 시각을 count 개수만큼 배열 빈환. 배열의 각 요소는 moment 객체이다.

0개의 댓글