Claude Code는 왜 코드 검색에 RAG를 쓰지 않는가
← 전체 목차 · 이전: 지식베이스를 왜 정기 전량 재구축하면 안 되나 · 다음: 고립된 데이터와의 작별 — 지식 그래프란 무엇인가
| 차원 | 문서 문답 | 코드 검색 |
|---|
| 목표 | 의미가 관련된 단락을 찾으면 충분 | 매우 정확한 것을 원한다 |
| 전형적 질문 | "이 부분이 무슨 이야기인가" | 어떤 함수가 어디 있는가, 어떤 변수가 어디서 호출되는가, 어떤 인터페이스를 누가 구현했는가 |
| 적합한 수단 | 의미 유사(RAG)가 유용 | 의미 유사가 오히려 전통 검색 도구만 못하다 |
코드 시나리오에서 RAG의 세 가지 급소
급소 1: 코드 구조가 잘게 부서지기 쉽다
하나의 함수는 흔히 위쪽의 타입 정의 + 아래쪽의 호출 로직 + 파일을 넘나드는 참조에 의존한다. 일단 청킹하면 모델이 보는 것은 반토막 로직일 수 있다.
급소 2: 검색 정밀도 부족
목표 조회 getUserById에 대해 벡터 유사도가 반환한 결과 순서:
| 순위 | 반환된 함수 | 유사도 |
|---|
| 1 | fetchUserData | 0.92 |
| 2 | queryUser | 0.88 |
| 3 | getUserById(정말 원하던 것) | 0.75 |
정확히 일치하는 것이 오히려 맨 뒤에 온다 — 식별자 검색에서 의미 유사도가 실효하는 지점이다.
급소 3: 인덱스 갱신 지연
코드베이스는 너무 빨리 변한다. 방금 코드를 고쳤는데 벡터 인덱스가 갱신되지 않아 모델이 읽는 것은 옛 코드다. 재구축은 느리고 증분 동기화는 복잡하다.
소결: 코드 Agent 시나리오에서 RAG는 "쓸 수 없는" 것이 아니라 — 충분히 실시간적이지 않고, 충분히 정확하지 않고, 충분히 통제 가능하지 않다.
대체 방안: 프로그래머처럼 코드를 조회한다
| 사람의 동작 | 대응 도구 |
|---|
| 먼저 키워드 검색: 함수명, 클래스명, 라우트명, 에러 메시지 | Grep |
| 파일 디렉터리로 단서를 찾음: Controller / Service / Hooks / Components | Glob |
| 구체적 파일을 특정한 뒤 필요한 만큼 열어 읽음 | Read |
이 "소박한" 방식이 코드에 특히 적합한 이유
- 실시간적이다: 읽는 것이 디스크 위의 최신 코드다.
- 정확하다: 찾으면 찾은 것이고 못 찾으면 못 찾은 것이다.
- 설명 가능하다: 모델이 매 단계에서 왜 검색했고 무엇을 찾았는지 사용자가 다 볼 수 있다.
가장 관건적인 전환: 검색 결정권을 모델에게 넘긴다
| RAG 모드 | 코드 Agent 모드 |
|---|
| 형태 | 일회성 검색 | 순환 |
| 흐름 | 질문 → 시스템이 조각 한 묶음을 리콜 → 모델이 그 조각들로 답변 | 모델이 무엇을 검색할지 판단 → 도구가 반환 → 모델이 결과에 따라 전략 조정 |
| 실패 처리 | 없음(리콜된 것이 전부) | 못 찾으면 키워드를 바꾸고, 단서를 찾으면 계속 파일을 읽고, 호출 관계를 찾으면 역추적한다 |
| 비유 | 한 번에 도달 | 검색하며, 보며, 추론하며, 수정하며 |
핵심은 "나에게 검색 시스템이 있다"가 아니라 **"모델이 스스로 코드를 조회하는 법을 배우게 한다"**이다.
탐색형 서브 Agent: 읽기 전용 서브 Agent로 컨텍스트 압축
문제: 메인 Agent가 계속 Grep / Read를 하면 중간 결과가 쌓여 컨텍스트가 금세 오염된다.
메인 Agent → 읽기 전용 서브 Agent: 작업 하달(예: "로그인 흐름을 정리하라")
읽기 전용 서브 Agent: 【독립된 컨텍스트】 안에서 반복 검색·독해·추론
읽기 전용 서브 Agent → 메인 Agent: 【압축된 핵심 결론】만 회신
효과: 메인 Agent의 컨텍스트가 순수하게 유지되고 핵심 정보만 획득 → 오염 방지
추가 수익: 여러 서브 Agent가 병렬로 서로 다른 모듈을 탐색해 전체 효율을 높일 수 있다.
왜 주로 RAG에 의존하지 않는가 — 여섯 가지 이유
- 코드는 모호한 의미 매칭이 아니라 정확 매칭이 필요하다
- 코드는 빠르게 변하고, 전통 도구는 최신 파일을 실시간으로 읽을 수 있다
- 인덱스 없이 빠르게 시작 — 미리 라이브러리를 구축할 필요가 없다
- Grep / Glob / Read의 검색 링크가 투명해 오류 추적이 쉽다
- 모델이 다중 라운드로 이터레이션 검색할 수 있고 일회성 리콜이 아니다
- 전통 도구가 프로그래머의 실제 코드 조회 방식에 더 부합한다
RAG의 대체 불가능성
적용 시나리오 4종: 저장소를 넘나드는 코드 / 프로젝트 문서 / Wiki 지식베이스 / API 설명. 여러 저장소를 가로지르거나 초대규모 코드베이스를 검색할 때는 RAG가 여전히 매우 유용하다.
결론
이 문제의 표준 답안은 "Claude Code가 RAG를 안 쓰는 것은 RAG가 안 되기 때문"이 아니라, **"코드 Agent 시나리오에서는 실시간적이고 정확하고 설명 가능한 도구 검색이, 미리 구축해 둔 벡터 검색보다 흔히 더 적합하다"**이다. 진짜 고급스러운 지점은 모델이 엔지니어처럼 다음에 무엇을 검색하고, 무엇을 읽고, 무엇을 검증할지 스스로 결정하게 하는 것이다.
핵심 통찰
- 검색 방식은 "조회의 정밀도 요구"가 결정해야지 기술의 유행도가 결정해서는 안 된다 — 문서 문답은 모호함을 용인하고 코드 위치 특정은 용인하지 않는다. 선택의 첫 질문은 **"내가 필요한 것이 관련성인가 정확성인가?"**여야 한다.
- 청킹의 대가는 구조성이 강한 코퍼스에서 증폭된다 — 코드에는 행을 넘고 파일을 넘는 강한 의존이 있어 어떤 고정 윈도우 청킹도 의미 단위를 절단한다. "위아래 의존이 강한" 모든 코퍼스(코드, 법조문, 재무제표의 대사 관계)는 RAG의 적합성을 다시 평가해야 한다.
- 벡터 유사도는 식별자 검색에서 체계적 편향을 갖는다 —
fetchUserData(0.92)가 getUserById(0.75)보다 앞선다는 것은, Embedding이 잡는 것이 "의미가 닮음"인 반면 정확 식별자 매칭은 본질적으로 문자열 문제임을 보여준다. 도구의 층위를 잘못 쓴 것이다.
- 인덱스는 상태이고, 상태에는 일관성 비용이 있다 — 쓰기가 빈번한 코퍼스에서는 어떤 사전 구축 인덱스든 "재구축이 느리고 증분 동기화가 복잡한" 대가를 치러야 한다. 반면 Grep은 디스크를 직접 읽으므로 애초에 일관성 문제가 없다.
- 가장 큰 아키텍처 승급은 "도구 교체"가 아니라 "결정권 이전"이다 — RAG는 "시스템이 모델에게 무엇을 보여줄지 결정"하고, Agent는 "모델이 자기가 무엇을 볼지 결정"한다. 전자는 단일 라운드 파이프라인이고 후자는 폐루프 제어이며, 능력 상한이 완전히 다르다.
- 컨텍스트 오염이 긴 작업의 제1의 적이며, 격리가 압축보다 철저하다 — 메인 컨텍스트에 검색 중간 결과를 가득 채워 놓고 압축하느니, 서브 Agent가 독립된 컨텍스트에서 그 중간 과정을 태워 버리고 결론만 회신하게 하는 편이 낫다.
- 읽기 전용 권한이 서브 Agent 보안 경계의 자연스러운 설계다 — 조사형 서브 Agent에는 Grep/Glob/Read만 필요하며, 쓰기 권한을 주지 않으면 오수정을 피하면서 병렬 실행에도 충돌 리스크가 없다.
- 결론을 절대화하지 말 것 — RAG가 쓸모없다는 것이 아니라, RAG가 여전히 대체 불가능한 경계(저장소를 넘나드는 검색, 초대규모, 멀티모달 혼합 검색)를 명확히 그은 것이다. "경계"를 제시하는 편이 "입장"을 제시하는 것보다 점수가 높다.
엔지니어링 실전 Tips
- 코드베이스에 기본적으로 벡터 라이브러리를 만들지 말 것: 중소 규모 저장소에서는 Grep + Glob의 리콜 품질과 지연이 대개 RAG보다 우수하며, Embedding 호출 비용과 벡터 DB 운영도 절약된다.
- RAG를 쓸 것이라면 "혼합 검색"에 쓸 것: 저장소를 넘나드는 코드 + 프로젝트 문서 + Wiki + API 설명 같은 다중 출처·멀티모달 시나리오가 RAG의 가치 구간이다.
- 꼭 코드에 RAG를 해야 한다면 문법 단위로 자를 것(함수/클래스를 최소 단위로 하고 파일 헤더의 import와 타입 정의를 함께), 고정 token 수로 자르지 말 것.
- 정확 식별자 조회는 exact match 채널로 가야 하며 벡터 리콜은 보완만 담당해야 한다. 순수 벡터 방안은
getUserById를 fetchUserData 뒤로 보낸다.
- 인덱스 신선도를 관측 가능 지표로: 벡터 인덱스를 고수한다면 **"인덱스가 HEAD보다 몇 커밋 뒤처져 있는지"**를 반드시 모니터링해야 하며, 그렇지 않으면 모델이 옛 코드를 근거로 방안을 낸다.
- 메인 Agent의 컨텍스트 예산은 의사결정에 남기고 중간 검색 결과에 쓰지 말 것: "궂은일"을 서브 Agent에 외주 주는 것이 가장 직접적인 Token 절약 수단이다.
- 서브 Agent의 반환 형식을 제약할 것(결론 + 핵심 파일 경로 + 행 번호만): 그렇지 않으면 서브 Agent가 중간 과정을 그대로 토해내 압축이 무의미해진다.
- 검색 링크의 투명성 자체가 유지보수성이다: 오류 시 "어떤 단어를 검색했고 어떤 파일에 적중했는지"를 볼 수 있는 편이, "왜 이 chunk가 리콜되었는지"를 파헤치는 것보다 한 자릿수 쉽다.
- 더 파고들 방향: 혼합 검색(정확 매칭 + 벡터 리콜 + 재정렬)으로 초대규모 코드베이스에서 정밀도와 커버리지를 양립시키기, AST / LSP 기반 코드 인덱스(token 청킹보다 코드 구조에 부합해 "반토막 로직" 문제를 해결), 서브 Agent의 동시 스케줄링과 결론 병합 전략(중복 제거, 결론 충돌 해소), 서브 Agent 결론의 신뢰도 표기.