
TS 코드 그래프 MCP 제작: 일반적인 질문에서도 토큰 사용량 10배 절감
요약
TypeScript 컴파일러의 인덱스를 활용하여 코딩 에이전트에게 코드 그래프를 전달하는 MCP 도구인 @ttsc/graph를 소개합니다. 소스 본문 대신 구조적 인덱스만 전달함으로써 답변 품질은 유지하면서 토큰 사용량을 약 10배 절감합니다.
핵심 포인트
- MCP를 통해 TypeScript 코드베이스의 구조적 그래프를 에이전트에 전달
- 소스 코드 대신 컴파일러 인덱스를 사용하여 토큰 비용 약 10배 절감
- 에이전트의 불필요한 파일 탐색 과정을 제거하여 효율성 증대
- 정확한 파일 위치(file:line)를 기반으로 한 구조적 답변 가능
요약 (TL;DR)
codegraph와 codebase-memory-mcp가 먼저 아이디어를 냈습니다: 코딩 에이전트에게 MCP를 통해 코드 그래프 (code graph)를 전달하는 방식입니다. 하지만 제가 직접 던지는 개방형 질문들에서는 토큰 비용이 줄어들지 않았고, 그래서 저는 @ttsc/graph를 직접 구축했습니다.
이 도구는 강제된 사고 사슬 (chain-of-thought)을 가진 하나의 도구를 통해, TypeScript 컴파일러가 이미 해결한 **인덱스 (index)**를 에이전트에게 전달합니다 (소스 본문은 절대 전달하지 않습니다). "이게 어떻게 작동하나요?"라는 질문에 대한 결과는 다음과 같습니다: 토큰 사용량 약 10배 감소, 답변 품질은 저하되지 않음. 이는 측정된 보수적인 중앙값입니다.
- 저장소 (Repository): https://github.com/samchon/ttsc
- 벤치마크 (Benchmark): https://ttsc.dev/docs/benchmark/graph
- 이슈 (Issues): https://github.com/samchon/ttsc/issues
1. 서문
1.1. @ttsc/graph란 무엇인가
코딩 에이전트에게 "이 프로젝트는 실제로 어떻게 작동하나요?"라고 물어보면 다음에 어떤 일이 벌어질지 알 것입니다. 에이전트는 파일을 하나 엽니다. 임포트 (import)를 따라가며 다른 파일을 엽니다. 또 다른 파일을 엽니다. 수십 개의 파일을 뒤진 후에야 답변을 내놓습니다.
@ttsc/graph는 그러한 탐색 과정을 없애줍니다. 이 도구는 MCP를 통해 **컴파일러가 직접 그린 TypeScript 코드베이스의 그래프 (graph of your TypeScript codebase)**를 에이전트에게 전달합니다: 무엇이 무엇을 호출하는지, 무엇이 무엇에 의존하는지, 각 조각이 어디에 위치하는지 등을 말이죠. 에이전트는 파일을 일일이 파헤치는 대신 그래프를 통해 구조적인 질문에 답하며, 모든 주장은 컴파일러가 지목한 정확한 파일:줄 (file:line)에 근거합니다. 이는 임의로 만들어낸 것이 아니라, 직접 열어서 확인할 수 있는 것입니다.
차이가 얼마나 클까요? 여기 차트가 있습니다.
동일한 질문, 동일한 에이전트입니다. @ttsc/graph만은 모든 리포지토리(repo)에 걸쳐 약 42k 토큰 수준을 일정하게 유지합니다. 다른 도구들은 수치가 급격하게 변동하며, 일부는 MCP를 전혀 사용하지 않은 기준점(baseline)보다 더 많은 토큰을 소비하기도 합니다. 전체 수치와 방법론은 벤치마크 페이지에서 확인할 수 있습니다.
1.2. 코드 그래프(Code Graph)란 무엇인가
먼저, "코드 그래프"가 무엇인지부터 알아봅시다. 한 번도 가본 적 없는 도시에 도착했을 때, 모든 거리 표지판을 순서대로 읽지는 않습니다. 대신 지하철 노선도를 슥 훑어보죠. 노선도는 건물, 나무, 실제 거리 등은 버리고 당신에게 필요한 단 한 가지, 즉 "무엇이 무엇과 연결되어 있는가"만을 남깁니다. 5초면 읽을 수 있죠.
코드도 동일한 구조를 가집니다. 노드(Nodes)는 함수, 클래스, 파일이며, 엣지(Edges)는 호출(calls), 임포트(imports), 확장(extends), 의존성(depends-on)입니다. 이 모든 것을 그리면 코드 그래프, 즉 "무엇이 무엇을 호출하는가"에 대한 **인덱스(index)**를 얻게 됩니다. 에이전트는 모든 거리를 일일이 걷는 대신 이 인덱스를 한 번 쿼리(query)합니다.
1.3. 소스 인라이닝(Source Inlining)이 아닌 인덱스
여기서 첫 번째 갈림길이 나타납니다. @ttsc/graph는 소스 본문(source bodies)을 반환하지 않습니다. 이름, 엣지, 시그니처(signatures), 스팬(spans) — 오직 인덱스일 뿐입니다.
그리고 핵심은 바로 스팬(span)입니다. 인덱스가 인용하는 모든 범위는 컴파일러가 보증한 좌표이므로, 답변이 의심스럽다면 해당 지점을 열어 확인하기만 하면 됩니다. 안심할 수 있는 부분은 "파일을 읽지 않았다"는 점이 아니라, 항상 검증할 수 있는 위치가 함께 제공된다는 점입니다.
응답 크기가 **리포지토리 크기와 무관하게 제한(bounded)**되기 때문에 토큰 사용량이 일정하게 유지됩니다. 10만 줄짜리 프로젝트든 1만 줄짜리 프로젝트든, 하나의 질문에 대해 유사한 크기의 인덱스 청크(chunk)를 반환합니다. 왜 다른 도구들은 소스 코드를 쏟아내는지에 대한 분석은 §2에서 다룹니다.
1.4. 작동 원리 요약
한 문장으로 요약하자면: TypeScript 컴파일러가 이미 해결(resolved)한 프로그램으로부터 인덱스를 읽어오며, 에이전트가 방황하지 않도록 단일 MCP 도구에 강제된 사고 사슬(chain-of-thought)을 제공합니다. 이것이 전부입니다. 자세한 내용은 §3에서 다룹니다. 먼저 — 제가 왜 이것을 만들었는지부터 시작하겠습니다.
2. 왜 만들었는가
2.1. 약속
솔직히 말해서, 제 아이디어는 아닙니다. codegraph가 MCP를 통해 에이전트(Agent) 앞에 코드 그래프(Code Graph)를 먼저 배치했고, codebase-memory-mcp도 158개 언어에 걸쳐 동일한 작업을 수행합니다. 이 포스트의 벤치마크(Benchmark)는 codegraph의 것을 충실히 이식한 것이며, 두 가지 프롬프트(Prompt) 제품군을 대상으로 실행되었습니다: 일반적인 온보딩(Onboarding) 질문과 codegraph 자체의 저장소(Repo)별 질문입니다. (codebase-memory-mcp의 것을 포함해 세 번째를 만들 수도 있었지만, 재현 가능한 방법(Reproducible method)을 제공하지 않습니다.)
그리고 그들의 핵심 주장은 타당합니다. 적은 에이전트의 **grep · find · Read 크롤링 루프(Crawl loop)**입니다.
요약하자면: 저와 같은 유형의 질문에는 이 도구들을 설치하기 전보다 상황이 더 나빠졌습니다. 그래서 저는 직접 파헤쳐 보기 시작했습니다.
2.3. 본문(Bodies), 그리고 매몰된 그래프(Buried Graph)
첫 번째 원인은 각 도구가 자신이 가진 정보를 처리하는 방식에 있었습니다.
codegraph는 **전체 소스 본문(whole source bodies)**을 반환합니다. 자체 설명에 따르면 "당신을 대신해 수행되는 Read"입니다. 편집하기에는 좋지만, "이게 어떻게 작동하나요?"와 같은 광범위한 질문에는 그 본문 자체가 **토큰 폭탄(token bomb)**이 됩니다. 파일이 12개를 넘어가면 내용을 잘라내고 "더 보려면 다시 호출하세요"라고 말합니다. 그래서 당신은 다시 호출하게 됩니다. 그러면 더 많은 본문이 들어옵니다.
codebase-memory-mcp는 내부적으로는 올바른 아이디어를 가지고 있기 때문에 더 흥미로운 사례입니다. 이 도구는 제가 결국 만들게 된 것과 매우 유사한 실제 **관계 그래프(relation graph)**를 구축합니다. 단순히 사물이 어디에 있는지가 아니라, 사물들이 서로를 어떻게 호출하고 의존하는지를 보여줍니다. 문제는 인터페이스(surface)입니다. 그 기능은 14개의 MCP 도구에 분산되어 있으며, 유용한 도구들은 Cypher 쿼리나 정확한 qualified_name과 같은 정밀한 입력을 요구합니다. 14개의 도구와 쿼리 언어가 주어지자, 에이전트는 관계 데이터에 거의 도달하지 못했습니다. 측정 결과, 에이전트는 매 실행마다 MCP를 0번 호출하고 쉘(shell)로 회귀했습니다. 즉, 포기하고 grep을 사용한 것입니다. 그래프는 바로 그곳에 있었지만, 인터페이스가 그것을 묻어버렸습니다.
결국 한쪽은 너무 많은 정보를 돌려주고, 다른 한쪽은 에이전트가 찾을 수 없는 문 뒤에 올바른 정보를 숨겨두고 있습니다. 둘 다 토큰 비용 문제를 해결하지 못합니다.
2.4. 지침(The Instructions)
두 번째 원인은 지침(instructions)이며, 이 지점에서 두 도구는 서로 반대 방향으로 향합니다.
codegraph는 자신의 도구를 강하게 강요합니다. 이 MCP의 지침(instructions)은 에이전트에게 "파일을 읽는 대신 이것을 사용하라", "Read를 하기 전에 호출하라", "먼저 grep이나 Read를 하지 마라"라고 지시하며, "거의 모든 질문"에 대해 이 도구를 사용하도록 유도합니다. 따라서 그래프가 정답이 아닌 경우(설정 문제, 작은 수정 사항, 혹은 아예 답할 수 없는 질문 등)에도 호출이 발생하며, 이러한 낭비되는 호출들이 실제 작업을 방해합니다. codegraph의 README는 그 이유를 솔직하게 밝히고 있습니다. 즉, 이 도구는 직접 쿼리할 때만 도움이 되고 그렇지 않으면 오버헤드(overhead)가 되기 때문에, 지침을 통해 그것이 오버헤드가 되지 않도록 강력하게 밀어붙이는 것입니다.
codebase-memory-mcp는 그 반대로 동작합니다. 이 MCP의 initialize 단계에서는 어떠한 지침도 보내지 않으며, 대부분의 도구에는 사용 가이드가 포함되어 있지 않습니다(특정 작업에 대해 "grep 대신 사용하라"는 몇 가지 설명이 있긴 하지만, 자동 인덱싱(auto-indexing) 기능이 존재함에도 기본적으로 꺼져 있습니다). 14개의 도구가 있음에도 에이전트에게 어떤 것을 언제 사용해야 하는지 알려주는 내용이 거의 없습니다. 그래서 에이전트는 대부분 도구를 사용하지 않았고, 방금 보았듯이 셸(shell)로 회귀했습니다.
결국 하나는 과도하게 지시하고, 다른 하나는 지시가 부족합니다. 둘 다 당신이 실제로 원하는 지점, 즉 "도움이 될 때는 그래프를 사용하고, 도움이 되지 않을 때는 멈추는 것"에 도달하지 못했습니다.
그들의 공로를 인정하자면, 어떤 제한 사항도 숨기지 않았습니다. codegraph의 README는 토큰 절감 효과가 규모에 따라 달라지며 일반적인 코드베이스에서는 미미하다고 명시합니다. codebase-memory-mcp는 개방형 질문이 아닌 소수의 구조적 쿼리(structural queries)에서 가장 큰 수치를 기록한다고 보고합니다. 이들은 이 이점들이 타겟팅된 시나리오에서 측정된 것임을 분명하게 알려줍니다.
그것이 바로 핵심입니다. 사용 사례가 일반적일수록 이러한 제한 사항은 더 커지며, 일반적인 사용이야말로 제가 주로 활동하는 영역입니다. 그래서 저는 이 두 도구를 있는 그대로, 즉 에이전트 앞에 코드 그래프를 배치한 개척자들로 받아들였습니다. 각 도구가 어디에서 한계에 부딪혔는지 학습했고, 그들이 겨냥하지 않았던 사례, 즉 토큰 사용량은 줄이면서 답변의 질은 유지하는 '개방형 질문'을 위해 구축했습니다.
3. 작동 원리
@ttsc/graph는 앞서 언급한 네 가지 고통을 하나씩 해결하도록 설계되었습니다. 아래의 순서는 그 처방전입니다.
| 고통 (§2) | 해독제 (§3) |
|---|---|
| 2.4 — 과도한 강제, 또는 가이드라인의 부재 | 3.1 — 강제하지 않는 가이드 (escape + stop) |
| ... |
3.1. 절대 강제하지 않습니다
첫째, 절대 강제하지 않습니다. 지침 중 단 한 줄도 "Read 대신 이것을 사용하라"고 말하지 않습니다. 대신 다음과 같은 조건을 명시합니다: "답변이 TypeScript 심볼(symbols), 호출(calls), 또는 타입(types)에 의존할 때 사용하십시오." 그리고 증거가 그래프 외부에 존재할 경우를 대비해 escape를 일급 선택지(first-class choice)로 만듭니다.
유일하고 강력한 규칙은 단 하나뿐입니다: "일단 사용했다면, 그것은 컴파일러가 발행한 진실입니다. 파일을 읽어서 다시 의심하지 마십시오." 충분한 증거가 확보되면, 결과는 에이전트에게 "이제 답변하고 멈추라"고 말해줍니다. 즉, 중단 지점을 가리킬 뿐, 더 많은 호출을 요구하지 않습니다.
따라서 작업을 방해하던 쓸모없는 강제 호출은 더 이상 발생하지 않습니다. 에이전트는 사용해야 할 때 그래프를 사용하고, 사용하지 말아야 할 때는 건너뜁니다. (§2.4에 대한 해독제)
3.2. 탈출구가 있는 단 하나의 도구
둘째, Claude Code가 항상 적절한 순간에 적절한 도구를 선택하는 것은 아닙니다. 옵션이 많아질수록 오작동도 많아지며, 이는 바로 묻혀있던 codebase-memory-mcp의 관계 그래프가 보여주는 모습입니다: 14개의 MCP 도구가 있었지만, 에이전트는 결코 올바른 도구를 찾아내지 못했습니다. (codegraph는 단 하나의 기본 도구를 사용하여 도구 개수의 함정을 피합니다. 다만 그 하나의 도구를 너무 강하게 밀어붙이는 문제, 즉 §3.1의 문제가 있을 뿐입니다.)
@ttsc/graph는 정확히 하나의 도구를 가집니다. 그 내부에서 무엇을 할지는 CoT(Chain of Thought, 사고의 사슬)로 공급되는 유니온 타입(union type)에 의해 나뉩니다. 그리고 안전장치가 겹겹이 쌓여 있습니다: CoT 중간의 review 단계에서 요청 유형을 전환할 수 있으며, 여전히 잘못되었다면 escape가 빠져나옵니다. 안전이 최우선입니다.
다음은 전체 인터페이스입니다 — 주석을 각각 한 줄로 줄인 하나의 타입입니다:
// 도구 설명: MCP를 통해 컴파일러가 구축한 TypeScript 코드 그래프를 조사합니다.
export interface ITtscGraphApplication {
inspect_typescript_graph(
...
보시다시피, question → draft → review는 **강제된 함수 인자(forced function arguments)**입니다. typia는 이 타입을 도구의 JSON 스키마(JSON schema) 및 검증기(validator)로 컴파일하므로, 채워지지 않은 CoT(Chain of Thought)는 호출 경계에서 거부됩니다. typia의 표현을 빌리자면 다음과 같습니다: "자유로운 산문은 건너뛴 단계를 숨길 수 있지만, 타입이 지정된 제출물은 그럴 수 없습니다." (더 자세한 내용은: CoT 준수 (CoT compliance), 함수 호출 하네스 (function-calling harness)를 참조하세요.)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
