
코딩 에이전트가 큰 코드베이스에서 자꾸 엉뚱한 걸 고친다면, 파일 하나 read시켰는데 관계없는 함수 19개가 딸려와 컨텍스트가 오염된다면, “1 file 1 concept” 같은 컨벤션이 정말 효과가 있는지 의심스럽다면 — 별 23k짜리 실전 프레임워크에서 그걸 측정한 결과가 여기 있다.
“파일이 너무 많아지지 않나?”
filefunc에 대해 가장 많이 받는 질문이다. 186개 파일을 626개로 쪼개면 관리가 안 되지 않겠느냐는 것.
답은 Hono에 있다. Cloudflare Workers, Deno, Bun, Node.js에서 돌아가는 초경량 웹 프레임워크. star 23k+, npm 주간 다운로드 100만+. 프로덕션에서 검증된 실전 코드다. 이 코드를 filefunc으로 리팩토링했다. 테스트 4419개, 전부 통과. (park-jun-woo/hono 포크에서 직접 검증 가능)
숫자
| 지표 | 원본 | 리팩토링 후 |
|---|---|---|
| 소스 파일 | 186 | 626 |
| 총 라인 | 24,653 | 30,244 |
| filefunc 위반 | 397 | 0 |
| vitest 통과 | 4419 | 4419 |
| vitest 실패 | 4 | 4 (기존 결함) |
| vitest 스킵 | 33 | 33 |
파일은 3.4배 늘었다. 라인은 23% 늘었다. 위반은 397에서 0이 됐다. 테스트는 한 개도 깨지지 않았다 — 정확히는 원본과 동일하게 4개가 실패했다(원본부터 있던 결함). 23%의 라인 증가는 어노테이션(//ff:func, //ff:what)과 re-export hub에서 왔다. 로직은 한 줄도 바뀌지 않았다. 순수 구조 리팩토링.
핵심은 파일 개수가 아니라 ‘읽는 길이’다
“위반 0, 626개 파일"은 사실 프록시다. filefunc의 진짜 목적은 파일을 잘게 쪼개는 게 아니다 — 에이전트가 개념 하나를 read할 때 그 개념만, 과도하게 길지 않게 읽도록 만드는 것이다. 그래서 증명해야 할 진짜 숫자는 위반 수가 아니라 파일당 읽는 길이다. 측정했다.
| 파일당 라인 | 원본 | 리팩토링 후 |
|---|---|---|
| 중앙값 | 60.0 | 17.5 (−71%) |
| p90 | 305 | 119 (−61%) |
| 최대 | 2,778 | 1,051 (−62%) |
| ≤20줄 파일 비율 | 26% | 54% |
에이전트가 개념 하나를 열면 이전엔 평균 60줄을 삼켰고, 이제 18줄을 삼킨다. 최악의 경우(p90)도 305줄에서 120줄로 떨어졌다. 함수 자체의 길이는 그대로다(중앙값 11→12줄) — 당연하다. 함수를 재작성한 게 아니라 재배치했으니까. 줄어든 건 “개념 하나를 읽으려고 어쩔 수 없이 같이 읽게 되는 주변 코드"다.
이게 왜 중요한가. 긴 컨텍스트는 공짜가 아니다. LLM은 긴 입력의 중간에 묻힌 정보를 체계적으로 놓친다(Liu et al., Lost in the Middle, TACL 2023, arXiv:2307.03172). 코딩 과제에서는 맥락이 길어질수록 성능이 급락한다 — 한 벤치마크에서 Claude 3.5 Sonnet의 정확도가 29%에서 3%로 무너졌다(Rando et al., LongCodeBench, 2025, arXiv:2505.07897). 그리고 파일 단위로 통째 주는 것보다 개념 단위로 잘라 정확히 필요한 부분만 주는 쪽이 코드 완성 품질을 더 높인다(Yusuf et al., 2025, arXiv:2510.06606). 읽는 길이를 줄이는 건 취향이 아니라 정확도 방어다.
types.ts 문제
추상이 아니라 구체로 보자. Hono의 원본 src/types.ts에는 인터페이스와 타입이 20개 넘게 있었다.
AI 에이전트가 HonoRequest 타입 하나를 찾으려고 이 파일을 read하면? 19개의 불필요한 타입이 딸려온다. 컨텍스트 오염.
리팩토링 후에는 각 타입이 독립 파일이다. HonoRequest 하나가 필요하면 hono_request.ts 하나만 read하면 된다. 원본 types.ts는 re-export hub로 남겨서 기존 import 경로를 보존했다.
# 원본
import { HonoRequest } from './types' // 20+ 타입이 딸려옴
# 리팩토링 후
import { HonoRequest } from './types' // 같은 경로, 같은 동작
// 내부적으로는 types.ts → hono_request.ts re-export
외부에서 보면 아무것도 안 바뀌었다. AI 에이전트가 보면 전부 바뀌었다.
깊이 6에서 2로
Hono의 라우터 알고리즘은 복잡하다. trie-router의 Node.search는 nesting depth가 6이었다.
for → if → if → for → if → if // depth 6
depth 6이 나쁜 코드인가? 아니다. 트라이 탐색은 원래 중첩이 깊다. 하지만 AI 에이전트가 이 함수를 이해하려면 6단계 중첩을 한 번에 머릿속에 넣어야 한다. 사람도 마찬가지다. filefunc은 내부 로직을 private 메서드와 모듈 레벨 화살표 함수로 추출했다. depth 6 → 2. 각 조각은 한 가지 제어 흐름만 가진다. 전체 알고리즘은 동일하다.
# 원본: 모놀리식 search
Node.search() // depth 6, 100줄+
# 리팩토링: 조각으로 분해
Node.search() // depth 2, 조합만 담당
→ matchParam() // depth 1, 파라미터 매칭
→ matchWildcard() // depth 1, 와일드카드 처리
→ mergeHandlers() // depth 1, 핸들러 병합
TypeScript의 F1, 그리고 정직한 꼬리
filefunc의 핵심 룰 F1은 “파일 하나에 함수 하나"다. Go에서는 직관적이다. 하지만 TypeScript에서 파일을 쪼개면 모듈 시스템이 깨진다. 클래스 메서드를 외부 파일로 빼면 this 바인딩이 사라진다. 그래서 filefunc의 TypeScript 파서(ts_ast.js)는 function 선언만 카운트하고 const 화살표 함수는 카운트하지 않는다. 원칙은 “파일 하나에 개념 하나"이지 “문법적으로 function이 하나"가 아니기 때문이다.
여기서 정직해야 한다. 이 접근은 쉬운 경우(타입, 단일 헬퍼)는 깔끔하게 분리했지만, 모든 걸 분리하지는 못했다. 리팩토링 후를 다시 측정해보면:
- 626개 중 90%(566개)가 함수 ≤1개 — “1파일1개념"을 만족한다. (원본은 70%였다.)
- 하지만 60개 파일(9.6%)이 여전히 함수 2개 이상을 동거시킨다. 그리고 하필 그것들이 길다 — 이 60개의 라인 중앙값은 151줄. 예를 들어
src/utils/url.ts는 14개의 함수가 319줄 안에 들어 있다.
즉 const 화살표 기법은 카운터는 통과시키지만 목적은 부분적으로만 달성했다. 한 파일에 화살표 함수가 여럿 남으면, 에이전트가 그 파일을 열 때 여전히 여러 개념을 읽는다. 메트릭이 목표가 되는 순간 메트릭은 망가진다(Goodhart). filefunc도 예외가 아니다 — 남은 read-length 위험의 대부분이 이 10%의 꼬리에 몰려 있다. 위반 0이라는 숫자를 정답으로 격상시키지 않고, 무엇이 아직 안 됐는지까지 측정하는 것 — 그게 검증이다.
“그래서 뭐가 좋아지는데?”
파일이 626개가 되면 사람은 불편할 수 있다. 디렉토리를 열면 파일이 쏟아진다. 하지만 AI 에이전트는 디렉토리를 열지 않는다. grep한다.
rg '//ff:func' --glob '*.ts' -l | head -20 # 후보 파일 추출
rg '//ff:what.*router' --glob '*.ts' # 라우터 관련 함수만
186개 파일에 함수가 평균 3-4개씩 들어 있으면, grep이 파일을 찾아도 read하면 불필요한 함수가 딸려온다. 626개 파일에 개념이 1개씩 들어 있으면, grep이 찾은 파일 = 필요한 개념. 중간 과정이 사라진다. 에이전트의 코드 탐색에서 “관련 위치를 찾는 일(localization)“이 다운스트림 문제 해결의 병목인데(Chen et al., LocAgent, 2025, arXiv:2503.09089), filefunc은 개념을 파일 경계와 일치시켜 이 탐색을 결정론적으로 만든다.
함수 단위가 항상 정답일까
반대 증거도 정직하게 보자. 한 통제 실험은 RAG 코드 자동완성에서 “함수 단위 청킹이 다른 전략보다 3.6~5.6pp 낮고 파레토-최적이 아니다"라고 보고한다(Wu et al., 2026, arXiv:2605.04763). 함수 단위가 만능이 아니라는 것이다.
단, 이건 층위가 다른 얘기다. 그 실험은 검색기가 코드를 잘라 프롬프트에 욱여넣는 자동완성 맥락이다. filefunc은 검색기가 청크를 만드는 게 아니라, 에이전트가 파일을 직접 골라 read하는 운용 단위를 다룬다. 청킹 전략(retrieval chunk)과 운용 단위(file an agent opens)는 다른 레이어다. 그래도 이 구분을 명시하는 건 중요하다 — “잘게 쪼개면 무조건 좋다"는 과장은 filefunc의 주장이 아니다. 주장은 “에이전트가 읽는 단위가 개념과 일치하면 읽는 길이가 짧아진다"이고, 위 숫자가 그걸 보인다.
제약은 자유다
Hono를 filefunc으로 리팩토링하면서 확인한 것은 하나다.
구조적 제약은 코드를 제한하지 않는다. 탐색을 해방한다.
파일이 많아지는 것은 비용이다. 하지만 각 파일이 하나의 개념만 담고 있을 때, 에이전트는 정확히 필요한 것만 read하고 불필요한 컨텍스트에 오염되지 않는다 — 읽는 길이가 60줄에서 18줄로 줄었다는 측정이 그 증거다. 사람도 마찬가지다. 함수 이름이 파일 이름이면 디렉토리가 곧 목차다.
397개의 위반이 0이 되고, 4419개의 테스트가 원본과 동일하게 통과했다는 것. 그리고 그 결과를 README의 리팩토링 리포트에서 누구나 다시 돌려볼 수 있다는 것. 이것이 “1 file 1 concept"이 이론이 아니라 실전이라는 증거다. 남은 9.6%의 꼬리까지 포함해서.
관련 글
- 에이전트가 운영할 수 있는 코드베이스 — “파일에 함수 20개면 에이전트 성능 30~85% 하락”, 이 글의 상위 명제
- filefunc: 파일 하나에 개념 하나 — 컨벤션 자체의 정의. 이 글은 그 대규모 실증편
- 에이전트 운영가능 시스템 만들기 — 잠긴 레거시를 에이전트가 열 수 있게 만드는 거시 서사
- 래칫 패턴 — 4419개 테스트가 “기계가 멈추라 할 때까지 끝내게 하는” 결정론적 게이트
더 읽을거리 (외부)
- Effective context engineering for AI agents — Anthropic. “context rot"과 유한한 어텐션 예산을 핵심 문제로 짚는 1차 출처 — filefunc가 불필요한 컨텍스트를 줄이는 이유의 같은 토대.
- Strategies and Tactics for working with Coding Agents — Brian Kihoon Lee. grep 기반 탐색과 정보 아키텍처를 손수 설계하라는 주장 — 에이전트가 “타깃만 읽게” 만드는 파일 구조 컨벤션과 직결.
- The Vibes Don’t Scale — Paul Stack. 바이브 코딩이 규모에서 아키텍처 드리프트로 무너지는 메커니즘 — filefunc가 풀려는 “큰 코드베이스가 에이전트를 깨뜨리는” 문제의식.
- Agentic Engineering Patterns — Simon Willison. Context Quarantine/Pruning 등 컨텍스트 관리 패턴 — filefunc의 agent-operable 주장을 실무 패턴 언어로 확장.
- Agent Harness Engineering — Addy Osmani. 에이전트 성능은 모델보다 주변 인프라에서 결정된다 — 코드 구조 컨벤션을 하니스의 한 축으로 재맥락화.
출처
- Liu et al. “Lost in the Middle: How Language Models Use Long Contexts” (TACL 2023, arXiv:2307.03172)
- Rando et al. “LongCodeBench: Evaluating Coding LLMs at 1M Context Windows” (2025, arXiv:2505.07897)
- Yusuf et al. “Beyond More Context: How Granularity and Order Drive Code Completion Quality” (2025, arXiv:2510.06606)
- Chen et al. “LocAgent: Graph-Guided LLM Agents for Code Localization” (2025, arXiv:2503.09089)
- Wu et al. “How Does Chunking Affect Retrieval-Augmented Code Completion? A Controlled Empirical Study” (2026, arXiv:2605.04763)
- 리팩토링 결과 검증: park-jun-woo/hono (README의 filefunc Refactoring Report) · 컨벤션: filefunc
- 대표 이미지: AI 생성 (Google Gemini)