나는 작은 Function Profiler를 구축해 JavaScript 디버깅 방식을 바꾸었다

발행: (2025년 12월 28일 오전 02:55 GMT+9)
9 분 소요
원문: Dev.to

Source: Dev.to

혹시 생각해 본 적 있나요:

  • “왜 이 함수가 이렇게 느린가요?”
  • “이 함수가 몇 번 호출되고 있나요?”
  • “내 코드가 정확히 어디서 실패하고 있나요?”

나는 매일 하루도 빠짐없이 이런 질문을 스스로에게 던졌습니다. 그래서 그 질문에 답할 수 있는 도구를 만들었습니다.

소개합니다 function‑trace

JavaScript와 TypeScript용 작고 의존성이 전혀 없는 함수 프로파일러입니다.

🤯 문제

지난 달에 프로덕션 이슈를 디버깅하고 있었습니다. API 엔드포인트가 무작위로 느려졌지만, 어떤 함수가 원인인지 파악하지 못했습니다.

디버깅 과정은 다음과 같았습니다:

const start = performance.now();
const result = await someFunction();
const end = performance.now();
console.log(`someFunction took ${end - start}ms`);

이 코드를 어디든 복사‑붙여넣기하고 있었어요. 그때는:

  • ❌ 보기 흉함
  • ❌ 시간 소모적임
  • ❌ 제거하는 것을 쉽게 잊음
  • ❌ 히스토리 데이터가 없음

더 나은 방법이 분명히 있었을 겁니다.

💡 솔루션

function‑trace를 만들었습니다 — 이 모든 작업을 자동으로 수행하는 래퍼입니다:

import { trace } from 'function-trace';

const fetchUser = trace(
  async (id) => {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
  },
  { log: true }
);

await fetchUser(1);
// [function-trace] fetchUser executed in 142.35ms

한 줄. 그게 전부입니다. 설정도 필요 없고, 구성도 필요 없습니다. 그냥 래핑하고 바로 사용하세요.

✨ 특별한 기능

1. 모든 환경에서 작동

유형지원 여부
Sync functions
Async functions
Arrow functions
Class methods
// Sync
const add = trace((a, b) => a + b, { log: true });

// Async
const fetchData = trace(async () => {
  return await fetch('/api/data');
}, { log: true });

2. 내장 통계

트레이스된 모든 함수는 .stats 속성을 가집니다:

const myFunction = trace(someFunction, { log: true });

myFunction();
myFunction();
myFunction();

console.log(myFunction.stats);
// {
//   calls: 3,
//   errors: 0,
//   lastTime: 0.02,
//   history: [0.02, 0.01, 0.02]
// }
  • calls – 호출 총 횟수
  • errors – 오류가 발생한 횟수
  • lastTime – 가장 최근 실행 시간 (ms)
  • history – 과거 실행 시간들의 배열

3. 성능 알림

통계를 활용해 직접 모니터링을 구축하세요:

const criticalOperation = trace(async () => {
  // Some critical operation
}, { log: true, maxHistory: 100 });

async function executeWithMonitoring() {
  await criticalOperation();

  const avgTime =
    criticalOperation.stats.history.reduce((a, b) => a + b, 0) /
    criticalOperation.stats.history.length;

  if (avgTime > 1000) {
    console.error('⚠️ SLA 임계값 초과!');
    // Send alert to your monitoring service
  }
}

4. 의존성 제로

전체 패키지는 순수 TypeScript로 구현되었습니다—lodash도, moment도, 불필요한 코드도 없습니다.

5. 타입 안전

적절한 타입 추론을 제공하는 완전한 TypeScript 지원:

const multiply = trace(
  (a: number, b: number): number => a * b,
  { log: true }
);

const result = multiply(3, 4); // result는 number 타입으로 추론됩니다

🚀 실제 사용 사례

데이터베이스 쿼리 모니터링

import { trace } from 'function-trace';

const getUserById = trace(
  async (userId) => {
    return await db.users.findById(userId);
  },
  { log: true, maxHistory: 100 }
);

// Later, check if queries are getting slow
const avgQueryTime =
  getUserById.stats.history.reduce((a, b) => a + b, 0) /
  getUserById.stats.history.length;

if (avgQueryTime > 100) {
  console.warn('⚠️ Database queries are slow. Consider adding an index.');
}

API 엔드포인트 프로파일링

const apiCall = trace(
  async (endpoint) => {
    const response = await fetch(endpoint);
    return response.json();
  },
  { log: true }
);

// After some usage
console.log(`Total API calls: ${apiCall.stats.calls}`);
console.log(`Failed calls: ${apiCall.stats.errors}`);
console.log(`Last response time: ${apiCall.stats.lastTime}ms`);

성능 병목 찾기

const step1 = trace(processData, { log: true });
const step2 = trace(transformData, { log: true });
const step3 = trace(saveData, { log: true });

await step1(data);
await step2(data);
await step3(data);

// Console output shows exactly where time is spent:
// [function-trace] processData executed in 5.23ms
// [function-trace] transformData executed in 234.56ms

옵션

옵션유형기본값설명
logbooleanfalse콘솔 로깅 활성화
maxHistorynumber50유지할 실행 시간 수
colorbooleantrue색상 출력 활성화

Stats 객체

속성유형설명
callsnumber총 호출 수
errorsnumber총 오류 수
lastTimenumber마지막 실행 시간 (ms)
historynumber[]최근 실행 시간 배열

history

유형: number[]
설명: 과거 실행 시간 배열

⚡ 성능

당신이 무슨 생각을 하는지 압니다: “이거 하면 코드가 느려지지 않을까?”

오버헤드는 호출당 약 ~0.05 ms 정도입니다. 초당 수백만 번 함수를 호출하지 않는 한 눈에 띄지는 않을 것입니다.

프로덕션용:

  • log: false를 사용해 콘솔 출력을 비활성화하세요.
  • 메모리 제한에 따라 maxHistory를 조정하세요.
  • 히스토리 배열은 자동으로 제한되므로 메모리 누수가 없습니다.

🆚 왜 Chrome DevTools만 사용하지 않을까?

Great question! Chrome DevTools는 훌륭하지만 function‑trace는 DevTools가 기본적으로 제공하지 않는 기능을 제공합니다.

기능Chrome DevToolsfunction‑trace
Node.js에서 작동
데이터에 대한 프로그래밍 접근
맞춤 알림
프로덕션 모니터링
설정 필요 없음
과거 데이터Limited

두 도구는 상호 보완적입니다: 깊은 프로파일링에는 DevTools를, 빠른 디버깅 및 프로덕션 모니터링에는 function‑trace를 사용하세요.

🛠️ 모범 사례

  • 개발: { log: true } 로 로깅을 활성화합니다.
  • 프로덕션: { log: false } 로 로깅을 비활성화하지만 통계는 유지합니다.
  • 고주파 함수: 메모리 절약을 위해 maxHistory 를 낮춥니다.
  • 핵심 경로: stats.history 를 기반으로 알림을 구축합니다.
  • 디버깅: 병목 현상을 찾기 위해 의심되는 함수를 래핑합니다.

🤝 Contributing

function‑trace는 MIT 라이선스 하에 오픈 소스이며, 기여를 환영합니다!

🔗 링크

🙏 최종 생각

저는 function‑trace를 제 문제를 해결하기 위해 만들었지만, 여러분에게도 도움이 되길 바랍니다.

시도해 보시고, 댓글로 알려 주세요! 듣고 싶습니다:

  • 어떻게 사용하고 계신가요?
  • 어떤 기능을 보고 싶으신가요?
  • 성능 문제를 찾는 데 도움이 되었나요?

디버깅 즐겁게! 🐛🔍

이 글이 도움이 되었다면, 더 많은 JavaScript/TypeScript 콘텐츠를 위해 저를 팔로우해 주세요. 저는 웹 개발, 오픈소스, 개발자 도구에 대해 글을 씁니다.

당신이 가장 즐겨 사용하는 디버깅 기법은 무엇인가요? 댓글에 남겨 주세요! 👇

Back to Blog

관련 글

더 보기 »

자바스크립트

핵심 트렌드 - 런타임 혁신: Deno의 최신 릴리스는 NPM 및 JSR 바이너리를 실행하기 위한 새로운 도구 dx를 도입하여 호환성을 개선하고 개발자 워크플로우를 향상시킵니다.

🚀 번들 사이즈를 죽이는 걸 멈춰라

자동으로 TypeScript에서 Barrel 파일 제거하기! 커버 이미지 설명: 왼쪽에 무겁고 뒤얽힌 번들이 표시되고, 오른쪽에 깔끔하고 직접적인 import가 표시된 분할 화면.