AI 에이전트가 대규모 코드베이스에서 작동하는 방식
AI 에이전트는 수백만 줄 규모의 모노레포, 수십 년 묵은 레거시 시스템, 수십 개 레포로 흩어진 분산 아키텍처, 수천 명 규모의 개발 조직에서 실제로 잘 굴러가고 있습니다. 이런 환경에는 작고 단순한 코드베이스에서는 만나기 어려운 문제들이 따라옵니다.
이 글은 에이전트를 대규모 코드베이스에 도입할 때 필요한 인사이트를 정리합니다. 수백만 줄짜리 모노레포, 수십 년에 걸쳐 쌓인 레거시, 여러 레포로 쪼개진 수십 개의 마이크로서비스 모두를 포함합니다. 모든 대규모 배포는 결국 그 조직의 버전 관리 방식, 팀 구조, 누적된 컨벤션에 맞춰 모양이 달라지지만, 여기서 소개하는 패턴들은 그런 차이를 가로질러 일반화될 수 있고, 도입을 검토 중인 팀에게 좋은 출발점이 될 것입니다.
이 글의 사례는 모두 Claude Code 기준입니다. Codex·Cursor·Gemini CLI 등 다른 에이전트도 비슷한 메커니즘을 다른 이름·다른 구조로 제공합니다(예: Codex의 AGENTS.md, Cursor의 Rules).
Claude Code는 어떻게 대규모 코드베이스를 탐색하는가
Claude Code는 사람 엔지니어가 코드베이스를 읽는 방식 그대로 움직입니다. 파일 시스템을 따라 이동하고, 파일을 읽고, grep으로 필요한 부분을 정확히 짚어내고, 코드베이스 곳곳의 참조를 따라갑니다. 이 작업이 모두 개발자 로컬 머신에서 일어나기 때문에 코드베이스 인덱스를 따로 만들고 유지하거나 서버로 올릴 필요가 없습니다.
RAG 기반의 AI 코딩 도구는 코드베이스 전체를 임베딩해두고 질의 시점에 관련 청크를 가져오는 방식으로 작동합니다. 문제는 대규모 환경에서 이 방식이 잘 안 굴러간다는 점입니다. 임베딩 파이프라인이 활발하게 작업하는 팀의 속도를 따라가지 못해, 개발자가 인덱스를 조회할 때쯤이면 인덱스는 이미 몇 주, 며칠, 길어야 몇 시간 전의 코드베이스를 비추고 있습니다. 그 결과 2주 전에 이름이 바뀐 함수, 지난 스프린트에 삭제된 모듈이 검색 결과에 그대로 끌려나오면서도, 이 정보가 낡았다는 표시는 어디에도 없습니다.
에이전틱 검색(agentic search) 은 이 문제를 정면으로 풉니다. 수천 명의 엔지니어가 새 코드를 커밋해도 유지해야 할 임베딩 파이프라인이나 중앙 인덱스가 없습니다. 각 개발자 인스턴스는 매번 실제 코드베이스 위에서 직접 동작합니다.
다만 이 방식에는 분명한 트레이드오프가 있습니다. Claude는 “어디를 봐야 할지” 알 수 있을 만큼 초기 컨텍스트가 충분히 주어졌을 때 가장 잘 작동합니다. 즉, 탐색 품질은 코드베이스가 얼마나 잘 정돈되어 있는지, 다시 말해 CLAUDE.md 파일과 스킬을 통해 컨텍스트가 계층적으로 잘 깔려 있는지에 달려 있습니다. 수십억 줄 규모의 코드베이스에서 모호한 패턴을 빠짐없이 찾아 달라고 시키면, 일을 시작하기도 전에 컨텍스트 윈도우 한계에 부딪힙니다. 코드베이스 셋업에 투자하는 팀일수록 더 좋은 결과를 얻는 이유가 여기에 있습니다.
모델만큼 중요한 것이 하네스(harness)다
AI 에이전트에 대한 가장 흔한 오해는 “결국 모델 성능이 전부다”라는 것입니다. 실제 결과를 가르는 것은 모델 그 자체가 아니라 모델을 둘러싼 생태계 — 즉 하네스 — 인 경우가 더 많습니다.
이 글에서 말하는 하네스는 확장 포인트들로 구성됩니다. CLAUDE.md 파일, 훅(hook), 스킬(skill), 플러그인(plugin), MCP 서버. 각 계층은 앞 계층 위에 얹히는 구조라서, 어떤 순서로 이걸 쌓아 올리는지가 중요합니다. 여기에 LSP 통합과 서브에이전트 두 가지가 더 붙습니다. 이 요소들이 쌓여서 만들어지는 하네스는 어떤 효과를 불러올까요? 아래에서 각 구성 요소를 하나씩 짚어보겠습니다.
CLAUDE.md 파일이 제일 먼저 쌓입니다. Claude가 매 세션 시작 시 자동으로 읽어들이는 컨텍스트 파일이죠. 루트 파일은 큰 그림을, 하위 디렉터리 파일은 그 영역의 로컬 컨벤션을 담습니다. 이 파일들이 Claude가 효율적으로 일하는 데 필요한 코드베이스 정보를 제공합니다. 작업 종류와 무관하게 모든 세션에 로드되기 때문에, 폭넓게 적용되는 내용만 담는 게 성능을 지키는 핵심입니다.
훅으로 셋업 자체를 스스로 개선시킵니다. 대부분 훅을 “Claude의 실수를 막아주는 스크립트” 정도로 생각하지만, 훅의 진짜 가치는 지속적인 개선에 있습니다. Stop 훅은 한 세션에서 일어난 일을 분석해서, 컨텍스트가 아직 신선할 때 CLAUDE.md 업데이트를 제안할 수 있습니다.
- 스킬은 필요한 전문성을 필요할 때만 끌어옵니다. 대규모 코드베이스에는 수십 가지 작업 유형이 있고, 그 모든 전문성이 매 세션에 필요한 건 아닙니다. 스킬은 점진적인 정보공개를 통해, 컨텍스트 공간을 두고 경쟁할 만한 ‘전문적인 워크플로’와 ‘도메인 지식’을 따로 빼두었다가 필요할 때만 로드합니다.
- 스킬은 특정 경로로 스코프를 좁힐 수도 있어서, 코드베이스 중에서도 관련된 영역에서만 활성화되도록 만들 수 있습니다. 결제 서비스 팀은 배포 스킬을 자기 디렉터리에 묶어두고, 모노레포의 다른 위치에서 작업할 때는 자동 로딩이 일어나지 않게 할 수 있습니다.
플러그인은 잘 다듬어진 셋업을 배포합니다. 대규모 환경의 흔한 문제 중 하나가, 잘 정돈된 환경이 특정 그룹 안에만 머무른다는 것입니다. 플러그인은 스킬·훅·MCP 설정을 하나의 설치 가능한 패키지로 묶기 때문에, 신입 엔지니어가 입사 첫날 플러그인 하나만 설치하면 기존 사용자와 동일한 컨텍스트와 능력을 즉시 갖출 수 있습니다. 업데이트는 관리형 마켓플레이스를 통해 조직 전체로 한 번에 배포됩니다.
LSP(Language Server Protocol) 통합은 에이전트에게 “개발자가 IDE에서 누리는 것과 같은 탐색 능력“을 줍니다. 대부분의 대규모 코드베이스용 IDE는 이미 LSP를 띄워두고 “정의로 이동”, “모든 참조 찾기”를 지원합니다. Claude에게 LSP를 쥐여주면 심볼 단위 정밀도가 생깁니다. 함수 호출을 따라 정의까지 따라가고, 파일을 가로지르는 참조를 추적하며, 서로 다른 언어에 같은 이름의 함수가 있어도 헷갈리지 않습니다. LSP가 없으면 Claude는 텍스트 패턴 매칭에 의존하기 때문에 엉뚱한 심볼을 잡을 수도 있죠.
MCP 서버는 그 모든 것을 바깥으로 확장합니다. 에이전트는 MCP 서버를 통해 자체적으로는 접근할 수 없는 내부 도구, 데이터 소스, API에 연결됩니다. 가장 성숙한 팀들은 한 발 더 나아가, Claude가 직접 호출할 수 있는 도구 형태로 구조화된 검색 기능을 제공하는 MCP 서버를 구축했습니다. 다른 팀들은 내부 문서, 티켓 시스템, 분석 플랫폼 같은 곳에 연결했습니다.
- 서브에이전트는 탐색과 편집을 분리해줍니다. 서브에이전트는 자기만의 컨텍스트 윈도우를 가진 격리된 Claude 인스턴스입니다. 작업을 받아 처리한 뒤 최종 결과만 부모 에이전트에 돌려줍니다. 어느 정도 시스템이 자리 잡은 팀들은 read-only 서브에이전트를 띄워 서브시스템을 매핑하고 결과를 파일에 적어두게 한 다음, 메인 에이전트는 그 정보를 받아서 실제 편집을 수행합니다.
대규모 환경에서도 코드베이스를 “쉽게 읽을 수 있게” 만들기
Claude가 대규모 코드베이스에서 도움이 될 수 있는지는 결국 적절한 컨텍스트를 찾아내는 능력에 달려 있습니다. 매 세션 너무 많은 컨텍스트를 끌고 가면 성능이 떨어지고, 너무 적게 가져가면 Claude가 길을 잃습니다. 가장 성공적인 사례들은 Claude가 코드베이스를 쉽게 이해할 수 있도록 사전 작업에 시간을 들였습니다. 그 사례 안에서는 다음과 같은 공통 패턴들이 보입니다.
1. CLAUDE.md를 가볍게, 그리고 계층적으로 둔다.
Claude는 코드베이스를 따라 이동하며 파일을 점진적으로 로드합니다. 루트 파일은 큰 그림, 하위 디렉터리 파일은 로컬 컨벤션을 담습니다. 루트에는 핵심 포인터와 결정적인 함정만 들어가야 하고, 그 외의 모든 것은 노이즈입니다.
2. 레포 루트가 아니라 하위 디렉터리에서 init 한다.
Claude는 작업과 실제로 관련된 영역으로 스코프가 좁혀졌을 때 가장 잘 작동합니다. 모노레포 환경에서는 “도구는 루트에서 돌리는 것”이라는 기존 관념 때문에 이게 직관에 어긋나 보일 수 있지만, Claude는 디렉터리 트리를 자동으로 거슬러 올라가며 만나는 CLAUDE.md를 모두 로드하기 때문에 루트 컨텍스트는 잃지 않습니다.
3. 테스트·린트 명령을 하위 디렉터리별로 지정한다.
Claude가 하나의 서비스를 변경했을 때 전체 테스트 스위트를 실행하면 타임아웃이 발생하고 관련 없는 출력에 컨텍스트가 낭비됩니다. 그래서 하위 디렉터리 수준의 CLAUDE.md 파일에 해당 코드베이스 부분에 적용되는 명령을 명시해야 합니다. 이는 각 디렉터리에 자체 테스트 및 빌드 명령이 있는 서비스 지향 코드베이스에서 효과적입니다. 디렉터리 간 종속성이 깊은 컴파일 언어 모노레포에서는 하위 디렉터리별 범위 지정이 더 어렵고 프로젝트별 빌드 구성이 필요할 수 있습니다.
4. .*ignore 파일로 생성 파일·빌드 산출물·서드파티 코드를 걸러낸다.
더 강하게 막아야 할 경로는 .claude/settings.json의 permissions.deny에 적어 커밋하면 팀 전체가 같은 노이즈 감소 효과를 누립니다. 어떤 코드베이스에서는 생성된 파일 자체가 작업 대상이기도 한데, 그런 코드를 다루는 개발자만 로컬 설정에서 프로젝트 단위 제외 규칙을 오버라이드하면 됩니다.
5. 디렉터리 구조만으로 부족하다면 코드베이스 맵을 만든다.
일반적인 디렉터리 구조로 정리되지 않은 조직이라면, 레포 루트에 가벼운 마크다운 파일 하나를 두고 최상위 폴더와 각 폴더의 내용을 한 줄씩 적어두는 것만으로도, Claude가 파일을 열기 전에 훑어볼 수 있는 “목차”가 됩니다. 최상위 폴더가 수백 개에 달한다면 계층적으로 가는 편이 낫습니다. 루트 파일은 최상위 구조만 설명하고, 하위 디렉터리의 CLAUDE.md가 다음 레벨의 세부 정보를 담아 Claude가 트리를 내려갈 때 필요한 만큼만 로드되도록 합니다. 더 단순한 경우엔, Claude가 봐줬으면 하는 파일·디렉터리를 그냥 @-mention 하는 것만으로도 같은 효과를 낼 수 있습니다.
6. 문자열이 아닌 심볼로 검색하도록 LSP 서버를 띄운다.
대규모 코드베이스에서 흔한 함수 이름을 grep 하면 수천 건의 일치 결과가 쏟아지고, Claude는 그중 뭐가 중요한지 가리려고 파일을 여느라 컨텍스트를 소모합니다. LSP는 같은 심볼을 가리키는 참조만 돌려주기 때문에, Claude가 코드를 읽기도 전에 이미 필터링이 끝난 상태가 됩니다. 셋업을 위해서는 해당 언어용 code intelligence 플러그인과 언어 서버 바이너리 설치가 필요합니다. 사용 가능한 플러그인과 트러블슈팅은 Claude Code 문서에서 확인할 수 있습니다.
마무리하며
이번에 살펴본 패턴에는 이전 글에서 먼저 공부했던 것들도 섞여 있지만, 다만 하위 디렉터리에서 init 하는 발상과 LSP를 띄워 심볼 단위로 검색하게 만드는 발상은 새로 얻은 것이었습니다. 둘 다 “도구는 레포 루트에서 돌린다”, “검색은 문자열로 한다” 는 관성을 깨는 선택이라는 점이 인상적이었습니다.
앞으로 어떠한 관성이 “지금의 환경에 어울되지 않는게 아닐까?” 라는 의문을 던지는 습관을 길러보려 합니다.
