얼마 전에 RAG 전략을 다룬 영상을 하나 봤습니다. 단순 RAG의 한계와 대안들을 정리한 내용이었는데, 보다 보니 익숙한 패턴이 나오더라고요. Anthropic이 발표한 Contextual Retrieval이라는 기법이었습니다. 문서 청크에 상위 맥락을 덧붙여서 검색 정확도를 올리는 방식인데, 보는 순간 이거 내가 모노레포에서 CLAUDE.md 깔아놓은 것과 구조가 같지 않나 싶었습니다.
단순 RAG가 왜 부족한가
RAG는 큰 문서를 잘게 쪼개서 벡터 DB에 넣어두고, 질문이 들어오면 관련 조각을 찾아서 AI에게 건네주는 방식입니다. 개념은 단순한데, 문제는 쪼개는 순간 앞뒤 맥락이 날아간다는 거거든요.
백과사전을 페이지 단위로 찢어서 서랍에 넣어뒀다고 생각해보세요. “광합성이 뭐야?”라고 물으면 해당 페이지를 꺼내주면 되는데, 그 페이지에 “앞서 설명한 틸라코이드 막에서 일어난다”라고 적혀있으면 어떻게 될까요. 앞장이 없으니 틸라코이드가 뭔지 알 수가 없습니다.
코드도 마찬가지입니다. getUserById(id) 함수만 떼어놓으면 이게 어떤 서비스에 속하는지, 에러 핸들링은 어떤 방식인지, 네이밍 컨벤션이 뭔지 알 수가 없습니다. AI가 이 함수를 수정하라고 요청받으면, 주변 맥락 없이는 프로젝트 스타일과 동떨어진 코드가 나올 수밖에 없는 거죠.
조각은 찾아오는데, 그 조각이 원래 어디에 있었는지 모르는 상황인 겁니다.
Contextual Retrieval: 조각에 맥락을 심는다
Anthropic이 내놓은 방법은 생각보다 단순합니다. 청크를 저장할 때, 앞에 “이 조각이 전체 문서에서 어디에 해당하는지” 설명을 붙이는 거거든요.
원래 청크가 이랬다면:
“이 과정은 틸라코이드 막에서 일어나며, 빛에너지를 화학에너지로 변환한다.”
맥락을 붙이면 이렇게 됩니다:
[맥락: ‘식물의 에너지 생산’ 챕터, 광합성 명반응 단계. 앞서 엽록체 구조와 틸라코이드 막 역할이 설명됨.] “이 과정은 틸라코이드 막에서 일어나며, 빛에너지를 화학에너지로 변환한다.”
찢어진 페이지에 “이건 3장에서 나온 거고, 앞에서 엽록체 얘기했어”라고 포스트잇을 붙여주는 셈이죠. 이렇게 하면 “광합성”뿐 아니라 “엽록체”, “에너지 생산” 같은 키워드로도 이 청크가 검색에 잡힙니다.
Anthropic 발표에 따르면 이 방식에 BM25 키워드 검색과 리랭킹까지 결합하면 검색 실패율이 67%까지 줄었다고 합니다. Contextual Retrieval 단독으로도 35% 감소했다고 하더라고요. 이건 Anthropic 공식 블로그에 나온 수치입니다.
이거 CLAUDE.md 아닌가
여기서 기시감이 들었습니다. 모노레포에 깔아둔 CLAUDE.md 파일들이 이 역할을 하고 있었거든요.
대규모 모노레포를 AI 코딩 도구로 작업하다 보면, 맥락 전달이 큰 과제입니다. 파일이 수백 개인데, AI가 특정 파일을 건드릴 때 그 파일의 위치와 규칙을 모르면 엉뚱한 코드가 나옵니다. 그래서 CLAUDE.md를 계층적으로 배치해두게 됐습니다:
루트/CLAUDE.md -- 프로젝트 전체 (기술 스택, 아키텍처, 공통 규칙)
backend/CLAUDE.md -- 백엔드 (언어 컨벤션, 프레임워크)
auth/CLAUDE.md -- 인증 서비스 (보안 규칙, 토큰 처리)
frontend/CLAUDE.md -- 프론트엔드 (컴포넌트 패턴, 스타일)
Claude Code는 세션이 시작되면 현재 디렉토리에서 루트까지 경로 위의 CLAUDE.md를 전부 읽습니다. 하위 디렉토리 것은 해당 파일을 실제로 열 때 온디맨드로 로드하고요. 어쨌든 AI가 auth/ 쪽 코드를 수정할 때는 루트, backend, auth 세 단계의 맥락이 쌓인 상태가 됩니다.
나란히 놓고 보면:
- Contextual Retrieval: 텍스트 청크 + 전체 문서 맥락
- 계층적 CLAUDE.md: 코드 파일 + 프로젝트/서비스 맥락
같은 문제를 같은 구조로 풀고 있는 겁니다. 이름만 달랐던 거죠.
적용 전후 차이
이 패턴을 의식적으로 깔기 전에는 AI한테 리팩토링을 시키면 프로젝트 컨벤션이 무시된 코드가 나오는 일이 잦았습니다. 에러 핸들링 방식이 다르거나, 네이밍이 기존 코드와 안 맞거나. CLAUDE.md를 계층적으로 배치하고 나서는 확실히 나아졌습니다.
근데 여기서 또 배운 게 있었는데, 너무 많이 쓰면 역효과가 납니다. CLAUDE.md에 이것저것 다 때려넣으면 AI가 정작 중요한 맥락을 놓치기 시작하더라고요. RAG 쪽에서도 비슷한 이야기가 있습니다. 문서 전체를 통째로 AI에 넣으면 비용이 크게 뛰는 것도 문제지만, 입력이 길어질수록 중간 내용을 놓치는 현상이 발생합니다. “Lost in the Middle”이라고 불리는 현상인데, 입력의 처음과 끝은 잘 기억하면서 중간은 무시하는 거거든요. CLAUDE.md도 마찬가지로, 적절한 양을 유지하는 게 중요합니다.
RAG 복잡도 스펙트럼
영상에서 정리해준 RAG 기법들을 복잡도 순으로 보면 이렇습니다:
단순 RAG - 벡터 검색만. 빠르고 싸지만 맥락이 날아갑니다.
Hybrid Search - 의미 검색과 키워드 검색을 동시에 돌리고, 리랭킹으로 정확도를 올립니다.
Contextual Retrieval - 청크에 상위 맥락을 붙입니다. 비용 대비 효과가 괜찮은 구간이더라고요.
GraphRAG - 지식 그래프 기반. 개체 간 관계를 파악할 수 있어서 강력하지만, 만들고 유지하는 비용이 큽니다.
Full Context Window - 문서를 통째로 넣습니다. 가장 직관적이지만 비용이 크게 올라가고, 입력이 길어지면 앞서 말한 Lost in the Middle 문제가 생깁니다.
요점은 “최고의 방법”이 있는 게 아니라 상황에 맞게 고르는 거라는 겁니다. 문서가 적으면 전부 넣어도 됩니다. 중간 규모면 Contextual Retrieval이 가성비가 좋고, 대규모면 Hybrid Search가 현실적입니다.
AI 코딩 도구도 같습니다. 파일 몇 개짜리 프로젝트면 루트에 CLAUDE.md 하나면 충분합니다. 서비스가 수십 개인 모노레포면 계층적으로 깔아야 AI가 맥락을 제대로 잡습니다.
로컬 RAG에서도 같은 이야기
직접 로컬 RAG를 운영하면서도 같은 패턴을 봤습니다. 코드 파일을 그대로 청킹해서 벡터 검색하면 정확도가 별로더라고요. 검색 거리가 0.8 이상으로 나와서 관련 없는 결과가 섞입니다.
근데 같은 코드베이스라도 git 커밋 메시지로 검색하면 거리가 0.57 정도까지 떨어집니다. 이유는 간단합니다. 커밋 메시지에는 “이 변경이 왜 필요했는지”가 자연어로 적혀있기 때문이거든요. fix: resolve null pointer in user auth flow라는 커밋은 코드 조각 자체보다 훨씬 풍부한 의미를 담고 있습니다. 벡터 검색이 의미 기반이니까, 맥락이 담긴 텍스트가 당연히 더 잘 잡힙니다.
이것도 결국 같은 구조입니다. 코드(청크)에 커밋 메시지(맥락)가 붙어있는 거니까요. 커밋을 잘 쓰는 게 일종의 맥락 주입이었던 셈이죠.
결국 맥락 문제
RAG든 AI 코딩이든, 맥락을 얼마나 잘 전달하느냐가 결과를 좌우합니다. 단순 RAG의 한계도 맥락 손실이고, CLAUDE.md가 효과적인 이유도 맥락 주입이고, 커밋 검색이 코드 검색보다 정확한 이유도 맥락의 유무입니다.
Contextual Retrieval이라는 이름이 붙었지만, 결국 “이 조각이 어디서 왔고 왜 중요한지” 한 줄 붙여주는 겁니다. 그게 CLAUDE.md든 커밋 메시지든 README든요.
AI 도구한테 “왜 엉뚱한 코드를 쓰냐”고 답답해하기 전에, 찢어진 페이지만 건네주고 있진 않았는지 한번 돌아보면 좋겠습니다.
메이커 에반의 RAG 전략 영상과 Anthropic 공식 블로그, 그리고 직접 AI 코딩 도구를 운영한 경험을 바탕으로 썼습니다.