본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 17. 04:36

당신의 Node.js 앱이 느립니다. 당신의 AI Agent는 지금까지 도움을 줄 수 없었습니다

요약

Node.js 애플리케이션의 성능 병목 현상을 분석할 때, 방대한 양의 CPU 프로파일 데이터를 LLM이 직접 처리하기 어렵다는 문제를 해결하기 위한 새로운 MCP 서버를 소개합니다. v8-cpu-profile-decoder-mcp는 복잡한 프로파일 데이터를 계산하여 AI Agent가 이해할 수 있는 핵심 요약본으로 변환해 주는 브릿지 역할을 합니다.

핵심 포인트

  • 기존 LLM은 대규모 CPU 프로파일 데이터의 복잡한 트리 구조와 수학적 계산(Exclusive/Inclusive time)을 처리하는 데 한계가 있음
  • v8-cpu-profile-decoder-mcp는 MCP(Model Context Protocol)를 통해 AI Agent가 프로파일 데이터를 효율적으로 분석할 수 있도록 지원함
  • extract_hottest_functions 도구를 통해 CPU 소모가 가장 큰 상위 함수를 즉시 식별할 수 있음
  • analyze_call_tree_path 도구를 통해 특정 함수의 호출 경로와 빈도를 추적하여 병목 지점의 원인을 파악할 수 있음
  • V8 내부 요소들을 필터링하여 사용자 코드 중심의 유의미한 분석 결과를 제공함

이런 경험이 있으실 겁니다. API가 느려지고 있습니다. P99 지연 시간(latency)이 상승하고 있습니다. 누군가 "그냥 프로파일링(profile) 해봐"라고 말합니다. 그래서 당신은 node --cpu-prof를 실행하고 CPU.20260516.154355.cpuprofile 같은 파일을 얻습니다. 파일을 열어봅니다. 28MB에 달하는 다음과 같은 내용뿐입니다: { "nodes" : [ { "id" : 1482 , "callFrame" : { "functionName" : "processRequest" , "scriptId" : "94" , "url" : "file:///app/dist/server.js" , "lineNumber" : 847 , "columnNumber" : 12 }, "hitCount" : 3241 , "children" : [ 1483 , 1490 , 1501 ] }, { "id" : 1483 , "callFrame" : { "functionName" : "parseBody" , "scriptId" : "94" , "url" : "file:///app/dist/middleware.js" , "lineNumber" : 23 , "columnNumber" : 4 }, "hitCount" : 0 , "children" : [ 1484 ] }, ... ], "samples" : [ 1482 , 1483 , 1482 , 1490 , 1501 , 1482 , 1483 , ... ], "timeDeltas" : [ 0 , 118 , 97 , 124 , 89 , 112 , ... ] } 수천 개의 노드(nodes). 수백만 개의 샘플(samples). 가공되지 않은 메모리 주소. 마이크로초 단위의 틱(tick) 간격. 이것을 Claude에 붙여넣습니다. 컨텍스트 윈도우(context window)가 무너집니다. 답변을 환각(hallucinate)합니다. 당신은 다시 원점으로 돌아옵니다. 이것이 바로 이 MCP가 해결하고자 하는 문제입니다.

AI Agent가 CPU 프로파일을 보지 못하는 이유

.cpuprofile은 단순한 "큰 파일"이 아닙니다. 유용하게 사용하려면 실제 계산이 필요한 특정 형식이 있습니다:

  • Exclusive time (self time) = 호출된 함수를 제외하고 함수 자체가 실행된 시간 = hitCount × avg(timeDeltas)
  • Inclusive time = self time + 모든 하위 노드의 inclusive time 합계 = 호출 트리(call tree)에 대한 재귀적 DFS(깊이 우선 탐색)가 필요함
  • Hot path = 어떤 호출자 체인이 병목 현상(bottleneck)으로 이어졌는지 = 역방향 부모 맵(reverse parent map) 구축이 필요함

LLM은 이 중 어떤 것도 할 수 없습니다. 알고리즘을 실행할 수 없습니다. 50,000개의 노드에 걸쳐 트리를 순회할 수 없습니다. 어떻게든 파일을 컨텍스트에 넣었다 하더라도, 함수 이름에 대해 패턴 매칭을 수행하고 추측할 뿐일 것입니다. 에이전트에게는 브릿지(bridge)가 필요합니다. 즉, 로컬에서 수학적 계산을 수행하고 10줄 정도의 요약본을 전달해 줄 무언가가 필요합니다.

v8-cpu-profile-decoder-mcp 소개

AI Agent를 위해 V8 CPU 프로파일을 토큰 효율적인 병목 현상 요약본으로 디코딩하는 MCP 서버입니다.

npx v8-cpu-profile-decoder-mcp

세 가지 도구.

각 도구는 에이전트(Agent)가 그렇지 않으면 인지하지 못했을 구체적인 질문에 답합니다.

도구 1: extract_hottest_functions "어떤 함수가 CPU를 가장 많이 소모하고 있는가?"
{ "profile_path" : "/app/profiles/CPU.20260516.cpuprofile" , "top_n" : 5 , "min_self_percent" : 1.0 }

응답: [ { "rank" : 1 , "functionName" : "hashPassword" , "url" : "file:///app/dist/auth/crypto.js" , "lineNumber" : 42 , "selfTimeMs" : 1842.5 , "totalTimeMs" : 1842.5 , "selfPercent" : 61.32 , "hitCount" : 3241 }, { "rank" : 2 , "functionName" : "parseJsonBody" , "url" : "file:///app/dist/middleware/body.js" , "lineNumber" : 18 , "selfTimeMs" : 412.1 , "selfPercent" : 13.71 , "hitCount" : 724 } ]

전체 CPU 시간의 61%가 단 하나의 함수에서 발생했습니다. 이제 에이전트는 원본 프로파일(Raw profile)을 읽지 않고도 정확히 어디를 살펴봐야 하는지 알게 됩니다. V8 내부 요소((program), (garbage collector), (idle))는 기본적으로 필터링됩니다. 사용자 코드만 확인할 수 있습니다.

도구 2: analyze_call_tree_path "내 느린 함수를 호출하는 것은 무엇이며, 얼마나 자주 호출하는가?"
{ "profile_path" : "/app/profiles/CPU.20260516.cpuprofile" , "function_name" : "hashPassword" , "top_callers" : 3 }

응답: { "targetFunction" : "hashPassword" , "matchedNodes" : 2 , "totalSelfTimeMs" : 1842.5 , "totalPercent" : 61.32 , "callers" : [ { "functionName" : "loginHandler" , "url" : "file:///app/dist/routes/auth.js" , "lineNumber" : 94 , "sampleCount" : 2180 , "attributedTimeMs" : 1235.4 }, { "functionName" : "validateSession" , "url" : "file:///app/dist/middleware/auth.js" , "lineNumber" : 31 , "sampleCount" : 1061 , "attributedTimeMs" : 607.1 } ] }

이제 에이전트는 전체 그림을 파악했습니다: hashPassword는 느리고, loginHandler는 해당 호출의 67%를 담당하며, validateSession은 모든 요청에서 불필요하게 이를 호출하고 있습니다. 함수 이름 매칭은 부분 일치 및 대소문자를 구분하지 않으므로, 정확한 내부 이름을 알 필요가 없습니다.

도구 3: correlate_source_code "병목 현상이 실제로 발생하는 TypeScript 파일과 라인은 어디인가?"
TypeScript 컴파일 후, 프로파일에는 dist/auth/crypto.js:42와 같이 표시됩니다.

그곳은 코드를 작성하는 위치가 아닙니다. 이 도구는 .js.map 소스 맵 (source maps)을 읽어 원래의 TypeScript로 역추적합니다: { "resolved" : [ { "rank" : 1 , "generatedUrl" : "file:///app/dist/auth/crypto.js" , "generatedLine" : 42 , "source" : { "originalFile" : "src/auth/crypto.ts" , "originalLine" : 38 , "originalColumn" : 2 , "originalFunction" : "hashPassword" }, "selfTimeMs" : 1842.5 , "selfPercent" : 61.32 } ], "sourcemapErrors" : [] }. 이제 에이전트 (agent)는 컴파일된 결과물이 아닌, src/auth/crypto.ts 파일의 38번 라인을 열어 실제 소스 코드를 수정할 수 있습니다. 만약 .map 파일을 찾을 수 없다면 컴파일된 JS 위치로 우아하게 폴백 (fallback) 합니다.

알고리즘 작동 방식
에이전트에 도달하기 전, 로컬에서 다음 세 단계가 수행됩니다:

  1. 노드 맵 (node map) 구축
    O(1) 조회를 위해 nodes 배열을 Map<id, node>로 파싱합니다.

  2. 독점 시간 (exclusive time) 계산
    selfTimeMs = hitCount × avg(timeDeltas[1:])
    참고: V8 명세 (V8 spec)에 따라 timeDeltas[0]은 항상 0(startTime으로부터의 오프셋)이므로 이를 건너뜁니다.

  3. 메모이제이션 (memoization)을 이용한 DFS를 통한 포함 시간 (inclusive time) 계산
    inclusiveTime(node) = selfTime(node) + Σ inclusiveTime(child)
    공유된 서브트리 (subtree)에서의 재계산을 방지하기 위해 캐싱합니다.

원시 프로파일 (raw profile)에는 50,000개의 노드와 200,000개의 샘플이 있을 수 있습니다. 하지만 에이전트에게 전달되는 것은 순위가 매겨진 10개의 객체입니다. 이것이 바로 이 도구를 유용하게 만드는 토큰 압축 (token compression)입니다.

설정

npm install -g v8-cpu-profile-decoder-mcp

Claude Desktop ( ~/.config/claude/claude_desktop_config.json ):
{
"mcpServers" : {
"v8-cpu-profile-decoder-mcp" : {
"command" : "npx" ,
"args" : [
"-y" ,
"v8-cpu-profile-decoder-mcp"
]
}
}
}

프로파일 생성:
node --cpu-prof --cpu-prof-dir ./profiles your-script.js

또는 실행 중인 서버의 경우:

kill -USR1 <pid> (--cpu-prof 플래그와 함께)

그 다음 에이전트에게 다음과 같이 요청하세요:
"프로파일이 ./profiles/CPU.cpuprofile 에 있습니다 — 병목 지점 (bottleneck)을 찾고 수정안을 제안해 주세요"

에이전트가 이를 통해 할 수 있는 일
디코딩된 프로파일이 컨텍스트 (context)에 포함되면, AI 에이전트는 다음과 같은 작업을 수행할 수 있습니다:

  • 핫 함수 (hot function)를 식별하고 해당 소스 코드를 읽기
  • 누가 해당 함수를 호출하는지, 그리고 얼마나 자주 호출하는지 추적하기
  • 메모이제이션 (memoization), 캐싱 (caching) 또는 알고리즘 개선 제안하기
  • 예상치 못한 호출자 식별하기 (예: 모든 요청마다 hashPassword를 호출하는 validateSession)
  • 수정 사항이 담긴 PR (Pull Request) 생성하기

이 MCP (Model Context Protocol) 없이는 정적 코드 (static code)를 바탕으로 추측만 해야 합니다. 하지만 이 도구가 있으면 측정된 런타임 데이터 (runtime data)를 바탕으로 작업할 수 있습니다.

링크
npm: v8-cpu-profile-decoder-mcp
GitHub: vola-trebla/v8-cpu-profile-decoder-mcp

이 시리즈의 다른 글:
playwright-trace-decoder-mcp — Playwright CI 실패에 대한 동일한 패턴
playwright-spatial-layout-mcp — 브라우저 레이아웃을 위한 기하학적 비전 (geometric vision)

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0