프로젝트 TIL

정승원·2023년 6월 23일
0
post-thumbnail

📒 목차

📌 크론(CRON)
✅ CRON이란??
✅ 설치 명령어
✅ 사용방법
✅ 프로젝트 적용
🌈 오늘 하루

📌 CRON

CRON이 무엇인지 알아보기 전에 내가 어떻게 CRON까지 도달하게 되었는지 배경 설명을 먼저 하고 싶다. 우선 내가 지금 개발하고 있는 서비스는 사용자간의 서로 재능을 사고 팔 수 있는 시스템이다. 여기서 문제는, 사용자간에 재능을 사고팔때 판매자가 구매자에게 결과물을 만들어 보내줬을 때, 구매자가 확정 버튼을 눌러야 판매자에게 금액이 전송되는 구조이다. 이러한 서비스에 대해 피드백을 받았는데, 생각지도 못한 부분에서 피드백을 받게 되었다.

구매자가 결과물만 받고 확정 버튼을 누르지 않을 수도 있다는 것이다!!!
서비스를 만들며 항상 생각하고 고민해야하는 부분이 이용자들이 행할 수 있는 모든 동작에 대한 대응이 있어야 한다는 것이다.

하지마, 나의 서비스에서는 이러한 부분이 누락되어 있었으며, 구매자가 확정 버튼을 누르지 않는다면, 판매자는 영영 금액을 받을 수 없었다. 따라서 판매자가 결과물을 전송하고 3일이 지나도 구매자가 확정을 누르지 않는다면 자동으로 확정으로 변경되어 금액이 판매자에게 가도록 리팩토링을 시작하였다.

하지만, 여기서 더 큰 문제를 만나게 되었다!!!

프론트엔드에서 API 요청을 보내지 않고 어떻게 일정 시간이 지난 뒤, 데이터베이스를 수정해야 하는가에 대한 방법이 생각이 나지 않았기 때문이다.

해결책을 찾기 위해 먼저, 인터넷 검색을 시작했다. 그러던 중 CRON이라는 라이브러리를 발견하게 되었다. CRON 라이브러리는 내가 개발하고 프레임워크인 nestjs 라이브러리이기 때문에 현재 만들고 있는 프로젝트와 일치했기 때문에 관심이 생겨 구체적으로 알아보게 되었다.

✅ CRON이란??

CRON이란 작업을 주기적으로 실행하기 위해 사용되는 시간 기반 스케줄링 시스템이다.
CRON을 사용하게 되면 특정 시간, 날짜, 주기와 같은 작업이 실행되도록 예약 할 수 있다.
예를 들어 특정 날짜 시간에 문자를 전송하도록 하는 기능을 할 수 있다는 것이다!! 나는 이러한 CRON을 발견하자마자 내가 구현하려는 기능에 적합하다고 생각했다.

결론적으로, CRON을 활용하여 내가 구현하고 싶은 기능을 만들어냈다.
하지만, 이후 아래에서 언급할 것이지만 CRON에 대한 한계점이 있기에 추가적인 내용을 아래에서 후술하도록 하겠다.

✅ 설치 명령어

이제 CRON의 사용에 앞서 설치해야하는 모듈에 대해 먼저 알아보록 해보자.
CRON을 사용하기 위해서는 아래의 두개의 모듈을 설치해야 한다. npm 또는 yarn 중 선택하여 설치하면 된다.

$ npm install --save @nestjs/schedule
$ npm install --save-dev @types/cron

yarn add @nestjs/schedule
yarn add --dev @types/cron

✅ 사용방법

이후에 또 다시 CRON 라이브러리를 사용하게 될 수도 있으니, 기록을 남겨 향후에 다시 찾아봐야하는 번거로움을 줄이기 위해 사용방법도 기록하도록 하겠다.

1. AppModule에 ScheduleModule 등록하기

import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule'; // 추가!!

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

2. 원하는 기능을 Service에서 적용시키기
Cron 데코레이터를 사용하여 스케줄링을 원하는 기능을 구현하면 모든 것이 끝나게 된다.

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

@Injectable()
export class DatabaseService {
  @Cron('0 0 * * *') // 매 시간 정각마다 실행
  modifyDatabase() {
    // 데이터베이스 수정 작업 수행
  }
}

3. 스케줄링 기간 설정하기
각각의 별표('*')는 특정 필드를 나타낸다. 해당 필드는 숫자 또는 특정 값으로 구성되는데, 순서대로 왼쪽부터
아래와 같이 의미를 갖는다.

첫 번째 : 분 (0 ~ 59)
두 번째 : 시간 (0 ~ 23)
세 번째 : 일 (1 ~ 31)
네 번째 : 월 (1 ~ 12)
다섯 번째 : 요일 (0 ~ 7, 0과 7은 일요일)

추가적인 설명을 하자면 예를들어 3으로 값을 구성할 수 있는 필드에 3을 입력하게 되면, 3일 또는 3시처럼 숫자 3에 해당하는 의미를 갖는다.

또한 별표('')는 와일드카드라고 하며 모든 값을 나타낼 때 사용한다. 예를들어 분 필드에 ''을 사용하면 매분마다 작업이 실행된다.

범위를 나타낼 때는 '-'를 사용하는데, 예를들어 1-15라고 표기하면 1부터 5까지의 값을 갖는다.

간격을 지정할 때는 '/'를 사용하는데, 예를들어 0/15라고 분 필들에 표기하면 매 15분마다 작업이 실행된다.

✅ 프로젝트 적용

이제 본격적으로 어떻게 프로젝트에 적용이 되었는지 설명하도록 해보겠다. 먼저 CRON 을 적용해야하는 기능으로는 판매자가 결과물을 구매자에게 보내고 나면 데이터베이스에 보낸 시간이 저장되는데 해당 시간에 3일을 더하여 3일 후까지 구매자가 확정버튼을 누르지 않으면 자동으로 확정이 되어 판매자에게 금액이 입금되는 기능이다.

작성한 코드를 보도록 하자.

아래의 코드에 대한 해석을 해보자면, 먼저 1시간마다 작업물을 보냈는데 구매자가 확정버튼을 누르지 않은 거래항목들을 조회한다. 조건에 일치하는 게시글을 찾으면 결과물 전송시간을 찾고 3일을 더한 뒤, 현재시간을 뺀다.

연산의 결과가 양수가 나오게 되면 아직 3일이 지나지 않았기 때문에 지나가고 음수가 된다면, requestComplete() 함수를 실행시켜 확정 버튼을 눌렀을 때 이루어지는 프로세스를 진행하도록 코드를 구현하였다.

  // 1시간마다 작업 완료 확정 만료시간 확인 및 업데이트
  @Cron('0 0-23/1 * * *')
  async checkRequestComplete() {
    const result = await this.requestsRepository.find();
    for (let i = 0; i < result.length; i++) {
      if (result[i].request_completedAt === null && result[i].request_sendAt) {
        const target = result[i].request_sendAt.getTime() + 259200000;
        const change = new Date().getTime();
        const differ = target - change;
        if (differ < 0) {
          const seller_id = result[i].seller_id;
          const seller_phone = (
            await this.usersRepository.findOne({
              where: { user_id: seller_id },
            })
          ).user_phone;
          const sellerNickname = result[i].seller_nickname;
          const buyerNickname = result[i].buyer_nickname;
          const requestTitle = result[i].request_title;
          const request_id = result[i].request_id;
          await this.requestComplete({
            seller_phone,
            sellerNickname,
            buyerNickname,
            requestTitle,
            request_id,
          });
        }
      } else {
        continue;
      }
    }
  }

🌈 오늘 하루

오늘 목표했던 기능은 구현했다. 하지만, 아쉬운점이 존재한다. Cron 라이브러리는 스케줄을 설정하여 프론트엔드의 API 요청 없이 함수를 실행시키고 데이터베이스를 조작할 수 있다는 장점이 있지만, 설정한 기간마다 함수가 실행되기 때문에 자원을 낭비하는 결과를 초래한다. 사실 내가 생각한 이상적인 동작은 판매자가 결과물을 전송하면 3일의 시간이 돌아가고 이후에 3일이 지나면 그 때, 딱 한번, 함수가 실행되도록 하고 싶었다.

물론, 나의 지식이 얕고, 내가 생각한 기능에 부합하는 라이브러리를 찾지 못해 구현하지 못한 것일 수 있다. 하지만, 인터넷을 검색하거나 다른 사람에게 자문을 구하는 등 여러 노력을 해봤지만 Cron보다 나은 라이브러리를 찾지 못했다. 따라서, Cron을 공부해볼 수 있는 좋은 기회였다고 생각하며, 언젠가 생각한 이상적인 기능을 구현할 수 있는 방법을 찾게 된다면 그 때, 다시 리팩토링을 하면 좋을 것 같다.

0개의 댓글