AI 코딩 에이전트에게 컨텍스트 비용은 얼마나 들까? 936회 실행을 통해 측정한 grep vs graph vs LSP
요약
AI 코딩 에이전트의 컨텍스트 제공 방식(grep, graph, LSP 등)에 따른 비용과 정확도를 936회 실험을 통해 비교 분석합니다. 태스크의 복잡도에 따라 단순 검색 방식과 구조적 도구 간의 효율성 차이가 극명하게 나타남을 보여줍니다.
핵심 포인트
- 단순 조회 태스크에서는 grep 방식이 저렴하지만 정확도는 구조적 도구와 동일함
- 복잡한 영향 범위 추정 시 grep은 정확도가 급감하고 비용은 최대 24배 증가함
- 에이전트의 작업 성격에 따라 최적의 컨텍스트 제공 아키텍처가 달라져야 함
- Claude Code와 MCP 서버를 활용한 실질적인 벤치마크 데이터 제시
저의 지난 포스트에서 저는 graphlens가 무엇을 하는지, 어떻게 작동하는지를 설명하며, 그 과정에서 에이전트가 "저장소를 grep(검색)하며 토큰을 낭비한다"라고 가볍게 주장했습니다. 하지만 이를 뒷받침할 구체적인 수치는 전혀 제시하지 않았습니다.
이 포스트에서 그 문제를 해결하고자 합니다. 여기에는 측정값, 데이터, 그리고 재현 가능한 테스트 환경(harness)이 포함되어 있습니다. 스포일러를 하자면: 결론은 제가 처음에 예상했던 것과 달랐으며, 바로 그 점이 흥미로운 부분입니다.
요약 (TL;DR)
저는 단 하나의 에이전트(Claude Code)를 사용했고, 정확히 한 가지 요소 — 즉, 어떤 MCP 서버가 코드 컨텍스트를 제공할 것인가 — 만 변경하여 apache/superset 프로젝트의 26개 태스크에 대해 실행했습니다. 네 가지 "팔(arms)": filesystem (grep + read), graphlens (구조적 그래프), serena (LSP), 그리고 codegraph를 비교했습니다. 세 가지 모델 (haiku / sonnet / opus)과 세 가지 시드(seed)를 사용하여 총 936회 실행했습니다.
핵심 요약: 태스크의 종류에 따라 답이 뒤바뀝니다.
- "X가 어디에 정의되어 있는가 / X는 무엇을 상속받는가"와 같은 단순한 조회 태스크에서는, 네 가지 도구 모두 정확도가 동일했습니다. 유일한 차이점은 비용(약 3배 차이)뿐이었습니다. 이 지점에서 graphlens는 특별할 것이 없습니다.
- "영향 범위(blast radius)를 추정하라 / 모든 오버라이드(override)를 찾아라 / 오버로딩된 이름을 모호성을 해소하라"와 같은 태스크에서는, 도구 간의 격차가 크게 벌어집니다. grep은 성능이 급격히 떨어지며 (정확도 0.71, 실행 완료율은 83%에 불과하며, 완료된 경우에도 비용이 6~24배 더 많이 발생함), 반면 구조적 도구들은 저렴하고 정확한 상태를 유지합니다.
만약 제가 쉬운 태스크만 측정했다면, "그래프는 필요 없다, grep이면 충분하다"라고 썼을 것입니다. 반대로 어려운 태스크만 측정했다면, "grep은 필요 없다, 그래프를 도입하라"라고 썼을 것입니다. 진실은 그 중간에 있으며, 핵심은 에이전트에게 어떤 작업을 맡기느냐에 달려 있습니다.
우리가 실제로 측정하고 있는 비즈니스 케이스
익숙한 상황을 상상해 보세요. 수십만 줄의 코드, Python 백엔드, TypeScript 프런트엔드, 그리고 건드리기 두려운 레거시 코드가 있는 대규모 프로젝트가 있습니다. 당신은 리뷰, 리팩터링, 또는 "이 메서드의 시그니처를 변경하면 무엇이 깨질까?"와 같은 질문에 답하기 위해 AI 에이전트를 이 프로젝트에 연결합니다.
에이전트는 전체 저장소(repo)를 한 번에 볼 수 없습니다. 어떤 방식으로든 컨텍스트(context)를 제공해야 합니다. 어떤 함수가 어디에 있는지, 누가 누구를 호출하는지, 무엇이 무엇을 상속받는지와 같은 정보 말입니다. 그리고 여기서 **비용이 따르는 아키텍처 결정(architectural decision)**이 발생합니다. 정확히 무엇을 에이전트에게 제공할 것인가 하는 문제입니다.
기본적으로 네 가지 유형의 답변이 있습니다.
- grep + 읽기 방식 제공 — 텍스트로 검색하고 파일을 직접 열도록 합니다. 인프라가 전혀 필요 없으며 어디서든 작동합니다.
- 구조적 코드 그래프 구축 (graphlens) — 엔티티 노드(entity nodes), 타입이 지정된 엣지(typed edges)를 통해 "누가 이것을 호출하는가"에 대한 정확한 답변을 제공합니다.
- LSP 구축 (language server 위에서 실행되는 serena) — 이미 사용 중인 IDE가 실행하고 있는 방식입니다.
- 기성 코드 그래프 제품 사용 (codegraph).
각 옵션은 비용(토큰), 시간(지연 시간, latency), 그리고 리스크(에이전트가 포기하고 턴 제한(turn cap)에 도달할 위험)를 발생시킵니다. apache/superset은 이 실험을 위한 거의 완벽한 대상입니다. 약 40만 줄의 코드(LOC), Python + TypeScript 구성, 그리고 프론트엔드와 백엔드 사이의 /api/v1/... 경계가 존재합니다. 대규모 다중 언어(polyglot) 프로젝트로, 바로 이런 상황에서 이 질문을 던질 가치가 있습니다.
그렇다면 각 옵션의 비용은 얼마나 될까요? 측정을 시작해 보겠습니다.
실험 설계: 하나의 변수만 변경하기
전체 방법론은 하나의 원칙에 기반합니다: 한 가지를 제외한 모든 것을 고정하는 것입니다. 모델, 시스템 프롬프트(system prompt), 설정, 작업 세트는 상수로 유지합니다. 오직 컨텍스트를 제공하는 MCP 서버만 변경합니다. 그러면 수치의 차이는 설정 오류가 아니라 해당 도구의 기여도가 됩니다.
어떤 도구도 "극복해야 할 기준점(baseline)"으로 지정되지 않습니다. 네 가지 모두 동등한 조건에서 측정되며, 수치를 통해 순위가 매겨집니다.
네 가지 실험군 (The four arms)
| 실험군 (Arm) | 컨텍스트 제공자 (Context provider, MCP server) | 인덱싱 단계 (Indexing step) |
|---|---|---|
filesystem | @modelcontextprotocol/server-filesystem (read_file + grep) | 없음 |
| ... |
공정성을 위해 중요한 세부 사항 하나가 있습니다: Claude Code의 내장 도구(Read / Grep / Bash 등)는 비활성화됩니다. 만약 이 도구들을 제거하지 않는다면, 에이전트는 MCP 서버를 무시하고 평소의 경로로 돌아갈 것이며, 그렇게 되면 잘못된 것을 측정하게 됩니다. 따라서 테스트 프레임워크(harness)는 claude -p를 클린 룸(clean room) 환경에서 실행합니다. 즉, 구독 자격 증명만 포함된 새로운 CLAUDE_CONFIG_DIR (훅, 플러그인, 스킬, 메모리 없음), --strict-mcp-config (해당 실험군의 서버만 보이도록 설정), 모든 내장 도구에 대한 --disallowedTools (헤드리스 모드에서는 허용 목록(allow-list)만으로는 아무것도 금지할 수 없으므로 명시적인 _거부(deny)_를 적용), 그리고 단 하나의 서버를 자동 승인하기 위한 --allowedTools mcp__<server> 설정을 사용합니다.
두 번째 축: 모델 (models)
이와 병행하여, 질문에 답하는 모델을 변경하며 실험했습니다:
| 키 (Key) | 모델 ID (model id) |
|---|---|
haiku | claude-haiku-4-5 |
| ... |
두 번째 축이 왜 필요한지는 실험 끝 무렵에 명확해집니다: 최적의 도구는 어떤 모델을 선택했느냐에 따라 달라집니다. 이것은 아마도 이번 실험 전체에서 가장 예상치 못한 발견일 것입니다.
총합: 4개 실험군 × 3개 모델 × 26개 태스크 × 3개 시드 = 936회 실행 (Claude Code 2.1.187 기준).
무엇이 정직한 측정인가
벤치마크는 원하는 결론에 맞춰 조작하기 쉽습니다. 따라서 규칙은 사전에 고정되어야 하며, 규칙이 없다면 수치는 신뢰할 수 없습니다:
- 골드 답변은 태그
6.0.0의 소스 코드를 기반으로 수동 검증됩니다 (모든 작업에는file:line참조가 포함됩니다). 결정적으로, 골드는 테스트 대상 도구에 의해 생성되지 않습니다 (ty도 아니고, pyright도 아니고, graphlens 자체도 아님) — 그렇지 않으면 비교 자체가 라벨링한 출력을 가진 쪽으로 편향될 수 있습니다. Set-task의 골드 답변은 독립적인 오라클인 Python의ast를 사용하여 확인합니다. - '순진한(naive)' 방식에도 도구가 있습니다.
filesystem은 grep + 읽기이며, '도구 없는 에이전트'가 아닙니다. Naive ≠ toolless입니다. - 인덱스 비용은 별도로, 한 번 측정됩니다. grep은 인덱싱에 아무것도 지불하지 않지만; 그래프는 상각(amortizes)합니다. 이 두 가지 통화는 섞을 수 없습니다.
- 결정론적이지 않습니다.
temperature=0이 이러한 모델들을 결정론적으로 만들지 않습니다. 따라서 시드 3개를 사용했으며, 보고서는 평균이 아닌 중앙값을 보여줍니다. - 버전은 기록됩니다 — 모델과 모든 MCP 서버에 대해 — 그리고 가격 스냅샷 및 날짜도 포함합니다.
cost_usd는 청구서가 아니라 API와 동등한 비용입니다. 구독료는 고정 요율이므로,cost_usd(CLI에서 출력됨)는 동일한 토큰을 API를 통해 사용했을 때의 비용을 의미합니다. 이것은 실제 청구서가 아니지만, 비교를 위한 정확한 상대적 $/작업 지표입니다.- 도구를 사용해야 계산됩니다. 시스템 프롬프트는 메모리에서 답변하는 것을 금지하며, 도구 호출이 0인 실행은 재시도되며 (완고하게 거부하면
__NO_TOOLS__로 태그가 지정됩니다). 잘 알려진 저장소에 대해
| 종류 | # | 조사 대상 |
|---|---|---|
where_defined | 7 | Python 클래스 → 정의된 파일 |
| ... | ||
| HARD (어려움) — 6개의 폭발 반경(blast-radius) 및 모호성 해소(disambiguation) 작업. 이는 구조와 의미론(semantics)이 텍스트 검색을 압도해야 하는 영역이며, 단순한 핀포인트 조회(pinpoint lookups)로는 측정할 수 없는 부분을 정확히 짚어냅니다: |
| 종류 | # | 조사 대상 | 점수 산정 방식 |
|---|---|---|---|
disambiguate | 2 | 모호한 메서드 이름 (예: 여러 클래스에 정의된 cache_key) → 정확한 클래스 | 부분 문자열 (substring) |
| ... | |||
설정된 작업들은 F1 점수로 측정됩니다: 재현율(recall, 모두 찾아내기)에 대해서는 보상을 주고, 정밀도(precision, 텍스트 검색은 .get_indexes(의 모든 발생 지점을 쏟아내는 것을 좋아함)에 대해서는 페널티를 부여합니다. 골드 세트(Gold sets)는 수작업으로 철저히 검증할 수 있도록 작게 유지합니다 (3~5개 요소, 하나당 ≈17개). |
평균을 내는 대신 층화(stratify)를 사용하는 이유
이 세트는 의도적으로 불균형하게 구성되었습니다 — 쉬운 작업 20개 대 어려운 작업 6개입니다. 단일 혼합 평균을 사용하면 결과가 완전히 쉬운 작업에 의해 좌우될 것이며, 어려운 작업이 드러내는 차이점을 가리게 될 것입니다. 따라서 저는 각 영역을 별도로 보고하며, 절대 섞지 않습니다.
그리고 아니요, 저는 쉬운 작업을 버리면서까지 의도적으로 "50/50으로 균형을 맞추지" 않습니다. 그렇게 하면 데이터와 통계적 검정력(statistical power)을 버리게 되고, 체리 피킹(cherry-picking)의 문을 열어주게 됩니다. 층화(Stratification)는 데이터를 버리지 않고도 편향을 중화합니다. (일반적인 원칙: 만약 영역별로 결과가 다르다면, 갈등을 평균 아래로 묻어버리는 것보다 양쪽을 모두 보여주는 것이 더 정직한 방법입니다.)
결과
SIMPLE (쉬움) — 20개의 핀포인트 조회 (pinpoint lookups)
| 도구 | 정확도 (accuracy) | 완전성 (complete) | 토큰 (tokens) | 호출 (calls) | 작업당 비용 ($/task) | 초 (sec) |
|---|---|---|---|---|---|---|
| filesystem | 0.97 | 100% | 1780 | 10 | $0.063 | 43 |
| ... | ||||||
| 정확도는 동점입니다 (공식적으로: Friedman χ²=0.40, 유의미하지 않음). 도구 간의 차이는 오직 비용 — 약 3배의 차이 — 에서만 나타나며, 간결한 도구들이 승리합니다. graphlens는 이 부분에서 눈에 띄지 않습니다 — 중간 정도의 준수한 성적을 보였습니다. |
이것은 오직 정밀한 조회(pinpoint lookups)만을 측정하는 벤치마크가 들려줄 법한 이야기입니다. 즉, "구조적 도구(structural tools)가 좋긴 하지만 grep이 거의 따라잡고 있으며, codegraph가 가장 저렴한 답을 제공한다"라는 식의 이야기 말입니다. 하지만 이는 불완전한 진실입니다.
HARD — 6 blast-radius 및 모호성 해소 작업
| 도구 | 정확도 (accuracy) | 완료율 (complete) | 토큰 (tokens) | 호출 횟수 (calls) | 작업당 비용 ($/task) | 시간 (sec) |
|---|---|---|---|---|---|---|
| filesystem | 0.71 | 83% | 12596 | 27 | $0.424 | 165 |
| ... | ||||||
| 이제 도구들의 성적이 갈립니다. |
grep은 무너집니다. 가장 낮은 정확도(0.71)를 기록했으며, 실행의 83%만이 완료되었습니다(나머지는 50회 호출 제한에 걸림). 또한 완료된 실행들은 6~24배 더 많은 비용($0.42 vs $0.018–0.065)이 들고, 6~18배 더 긴 시간(~165s vs 9–29s)이 소요됩니다. 질문이 "이것을 호출하는 모든 곳" 또는 "동일한 이름을 가진 수십 개의 메서드 중 어느 것인가"와 같을 때, 텍스트 검색은 노이즈에 파묻혀 버립니다.
그리고 핵심적인 부분은 다음과 같습니다: 쉬운 작업에서 중간 순위였던 graphlens가 여기서는 가장 저렴하며($0.018) 가장 빠릅니다(9s). graphlens의 시맨틱 그래프(semantic graph)가 마침내 빛을 발한 것입니다. 27번의 호출 대신 단 한 번의 호출로 해결했습니다. 가장 정확한 도구는 codegraph(0.93)입니다. serena도 경쟁력이 있습니다(0.85).
따라서 정밀한 조회에서는 눈에 띄지 않았던 graphlens가, 작업의 범위(blast radius)나 리팩터링(refactoring)과 같이 실제적인 작업이 시작되는 순간 가장 경제적인 도구로 변모합니다. 작업 환경에 따라 순위가 역전되는 것입니다.
공정성 참고 사항. 모든 실험군에서 MCP 리소스(resources)는 비활성화되었습니다. graphlens는 리소스를 노출하는 유일한 서버였으며, 초기 실행에서 에이전트가 리소스를 열거하려 시도하면서 비용이 약 24% 부풀려졌으나, 이후 접근을 차단했습니다. 위의 모든 수치는 깨끗하게 다시 실행한 결과에서 도출되었습니다.
비용이 발생하는 곳: 메커니즘은 왕복 호출(round-trips)에 있다
비용 차이는 주로 에이전트가 도구를 호출하는 횟수에서 발생하며, 이는 서버가 자신의 기본 기능(primitives)을 어떻게 나누어 제공하느냐에 따라 결정됩니다.
단순한 "심볼 → 파일" (where_defined) 쿼리의 경우, 모두에게 한 번의 호출이면 충분합니다. 격차는 관계 쿼리 (relationship queries) — 상속(inheritance), 라우트 → 핸들러(route → handler), 언어 간 링크(cross-language links) 등에서 발생합니다. graphlens는 세밀한 기본 기능(primitives)을 체인 형태로 연결하는 방식(find → neighbors → references)을 사용하는 반면, codegraph는 "소스 + 호출 경로를 한 번에" 묶어서 제공합니다(explore / node).
이는 그래프가 _무엇을 알고 있는가_의 차이가 아닙니다. 그래프들은 대략적으로 비슷한 것들을 알고 있습니다. 이것은 API 입도(granularity)의 차이입니다. 왕복 호출(round-trips) 횟수가 적을수록 더 저렴하고 빠릅니다. 이것이 codegraph가 단순한 작업에서 효율성 우위를 점하는 이유이며, grep이 어려운 작업에서 파산하는 이유입니다. 그래프가 한두 번의 호출이면 될 때, grep은 27번의 왕복 호출을 수행하기 때문입니다.
모델 × 도구 상호작용: 모델 가격에 따라 순위가 변동됨
이 부분은 가장 직관적이지 않은 대목입니다. 모델별로 분류된 (두 체제 모두에 걸친) 작업당 중앙값 비용($/task)을 살펴보겠습니다:
| 도구 | haiku | sonnet | opus |
|---|---|---|---|
| filesystem | $0.053 | $0.080 | $0.087 |
| ... |
각 모델 내에서의 저렴한 순위:
- haiku: graphlens $0.020 < codegraph $0.023 < serena $0.026 < filesystem $0.053
- sonnet: serena $0.033 < graphlens $0.041 < codegraph $0.041 < filesystem $0.080
- opus: codegraph $0.031 < serena $0.042 < graphlens $0.046 < filesystem $0.087
graphlens에 어떤 일이 일어나는지 주목하십시오. haiku에서는 모든 도구 중 가장 저렴합니다. 하지만 opus에서는 구조적 도구들 중 가장 비싸집니다 (그래도 grep보다는 저렴합니다).
그 메커니즘은 다음과 같습니다: graphlens의 결과는 그래프 인접 노드(neighborhoods), 참조 목록(reference lists) 등 토큰 소모가 많습니다 (token-heavy). 저렴한 모델에서는 이러한 장황한 컨텍스트(context) 비용이 거의 들지 않지만, 비싼 모델인 opus에서는 동일한 토큰에 대해 훨씬 높은 가격을 책정하므로 장황함이 비용 부담으로 직결됩니다. 반면 serena와 codegraph는 어떤 모델에서도 저렴함을 유지합니다. 이들은 정확히 짚어내는 결과(pinpoint results)를 반환하기 때문입니다. 즉, 이들은 모델 선택에 대해 견고(robust)하지만, graphlens는 그렇지 않습니다.
이는 이들 중 가장 가치 있는 시사점을 제공합니다: 구조적 도구(structural tool)를 사용하는 저렴한 모델이 grep을 사용하는 비싼 모델보다 낫습니다. codegraph + haiku (~$0.023, 정확도 0.99)는 모든 축(axis)에서 filesystem + opus ($0.087, 정확도 ~0.93)를 동시에 압도합니다.
성립하지 않은 가설
나는 스트레스 테스트(stress test)로서 두 개의 xlang_link 작업을 배치했습니다. 즉, TS(TypeScript) 호출이 /api/v1/... 경계를 넘어 Python 핸들러로 해결(resolve)되는 상황을 설정했으며, 단일 언어 도구들은 이 지점에서 걸려 넘어질 것이라고 확신했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기