커스텀 Logger npm 패키지 구현해보기

정성엽·2025년 7월 21일
0

이것저것

목록 보기
2/4

INTRO

요즘 개발하면서 디버깅할 때 console.log를 자주 사용하는데, 로깅을 너무 대충찍다보니 관리하는게 꽤 복잡했다

특히 복잡한 프로젝트에서 여러 파일에 걸쳐 로그가 흩어져 있으면, 어느 파일의 어느 함수에서 출력된 건지 추적하기가 쉽지 않았다.

그래서 이번에는 호출 위치를 자동으로 추적해주는 Logger 유틸리티를 만들어보기로 했다


1. 초기 아이디어

개발하다 보면 이런 상황들이 자주 발생한다

여러 곳에서 console.log를 찍어놨는데 어디서 나온 로그인지 모르겠음
시간순으로 로그를 추적하고 싶은데 타임스탬프가 없어서 불편함
로그 레벨을 구분해서 관리하고 싶음

그래서 다음과 같은 기능들이 있으면 좋겠다고 생각했다

💡 원하는 기능들

파일 위치 자동 추적

  • 어느 파일에서 호출됐는지 알 수 있으면 좋겠음
    타임스탬프
  • 언제 호출됐는지 시간 정보 제공
    로그 레벨
  • INFO, ERROR, WARN으로 구분
    일관된 포맷
  • 일관화된 로그 출력

2. Stack Trace 활용하기

사실 많은 삽질을 했는데, 결국 찾아보니까 JavaScript의 Error 객체를 사용하면 현재 호출 스택 정보를 가져올 수 있다고 한다.

이를 활용해서 Logger가 어디서 호출되었는지 추적할 수 있다는 레퍼런스를 통해 구현해보기로 했다.

💡 첫 번째 시도

우선 간단하게 스택 정보를 확인해보자.

import { LogProps } from "../types/Logger.type";

export class Logger {
  private static isDev = process.env.NODE_ENV === "development";
  
  private static getCallerInfo() {
    const stack = new Error().stack; // 스택 추적
    console.log("stack : ", stack);
    if (!stack) return null;
    const caller = stack[1];
    return { caller };
  }
  
  static log({ message, logLevel }: LogProps) {
    const timestamp = new Date().toISOString();
    const callerInfo = Logger.getCallerInfo();
    
    if (logLevel === "INFO") {
      console.log("==============================");
      console.log(
        "파일 위치:", callerInfo?.caller,
        "\n", "Message:", message,
        "\n", "TimeStamp:", timestamp
      );
    }
  }
}

로그 결과를 살펴보면 파일 위치는 stack[3]에 저장되는 모습을 볼 수 있었다.


3. 파일 위치 파싱 로직 구현

스택 정보를 파싱해서 읽기 쉬운 형태로 변환해보자

import { LogProps } from "../types/Logger.type";

export class Logger {
  private static isDev = process.env.NODE_ENV === "development";
  
  private static getCallerInfo() {
    const stack = new Error().stack; // 스택 추적
    console.log("stack : ", stack);
    if (!stack) return null;
    
    const caller = stack.split("\n");
    const parserLineArr = caller[3].replace(/:\d+:\d+\)/, "").split("/");
    const fileLocation =
      parserLineArr.length > 2
        ? parserLineArr[parserLineArr.length - 2].concat(
            "/",
            parserLineArr[parserLineArr.length - 1]
          )
        : parserLineArr[parserLineArr.length - 1];
    
    return {
      fileLocation,
    };
  }
  
  static log({ message, logLevel }: LogProps) {
    const timestamp = new Date().toISOString();
    const callerInfo = Logger.getCallerInfo();
    
    if (logLevel === "INFO") {
      console.log("==============================");
      console.log(
        "파일 위치:", callerInfo?.fileLocation,
        "\n", "Message:", message,
        "\n", "TimeStamp:", timestamp
      );
    }
  }
}

caller[3]에서 마지막 : 으로 시작하는 코드 라인은 제거하고 파일 위치만 가져오도록 구현했다.

로컬에서는 제대로 동작하는 모습을 확인하고 다른 프로젝트에서 적용해보니 다음과 같이 로그가 찍히는 문제가 있었다.

dist/ 는 패키지를 빌드하면서 생긴 빌드 결과물을 저장하는 디렉토리인데, 트레이싱을 제대로 못하고 있는 것 같았다.


4. 빌드 환경 대응하기

패키지로 배포했을 때 dist/index 같은 빌드된 파일 경로가 나오는 문제를 해결해야 했다.

💡 실제 호출 위치 찾기

Logger 자체의 스택이 아닌, 실제로 Logger를 호출한 위치를 찾아야 했다.

따라서, 다음과 같은 로직을 추가했다.

let targetCaller = caller[3];
for (let i = 3; i < caller.length; i++) {
  if (
    !caller[i].includes("Logger.util") &&
    !caller[i].includes("dist/index")
  ) {
    targetCaller = caller[i];
    break;
  }
}

기존에는 caller[3]만 추적했으나, 해당 로직을 추가해서 dist/indexLogger.util.ts 파일을 제외한 실제 호출 위치를 찾도록 수정했다.

제대로 호출되는 모습을 볼 수 있었다.


OUTRO

이번에 Logger 유틸리티를 만들어보면서 JavaScript의 스택 추적 기능을 활용하는 방법을 배울 수 있었다.

처음에는 단순히 로그에 파일 위치만 추가하려고 했는데, 패키지 빌드 환경까지 고려해야한다는 걸 앞으로는 생각해야겠다.

이제부터 이 패키지에서 Logger.api, Logger.state 와 같이 로깅을 조금 더 쉽게할 수 있도록 유틸을 추가해나가면 될 것 같다!

(만들고나서 생각해보니 파일 위치 추적이 필요한가..?)

profile
코린이

0개의 댓글