나는 작은 Function Profiler를 구축해 JavaScript 디버깅 방식을 바꾸었다
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
옵션
| 옵션 | 유형 | 기본값 | 설명 |
|---|---|---|---|
log | boolean | false | 콘솔 로깅 활성화 |
maxHistory | number | 50 | 유지할 실행 시간 수 |
color | boolean | true | 색상 출력 활성화 |
Stats 객체
| 속성 | 유형 | 설명 |
|---|---|---|
calls | number | 총 호출 수 |
errors | number | 총 오류 수 |
lastTime | number | 마지막 실행 시간 (ms) |
history | number[] | 최근 실행 시간 배열 |
history
유형: number[]
설명: 과거 실행 시간 배열
⚡ 성능
당신이 무슨 생각을 하는지 압니다: “이거 하면 코드가 느려지지 않을까?”
오버헤드는 호출당 약 ~0.05 ms 정도입니다. 초당 수백만 번 함수를 호출하지 않는 한 눈에 띄지는 않을 것입니다.
프로덕션용:
log: false를 사용해 콘솔 출력을 비활성화하세요.- 메모리 제한에 따라
maxHistory를 조정하세요. - 히스토리 배열은 자동으로 제한되므로 메모리 누수가 없습니다.
🆚 왜 Chrome DevTools만 사용하지 않을까?
Great question! Chrome DevTools는 훌륭하지만 function‑trace는 DevTools가 기본적으로 제공하지 않는 기능을 제공합니다.
| 기능 | Chrome DevTools | function‑trace |
|---|---|---|
| Node.js에서 작동 | ❌ | ✅ |
| 데이터에 대한 프로그래밍 접근 | ❌ | ✅ |
| 맞춤 알림 | ❌ | ✅ |
| 프로덕션 모니터링 | ❌ | ✅ |
| 설정 필요 없음 | ❌ | ✅ |
| 과거 데이터 | Limited | ✅ |
두 도구는 상호 보완적입니다: 깊은 프로파일링에는 DevTools를, 빠른 디버깅 및 프로덕션 모니터링에는 function‑trace를 사용하세요.
🛠️ 모범 사례
- 개발:
{ log: true }로 로깅을 활성화합니다. - 프로덕션:
{ log: false }로 로깅을 비활성화하지만 통계는 유지합니다. - 고주파 함수: 메모리 절약을 위해
maxHistory를 낮춥니다. - 핵심 경로:
stats.history를 기반으로 알림을 구축합니다. - 디버깅: 병목 현상을 찾기 위해 의심되는 함수를 래핑합니다.
🤝 Contributing
function‑trace는 MIT 라이선스 하에 오픈 소스이며, 기여를 환영합니다!
🔗 링크
🙏 최종 생각
저는 function‑trace를 제 문제를 해결하기 위해 만들었지만, 여러분에게도 도움이 되길 바랍니다.
시도해 보시고, 댓글로 알려 주세요! 듣고 싶습니다:
- 어떻게 사용하고 계신가요?
- 어떤 기능을 보고 싶으신가요?
- 성능 문제를 찾는 데 도움이 되었나요?
디버깅 즐겁게! 🐛🔍
이 글이 도움이 되었다면, 더 많은 JavaScript/TypeScript 콘텐츠를 위해 저를 팔로우해 주세요. 저는 웹 개발, 오픈소스, 개발자 도구에 대해 글을 씁니다.
당신이 가장 즐겨 사용하는 디버깅 기법은 무엇인가요? 댓글에 남겨 주세요! 👇