[nest.js] Cron(작업 스케줄링) : onModuleInit()

김민재·2025년 2월 15일

nest.js

목록 보기
32/63

resetAnnualLeaveOfMonth 수정하기 resetAnnualLeave로

onModuleImit()이란?

  • Cron에서 사용하는 메서드로, 서버가 재시작 되었을 때 메서드를 이용할 수 있다.

문제(1)

Cron을 이용해 신입은 매월 1일에 DB에 연차 필드를 1로 업데이트 해줘야하는데, 1일에 서버가 꺼지면 Cron은 실행되지 않는다.

  • 매월 1일에 실행되는 Cron
    // 고용된지 1년 미만 유저는 월마다 연차 1로 바뀜
    @Cron('0 0 1 * *')
    async resetAnnualLeaveOfMonth() {
        // 모든 유저 정보를 불러온다.
        const users: User[] = await this.userRepository.find();

        // 1년 미만
        await this.getLessThan1yearUsers(users);

        // 1년 이상
        await this.getMoreThan1year(users);
    }

해결방법

Cron에 onModuleInit()을 이용하고, if문을 이용하여 오늘이 1일이라면, 서버가 재시작되었을 때, 업데이트가 발생된다.

  • onModuleInit()을 이용하여 서버가 재시작되면 호출
export class UserService implements OnModuleInit {
    // onModuleInit()은 서버가 재 시작되었을 때 발생하는 함수
    async onModuleInit() {
        console.log('서버가 꺼졌네요. Cron을 재실행할게요.');

        const one_day = new Date().getDate();

        // 오늘이 1일이라면 missedJob cron 실행
        if (one_day === 1 ) {
            await this.resetAnnualLeaveOfMonth();
        }
    }

문제(2)

만약, 1일에 서버가 꺼지고 2일에 서버가 켜지면 onModuleInit()에서 지정한 if문이 실행되지 않는다.

해결방법

1. 매월 1일에 실행하는 reset 함수 Cron이 실행됐다면 redis에 usedCron을 1로 설정해준다.

    // 고용된지 1년 미만 유저는 월마다 연차 1로 바뀜
    @Cron('0 0 1 * *')
    async resetAnnualLeaveOfMonth() {
        // 모든 유저 정보를 불러온다.
        const users: User[] = await this.userRepository.find();

        // 1년 미만
        await this.getLessThan1yearUsers(users);

        // 1년 이상
        await this.getMoreThan1year(users);

        // redis에 저장
   await this.redis.set('usedCron', '1');
    }

2. 매월 마지막 일에 Cron을 이용하여 usedCron을 0으로 바꿔준다.

    // 매월 마지막 일에 usedCron을 0으로 바꾼다.
    @Cron('0 0 L * *')
    async resetUsedCron() {
              // redis에 저장
   await this.redis.set('usedCron', '0');
    }

3. 서버가 재시작 됐을 때, 매월 1일에 실행되는 Cron이 실행되지 않았다면, 실행시킨다. 또한, usedCron을 이용하여 실행됐는지 안 됐는지를 판단 후 실행시킨다.

    // onModuleInit()은 서버가 재 시작되었을 때 발생하는 함수
    async onModuleInit() {
        console.log('서버가 꺼졌네요. Cron을 재실행할게요.');

        const one_day = new Date().getDate();
        
        const usedCron = await this.redis.get('usedCron');

        // 오늘이 1일이라면 함수 실행
        // 1일이 아닌데, 함수가 실행되지 않았다면 함수 실행
        if (one_day === 1 && +this.usedCron === 0) {
            await this.resetAnnualLeaveOfMonth();
        } else {
            if (+this.usedCron === 0) {
                await this.resetAnnualLeaveOfMonth();
            }
        }
    }
  • 전역 변수를 사용하려 했지만, 전역 변수를 사용하게 되면 서버가 재시작될 때마다 값이 초기화 되기 때문에 redis에 저장하는 방식이 좋다.
profile
개발 경험치 쌓는 곳

0개의 댓글