위키가 git 으로 관리된다면, 팀끼리 작업하다가 충돌이라던가 병합이라던가 등등.. 위키 자체는 어떻게 관리해야할까?
뜬금없이 갑자기 생각난 웃긴 밈: 토르발즈 아저씨가 바이브코딩을 했다고??
https://github.com/torvalds/AudioNoise/issues?q=label%3AGarbage



리누스 토르발즈: 응 ~ 원래도 휴-먼한테 시키던거였어 ㅋㅋ

(대충 웃긴 짤 주저리 끝)
AI 에이전트 메모리, 파일 더미로는 왜 무너지는가
프롤로그에서 인용했던 글을 다시 읽어보겠습니다.
지금 많은 AI 에이전트 메모리는 사실 메모리가 아니라, 긴 프롬프트를 RAM 처럼 쓰는 것에 가깝다. 모든 히스토리와 결정 로그를 마크다운에 몰아넣고 장기 메모리라고 부르면 2주 안에 무너진다. 같은 사실의 버전이 여러 개 생기고, 오래된 선호와 어제 결정이 같은 무게로 섞이고, 호출할 때마다 문서를 전부 컨텍스트에 넣으니 느려진다. 진짜 메모리는 그래프와 노드, 임베딩, 순회, 랭킹, 감쇠, 모순 해결을 가져야 한다.
다시 초심으로 돌아가서 떠올려봅시다.
문제의식과 AYi_AInotes 의 X 글 에서 공감하면서였어요.
여기서 “파일 더미를 메모리라고 착각하면 무너진다”는 문제의식을 잡았습니다.
그다음 이 글을 소개한 aiedge_ 의 3-layer memory 아티클을 읽었고,
거기서 다시 Karpathy 의 LLM knowledge base gist로 넘어갔습니다.
그 다음 저는 사실 간단한 작업을 한 것뿐입니다.
@AYi_AInotes 글에서 문제를 잡고, aiedge_ 글을 통해 Karpathy 의 wiki / knowledge-base mega prompt 를 알게 됐고, 그 내용을 거의 그대로 Claude Code 에 붙여 넣었습니다. 여기에 이미 쌓여 있던 제 레포의 docs/ 전체를 같이 참고시키고, /plan 모드로 계속 질의했습니다. 저는 “이 방향은 맞다 / 이건 과하다 / 이 기술은 지금은 보류” 같은 결정과 trade-off만 잡았고, 실제 구조화와 문서·hook·command 설계는 대부분 에이전트와 논의하면서 진행했습니다. 전체 셋업은 대략 2시간 정도 걸렸습니다.
4편 시리즈, 지금은 어디
| 본편 1편 | 프롬프트만으로는 안 되는 순간, 문서 메모리는 왜 무너지는가 | 답이 오락가락하는 원인 진단 |
| 본편 2편 | 위키 관리도 자동화하고 사람의 실수를 막자! karpathy llm wiki 위에 얹은 자동화 시스템 | 폴더 구조와 frontmatter 로 마찰을 정리한 과정 |
| 본편 3편 (지금 이 글) | AI 에이전트 메모리, 파일 더미로는 왜 무너지는가 | git 위에서 오래 가는 운영 구조 |
정리하면
마크다운만으로는 메모리가 아닙니다.
하지만 마크다운을 그냥 컨텍스트에 통째로 넣지 않고, token 과 context 를 나눠 운영하면 꽤 실용적인 팀 메모리가 됩니다.
token: 매 세션에 반드시 넣을 압축 문서와, 필요할 때만 읽을 원본 문서를 나눈다.context: 선호, 규칙, 현재 프로젝트 상태, 공식 결정을 서로 다른 파일에 나눠 저장한다.operation: hook, slash command, git, Obsidian, 백업 루틴으로 이 구조가 계속 살아 있게 한다.
완전한 그래프 메모리 시스템은 아닙니다. 중복 제거, 감쇠, 랭킹, 모순 자동 해결도 아직 제한적입니다. 그래도 “긴 프롬프트를 매번 붙여 넣는 방식”보다는 훨씬 덜 흔들립니다.
처음 읽는 분이라면 이 글에서 쓰는 단어 먼저 정리하고 갈게요.
token 은 LLM 에게 실제로 읽히는 글자 비용이에요. Claude Code 가 파일을 읽거나 명령 출력을 보면 그 내용도 컨텍스트 윈도우에 들어가는데, 이 공간이 무한하지 않거든요.
context 는 지금 답변을 만들 때 참고하는 주변 정보입니다. 프로젝트 설명, 대화 기록, 읽은 파일, 명령 출력이 모두 context 죠.
operation 은 이 구조가 계속 살아 있게 만들어 주는 운영 장치예요. hook, slash command, git hook, 백업, Obsidian 점검판이 여기에 들어갑니다.
이 세 단어로 나눠야 하는 이유는 간단합니다. 실패의 대부분은 "문서를 어디에 저장할까"에서 생기지 않습니다. 무엇을 매번 읽히고, 무엇은 필요할 때만 찾게 하고, 무엇은 사람이 주기적으로 점검할지 — 이 구분이 안 잡혀 있어서 생기거든요.
1. 실제로 만든 것은 세 영역입니다
이 프로젝트에는 비슷해 보이지만 역할이 다른 세 영역이 있습니다.
| 영역 | 위치 | 역할 |
|---|---|---|
| Claude Code 하네스 | .claude/ |
hook, slash command, 설정 |
| Layer 2 마스터 폴더 | docs/_claude/ |
Claude 가 읽는 규칙, 메모리, 압축 컨텍스트, 운영 표준 |
| 위키 본체 | docs/ 전체 |
전략, 스펙, 디자인, 데이터 모델, 의사결정 원본 |
세 영역이 어떻게 서로 호출하는지 한 그림으로 보면 이렇습니다.

세 영역의 관계는 두 흐름으로 나뉩니다. 위쪽 실선 (.claude/ → docs/_claude/ → docs/) 은 하네스가 마스터 폴더를 거쳐 위키 본체에 접근하는 흐름이고, 아래쪽 점선은 마스터 폴더가 하네스의 운영 룰과 위키 본체의 frontmatter 표준을 정의하는 흐름이에요. 마스터 폴더가 양쪽을 잇는 가운데 지점이고요.
이 셋을 구분하는 게 중요합니다. 처음에는 전부 “문서”로 보입니다. 하지만 실제로는 다릅니다.
.claude/ 는 기계 장치입니다. Claude Code 가 언제 어떤 스크립트를 실행할지 정합니다. 예를 들어 세션 시작 때 Context.md 를 읽히고, 파일을 수정한 뒤에는 코드 그래프를 갱신합니다.
docs/_claude/ 는 에이전트용 작업 메모리이자 운영 표준 폴더입니다. 사람이 매번 설명하기 귀찮은 규칙과 프로젝트 요약을 넣고, frontmatter·wiki 운영·백업·점검 규칙도 같이 둡니다.
docs/ 전체는 위키 본체입니다. 전략 문서, PRD, 결정 로그, 디자인 문서가 여기 있습니다. 이 전체를 매 세션 컨텍스트에 넣지는 않습니다. 필요할 때 찾아 읽습니다.
이 구분이 없으면 곧바로 “문서가 많아졌으니 다 넣자”가 돼요. 그러면 X 원문이 말하던 문제가 그대로 옵니다. 컨텍스트는 무거워지고, 오래된 문서와 최신 결정이 같은 무게로 섞이거든요.
좀 더 개발자식으로 비유하면 이런 식이에요.
.claude/는 CI 설정이나 dev script 에 가깝습니다. 사람이 매번 기억하지 않아도 특정 타이밍에 자동으로 실행됩니다.docs/_claude/는 프로젝트의 짧은 runbook 에 가깝습니다. "이 프로젝트를 이해하려면 먼저 이것부터 보라"는 압축 브리핑입니다.docs/는 원본 문서 저장소입니다. PRD, 계획, 결정, 디자인 문서가 모두 여기에 있습니다.
셋을 섞으면 유지보수가 어려워져요. 예를 들어 Context.md 에 너무 긴 PRD 를 넣으면 매 세션 토큰이 낭비됩니다. 반대로 중요한 현재 우선순위를 깊은 PRD 어딘가에만 묻어 두면 Claude 는 새 세션에서 그 사실을 모르고요. 그래서 압축본과 원본을 분리했습니다.
2. token: 매 세션에 전부 넣지 않습니다
Claude Code 기준으로 가장 중요한 파일은 이겁니다.
.claude/hooks/load-context.sh
실제 내용은 거의 이 정도입니다.
#!/bin/bash
set -e
REPO="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
CTX="$REPO/docs/_claude/Context.md"
if [[ -f "$CTX" ]]; then
echo "=== docs/_claude/Context.md (project context — read this first) ==="
cat "$CTX"
echo ""
echo "=== /Context.md ==="
fi
동작은 어렵지 않습니다. 세션이 시작되면 docs/_claude/Context.md 만 stdout 으로 출력해요.
여기서 기대는 Claude Code 공식 문서는 Hooks reference 입니다. 문서 기준으로 SessionStart 는 새 세션이 시작되거나 재개될 때 실행되고, 이 이벤트에서 stdout 으로 출력한 텍스트는 Claude 가 볼 수 있는 컨텍스트에 들어갑니다. 그래서 이 스크립트는 “새 대화를 열 때마다 프로젝트 요약 파일을 자동으로 붙여 넣는 장치”가 됩니다.
공식 문서 원문으로 보면 이 부분입니다.
"SessionStart hooks allow you to load in context at the start of a session."
(번역:
SessionStarthook 은 세션 시작 시 추가 컨텍스트를 로드할 수 있게 해 줍니다.)
그리고 단순 stdout 방식도 문서에 명시되어 있습니다.
"for UserPromptSubmit and SessionStart, stdout is added to the context."
(번역:
UserPromptSubmit과SessionStart에서는 stdout 이 컨텍스트에 추가됩니다.)
공식 문서에는 더 명시적인 JSON 방식도 있습니다. SessionStart hook 이 hookSpecificOutput.additionalContext 를 반환하면 그 문자열이 추가 컨텍스트로 들어갑니다. 이 프로젝트는 단순한 shell script 로 시작했기 때문에 stdout 방식을 썼습니다. 팀 규모가 커지거나 hook 출력이 여러 개 섞이기 시작하면 JSON 방식이 더 안전합니다. 예를 들면 아래처럼 만들 수 있습니다.
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "여기에 Context.md 요약이 들어갑니다."
}
}
지금은 단순함을 택했습니다. 이 hook 의 목적은 추론이 아니라 파일 하나를 읽혀 주는 것이기 때문입니다.
.claude/settings.json 에서는 이렇게 연결합니다.
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "code-review-graph status",
"timeout": 10
},
{
"type": "command",
"command": ".claude/hooks/memory-health.sh",
"timeout": 5
},
{
"type": "command",
"command": ".claude/hooks/load-context.sh",
"timeout": 5
}
]
}
]
}
}
이 settings.json + load-context.sh 가 깔린 상태에서 새 세션이 시작될 때 어떤 일이 일어나는지 한 그림으로 보면 이렇습니다. 프롤로그에서 한 번 보여드린 그림인데, 여기서 hook 코드와 같이 보면 의미가 더 또렷해져요.

load-context.sh 가 SessionStart 시점에 Context.md 를 stdout 으로 출력하고, 그 출력이 컨텍스트로 들어가요. CLAUDE.md 의 안내는 따로 들어가서 Instructions.md / Memory.md 를 첫 프롬프트 이후 read 하게 트리거합니다. 자동 로드 두 번 (CLAUDE.md / SessionStart hook) 과 반자동 read 한 번 (CLAUDE.md 안내 → Instructions.md / Memory.md) 이 한 세션 시작에 모두 일어나는 셈이에요.
(repo root)/CLAUDE.md
이 "안내" 가 어떻게 생겼는지 궁금하실 것 같아서, 현재 이 프로젝트의 루트 CLAUDE.md 윗부분을 그대로 옮겨 둡니다.
## Layer 2 컨텍스트 (제품·전략·메모리)
**제품/전략/팀 관련 질문은 답하기 전에 반드시 다음 3개 파일을 먼저 읽어라:**
- [`docs/_claude/Instructions.md`](./docs/_claude/Instructions.md) — Claude 행동 룰 (Who/What/Rules/Outputs)
- [`docs/_claude/Context.md`](./docs/_claude/Context.md) — 프로젝트·팀·스택·결정 single source
- [`docs/_claude/Memory.md`](./docs/_claude/Memory.md) — 사용자 preferences·corrections 러닝 로그
**자동 메모리 갱신**: 사용자가 "기억해 / 잊지마 / 이제부터 / 하지마 / 이렇게 해줘" 라고 말하면
[`docs/_claude/Memory.md`](./docs/_claude/Memory.md) 즉시 업데이트 (프로토콜은 Instructions.md § Memory Update Protocol).
**충돌 우선순위**: 코드 안전 룰(이 파일의 gstack · Supabase 마이그) > Layer 2 룰.
Context.md 는 hook 이 매 세션 자동으로 inject 해 주고, Instructions.md 와 Memory.md 는 위 안내가 트리거가 돼서 Claude 가 첫 답변을 만들기 전에 read 합니다. 두 경로가 합쳐져 한 세션의 base context 가 채워지는 식이에요.
제가 일부러 비운 부분이 하나 있어요. docs/ 전체를 읽히지 않는다는 점입니다. 매 세션에 자동으로 들어가는 건 압축본인 Context.md 하나뿐이고요.
이 프로젝트의 Context.md 는 이런 내용을 담습니다.
- 제품 한 줄 설명
- 현재 우선순위
- 팀원과 역할
- 기술 스택
- 중요한 D 번호 결정 요약
- 용어 사전의 빠른 참조
- 절대 하지 않는 것
반대로 docs/strategy/08_mogui_prd.md, docs/strategy/14_decision_log.md, docs/mobile-design/* 같은 원본 문서는 매번 넣지 않습니다. 질문이 들어왔을 때 필요한 부분만 읽습니다.
구조는 이렇습니다.
매 세션 자동 주입:
docs/_claude/Context.md
답변 전 필요하면 읽음:
docs/_claude/Instructions.md
docs/_claude/Memory.md
docs/strategy/14_decision_log.md
docs/strategy/*
docs/mobile-design/*
docs/data-model/*
token 축은 여기서부터입니다.
모든 지식을 매번 넣는 게 아니에요. 항상 필요한 압축 컨텍스트 만 넣고, 나머지는 검색해서 읽게 합니다.
이 선택에는 비용이 있습니다. Context.md 가 길어지면 모든 세션이 무거워집니다. 반대로 너무 짧으면 Claude 가 현재 프로젝트를 제대로 못 잡습니다. 그래서 Context.md 에는 "모든 설명"이 아니라 "다음 문서를 찾아갈 수 있는 지도"를 넣는 편이 낫습니다.
제가 잡은 기준은 이렇습니다.
| 현재 sprint 와 우선순위 | 과거 완료 작업 상세 로그 |
| 최신 결정의 D 번호 요약 | 모든 결정 로그 본문 |
| 하지 않기로 한 큰 방향 | 회의 중 나온 임시 아이디어 전체 |
| 용어 사전의 핵심 표현 | 긴 인터뷰 원문 |
새 팀원에게 처음 10분 동안 설명할 내용이면 Context.md 후보입니다. 1시간짜리 온보딩 때 볼 자료면 원본 docs/ 에 두는 편이 낫습니다.
3. context: 바뀌는 상태를 네 종류로 나눕니다
문서 메모리가 무너지는 이유 중 하나는 서로 다른 성격의 정보가 한 파일에 섞이기 때문입니다.
예를 들어 이런 것들이 한 문서에 같이 있으면 곧 엉킵니다.
- “답변은 한국어로 짧게 해줘”
- “현재 MVP 우선순위는 Tweet to Swatch 흐름이다”
- “D52 에서 color_family 텍스트 매칭은 검색 스택 밖으로 뺐다”
- “이 팀은 Obsidian Smart Connections 를 쓴다”
- “이전에는 Atlas 가 코어 전용이었지만 D40 에서 Explore 내부 토글로 바뀌었다”
전부 중요한 정보지만 수명이 다릅니다. 그래서 docs/_claude/ 는 상태를 나눕니다.
| 파일 | 담는 것 | 수명 |
|---|---|---|
Instructions.md |
Claude 행동 규칙, 문서 반영 프로토콜 | 길다 |
Memory.md |
사용자 선호, correction, 반복 패턴 | 중간 |
Context.md |
현재 프로젝트 상태의 압축본 | 짧거나 중간 |
Archive-Guide.md |
백업과 복구 절차 | 길다 |
Frontmatter-Standard.md |
문서 상태·타입·related 표준, D 번호 충돌 방지 | 길다 |
LLM-Wiki-Pattern.md |
Karpathy식 ingest/query/lint 운영 모델 | 길다 |
Obsidian-Setup.md |
vault 설정, 플러그인, 사람용 탐색 도구 | 길다 |
Maintenance.md |
Layer 2 점검 queryboard | 중간 |
README.md |
_claude/ 폴더 입구 |
길다 |
14_decision_log.md |
공식 결정, 번복 이력 | append-only |
여기서 14_decision_log.md 는 docs/_claude/ 안에 있지 않고 docs/strategy/ 에 있습니다. 이유가 있습니다. 이건 Claude 의 작업 메모리가 아니라 제품의 공식 결정 로그입니다.
충돌하면 우선순위도 다릅니다.
최신 결정 로그 > Context.md 요약 > Memory.md 작업 메모리
Context.md 는 편의를 위한 압축본입니다. 틀릴 수 있습니다. Memory.md 도 Claude 가 자동 갱신하는 파일이라 틀릴 수 있습니다. 최종 기준은 docs/strategy/14_decision_log.md 입니다.
이 구분만 있어도 “지난달 선호와 어제 결정이 같은 무게로 섞이는 문제”가 꽤 줄어듭니다.
이 구분도 Claude Code 공식 Memory 문서와 맞닿아 있습니다. 공식 문서는 CLAUDE.md 와 auto memory 를 세션 사이 지식을 이어 주는 두 장치로 설명하지만, 둘 다 매 세션 컨텍스트로 로드되는 자료입니다. 그래서 이 글에서는 CLAUDE.md 와 Memory.md 를 “진실의 원장”으로 두지 않고, 사람이 관리하는 decision log 를 따로 둡니다.
여기서 Memory.md 를 너무 신뢰하면 다시 문제가 생깁니다. Memory.md 는 선호와 반복 패턴을 적는 파일입니다. 예를 들어 "답변은 짧게", "기술 문서에는 이모지를 쓰지 않기", "같은 지적이 두 번 나오면 Patterns 로 승격" 같은 정보가 들어갑니다. 반면 제품 정책, DB 스키마, 기능 범위는 Memory.md 에 두면 안 됩니다. 그런 내용은 공식 문서나 decision log 로 가야 합니다.
이 구분이 팀 작업에서 중요합니다. 개인 선호와 제품 결정이 한 파일에 섞이면, 나중에 누가 봐도 무엇이 팀의 공식 결정인지 알기 어렵습니다.
4. decision log 는 Dtable 처럼 씁니다
팀원이 최근에 작업한 커밋들을 보면 docs/strategy/*plan* 문서가 계속 추가되고, 그 옆에서 docs/strategy/14_decision_log.md 가 갱신됩니다.
처음엔 그냥 문서를 많이 만든 것처럼 보였습니다. 다시 보니 패턴이 있었습니다.
구체적으로 실제 (PR 커밋을 살펴볼까요.)
살펴보니 코드 쪽은 어드민 칩 입력 UI, 카테고리 트리, merge helper, dead column drop 마이그레이션을 넣었습니다.
동시에 문서 쪽은 다음처럼 움직였습니다.
| 파일 | 실제로 일어난 일 |
|---|---|
docs/strategy/27_plan_...admin_분류칩_입력UI.md |
status: stub 에서 completed 로 바뀌고 Phase 1~4 완료 내역이 들어감 |
docs/strategy/14_decision_log.md |
D53, D54, D55 결정이 추가됨 |
docs/strategy/30_plan_...shade_detail...md |
4축 dot scatter 위젯을 별도 후속 작업으로 분리한 새 stub 생성 |
docs/strategy/28_plan..., 29_plan... |
related: 에 30번 plan 연결 추가 |
docs/_claude/Context.md |
현재 단계 요약에 “27_plan completed”, “30_plan stub” 이 반영됨 |
이 흐름이 Dtable 방식의 실제 사용입니다.
- 계획 문서는
docs/strategy/27_plan_...md,28_plan_...md,29_plan_...md,30_plan_...md처럼 기능 단위로 남긴다. - 공식 결정은
14_decision_log.md에 D 번호로 남긴다. - 다음 작업은 새 plan 으로 분리한다.
Context.md는 그중 현재 세션에 필요한 요약만 가져간다.
두 모양이 한 자리에서 같이 굴러가는 게 핵심이에요.
결정 한 줄로 정리되는 건 14_decision_log.md 에 D 번호로 append 합니다 (D53, D54, D55 처럼 한 commit 에 여러 D 가 같이 들어가도 돼요). 여러 phase 가 있는 큰 작업은 별도 plan 문서로 새로 만들고요 (27_plan_2026-05-02_admin_분류칩_입력UI.md, 30_plan_2026-05-03_shade_detail_시트통합_4축위젯.md 처럼 날짜와 주제가 파일 이름에 들어갑니다).
어디로 갈지 기준은 어렵지 않습니다. 한 commit 안에서 끝나는 결정이면 D 번호. 여러 commit, 며칠, 또는 phase 단위로 진행되는 작업이면 plan 문서. plan 본문에는 phase 별 진행 상황 + frontmatter status (stub / in-progress / completed) 가 명시되고, 14_decision_log.md 의 D 번호는 그 plan 안에서 결정된 핵심 한 마디를 인덱싱하는 식이에요.
즉 plan 문서는 append-only 가 아니라 새 파일로 더해집니다.
16_plan, 21_plan, 22_plan, 23_plan, 25_plan, 26_plan, 27_plan, 28_plan, 29_plan, 30_plan 식으로 번호 prefix 가 그대로 인덱스 역할을 합니다. 14_decision_log 처럼 sentinel 주석으로 잠금을 만들 필요가 없거든요. 두 worktree 가 동시에 새 plan 을 만들면 파일 이름 (날짜 + 주제) 이 보통 다르고, 같아도 git 이 이미 충돌을 띄웁니다.
docs/strategy/14_decision_log.md 는 일종의 Dtable 입니다. 테이블로 인덱스를 보고, 본문에서 D 번호별 상세 근거를 찾습니다.
구조는 대략 이렇습니다.
## 결정 인덱스
| ID | 날짜 | 주제 | 상태 |
| --- | ---------- | ---------------------------------------- | ---- |
| D53 | 2026-05-03 | Atlas 코어 4축 활용 | 확정 |
| D54 | 2026-05-03 | color_family per-shade <= 2 | 확정 |
| D55 | 2026-05-03 | shades.saturation / brightness 컬럼 drop | 확정 |
<!-- D-claim-pointer: D56 @commit: 42ab83a @branch: dev @by: Dorito -->
---
## D53 · Atlas 코어 4축 활용
...
D-claim-pointer 는 다음에 사용할 D 번호예요. 인덱스 마지막이 D55 면 포인터는 D56 이어야 합니다. 그 옆 @commit / @branch / @by 세 좌표는 사람이 손으로 채우는 게 아니라 pre-commit hook 이 자동으로 stamp 해 줍니다. 이 세 좌표가 lock primitive 의 핵심이라 §4.1 에서 따로 풀게요.
새 결정을 추가할 때는 세 곳을 한 commit 에 같이 바꿉니다.
- 인덱스 표에
D56행 추가 - 본문에
## D56 · 제목섹션 추가 D-claim-pointer: D56을D57로 증가 (나머지 좌표는 hook 이 알아서)
4.1 sentinel 한 줄이 왜 lock 이 되는가
멀티 에이전트 / worktree (한 레포에서 브랜치별로 동시에 일할 수 있게 만든 별도 작업 폴더) 환경을 떠올려 봅시다. Claude Code 를 worktree 두 개로 동시에 돌리면, 두 에이전트가 같은 D 번호를 동시에 claim 하는 일이 생깁니다.
| B worktree | D56 = "RLS hardening 2 차" |
각 worktree 는 본인 브랜치에서 인덱스 표 마지막에 D56 행을 한 줄 추가합니다. 머지할 때 git 입장에서는 A 가 추가한 줄과 B 가 추가한 줄이 서로 다른 줄 번호에 들어 있으니 둘 다 보존하면서 자연스럽게 머지해 버려요. 충돌이 안 나는 거죠. 결과는 D56 이 두 번 등장하는 결정 기록. 사람이 한 주 후에 발견하면 어느 게 진짜 D56 인지 git log 를 한참 뒤져야 하고요. 본편 1편의 14개 충돌 문서와 같은 패턴이 결정 기록 안에서 또 일어나는 셈이에요. 이걸 silent collision 이라고 부릅니다.
해결은 단순해요. 인덱스 표 row 가 아니라, sentinel 주석 자체를 lock primitive 로 쓰는 겁니다. 인덱스 row 는 위치가 다르니 git 이 둘 다 보존하지만, sentinel 은 한 줄에 모여 있고 두 worktree 가 모두 D56 → D57 로 +1 시키려 들거든요. 머지 단계에서 git 이 정확히 그 줄에서 conflict 를 띄워 줍니다.
나쁜 경우 (인덱스 표 row 만 추가):
A: 표 끝에 D56 추가
B: 표 끝에 D56 추가
git merge: 둘 다 보존
결과: D56 중복 (silent collision)
나은 경우 (sentinel 도 같이 +1):
A: D-claim-pointer D56 -> D57
B: D-claim-pointer D56 -> D57
같은 줄을 건드리므로 git conflict
사람이 한쪽 D56, 한쪽 D57 로 강제 결정
같은 흐름을 그림으로 보면 두 길이 더 또렷해집니다.

왼쪽 상자 (안 됨) 는 충돌이 silent 로 묻혀 D 번호 두 개가 한 결정 로그에 살아 있고, 오른쪽 상자 (권장) 는 git merge 단계에서 사람이 한 번 강제로 해결하는 지점을 만들어 둡니다. sentinel 주석을 같이 건드리는 게 그 강제 지점을 만드는 트릭이에요.
4.2 D 번호만 +1 으로는 못 막는 한 가지 케이스
여기까지 와도 구멍이 하나 남습니다. 같은 base 에서 둘이 완전히 똑같은 한 줄 변경 (D56 → D57) 을 만들면, 두 변경이 텍스트로 동일하니 git 이 일부 머지 전략에서 충돌 없이 그냥 합쳐 버려요. D 번호만으로는 lock 이 안 잡히는 케이스가 바로 이거예요.
그래서 sentinel 주석에 세 좌표를 더 붙였습니다.
<!-- D-claim-pointer: D56 @commit: 42ab83a @branch: dev @by: Dorito -->
@commit: 그 commit 의 HEAD short hash@branch: 그 commit 이 만들어진 브랜치 이름@by:git config user.name(committer)
셋 중 하나라도 다르면 두 worktree 의 sentinel 줄이 텍스트로 서로 갈립니다. 같은 D 번호를 claim 했더라도 commit hash 는 거의 항상 다르고 (같은 base 라도 staged diff 가 다르면 hash 가 갈리거든요), 다른 worktree 라면 branch 도 다르고, 페어 작업이 아니면 committer 까지 다릅니다. 셋이 모두 일치하는 극단 케이스 (같은 사람이 같은 브랜치에서 똑같은 diff 를 두 번) 만 통과하는데, 그건 사실상 같은 작업이라 충돌이 아니라 중복이에요.
이 좌표들을 사람이 손으로 채우면 또 까먹어요 (저도 한 번 그랬고요). 그래서 .githooks/pre-commit 이 docs/strategy/14_decision_log.md 가 staged 되어 있을 때 sentinel 줄을 가로채서 자동으로 stamp 하게 만들었습니다.
# .githooks/pre-commit (요약)
HEAD_HASH=$(git rev-parse --short=7 HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
COMMITTER=$(git config user.name | tr ' ' '_')
# sentinel 줄을 정규식으로 잡아서 세 좌표를 덮어씀
<!-- D-claim-pointer: D{n} -->
→ <!-- D-claim-pointer: D{n} @commit: {hash} @branch: {name} @by: {committer} -->
참고: 마크다운 raw 모드로 봐야 sentinel 주석이 보입니다.
사람은 D{n} → D{n+1} 한 곳만 건드리고, 나머지는 hook 이 채워 줍니다. 까먹을 수가 없어요. commit 을 막는 게 아니라, commit 자체가 sentinel 을 다시 써 주거든요.
4.3 sync 검증 — sentinel 이 실제로 인덱스를 따라가는지
여기까지 해도 사고가 한 번 났습니다. 어느 날 D53·D54·D55 를 한 commit 에 추가하면서 인덱스 표는 D55 까지 갔는데, sentinel 은 D-claim-pointer: D53 으로 남아 있던 거예요. 머지 직전에 발견했고, 잠깐 panic. 덕분에 sentinel 의 의미가 더 또렷해졌습니다 — 인덱스가 한참 앞서 있는데 포인터가 뒤에 남아 있으면, 다음 사람이 D53 을 또 claim 하니까요. 같은 silent collision.
그래서 hook 에 두 번째 검사를 얹었습니다. 인덱스 마지막 D 번호 + 1 == sentinel D 번호가 아니면 commit 자체를 차단하는 식으로요.
# .githooks/pre-commit (요약)
SENTINEL_D=$(grep -oE 'D-claim-pointer: D[0-9]+' "$DECISION_LOG" | grep -oE '[0-9]+' | head -1)
LAST_D=$(grep -oE '^\| D[0-9]+' "$DECISION_LOG" | grep -oE '[0-9]+' | sort -n | tail -1)
EXPECTED_D=$((LAST_D + 1))
if [[ "$SENTINEL_D" != "$EXPECTED_D" ]]; then
echo "[Sentinel sync 차단] sentinel = D${SENTINEL_D}, 마지막 = D${LAST_D}, 기대값 = D${EXPECTED_D}"
exit 1
fi
sentinel 이 인덱스를 따라가지 못하면 commit 이 안 됩니다. 자동 stamp 와 sync 검증을 한 hook 이 같이 처리해 주는 거예요.
한 마디로요. 주석 한 라인이 lock 이 되고, 추가 인프라는 0. CI 도, 별도 DB 도, 외부 서비스도 안 깝니다. git native 잠금인 셈이에요. commit 메시지 패턴도 자연스럽게 굳습니다. sentinel 주석, 인덱스 표 row, 본문 섹션 — 세 곳이 한 commit 에 같이 들어가니까요.
docs(decision): D56 — Atlas 비교발색 hover 단방향 vs 양방향
- D-claim-pointer: D56 → D57 (hook 이 @commit/@branch/@by 자동 stamp)
- 인덱스 표 D56 행 추가
- 본문 ## D56 섹션
한 commit 안에 결정이 두 개 섞이면 sentinel 이 D56 → D58 처럼 두 번 +1 되니, sync 검사에서 LAST_D = 57, SENTINEL = 58 로 맞아떨어져 통과합니다. 반대로 결정을 두 개 추가하면서 sentinel 은 한 번만 +1 했다면 LAST_D = 57, SENTINEL = 57 이 되고 EXPECTED = 58 과 어긋나서 차단되고요. commit 단위로 정합성이 잡히는 거죠. 양식 강제 없이요.
완벽한 분산 락은 아닙니다. git 은 데이터베이스가 아니고, 마크다운 주석 한 줄도 분산 락은 아니에요. 그래도 작은 팀이 인프라 없이 손에 쥘 수 있는 가장 싼 잠금 장치입니다. 중요한 건 "문서에 규칙을 적었다" 에서 끝나지 않고, commit 시점에 한 번 더 강제하게 만들었다는 점입니다.
5. 파일 메모리를 운영 시스템으로 만드는 장치
여기까지는 "파일을 어떻게 나눴는가" 였습니다. 파일만 나누면 시간이 지나면서 다시 stale 됩니다. 그래서 이 프로젝트는 .claude/ 아래에 운영 장치를 둡니다.
여기서부터가 하네스 엔지니어링 영역입니다. 다만 제가 진짜 말하고 싶은 건 하네스 자체가 아니라, 메모리 구조를 지키기 위해 하네스를 어디까지 썼는가예요. 위 2장에서 본 SessionStart hook 위에, 다른 두 시점 (PreToolUse / PostToolUse) 과 별도 git pre-commit 까지 합쳐서 hook 이 도는 곳이 모두 네 군데입니다.
| hook | matcher | 무엇 | 왜 |
|---|---|---|---|
| SessionStart × 4 | (any) | 코드 그래프 status + setup-check + memory-health + Context.md inject | 백지 컨텍스트를 매 세션 미리 채워서 사람이 brief 안 해도 + setup 누락 자동 감지 |
| PreToolUse | Write|Edit |
이모지 발견 시 permissionDecision: deny JSON 출력 |
Memory.md Corrections 의 룰을 코드 단계에서 강제 |
| PostToolUse | Edit|Write|Bash |
MCP 서버 code-review-graph update --skip-flows 증분 갱신 |
다음 query 가 stale 된 그래프를 안 쓰게 |
| (별도) git pre-commit | docs/*.md staged |
frontmatter updated: 자동 bump + decision_log sentinel sync 검증 + sentinel 자동 stamp (@commit/@branch/@by) |
사람이 깜빡해도 메타데이터가 거짓말 안 하고 D 번호 충돌이 commit 단계에서 잡힘 |
중요한 것은 모든 자동화를 다 붙이지 않았다는 점입니다. 예를 들어 "코드가 바뀌면 관련 docs 를 자동으로 수정" 같은 hook 은 만들지 않았습니다. 노이즈가 너무 커질 수 있기 때문입니다. 대신 자동화는 발견과 차단에 집중하고, 의미 있는 문서 수정은 /docs-sync 처럼 계획을 먼저 세우게 했습니다.
실제 저의 사이드플젝에서 /docs-sync 를 돌려보니 잘 동작하는 모습입니다.

→ (깃헙 세팅 귀찮아가지고; dev 브랜치 룰 관련 Memory.md 에다가 브랜치 컨벤션을 feature/기능-* 라고 적고, feat 이 컨벤션이다. 수정하자. (네...깃헙 브랜치 룰 세팅할게요...) 이렇게 제안하니까 너무나도. 잘 동작하는 모습입니다.
<레포>/claude/commands/docs-sync.md 에 이렇게 적혀있어요. 이건 확실하게 프롬프트 공유드립니다 커스텀해서 쓰세욥
---
description: docs 문서 정합성 점검 및 related/status/Context/decision 반영 후보 제안
---
# /docs-sync
새 피드백, QA 결과, 회의록, PM 에이전트 산출물, 문서 초안을 `docs/` 체계에 반영하기 전 정합성을 점검합니다.
## 원칙
- 바로 수정하지 말고 먼저 계획을 제안합니다.
- `related:`, `status:`, `Context.md`, `Memory.md`, `strategy/14_decision_log.md` 변경은 승인 후 반영합니다.
- Obsidian backlink 는 이미 있는 `[[링크]]`를 보여주는 기능일 뿐이므로, 필요한 링크는 직접 제안합니다.
- Smart Connections 추천은 참고만 하고 최종 관계로 확정하지 않습니다.
## 실행 단계
1. 입력 내용의 성격 분류
- 제품 가설 / 전략 / PRD → `docs/strategy/`
- QA / 운영 시나리오 / 에러 케이스 → `docs/spec/`
- 화면 흐름 / UX / 디자인 정책 → `docs/mobile-design/`
- 데이터 모델 / 이벤트 / 스키마 → `docs/data-model/`
- 반복 협업 규칙 → `docs/_claude/Memory.md`
2. 반영 방식 제안
- 새 문서 생성이 나은지
- 기존 문서에 합치는 게 나은지
- 단순 메모로 남기고 결정 로그에는 올리지 않는 게 나은지
3. 문서 메타데이터 제안
- `type`
- `status`
- `tags`
- `updated`
- `related`
- 필요 시 `replaced_by`, `replaces`, `decision`, `owner`
4. 관련 문서 후보 찾기
- 본문 링크 후보: `[[...]]`
- frontmatter `related:` 후보
- 충돌 가능 문서
- 낡았거나 대체가 필요한 문서
5. 상위 컨텍스트 반영 여부 판단
- `docs/_claude/Context.md` 갱신 필요 여부
- `docs/_claude/Memory.md` 갱신 필요 여부
- `docs/strategy/14_decision_log.md` D번호 추가 필요 여부
6. 계획 출력
- 수정할 파일
- 만들 파일
- 바꾸지 않을 파일
- 사람 승인 필요한 항목
7. 사용자 승인 후에만 파일 수정
## 출력 형식
```markdown
## Docs Sync Plan
### 분류
- 종류:
- 권장 위치:
- 새 문서 여부:
### 수정 후보
| 파일 | 작업 | 이유 | 승인 필요 |
|---|---|---|---|
### related / 링크 후보
| 대상 파일 | related 후보 | 본문 링크 후보 | 이유 |
|---|---|---|---|
### status 후보
| 파일 | 현재 | 제안 | 이유 |
|---|---|---|---|
### 상위 컨텍스트
- Context.md:
- Memory.md:
- 결정 로그:
### 다음 액션
1. 승인 받을 항목
2. 승인 후 반영할 변경
```
## 사용 예시
```text
/docs-sync
아래 QA 피드백을 docs에 반영하려고 해. 먼저 계획만 보여줘.
...
```
승인 후:
```text
위 계획에서 1, 2, 4번만 승인. 승인한 항목만 반영해줘.
```
잠깐, code-review-graph 는 정확히 뭘 어떻게 전달하나
https://github.com/tirth8205/code-review-graph/blob/main/README.ko-KR.md
code-review-graph/README.ko-KR.md at main · tirth8205/code-review-graph
Local knowledge graph for Claude Code. Builds a persistent map of your codebase so Claude reads only what matters — 6.8× fewer tokens on reviews and up to 49× on daily coding tasks. - tirth8205/cod...
github.com
위 표에 두 번 등장하는 code-review-graph 의 동작을 짚고 가보겠습니다.
저는 이 툴을 적극 활용해서 메모리 시스템을 최적화 + 구축했습니다.
이 도구는 Tree-sitter 로 레포의 코드를 파싱해서 함수, 클래스, import, 호출 관계, 테스트 연결을 노드와 엣지로 만든 지식 그래프입니다. 결과물은 레포 안 .code-review-graph/ (gitignore) 에 로컬 파일로 저장돼요. Claude Code 에는 MCP 서버로 등록되어 있어서, Claude 가 필요할 때 도구로 호출해 결과를 받습니다.
핵심은 Claude 컨텍스트에 들어가는 양이 두 단계로 나뉜다는 점입니다.
- 세션 시작 자동 inject. claude code v2.1.x 부터 SessionStart hook 은 silent inject 이라 사용자 화면엔 안 보이고 모델 컨텍스트로만 들어갑니다. 간단한 질의로 확인할 수 있어요. 새 세션을 띄우고 첫 발화로 "지금 프로젝트 어디까지 왔어?" 같은 가벼운 질문을 던져 보세요. 답이 프로젝트 이름이나 최근 결정을 자연스럽게 짚으면 SessionStart hook 이 Context.md 를 컨텍스트로 잘 가져온 거예요.
- 명시 호출 시점에만 본체 query. Claude 가 코드 질문을 받으면 ("이 함수 어디서 쓰여?", "이 변경의 영향 범위는?") 그때 MCP 도구를 호출합니다.
query_graph(pattern=callers_of, target=func_name),get_impact_radius,semantic_search_nodes같은 도구죠. 호출 결과 (관련 노드와 엣지 일부, 보통 수십 개) 만 컨텍스트로 들어와요. 전체 그래프 (수천 개 노드) 가 한 번에 들어오는 일은 없습니다.

PostToolUse hook 의 code-review-graph update --skip-flows 는 컨텍스트로 inject 가 아니라 로컬 그래프 파일을 증분 갱신 하는 일이에요. 다음에 Claude 가 query 했을 때 stale 한 결과를 받지 않게 하는 용도죠. update 출력 자체는 컨텍스트에 거의 안 들어옵니다 (간단한 완료 표시).
즉 code-review-graph 의 흐름은 세션 시작에 통계 한 줄 + 명시 호출 시 query 결과입니다. 매 세션 컨텍스트에 들어가는 토큰 비용은 한 라인 (몇십 토큰) 수준이고, 진짜 그래프 데이터는 사용자가 그래프를 활용할 만한 질문을 했을 때만 일부씩 들어와요. Claude 가 한 번도 "이 함수 어디서 쓰여" 류의 질문을 받지 않은 세션이라면 그래프 본체는 컨텍스트에 한 번도 들어오지 않고요.
5.1 : 세션 시작 때 상태를 한눈에 봅니다
SessionStart 에서 load-context.sh 보다 먼저 도는 스크립트가 있습니다.
.claude/hooks/memory-health.sh
이 스크립트는 세 가지를 봅니다.
Memory.md의updated:가 며칠 전인지~/Documents/swatch-archive에 마지막 백업이 언제인지docs/안의 마크다운 중 frontmatter 가 있는 파일이 몇 개인지
출력은 이런 식입니다.
[layer-2] Memory: 3d ago | Archive: 5d ago | Frontmatter: 67/67
이건 별것 아닌 것처럼 보이지만 꽤 중요합니다. LLM 메모리는 조용히 낡습니다. 매 세션 시작 때 “지금 메모리가 오래됐는지, 백업이 끊겼는지, frontmatter 적용이 누락됐는지”를 보이면 사람이 알아차릴 확률이 올라갑니다.
실제 예시 캡처




이 캡처를 보다가 한 가지 빈틈이 보였습니다. 위 두 번째 캡처의 환경은 평소 쓰는 맥북이 아니어서 git hook · launchd · Obsidian 설치 같은 셋업이 부분적으로 빠진 상태였어요.
scripts/setup.sh하나만 돌리면 다 잡히는 항목인데, 새 머신에서 그걸 까먹으면 메모리 시스템의 자동화 절반이 조용히 빠진 채로 동작하는 셈입니다. 이걸 다음 절 5.5 의 setup 자동 감지 hook 으로 잡았습니다.
5.2 : 문서 반영 전에 계획부터 세웁니다
.claude/commands/docs-sync.md 는 새 피드백이나 회의록을 바로 문서에 반영하지 말고, 먼저 계획을 세우게 하는 slash command 입니다.
흐름은 이렇습니다.
새 자료 입력
-> strategy / spec / mobile-design / data-model / Memory 중 어디에 들어갈지 분류
-> 새 문서가 나은지, 기존 문서 병합이 나은지 제안
-> related, status, tags 후보 제안
-> Context.md, Memory.md, decision log 갱신 필요 여부 판단
-> 사람 승인 후 반영
이게 중요한 이유는, 위키가 무너지는 순간이 보통 “좋은 답변을 바로 아무 데나 저장할 때”거든요.
회의록은 Memory.md 에 두면 안 됩니다. 반복되는 선호가 아니니까요. Context.md 도 마찬가지로 무거워지는데, 너무 상세하면 매 세션 토큰을 잡아먹어요. 결정은 decision log 에 올려야 나중에 번복 이력까지 따라갈 수 있고요.
/docs-sync 는 이 분류를 강제로 한 번 거치게 합니다.
이 명령은 "문서를 자동으로 고치는 버튼"이 아닙니다. 문서 반영 전 회의 안건을 만들어 주는 명령에 가깝습니다. 어디에 넣을지, 무엇을 수정할지, 어떤 문서를 바꾸지 않을지 먼저 보여 주고, 사람 승인을 받은 뒤에만 반영하게 합니다.
이렇게 한 이유는 LLM wiki 에서 가장 위험한 순간이 "좋은 답변을 저장해 둬"라고 말하는 순간이기 때문입니다. 좋은 답변도 위치를 잘못 잡으면 오염이 됩니다. 회의록이 Memory.md 에 들어가거나, 개인 선호가 decision log 에 들어가거나, 임시 가설이 Context.md 에 들어가면 다음 세션부터 잘못된 무게를 갖습니다.
5.3 : 주간 점검을 명령으로 만듭니다
/memory-status 는 Layer 2 상태를 자세히 점검하는 명령입니다.
확인하는 항목은 이렇습니다.
| 항목 | 보는 것 |
|---|---|
Memory.md |
Preferences, Corrections, Patterns, Decisions entry 수 |
Context.md |
현재 sprint 와 맞는지 |
| archive | 최근 백업 날짜 |
| frontmatter | 전체 문서 중 frontmatter 적용 비율 |
| decision sync | Memory.md 의 D 번호 미러와 14_decision_log.md 동기 상태 |
자동화가 모든 걸 해결하지 않습니다. 대신 사람이 매주 30분만 써도 볼 수 있게 점검판을 줄입니다.
이 명령이 잡는 영역은 hook 이 못 잡는 구간이에요. §4 의 sentinel sync 검증은 decision_log.md 안의 마지막 D 번호 ↔ pointer 짝만 봅니다. 반면 Context.md 가 결정 로그의 최신 범위를 따라가지 못하는 drift 는 hook 이 못 봐요 (두 파일이 같은 commit 에 staged 안 될 때가 많아서요). /memory-status 가 보는 지점이 정확히 거기고요. 자동화의 목표를 완전 무결성이 아니라 보이게 하기로 잡으면, 작은 팀에서도 운영 가능한 수준이 됩니다.
5.4 : 위험한 작업 전 스냅샷을 남깁니다
Memory.md 는 Claude 가 자동으로 고칠 수 있는 파일이에요. 편하지만 위험하거든요. 잘못된 줄 매칭으로 기존 선호가 지워질 수도 있고요.
그래서 docs/_claude/Archive-Guide.md 와 /memory-archive 가 있습니다.
백업 위치는 repo 밖이에요.
~/Documents/swatch-archive/claude_YYYY-MM-DD/
repo 안에 백업을 두지 않는 이유는 별것 아니에요. Claude Code 가 자동으로 접근하고 수정할 수 있는 범위에서 빼려고요. git history 가 1차 백업이고, repo 밖 archive 가 2차 백업입니다.
macOS 에서는 launchd 로 매주 월요일 09:00 백업도 걸 수 있습니다.
<key>StartCalendarInterval</key>
<dict>
<key>Weekday</key><integer>1</integer>
<key>Hour</key><integer>9</integer>
<key>Minute</key><integer>0</integer>
</dict>
5.5 : 셋업 누락을 hook 이 알아서 잡습니다
아까 §5 도입에서 슬쩍 털어놓은 일이 있었는데요. 새 머신에서 scripts/setup.sh 를 돌리는 걸 까먹어서, git hook·백업 launchd·Obsidian 플러그인이 부분적으로 빠진 채로 며칠을 작업한 적이 있습니다. 셋업 자체는 idempotent 하게 잘 짜 뒀는데, 사람이 그 한 줄을 까먹는 게 진짜 문제였거든요. 그래서 그 망각을 hook 이 대신 잡아 주게 만들었어요. 사람이 깜빡한 일을 기계가 받쳐 주는 셈이죠.
.claude/hooks/setup-check.sh 는 SessionStart 때마다 두 가지를 합니다.
# .claude/hooks/setup-check.sh (요약)
# 1) git hooksPath 가 .githooks 가 아니면 자동 활성화
current_hooks=$(git config --get core.hooksPath || echo "")
if [[ "$current_hooks" != ".githooks" && -d ".githooks" ]]; then
chmod +x .githooks/* 2>/dev/null || true
git config core.hooksPath .githooks
echo "[setup-check] git hooks 자동 활성화 (core.hooksPath = .githooks)"
fi
# 2) .setup-done 마커가 없으면 안내. launchd · Obsidian 은 자동 실행 안 함.
if [[ ! -f ".setup-done" ]]; then
echo "[setup-check] ./scripts/setup.sh 미실행 — 백업 launchd · Obsidian 플러그인 셋업이 빠졌을 수 있음."
fi
두 가지로 나눠 둡니다.
- 자동 실행 (가벼운 것만):
git config core.hooksPath .githooks한 줄은 가볍고 idempotent 라 그냥 돌립니다. 이게 빠지면 §4 의 sentinel auto-stamp / sync 검증이 통째로 안 돌고, frontmatterupdated:bump 도 안 도는데요. 메모리 시스템의 자동화 절반이 조용히 죽어 있는 거라, 자동 활성화의 가치가 큽니다. - 안내만 (사람 결정 필요한 것): launchd 등록은 시스템 데몬에 plist 를 박는 일이고, Obsidian 플러그인 설치는 외부 앱에 뭔가를 깔아야 하는 일이에요. 이건 사용자가 alert 받고 본인이 결정해야 합니다. 그래서 자동 실행은 안 하고 안내만 띄워요.
기준은 단순해요. 롤백 비용이 0 인 자동화는 자동으로, 그렇지 않은 자동화는 알림으로. 같은 발상이 §4 의 sentinel hook 과도 맞닿아 있습니다. sentinel auto-stamp 는 commit 안에서 일어나니까 git 으로 즉시 되돌릴 수 있어서 자동, launchd plist 는 시스템 전역이라 안내. 그런 식이에요.
전체 흐름은 이렇게 흘러갑니다.
새 머신에서 클론
-> 첫 Claude Code 세션 시작
-> setup-check hook 이 git hooksPath 자동 활성화
-> setup-check hook 이 ".setup-done 없음" 안내 출력
-> 사용자가 ./scripts/setup.sh 한 번 실행
-> .setup-done 파일이 만들어짐
-> 다음 세션부터는 안내 안 뜸
gstack 도 이미 비슷한 패턴이에요. CLAUDE.md 에서 ~/.claude/skills/gstack/bin 이 없으면 STOP. 같은 발상을 메모리 시스템 셋업에도 옮겨 둔 거고요. 자동화는 처음 한 번 돌리는 게 가장 어려운데, 그 한 번을 잊으면 시스템이 살아 있는데 죽은 척하는 상태가 됩니다. hook 한 줄이 그 살아 있음을 매 세션 확인해 주는 거예요.
5.6 : 메모리 규칙을 hook 으로 강제합니다
본편 2편 「룰의 두 층, 메모리 자동 갱신과 hook 승격」 단락에서 이 hook 의 흐름을 그림과 함께 자세히 봤습니다. 여기서는 같은 hook 이 5장 (운영 시스템) 안에서 어떤 역할인지로 짧게 짚어요.
재미있는 부분도 있습니다.
Memory.md 에 “기술 글과 문서 작성 시 이모지를 쓰지 않는다”는 correction 이 들어갔습니다. 그런데 LLM 은 이런 룰을 가끔 어깁니다. 그래서 팀원이 PreToolUse hook 으로 아예 차단했습니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/no-emoji-check.sh",
"timeout": 5
}
]
}
]
}
}
Claude Code 의 PreToolUse hook 은 permissionDecision: "deny" 를 반환하면 도구 호출을 막을 수 있습니다. 이 동작은 공식 Hooks guide 에서 설명하는 패턴입니다. “메모리에 적힌 선호”가 “파일 쓰기 단계의 강제 규칙”으로 승격된 셈입니다.
이 시스템의 감각이 여기서 드러나요.
메모리는 부탁으로만 두면 약합니다. 반복해서 어기는 규칙은 hook 으로 내려야죠.
Memory.md 와 hook 의 차이가 선명해지는 지점이에요. Memory.md 는 "앞으로 이렇게 해 주세요"라는 기억이고, hook 은 "이 상황에서는 실제로 막겠습니다"라는 실행 규칙입니다. 모든 선호를 hook 으로 내릴 필요는 없지만, 반복해서 어기는 규칙, 검출이 쉬운 규칙, 어겼을 때 비용이 큰 규칙은 hook 으로 내리는 편이 나아요.
이모지 금지는 사소해 보여도 예시로 좋습니다. 사람이 매번 "문서에 이모지 쓰지 말라고 했잖아"라고 말하는 대신, 쓰기 직전에 검사하고 막거든요. 하네스가 메모리를 받쳐 주는 모양은 이런 식이에요.
6. Obsidian 은 에이전트 메모리가 아니라 사람의 관측 도구입니다
https://obsidian.md/
[Obsidian - Sharpen your thinking
The free and flexible app for your private thoughts.
obsidian.md](https://obsidian.md/)
docs/ 전체는 Obsidian vault 로 열 수 있습니다.
처음에는 “Obsidian Smart Connections 를 쓰면 그래프 메모리처럼 되는 건가?”라고 생각하기 쉬워요. 그게 아닙니다. 이 프로젝트 문서도 그 점을 따로 정리해 두었거든요.
Smart Connections 는 Claude Code 의 SessionStart hook 과 별개예요. Claude 가 매 세션 자동으로 Smart Connections 를 돌려서 검색하는 구조가 아니거든요. 주로 사람이 Obsidian 에서 관련 노트를 빠르게 찾을 때 쓰는 도구죠.
그래도 의미는 큽니다.
| 도구 | 역할 |
|---|---|
| Backlinks | [[문서명]] 링크를 따라 관계를 보여줌 |
| Dataview | frontmatter 기반으로 active 문서, stale 문서, decision 문서 등을 표로 봄 |
| Smart Connections | 로컬 임베딩으로 비슷한 노트를 추천 |
Obsidian 은 에이전트의 자동 메모리 엔진이라기보다, 사람이 위키의 상태를 들여다보는 도구에 가까워요.
이 차이를 또렷이 해야 합니다. X 원문이 말하는 “그래프 기반 프로덕션 메모리”와 이 시스템은 달라요.
새 팀원이 가장 많이 오해하는 대목이 여기입니다. Obsidian Smart Connections 를 켰다고 해서 Claude Code 가 그 임베딩 인덱스를 자동으로 쓰는 게 아니거든요. 두 시스템의 실행 경로는 완전히 다릅니다.
Smart Connections 는 Obsidian 플러그인이에요.
Obsidian 앱이 켜져 있을 때만 동작하고, vault 안 마크다운 파일을 읽어 로컬에서 임베딩을 만들어 .obsidian/plugins/smart-connections/ 같은 경로에 인덱스를 저장합니다. 사람이 Obsidian 에서 한 노트를 열면 사이드 패널에 "비슷한 노트" 가 자동으로 추천돼요.
Claude Code 와는 무관한 별도 프로세스입니다.
Claude Code 의 SessionStart hook 은 별도 경로로 Context.md 본문만 읽힙니다.
Smart Connections 의 임베딩 인덱스를 호출하지 않거든요. MCP 도 없고, 슬래시 커맨드도 없고, hook 통합도 없습니다. 두 시스템은 같은 docs/ 폴더를 바라볼 뿐, 서로 정보를 주고받진 않아요.
그래서 Obsidian 을 안 깔아도 Claude Code 하네스는 동작합니다.
반대로 Claude Code 를 안 쓰는 팀원도 Obsidian 으로 docs/ 를 열어 문서 관계를 볼 수 있어요. 느슨하게 묶여 있어서 좋습니다. 한쪽이 깨져도 다른 쪽은 계속 굴러가니까요.
code-review-graph 와 비교하면 차이가 또렷해요.
code-review-graph 는 Claude Code 의 MCP 서버로 등록되어 있어서 Claude 가 명시 호출하면 그래프 결과를 받아 컨텍스트에 inject 합니다 (앞 5장에서 본 흐름이죠). Smart Connections 는 그 통합 지점이 없어요. Obsidian 안에서 사람이 쓰는 도구로 머뭅니다. 이 차이를 또렷이 해 두면 "Smart Connections 켰는데 왜 Claude 가 비슷한 문서를 자동으로 찾아 주지 않지?" 같은 의문이 생기지 않습니다.
같은 흐름을 그림으로 보면 두 경로의 분리가 한눈에 보입니다.

가운데 docs/ 폴더를 두 경로가 각자 따로 읽습니다. 왼쪽 (사람용) 은 Obsidian 앱 → Smart Connections 플러그인 → 로컬 임베딩 인덱스 → 사이드 패널 추천. 오른쪽 (에이전트용) 은 Claude Code 새 세션 → SessionStart hook → Context.md 본문 inject. 두 경로 사이 (점선) 는 정보가 흐르지 않습니다. Smart Connections 의 임베딩 결과는 Claude 컨텍스트에 안 들어가고, Claude 가 Memory.md 에 갱신한 줄도 Obsidian 사이드 패널에 안 보입니다. 같은 폴더를 보고 있을 뿐, 시스템은 별개입니다.
이 프로젝트가 가진 그래프성은 세 가지입니다.
- 문서 간
[[wiki-link]]와related:frontmatter - Obsidian 의 backlink, Dataview, Smart Connections
- 코드 쪽은
code-review-graphMCP
하지만 아직 없는 것도 있습니다.
그래서 이 글의 결론은 “마크다운이 그래프 메모리를 대체한다”가 아니에요. 다음 한 마디에 가깝습니다.
작은 팀과 한 레포에서는 markdown + git + hook + Obsidian 만으로도 꽤 오래 가는 실용 메모리 시스템을 만들 수 있다. 다만 규모가 커지면 그래프 메모리나 별도 검색 엔진으로 넘어갈 준비를 해야 한다.
7. 검토해 본 기술 스택들: claude-mem, Beads
7.1 claude-mem
https://github.com/thedotmack/claude-mem
GitHub - thedotmack/claude-mem: A Claude Code plugin that automatically captures everything Claude does during your coding sessi
A Claude Code plugin that automatically captures everything Claude does during your coding sessions, compresses it with AI (using Claude's agent-sdk), and injects relevant context back into fut...
github.com
이 라이브러리를 깔면 자동으로 세션 메모리를 압축, 저장, 검색합니다.
어떻게 동작하나. 세션 트랜스크립트를 LLM 이 압축해서 외부 메모리에 누적하고, 다음 세션 시작에 다시 주입합니다. Claude Code 의 hook 시점에 트랜스크립트를 가로채서 처리합니다.
흐름은 대략 이렇습니다.
- 세션이 끝나거나 컨텍스트가 차오르는 시점에 hook 이 돕니다.
- 트랜스크립트를 LLM 한테 한 번 더 던지고 「핵심만 추려서 저장 가능한 형태로 압축해 줘」 같은 식으로 호출합니다.
- 압축본을 외부 메모리 저장소에 누적합니다.
- 다음 세션 시작 시점에 의미 검색으로 관련 압축본을 다시 컨텍스트로 inject 합니다.
어떤 곳에 잘 맞나.
- 솔로 자동화 + 매 세션 토큰 절약 우선. 트랜스크립트가 길어지는 작업 (장시간 디버깅, 여러 세션 걸친 한 task) 에서 매번 처음부터 컨텍스트 다시 깔지 않아도 됩니다.
- 여러 세션이 한 task 를 이어 가는 패턴. 한 task 에 한 메모리 묶음이 자동으로 누적됩니다.
어떤 곳에 안 맞나.
- API 비용 + 외부 의존. 컨텍스트 압축마다 LLM API 호출이 한 번 더 들어갑니다. 메모리 운영 자체가 외부 API 에 묶입니다. API 키 회전, rate limit, 가격 변동에 그대로 노출됩니다.
- 로컬 LLM 셋업도 부담입니다. 외부 API 안 쓰려면 로컬 LLM 을 깔아야 합니다. 이게 또 한 도구입니다.
- 검토 가능성이 떨어집니다. 메모리에 무엇이 들어갔는지 사람이 매번 확인하기 어렵습니다. LLM 이 압축한 결과물이라 정확히 어떤 사실이 누적됐는지가 불투명합니다. 「이 메모리는 왜 이렇게 적혔지」 가 들면 디버깅 경로가 길어집니다.
- 팀 공유 시 별도 동기화 필요. 메모리가 외부 저장소에 있으니 팀 공유가 repo 자체에 자연스럽게 안 됩니다.
본 시스템과의 차이는?: claude-mem 의 핵심은 LLM 이 메모리 압축을 하는 방식입니다. 본 시리즈 시스템은 사람과 git 이 메모리를 관리하는 방식이고요. 두 도구가 같은 문제를 풀지만, 누가 메모리를 만지는지가 다른 셈입니다.
git diff 한 라인으로 모든 메모리 변경을 추적할 수 있다는 게 본 시스템의 강점입니다. 「Claude 가 어제 무엇을 메모리에 추가했지」 → git log docs/_claude/Memory.md 만 치면 어떤 줄이 어떻게 바뀌었는지가 그대로 보이거든요. claude-mem 은 그 검토가 LLM API 응답을 다시 분석하는 일이 됩니다.
7.2 Beads
https://github.com/gastownhall/beads
git-backed issue tracker 입니다. Dolt SQL DB 위에 hash-based ID 로 작업 단위를 관리합니다.
어떻게 동작하나.
GitHub - gastownhall/beads: Beads - A memory upgrade for your coding agent
Beads - A memory upgrade for your coding agent. Contribute to gastownhall/beads development by creating an account on GitHub.
github.com
- 새 작업이 들어오면
bd-a1b2같은 hash ID 를 자동으로 부여해서 충돌 없이 식별 - 저장은 Dolt (git-like SQL DB). 브랜치, 머지, 셀 단위 머지까지
- 작업 dependency 추적. dependency-aware 실행 순서를 에이전트한테 dispatch
- formula 시스템으로 작업 자동 생성 (예: 테스트가 깨진 모든 파일에 fix 작업 자동 생성)
어떤 곳에 잘 맞나.
- 멀티 에이전트 + 작업 단위 본격 추적. 에이전트 여러 대가 동시에 작업을 dispatch 받는 모양에서 충돌 0 이 진짜 가치를 발휘합니다.
- 작업 단위가 수백 개로 넘어갈 때. 사람이 ID 못 외우는 trade-off 가 작업이 많아서 어차피 ID 로 다뤄야 하는 곳에서 사라집니다.
- dependency 가 복잡한 작업. A → B → C 순서로 풀려야 하는 작업 그래프를 도구가 추적합니다.
어떤 곳에 안 맞나.
- 솔로 + 작업 단위 한 자리 수. 마크다운 + sentinel 한 줄로 충분합니다.
- 사람이 ID 를 외우는 게 가치가 큰 곳. D47 같은 sequential 번호가 머릿속에서 가볍게 굴러가는 작업 규모.
본 시스템과의 차이. 본 시리즈는 마크다운 주석 한 라인에 잠금 primitive 를 넣었고, Beads 는 별도 도구 (Dolt + CLI + 원격 저장소) 로 같은 발상을 본격화했어요. 작업 단위가 수십 개일 때는 주석 하나가 가볍고, 수백 개일 때는 도구가 자연스럽습니다.
7.3 세 도구 비교
| 본 시스템 (sentinel + git) | Beads | claude-mem | |
|---|---|---|---|
| 누가 메모리 관리 | 사람 + git | Beads (Dolt 기반) | claude-mem (LLM API) |
| 검토 가능성 | git diff 로 매 변경 확인 |
Dolt 이력 + 셀 머지 | API 응답 (불투명) |
| 락인 | 0 (마크다운 + git) | Beads 도구 의존 | API + 비용 의존 |
| 팀 공유 | repo 자체가 공유 매체 | Dolt-native replication | 별도 동기화 필요 |
| 멀티 에이전트 | sentinel 한 줄로 충돌 강제 | Hash ID 로 충돌 0 | 솔로 가정 (협업은 별도) |
| 적합한 곳 | 팀, 장기, 휴대 우선 | 멀티 에이전트 작업 단위 추적 | 솔로, 자동화, 토큰 절약 |
- 아, 또 맞다 이 레포도 예전에 스타 찍어놧엇네요 karpathy wiki llm pattern 기반이고 약간 저희 사이드플젝이랑은 안맞아서 따로 구축했습니다. 자세하게는 안봄.
https://github.com/coleam00/claude-memory-compiler
GitHub - coleam00/claude-memory-compiler: Give Claude Code a memory that evolves with your codebase. Hooks automatically capture
Give Claude Code a memory that evolves with your codebase. Hooks automatically capture sessions, the Claude Agent SDK extracts key decisions and lessons, and an LLM compiler organizes everything in...
github.com
7.4 본 프로젝트는 둘 다 안 쓰고 있습니다
명시해 둡니다. 본 프로젝트는 claude-mem 도, Beads 도 현재 적용하지 않습니다. 마크다운 + git + sentinel 한 줄 + 슬래시 커맨드만으로 굴러갑니다.
각각 안 택한 이유:
- claude-mem. API 비용 + 로컬 LLM 셋업 부담이 있습니다. 메모리 운영에 외부 API 의존을 더하고 싶지 않거든요. 마크다운 + git 만으로 같은 일을 돈 안 들이고,
git diff로 100% 검토 가능하게 처리하고 있습니다. - Beads. sentinel D 번호 + git 만으로 충분해요. 작업 단위가 한 자리 수에서 수십 개 규모라 별도 도구의 무게가 과합니다.
7.5 규모가 커지면 둘 다 후보
다만 조건이 바뀌면 두 도구 모두 더 맞는 영역이 있긴 해요.
- 솔로 작업 + 자동화 중심 + 매 세션 토큰 절약이 우선이라면 claude-mem 이 가벼운 답입니다.
- 멀티 에이전트 + 작업 단위 본격 추적 + 수백 개 규모로 늘어난다면 Beads 가 자연스러운 다음 단계입니다.
본 프로젝트는 지금 개발자 2명 + 코덕 친구 피드백 받는 모양이라 둘 다 yagni, simple is the best 이지요!
규모가 커지면 그때 검토할 생각입니다.
8. 한계
이 시스템은 유용하지만 과장하면 안 돼요. 자동이 아닌 부분, 규모가 커지면 깨지는 지점, 일부러 안 한 곳이 꽤 있어요.
가장 먼저: 프롤로그에서 언급 했듯, Claude 에 종속적인....시스템, 구조라는 점입니다
왜냐면?
.....
@ Antropic 의 Open code 레전드 쫌팽이 대응 사건 관련 . . . .. 을 보여드리겟습니다.
(26년 5월 5일 기준: 삘받아서 자막까지 붙였다네요 자막 키고 보세요 오역 잇으면 지적 ㄱㅅㄱㅅ)
- 중복 제거는 자동이 아닙니다. 비슷한 내용이 두 문서에 생기면 Claude 가 알아서 합쳐 주지 않아요.
/docs-sync와 주간 점검으로 줄이는 구조입니다. - 감쇠도 자동이 아닙니다. 오래된 정보가 알아서 희미해지지 않아요.
status: archived,status: superseded,updated:같은 메타데이터로 사람이 직접 관리합니다. - 랭킹은 제한적입니다.
Context.md에 올라간 정보가 사실상 우선순위를 갖긴 하지만, 전역적으로 "이 기억이 더 중요하다" 고 점수화하는 시스템은 없어요. - causal graph 는 아직 약하고요. 문서 링크와 decision log 로 관계를 남기지만, "이 버그가 지난번 어떤 결정 때문에 생겼는지" 를 그래프 순회로 자동 추적하는 수준까지는 못 갑니다.
- 팀이 커지면 충돌이 늘어납니다. 세 명 사이드 프로젝트라면
Memory.md와Context.md를 공유해도 버티지만, 네 명·다섯 명 이상으로 늘면 개인 메모리와 팀 메모리를 더 강하게 분리해야 해요. - vault 가 커지면 검색 엔진이 필요해지고요. 문서가 200개를 넘으면 grep · Obsidian · Smart Connections 만으로는 답답해집니다. 그때 검토할 후보는 앞 7장 비교 표에 있어요 (claude-mem, Beads + qmd / Neo4j / Mem0 / Zep / Cognee).
- 자동 문서 갱신은 일부러 하지 않았습니다. 코드 수정 때마다 관련 문서를 자동으로 찾아 고치는 hook 도 만들 수는 있지만 안 만들었어요. 이유는 세 가지인데, (1) 코드 변경이 곧 제품 결정 변경은 아니고, (2) LLM 이 관련 문서를 잘못 추정하면 문서 오염이 더 빨라지고, (3) 문서 변경은 사람의 승인과 해석이 필요한 경우가 많거든요. 그래서 이 시스템은 자동 갱신보다 자동 진단에 가깝습니다. stale 가능성을 보여 주고,
/docs-sync로 반영 계획을 세우고, 사람이 승인하면 수정. 느려 보여도 문서 메모리에서는 이쪽이 더 안전했어요.
9. 다른 레포에서 시작하는 최소 버전
이 시스템을 그대로 복사할 필요는 없습니다. 최소 출발선은 더 작게 잡을 수 있습니다.
9.1 폴더를 만듭니다
처음에는 네 파일이면 됩니다. 지금 이 레포처럼 Frontmatter-Standard.md, LLM-Wiki-Pattern.md, Obsidian-Setup.md, Maintenance.md 까지 한 번에 만들 필요는 없습니다.
docs/_claude/
Instructions.md
Memory.md
Context.md
Archive-Guide.md
각 파일은 이렇게 나눕니다.
| 파일 | 처음 넣을 내용 |
|---|---|
Instructions.md |
에이전트 역할, 답변 규칙, 문서 수정 규칙 |
Memory.md |
Preferences, Corrections, Patterns, Decisions |
Context.md |
프로젝트 소개, 현재 우선순위, 스택, 주요 결정 |
Archive-Guide.md |
백업 위치, 복구 방법 |
처음부터 완벽하게 쓸 필요는 없습니다. 다만 섞이면 다시 무너집니다.
9.2 루트 에 진입점을 둡니다
루트 CLAUDE.md 에 다음 규칙을 넣습니다.
## Project Context
제품/전략/팀 관련 질문에 답하기 전 다음 파일을 먼저 읽어라.
- docs/\_claude/Instructions.md
- docs/\_claude/Context.md
- docs/\_claude/Memory.md
충돌하면 docs/strategy/14_decision_log.md 최신 결정을 우선한다.
Claude Code 는 CLAUDE.md 를 자동으로 읽습니다. 공식 문서 기준으로 현재 작업 디렉터리에서 위로 올라가며 CLAUDE.md 와 CLAUDE.local.md 를 찾고, 발견한 내용을 컨텍스트에 합칩니다.
다만 CLAUDE.md 에 너무 많은 내용을 넣지 않는 것이 좋습니다. 공식 문서에서도 CLAUDE.md 는 짧고 구체적일수록 잘 따른다고 설명합니다. 큰 팀이라면 .claude/rules/ 같은 path-specific rule 로 쪼갤 수도 있지만, 처음 시작할 때는 루트 CLAUDE.md 를 "지도"로만 쓰는 편이 안전합니다.
공식 문서 원문으로 보면 핵심은 두 줄입니다.
"Claude treats them as context, not enforced configuration."
(번역: Claude 는
CLAUDE.md와 auto memory 를 강제 설정이 아니라 컨텍스트로 다룹니다.)
"All discovered files are concatenated into context rather than overriding each other."
(번역: 발견된
CLAUDE.md파일들은 서로 덮어쓰지 않고 컨텍스트에 이어 붙습니다.)
9.3 SessionStart hook 으로 만 자동 주입합니다
.claude/hooks/load-context.sh (한 줄짜리 cat 스크립트) + .claude/settings.json 에 SessionStart hook 등록 — 이 두 군데만 있으면 됩니다. 코드는 2장의 예시를 그대로 가져다 쓸 수 있고요.
스크립트의 stdout 이 매 세션 컨텍스트에 자동으로 들어갑니다.
처음에는 이것만 해도 체감이 큽니다. 새 세션을 열 때마다 “이 프로젝트가 뭐였지?”부터 다시 설명하지 않아도 됩니다.
9.4 decision log 를 하나 만듭니다
docs/strategy/decision_log.md 같은 파일을 만듭니다. 이름은 달라도 됩니다.
규칙은 세 개면 충분합니다.
- 결정은 append-only 로 남긴다.
- 번복은 기존 항목 수정이 아니라 새 D 번호로 남긴다.
- 인덱스 표 아래
D-claim-pointersentinel 한 줄을 둔다. 멀티 에이전트나 worktree 동시 작업에서 D 번호 중복을 git 단계에서 잡아 주는 lock primitive 입니다 (메커니즘은 4장 참고).
예시입니다.
# Decision Log
| ID | 날짜 | 주제 | 상태 |
| --- | ---------- | ------------------ | ---- |
| D1 | 2026-05-04 | 이메일 로그인 only | 확정 |
<!-- D-claim-pointer: D2 -->
---
## D1 · 이메일 로그인 only
결정:
근거:
영향:
후속:
멀티 에이전트 / worktree 환경이라면 보강형 sentinel 까지 둡니다. commit hash · branch · committer 세 좌표를 sentinel 줄에 같이 박아서, 두 worktree 가 같은 D 번호를 claim 했을 때 한 좌표라도 다르면 sentinel 라인이 텍스트로 달라져 git 머지 단계에서 강제로 conflict 가 나게 만들어요 (메커니즘은 4.2 참고).
<!-- D-claim-pointer: D2 @commit: abc1234 @branch: feature-x @by: Dorito -->
.githooks/pre-commit 이 결정 로그가 staged 되어 있으면 자동으로 세 좌표를 sentinel 줄에 stamp 합니다. 사람은 D{n} → D{n+1} 한 곳만 건드리면 되고 나머지는 hook 이 채워 줘요.
9.5 같은 계획 명령을 둡니다
새 문서를 바로 쓰게 하지 말고, 먼저 물어보게 합니다.
---
description: docs 반영 전 정합성 계획
---
# /docs-sync
입력 자료를 바로 수정하지 말고 먼저 계획을 제안한다.
1. 어떤 폴더에 들어갈지 분류한다.
2. 새 문서가 나은지 기존 문서 병합이 나은지 제안한다.
3. related, tags, status 후보를 낸다.
4. Context.md, Memory.md, decision log 갱신 필요 여부를 판단한다.
5. 사람 승인 후에만 수정한다.
이 한 장이 있으면 위키가 훨씬 깔끔해져요.
Claude Code 공식 문서에서도 project command 의 위치를 또렷이 짚어 둡니다.
"Commands stored in your repository and shared with your team."
(번역: 저장소 안에 두고 팀과 공유하는 명령입니다.)
그리고 위치는 아래처럼 잡습니다.
"Location:
.claude/commands/"(번역: project command 는
.claude/commands/에 둡니다.)
최신 Claude Code 문서에서는 skills 설명도 함께 중요해졌습니다. skill 은 command 보다 조금 더 큰 단위입니다. 프롬프트뿐 아니라 참고 파일, 스크립트, 실행 권한, 자동 호출 조건까지 묶을 수 있습니다.
"Every skill needs a
SKILL.mdfile"(번역: skill 은
SKILL.md파일을 중심으로 구성됩니다.)
처음에는 .claude/commands/docs-sync.md 처럼 단순 command 로 시작해도 충분합니다. 나중에 더 복잡해지면 skill 로 옮기고, 필요한 참고 파일이나 스크립트를 같이 묶을 수 있습니다. 이 프로젝트도 처음엔 commands 와 skills 의 차이를 헷갈렸고, 실제 slash command 는 .claude/commands/ 로 옮겨야 동작한다는 시행착오를 겪었습니다. 이 정도 시행착오는 정상입니다.
9.6 주간 점검과 백업을 둡니다
처음부터 자동화가 부담이면 수동으로 시작해도 됩니다.
mkdir -p ~/Documents/my-project-archive
cp -R docs/_claude ~/Documents/my-project-archive/claude_$(date +%Y-%m-%d)
그다음 여유가 생기면 launchd 나 cron 으로 올립니다.
점검은 이 네 가지면 충분합니다.
Memory.md에 낡은 preference 가 없는가Context.md의 현재 우선순위가 맞는가- decision log 마지막 D 번호와 pointer 가 맞는가
- 새 문서에 frontmatter 가 있는가
9.7 docs 를 Obsidian vault 로 엽니다
Obsidian 은 필수가 아닙니다. 그래도 팀 문서가 30개를 넘기 시작하면 도움이 됩니다.
추천 기본값은 이 정도입니다.
docs/폴더를 vault 로 열기- Backlinks 켜기
- Dataview 설치
- Smart Connections 는 선택
- 새 문서에는 frontmatter 추가
frontmatter 는 최소 이 정도면 됩니다.
---
type: strategy
status: active
tags: [mvp, decision]
related: ["[[14_decision_log]]"]
updated: 2026-05-04
---
리마인드: 메모리 시스템 찍먹 전에 한번 이것도 살펴보고 가세유 (프롤로그에서도 언급한 것)
https://github.com/forrestchang/andrej-karpathy-skills
GitHub - forrestchang/andrej-karpathy-skills: A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpa
A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathy's observations on LLM coding pitfalls. - forrestchang/andrej-karpathy-skills
github.com
OpenAI 공동 창업자이자 전 Tesla AI 책임자인 Karpathy(네임드) 가 제창한 "LLM에 올바르게 쓰게 하는" 4원칙을 1파일에 모은 것인데요. 초반 claude code 어쩌구... 다 모르겠다! 이 레포 셋업 해보시길 추천합니다.
10. 그래도 이 방식이 유효한 영역
𝕏 @AYi_AInotes 의 비판은 맞습니다. 파일 더미를 메모리라고 부르면 무너집니다.
X의 阿绎 AYi님(@AYi_AInotes)
하지만 모든 팀이 처음부터 그래프 DB, 장기 메모리 프레임워크, 임베딩 파이프라인을 구축해야 하는 건 아닙니다.
작은 팀이 제품을 만들고, 결정이 자주 바뀌고, Claude Code 를 매일 쓴다면 더 현실적인 출발점이 있습니다.
그 출발점은 이렇습니다.
1. 매 세션 자동 주입할 압축 컨텍스트를 하나 둔다.
2. 원본 문서는 docs/ 안에 남기고 필요할 때만 읽는다.
3. 선호, 현재 상태, 공식 결정을 서로 다른 파일로 나눈다.
4. 중요한 결정은 append-only 로 기록한다.
5. 문서 반영 전에는 계획을 먼저 세운다.
6. 주간 점검과 백업을 운영 루틴으로 둔다.
7. 반복해서 어기는 규칙은 hook 으로 강제한다.
이 정도만 해도 “매번 긴 프롬프트를 붙여 넣는 방식”에서 벗어납니다.
메모리는 파일이 아니에요. 하지만 파일을 기반으로 한 운영 시스템은 될 수 있습니다. 이 프로젝트의 docs/_claude, docs/strategy, .claude/hooks/load-context.sh 는 그 중간 지점에 있어요.
완전한 그래프 메모리로 가기 전, git 위에서 팀이 바로 쓸 수 있는 가장 싼 형태의 장기 기억이고요.
Recall: 블로그 글은 거창했지만, 저의 실제 작업 방식은 이랬어요.
- 𝕏 (구 Twitter) 의 @AYi_AInotes 글에서 문제의식을 접하고 공감했습니다.
- 그 글을 소개한 aiedge 의 3-layer memory 아티클 아티클을 읽고, 거기서 연결된 Karpathy 의 LLM knowledge mega promt gist 를 보고 분석했습니다.
- 한 세션에서 문제 의식 - 아티클 - git gist 내용을 모두 agent 에게 전달하고
- 이미 쌓여 있던 제 레포의
docs/도 같이 참고하라고 했습니다. - Claude Code 에서
/plan모드로 "Karpathy 의 LLM wiki 시스템을 참고해서, 이 프로젝트에 맞는 문서 메모리 / wiki 시스템을 설계해 달라" 라는 맥락으로 요청했습니다. - 그다음에는 계획을 따라가면서 에이전트와 함께 질의 및 개선하고 기술 trade off 를 고려하며 과한 부분은 빼고, 필요한 부분은 살리면서 방향을 잡고 설계했습니다.
- 파일 구조, hook, slash command, decision log 규칙 같은 세부 작업은 대부분 에이전트와 함께 개선하고 구축했습니다.
셋업에는 대략 2시간 정도 걸렸어요.
솔직히 여기서 얻은 포인트를 한 마디로 하자면, 제가 메모리 시스템을 두고 "수제 코딩" 은 단 하나도 하지 않았어요.
𝕏 (구 Twitter) 의 @AYi_AInotes 글에서 문제를 잡고, aiedge_ 의 3-layer memory 아티클 을 통해 Karpathy 의 LLM wiki gist 를 발견했고, 그 mega prompt 와 기존 docs 를 에이전트에게 거의 통째로 먹였습니다.
그다음 /plan 으로 구조를 뽑게 하고, 저는 어떻게 할지에 설계하고, 결정 방향과 가이드만 잡았습니다.
문제의식 원본: (grok 자동 번역으로 보세요. 아니면 글 프롤로그에 원문 적어둿슴다.)
𝕏의 (@AYi_AInotes)
X의 阿绎 AYi님(@AYi_AInotes)
说个暴论,现在90%的AI Agent记忆,全都是假的。 我之前也踩过这个坑,把所有历史记录决策日志全堆进Markdown文件里,以为这就是给Agent加了长期记忆,结果用了两周就崩了,
x.com
아티클 원본: https://x.com/aiedge_/article/2046966170868486512
→ 노션에 정리해둿슴다 ㅋ
https://dorito-dev.notion.site/3-Memory-Layer-ad2fd43a1db24d7c825d9905089aea49?source=copy_link
X
x.com
3 Memory Layer 시스템 아티클 | Notion
I don't care what anyone tells you - by default, Claude's memory is basically useless.
dorito-dev.notion.site
Kapathy mega prompt LLM wiki git gist (복붙하면 됨ㅋ)
https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f
llm-wiki
llm-wiki. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
처음에는 세 가지만 해도 충분해요.
핵심은 사람이 모든 구조를 손으로 짠 게 아니라는 점이에요. 사람은 방향, trade-off, 과한 것과 필요한 것을 결정했고, 에이전트가 문서 구조·hook·slash command·decision log 규칙을 대부분 제안하고 정리했거든요. 실제 셋업은 약 2시간 정도 걸렸고요.
docs/_claude/Context.md를 만들고 프로젝트에 대해서 10분 브리핑을 검토합니다.- 루트
CLAUDE.md에 그 파일을 먼저 읽으라고 적고요. - 결정 로그 하나를 만들고, 번복은 수정이 아니라 새 항목으로 남깁니다.
그 다음에 문서가 늘고, 충돌이 보이고, 매번 같은 설명을 다시 하게 되면 hook 과 Obsidian 을 붙이면 됩니다. 시스템은 처음부터 크게 만들수록 좋은 것이 아니라, 문서가 실제로 무너지기 시작하는 지점에서 한 층씩 올리는 편이 오래 갑니다.
맺으며
이 시리즈를 정답이라고 적은 건 아니에요. 레퍼런스는 3개였고
작은 팀에서 빠르게 작업하면서 맥락 유지, 정합성 관련 빠르게 애자일하게 작업이 가능해졌다 관련한 이야기입니다.
사실 제가 3개월 뒤에 이 글을 다시 읽으면 어색한 대목이 적잖이 보일 것 같아요. Claude Code 의 패악질도 그렇고...ㅠㅠ (OpenClaw, Bad Claude - 이거는 약간 밈적인 반응이라고 생각하긴 함, Open Code 등.. 폐쇄적으로 구는 점이 조금 괘씸합니다. 구독제 없애고 종량제 토큰 비용으로 바뀐다..는 흉흉한 말도 돌고요.)
모델이 바뀌면 컨텍스트 한계가 늘어나고, MCP 생태계가 커지면 지금 hook 에 맡긴 일들이 그쪽으로 넘어갈 수도 있을지도 모릅니다.decision_log.md 도 어느 시점에는 쪼개야 할 거고 (현재는 점진적 마이그레이션), Beads 도입을 검토해볼 수 도 있을 것 같습니다.
또한 docs/_claude/ 에 적은 룰 중 절반은 폐기될지도 모릅니다.
그래도 한 가지는 안 바뀔 것이라 확신하는 지점이 토큰을 최대한 효율적으로 쓰면서 맥락을 어떻게 관리하고 다룰지는 도구가 바뀌어도 중요한 문제일 거라고 생각합니다.
팀끼리 결정된 결정을 어딘가에 적어 둬야, 그다음 사람 (혹은 그다음 세션의 자기 자신) 이 그 위에서 다시 일을 시작할 수 있다는 점입니다
이 글을 따라 읽으면서 한 가지라도 본인 팀에 맞춰 줄여 볼 만한 대목이 있었다면 그걸로 충분합니다.
4편 묶음을 한 번에 다 따라 할 필요는 없어요.
긴 글 따라와 주셔서 고맙습니다. 본인 레포에서 한번 시도해보시면서 더 맛도리로 적용해보시길 바라요! m( _ _ ) m 꾸벅
'AI 메모리 관리 시스템 (연재 중)' 카테고리의 다른 글
| [AX 사내도입까지? 시즌 2-01] Dorito's AI Agent Memory System (진행 중) (1) | 2026.06.24 |
|---|---|
| [AI 메모리 시스템 적용기 회고:02편] 위키 관리도 자동화하고 사람의 실수를 막자! karpathy llm wiki 위에 얹은 자동화 시스템 (1) | 2026.05.02 |
| [AI 메모리 시스템 적용기 회고:01편] 문서를 많이 줬는데 AI는 왜 더 헷갈렸을까 (0) | 2026.04.27 |
| [AI 메모리 시스템 적용기 회고:프롤로그] AI를 잘 쓰려면, 프롬프트보다 먼저 메모리를 설계해야 한다 (4) | 2026.04.26 |