728x90

실제 프로덕션 환경에서 DB 백업과 같은 정기 작업은 매우 중요합니다. NestJS의 스케줄러를 사용하여 MySQL 데이터베이스를 정기적으로 백업하는 실습 코드를 구현해 보겠습니다.
1. 🛠️ 사전 준비: 외부 명령어 실행 환경
DB 백업은 보통 **mysqldump**와 같은 외부 명령어를 실행하여 데이터베이스 스키마와 데이터를 파일로 추출하는 방식으로 이루어집니다. NestJS (Node.js)에서 외부 명령어를 실행하려면 child_process 모듈을 사용합니다.
# child_process는 내장 모듈이므로 별도 설치 불필요
# 💡 .env 파일에 DB 접속 정보가 정의되어 있어야 합니다.
# DATABASE_HOST=localhost
# DATABASE_USER=root
# DATABASE_PASSWORD=password
# DATABASE_NAME=mynestdb
728x90
2. ⚙️ TasksService에 Cron 백업 로직 구현
TasksService에 @Cron 데코레이터를 사용하여 매일 정해진 시각에 mysqldump 명령어를 실행하는 메서드를 구현합니다.
// src/tasks/tasks.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron } from '@nestjs/schedule';
import { exec } from 'child_process'; // 💡 외부 명령어 실행을 위한 모듈
import * as path from 'path'; // 경로 처리를 위한 모듈
@Injectable()
export class TasksService {
private readonly logger = new Logger(TasksService.name);
// ConfigService를 주입받아 .env의 DB 정보를 사용합니다.
constructor(private configService: ConfigService) {}
// 💡 매일 새벽 3시 0분 0초에 실행
@Cron('0 0 3 * * *')
async handleDbBackup() {
this.logger.warn('--- ⏳ 정기 MySQL DB 백업 작업 시작 ---');
const host = this.configService.get<string>('DATABASE_HOST');
const user = this.configService.get<string>('DATABASE_USER');
const password = this.configService.get<string>('DATABASE_PASSWORD');
const dbName = this.configService.get<string>('DATABASE_NAME');
// 💡 백업 파일 경로 설정 (프로젝트 루트/backup 폴더에 저장)
const backupDir = path.join(process.cwd(), 'backup');
const timestamp = new Date().toISOString().replace(/:/g, '-');
const backupFileName = `${dbName}_${timestamp}.sql`;
const backupPath = path.join(backupDir, backupFileName);
// 💡 mysqldump 명령어 생성
// --single-transaction: 백업 중에도 DB 작업이 가능하도록 함 (InnoDB 사용 시)
const command = `mysqldump -h ${host} -u ${user} -p${password} ${dbName} --single-transaction > ${backupPath}`;
// 💡 exec 명령을 Promise로 감싸 비동기 처리
try {
await new Promise<void>((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
this.logger.error(`백업 실패: ${error.message}`);
return reject(error);
}
if (stderr) {
this.logger.warn(`백업 경고: ${stderr}`);
}
resolve();
});
});
this.logger.warn(`--- ✅ DB 백업 완료: ${backupFileName} ---`);
} catch (e) {
this.logger.error(`DB 백업 중 치명적인 오류 발생!`);
}
}
}
728x90
3. ⏱️ @Interval(): 정기 작업 실습 (오래된 백업 파일 정리)
DB 백업 외에도, 정기적으로 서버의 임시 파일을 정리하거나 오래된 로그를 삭제하는 등의 관리 작업을 @Interval로 구현할 수 있습니다. 여기서는 1시간마다 오래된 백업 파일을 정리하는 예시를 추가합니다.
// src/tasks/tasks.service.ts (계속)
import { Interval } from '@nestjs/schedule';
import * as fs from 'fs/promises'; // Node.js 파일 시스템 Promise API 사용
// ... TasksService 내부 ...
// 💡 1시간(3,600,000ms)마다 실행
@Interval(3600000)
async cleanOldBackups() {
const backupDir = path.join(process.cwd(), 'backup');
const oneWeekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000); // 7일 전 시간
try {
const files = await fs.readdir(backupDir);
for (const file of files) {
if (file.endsWith('.sql')) {
const filePath = path.join(backupDir, file);
const stat = await fs.stat(filePath);
// 💡 파일 생성 시간이 7일이 넘었으면 삭제
if (stat.birthtimeMs < oneWeekAgo) {
await fs.unlink(filePath);
this.logger.log(`오래된 백업 파일 삭제: ${file}`);
}
}
}
} catch (e) {
this.logger.error(`오래된 백업 파일 정리 중 오류 발생: ${e.message}`);
}
}
}
4. 📝 실습 시 주의 사항
- mysqldump 경로: 위 코드는 mysqldump 명령어가 시스템 환경 변수에 등록되어 있다고 가정합니다. 그렇지 않다면 명령어 경로를 명시해야 합니다.
- 권한: mysqldump를 실행하는 사용자(NestJS 프로세스)에게 DB 접근 권한 및 백업 폴더에 쓰기/삭제 권한이 있어야 합니다.
- 동시성: @Cron 메서드가 실행되는 동안 서버가 재부팅되면 작업이 중단되거나, 여러 인스턴스 환경에서 코드가 중복 실행될 수 있습니다. 프로덕션에서는 이를 방지하기 위해 **잠금 메커니즘(Locking)**을 구현해야 합니다.
728x90
'Nest.js를 배워보자 > 8. NestJS에서 Cron, Scheduler 구현하기' 카테고리의 다른 글
| Cron, Interval, Timeout 실습: 정기 작업 구현 (0) | 2025.12.02 |
|---|---|
| NestJS 스케줄러: @nestjs/schedule 소개 (0) | 2025.12.02 |