Dandy Now!
  • [Node.js] 배치 프로그램에 강력한 로깅, Winston 적용하기
    2025년 06월 04일 15시 05분 51초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    Node.js 배치 프로그램에 강력한 로깅, Winston 적용하기

    Node.js 기반의 배치 프로그램을 운영하다 보면, 특정 시간에만 실행되는 코드의 오류를 파악하기 어려운 경우가 많다. 이때 정확하고 체계적인 로그 기록은 문제 진단 시간을 획기적으로 줄여주는 핵심 요소이다. 이번 포스팅에서는 Node.js 로깅 라이브러리인 Winston을 사용하여 배치 프로그램에 강력한 로그 기록 기능을 추가하는 방법을 소개한다.

    1. Winston, 왜 선택해야 할까?

    Winston은 Node.js에서 가장 널리 사용되는 로깅 라이브러리 중 하나이다. 유연하고 확장성이 뛰어나며, 다양한 "전송(Transports)" 방식을 지원하여 개발 및 운영 환경에 맞는 최적의 로깅 환경을 구축할 수 있다.

    • 주요 특징
      • 다양한 전송 방식: 콘솔 출력, 파일 저장, 데이터베이스, 외부 로깅 서비스 등 다양한 곳으로 로그를 보낼 수 있다.
      • 로그 레벨 관리: error, warn, info, debug 등 메시지의 중요도에 따라 레벨을 부여하여 로그를 체계적으로 분류한다.
      • 커스터마이징 가능한 포맷: 로그 메시지의 형식, 타임스탬프, 색상 등을 자유롭게 설정할 수 있다.
      • 일별 파일 로테이션: winston-daily-rotate-file 플러그인을 통해 로그 파일을 일별로 자동 생성하고 관리한다.

    2. Winston 설치하기

    Winston과 파일 로테이션 기능을 위해 winston-daily-rotate-file 플러그인을 함께 설치한다.

    • 설치 명령어
      npm install winston winston-daily-rotate-file

    3. 로거 설정하기 (logger.js)

    프로젝트 내부에 logger.js와 같은 파일을 생성하여 로거 설정을 한 곳에서 관리하는 것이 좋다.

    // logger.js
    const winston = require('winston');
    const { combine, timestamp, printf, colorize } = winston.format;
    require('winston-daily-rotate-file'); // 일별 로그 파일 로테이션 플러그인
    
    // 로그 메시지 포맷 정의
    const logFormat = printf(({ level, message, timestamp }) => {
      return `${timestamp} ${level}: ${message}`;
    });
    
    const logger = winston.createLogger({
      level: 'info', // 기본 로그 레벨 (info 이상만 기록)
      format: combine(
        timestamp({
          format: 'YYYY-MM-DD HH:mm:ss', // 타임스탬프 형식
        }),
        logFormat // 위에서 정의한 로그 포맷 적용
      ),
      transports: [
        // 1. 콘솔로 출력하는 트랜스포트
        new winston.transports.Console({
          format: combine(
            colorize(), // 콘솔 출력 시 레벨별 색상 적용
            logFormat
          )
        }),
        // 2. 일반 로그를 일별 파일로 저장하는 트랜스포트
        new winston.transports.DailyRotateFile({
          filename: 'logs/application-%DATE%.log', // 로그 파일명 패턴 (예: application-2025-06-04.log)
          datePattern: 'YYYY-MM-DD', // 날짜 패턴
          zippedArchive: true, // 오래된 로그 파일 압축 여부
          maxSize: '20m', // 단일 파일 최대 크기
          maxFiles: '14d', // 보관할 파일 기간 (14일)
          level: 'info', // 이 파일에는 info 레벨 이상 (info, warn, error) 기록
        }),
        // 3. 에러 로그만 별도 파일로 저장하는 트랜스포트
        new winston.transports.DailyRotateFile({
          filename: 'logs/error-%DATE%.log', // 에러 전용 파일명 (예: error-2025-06-04.log)
          datePattern: 'YYYY-MM-DD',
          zippedArchive: true,
          maxSize: '20m',
          maxFiles: '14d',
          level: 'error', // !!! 오직 error 레벨만 기록 !!!
        }),
      ],
    });
    
    module.exports = logger;
    • 코드 설명
      • level: 'info': 로거의 기본 레벨을 info로 설정하여, info, warn, error 레벨의 로그만 처리한다. (개발 시 debug로 변경하여 상세 로그 확인 가능)
      • winston.transports.Console: 콘솔에 로그를 출력하도록 설정하여, 개발 및 테스트 시 실시간으로 로그를 확인할 수 있다.
      • winston.transports.DailyRotateFile: 로그를 파일로 저장하며, filenamedatePattern을 통해 일별로 새로운 파일을 생성한다. zippedArchive로 자동 압축, maxSizemaxFiles로 파일 크기 및 보관 기간을 관리한다.
      • 핵심: 두 번째 DailyRotateFile 트랜스포트는 level: 'error'로 설정되어, 오직 error 레벨의 로그만 logs/error-%DATE%.log 파일에 기록한다.

    4. 배치 프로그램에서 로거 사용하기

    설정된 logger 객체를 필요한 파일에서 require하여 사용한다. 특히 try-catch 블록 내에서 error 레벨을 활용하는 것이 중요하다.

    // batch-program.js
    const logger = require('./logger'); // logger.js 파일 경로에 맞게 수정
    
    async function updateBatchAPUnjoinTable() {
      logger.info('batch_ap_unjoin 테이블 업데이트 작업 시작.'); // 정보성 로그
    
      try {
        // 실제 테이블 업데이트 로직
        // 예시: await db.query('UPDATE batch_ap_unjoin SET ...');
        logger.debug('SQL 쿼리 실행 중...'); // 디버깅용 상세 로그
        const isSuccess = Math.random() > 0.3; // 성공/실패 시뮬레이션
    
        if (!isSuccess) {
          throw new Error('데이터베이스 업데이트 쿼리 실행 중 예외 발생.');
        }
    
        logger.info('batch_ap_unjoin 테이블 업데이트 성공.');
    
      } catch (error) {
        // 오류 발생 시 error 레벨로 상세 로그 기록
        logger.error('batch_ap_unjoin 테이블 업데이트 실패:', error); // 에러 객체 포함
        // error 객체를 두 번째 인자로 넘기면 Winston이 스택 트레이스 등 상세 정보를 기록한다.
    
        // 배치 특성상, 치명적인 오류 발생 시 슬랙/메일 등으로 알림 발송 로직 추가 고려
        // notifyAdmin(`배치 오류 발생: ${error.message}`);
      } finally {
        logger.info('batch_ap_unjoin 테이블 업데이트 작업 종료.');
      }
    }
    
    // 배치 작업 실행 함수
    async function runBatchJob() {
        logger.info('========== 전체 배치 프로그램 실행 시작 ==========');
        await updateBatchAPUnjoinTable();
        // 다른 배치 작업들...
        logger.info('========== 전체 배치 프로그램 실행 종료 ==========');
    }
    
    runBatchJob();
    • 핵심 내용
      • logger.info(): 배치 작업의 시작, 종료, 중요한 단계 등 일반적인 상황을 기록한다.
      • logger.debug(): 개발 또는 디버깅 시에만 필요한 상세 정보를 기록한다. (운영 환경에서는 보통 비활성화)
      • logger.error(): try-catch 블록에서 예외가 발생했을 때 사용한다. 두 번째 인자로 Error 객체를 넘겨주면, Winston이 에러 메시지뿐만 아니라 스택 트레이스까지 로그에 기록하여 문제 원인 파악에 큰 도움을 준다.
      • console.error 대신 logger.error를 사용하면, 콘솔 출력 외에도 파일에 기록되고 설정된 로그 레벨 및 포맷이 적용된다.

    5. 오류 로그 관리의 이점

    위와 같이 Winston을 설정하고 활용하면 다음과 같은 이점을 얻을 수 있다.

    • 빠른 오류 진단: logs/error-YYYY-MM-DD.log 파일만 확인하면, 배치 작업 중 발생한 치명적인 오류들을 한눈에 파악할 수 있다.
    • 상세한 오류 정보: error 객체를 포함시켜 스택 트레이스까지 기록함으로써, 오류 발생 지점과 원인을 정확히 추적할 수 있다.
    • 유연한 로그 관리: application.logerror.log를 분리하여 중요도에 따라 로그 보관 정책을 다르게 가져갈 수 있다.
    • 자동 폴더 생성: 로그 파일이 저장될 logs 폴더가 존재하지 않아도 Winston이 자동으로 생성해주므로, 수동으로 폴더를 만들 필요가 없다.

    Node.js 배치 프로그램에 Winston 로깅을 적용하여 더욱 안정적이고 효율적인 운영 환경을 구축해보자.

    728x90
    반응형
    댓글