
Claude Code를 사용하다 보면 프로젝트 루트에 CLAUDE.md 파일을 만들어 Claude에게 프로젝트 컨텍스트를 전달하게 됩니다. 처음에는 간단한 지침 몇 줄로 시작하지만, 프로젝트가 성장하고 학습한 내용이 쌓이면서 이 파일은 점점 비대해집니다.
이 글에서는 CLAUDE.md의 근본적인 한계를 분석하고, ChromaDB 기반 벡터 저장소로 이를 해결한 과정을 공유합니다.
CLAUDE.md의 문제점
선형적 구조의 한계
CLAUDE.md는 본질적으로 마크다운 문서입니다. 모든 지식이 순차적으로 나열되어 있어, Claude가 관련 정보를 찾으려면 전체 문서를 스캔해야 합니다. 문서가 길어질수록 Claude가 필요한 정보를 놓칠 확률이 높아집니다. 특히 과거에 기록한 "실수"나 "지식"은 문서 하단에 묻혀버리기 쉽습니다.
# CLAUDE.md (500줄이 넘어가면...)
## 프로젝트 개요
...
## 코딩 컨벤션
...
## 지난주 배운 것
- ChromaDB는 flat metadata만 지원함
- MCP 서버는 stdio로 통신함
...
## 저번 달 배운 것
...
컨텍스트 윈도우 낭비
CLAUDE.md의 모든 내용은 매 세션마다 컨텍스트에 로드됩니다. 현재 작업과 무관한 정보도 함께 로드되어 귀중한 컨텍스트 윈도우를 차지합니다.
크로스 프로젝트 학습 불가
프로젝트 A에서 배운 내용을 프로젝트 B에서 활용하려면? CLAUDE.md는 프로젝트별로 분리되어 있어 지식의 재사용이 어렵습니다.
검색 불가능
"지난번에 MCP 서버 만들 때 뭔가 주의사항이 있었는데..." 이런 막연한 기억을 CLAUDE.md에서 찾으려면 전체를 다시 읽어야 합니다.
어떻게 접근할 것인가?
CLAUDE.md가 길어지면 지침을 잘 못 찾습니다. 그렇다면 어떻게 해결할 수 있을까요?
파일 분리
가장 먼저 시도해볼 수 있는 방법입니다. 그러나 결국 "어느 파일에 뭐가 있더라?"를 기억해야 합니다. 파일이 많아지면 CLAUDE.md가 길어지는 것과 본질적으로 같은 문제가 발생합니다.
.claude/
├── coding-conventions.md
├── project-overview.md
├── learnings/
│ ├── 2026-01-chromadb.md
│ └── 2026-01-mcp.md
└── gotchas.md
태그 기반 분류
태그 체계를 설계하고 유지해야 합니다. "이거 어떤 태그로 찾지?"라는 새로운 고민이 생깁니다. 결국 태그를 기억해야 하는 부담이 추가됩니다.
<!-- tags: chromadb, metadata, gotcha -->
## ChromaDB는 flat metadata만 지원
...
키워드 검색
SQLite에 저장하고 LIKE '%keyword%'로 검색하는 방법도 괜찮습니다. 다만 "MCP 서버 만들 때 주의사항"을 찾고 싶은데, 실제 기록은 "Model Context Protocol의 stdio 통신"으로 되어 있다면? 키워드가 일치하지 않으면 찾을 수 없습니다.
"지난번에 뭔가 배웠는데... MCP 관련이었나? 서버 만들 때 뭔가 있었어"
이런 막연한 기억만으로도 관련 내용을 찾아주는 것. 정확한 키워드나 태그를 기억 필요 없이, 의미적으로 비슷한 내용을 찾아주는 검색이 필요합니다. 이것이 바로 벡터 검색(Semantic Search)이 하는 일입니다.
왜 벡터 DB인가?
의미 기반 검색을 하려면 텍스트를 벡터로 변환하고 유사도를 계산해야 합니다.
| 옵션 | 장점 | 단점 |
| OpenAI Embeddings + Pinecone | 고품질 임베딩 | API 비용, 외부 의존성 |
| Elasticsearch | 강력한 검색 | 무겁고 운영 부담 |
| FAISS | 빠름 | 영속성 없음, 직접 관리 필요 |
| ChromaDB | 로컬, 경량, 임베딩 내장 | 대규모에는 부적합 |
Claude Code 플러그인 용도로는
- 외부 API 의존 없이 로컬에서 동작해야 합니다 (오프라인, 비용, 개인정보)
- 설치/운영이 간단해야 합니다 (플러그인인데 DB 서버 띄우라고 할 순 없습니다)
- 임베딩 모델이 내장되어야 합니다 (별도 설정 없이 바로 사용)
ChromaDB는 이 조건을 모두 만족합니다. SQLite 기반이라 파일 하나로 영속성이 보장되고, pip install chromadb 하나면 임베딩까지 바로 사용 가능합니다. 개인 지식 저장소 규모(수백~수천 개 문서)에서는 충분한 성능입니다.
knowledge-store 플러그인 구현
knowledge-store/
├── .claude-plugin/plugin.json # 플러그인 메타데이터
├── .mcp.json # MCP 서버 설정
├── mcp-server/
│ └── server.py # ChromaDB MCP 서버 (핵심)
├── hooks/
│ ├── hooks.json
│ └── knowledge_preload.py # 세션 시작 시 자동 검색 트리거
├── skills/knowledge/SKILL.md # 자연어 명령 처리 가이드
├── CLAUDE.md # Claude에게 자동 검색 지시
└── README.md
Claude Code는 MCP(Model Context Protocol)를 통해 외부 도구와 통신합니다. ChromaDB를 MCP 서버로 래핑하여 Claude가 직접 지식을 저장/검색할 수 있게 했습니다.
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "chromadb>=0.5.0",
# "mcp>=1.0.0",
# ]
# ///
PEP 723 인라인 의존성을 사용해 uv run server.py 한 줄로 실행 가능합니다.
| 도구 | 설명 |
| add_knowledge | 지식 저장 (중복 체크 포함) |
| search_knowledge | 의미 기반 검색 |
| list_knowledge | 저장된 지식 목록 |
| delete_knowledge | 지식 삭제 |
| update_knowledge | 기존 지식 수정 |
중복 체크 구현
같은 내용을 여러 번 저장하면 지식 베이스가 오염됩니다. 저장 전 유사도 검사를 수행합니다. 80% 이상 유사한 내용이 있으면 경고하고, force: true 옵션으로 강제 저장할 수 있습니다.
def check_duplicates(content: str, threshold: float = 0.8) -> list[dict]:
results = collection.query(
query_texts=[content],
n_results=3,
include=["documents", "metadatas", "distances"]
)
duplicates = []
for doc, meta, doc_id, distance in zip(...):
# ChromaDB L2 distance를 similarity로 변환
similarity = 1 - (distance / 2)
if similarity >= threshold:
duplicates.append({
"id": doc_id,
"similarity": round(similarity * 100, 1),
...
})
return duplicates
데이터 스키마
{
"id": "learning:project-name:2026-02-01T10:30:00:abc12345",
"content": "## ChromaDB Metadata\n\n**Context**: ...\n**Learning**: ...",
"metadata": {
"type": "learning",
"project": "knowledge-store",
"date": "2026-02-01T10:30:00Z",
"tags": ["chromadb", "python"],
"source": "learning-extractor"
}
}
자동 검색 Hook
세션 시작 시 관련 지식을 자동으로 검색하도록 UserPromptSubmit hook을 구현했습니다
def main():
# 이미 프리로드 했으면 스킵 (하루에 한 번만)
if check_and_set_preloaded():
print(json.dumps({"continue": True}))
return
project = get_project_name()
reminder = {
"continue": True,
"message": f"""<knowledge-preload-reminder>
Before starting this task, search the knowledge base:
mcp__knowledge_store__search_knowledge(query="[task topic]",
filter={{"project": "{project}"}})
</knowledge-preload-reminder>"""
}
print(json.dumps(reminder))
자연어 명령 처리
MCP 도구를 직접 호출하는 것 외에도, 자연어로 지식을 관리할 수 있습니다. skills/knowledge/SKILL.md에 트리거 패턴을 정의해두었습니다. 그리고 CLAUDE.md에는 Claude가 복잡한 작업 전에 자동으로 지식을 검색하도록 지시가 포함되어 있습니다. 즉, 사용자가 "MCP 서버 만들어줘"라고 하면 Claude가 먼저 관련 지식을 검색하고, 과거에 배운 주의사항을 참고해서 작업을 수행합니다.
다른 Claude 플러그인과의 연계
저는 session-wrap(https://github.com/team-attention/plugins-for-claude-natives/)이라는 세션 종료 시 멀티 에이전트 분석을 수행하는 플러그인을 사용하고 있습니다. session-wrap은 실행 시 아래 5개의 에이전트가 병렬로 실행됩니다.
- doc-updater: 문서 업데이트 필요 사항 분석
- automation-scout: 자동화 가능한 패턴 탐지
- learning-extractor: 학습 내용 추출 (TIL 형식)
- followup-suggester: 후속 작업 제안
- duplicate-checker: 제안 중복 검증
이 때 learning-extractor의 출력을 knowledge-store에 자동 저장하도록 session-wrap의 워크플로우에 추가를 하였습니다. 이를 통해 /session-wrap:wrap 실행 시 학습 내용이 자동으로 벡터 DB에 저장되고, 다음 세션에서 의미 기반 검색으로 찾을 수 있습니다.
마치며
프로젝트 개요, 코딩 컨벤션, 빌드 방법처럼 항상 참고해야 하는 정보는 여전히 CLAUDE.md에 두는 게 맞습니다.
하지만 "지난번에 이거 하다가 삽질했는데..."같은 학습 내용은 성격이 다릅니다. 계속 쌓이고, 나중에 막연한 기억으로 찾아야 하고, 때로는 다른 프로젝트에서도 써먹고 싶습니다. 이런 종류의 지식은 벡터 DB에 넣어두고 필요할 때 검색하는 게 훨씬 효율적입니다.
결국 CLAUDE.md와 knowledge-store는 대체 관계가 아니라 보완 관계입니다. 고정된 컨텍스트는 CLAUDE.md에, 누적되는 학습은 knowledge-store에 저장되도록 역할을 나누면 CLAUDE.md가 비대해지는 문제도 해결되고, 과거에 배운 내용을 놓치는 일도 줄어듭니다.