L32 — 교훈 전파 시스템: 한 프로젝트의 삽질이 다음 프로젝트를 구하기까지 (수정중)
/develop 에서 자동으로 소비되는 경로를 설명할 수 있다.1. 풀려는 문제 — 이슈는 프로젝트 안에 갇힌다
우리는 비자명한 버그(원인이 한눈에 안 보이는 버그)를 고치면 docs/issues/{번호}_{slug}.md 이슈 문서를 남긴다(L23 의 403 케이스가 그 예다).
문제는 이 문서가 그 프로젝트 안에만 쌓인다는 것이다.
그런데 같은 교훈은 다른 프로젝트에서 똑같이 반복된다. "리버스 프록시(요청을 앞단에서 받아 뒤 서버로 넘겨주는 중계 서버) 뒤 Django 가 POST 만 403" 같은 함정은 education 에서 한 번 겪고, ndmarket 에서 또 겪는다.
그러면 "그 코드를 standarda-core/standarda-template 에 넣어 공유하면 되지 않나?" 싶다.
하지만 정작 버그를 일으킨 코드는 대개 프로젝트 스페시픽이라 공유 패키지에 올릴 수가 없다.
403 은 그 프로젝트의 웹서버 설정(vhost)·Django settings 가 맞물려 난 것이지, core 함수 하나로 뽑아낼 수 있는 게 아니다.
그래서 전파되는 단위는 코드가 아니라 추상화된 교훈이어야 한다. "언제 이런 상황이 오면 무엇을 점검하라" 를 다음 프로젝트가 읽을 수 있는 형태로 남기는 것 — 이게 교훈 전파 시스템이다.
시스템 전체 설계의 SoT 는 팀 wiki 의
lessons/README.md다(repo 밖이라 경로만 평문으로 적는다 — 사이트 링크로 걸지 않는다). 이 강의는 그 시스템을 쓰는 사람 관점으로 푼다.
2. 전파 4층 모델
이슈를 닫을 때, 교훈을 아래 4층 중 하나로 분류해 보낸다. 분류 기준은 두 가지다 — 얼마나 일반적인가(추상화 수준), 그리고 늘 켜 두는 비용(always-on 비용 — 모두의 컨텍스트에 항상 실어 둘 값어치가 있나).
| 층 | 무엇 | 어디에 | 전파 방식 |
|---|---|---|---|
| 1. 코드 규칙 | 짧고 강제 가능한 보편 규칙("절대 X 금지") | 프로젝트 CLAUDE.md ## Code Patterns (+ 가능하면 .claude/hooks/) |
공통 파일 → /sync-template → 전 프로젝트 상속. hook이면 자동 강제 |
| 2. 공통(general) 교훈 | 도메인 무관, 여러 곳에 두루 걸치는 교훈 (서사·상황 설명이 필요) | wiki/lessons/general/ |
/develop 이 항상 general 색인 조회, 본문은 매칭될 때만 |
| 3. 도메인 교훈 | 특정 도메인 프로젝트에만 유용 (llm-chat·audio·sheets·deploy…) | wiki/lessons/{domain}/ |
/develop 이 task 가 그 도메인일 때만 색인 조회 |
| 4. 프로젝트 한정 | 일반화 불가, 그 프로젝트 내부 동작에만 해당 | docs/issues/ (그 프로젝트) |
전파 안 함 |
핵심 직관은 "위로 갈수록 비싸다" 는 것이다. 1층은 모두의 CLAUDE.md 에 항상 실려 매번 자리를 차지하니, 그만큼 보편적이고 간결하고 강제 가능해야 한다. 2·3층은 색인(목차)에만 늘 있고 본문은 들어맞을 때만 읽히니 부담이 적다 — "범용이지만 늘 실어 둘 정도는 아닌" 다수가 여기 산다. 4층은 아무 데도 안 보낸다.
3. 어떻게 분류하나 — 이슈 문서의 "전파" 섹션
분류는 별도 작업이 아니라 이슈 문서를 쓸 때 그 안에서 한다.
docs/issues/ 문서 템플릿 마지막에 ## 전파 (Propagation) 섹션이 있고, 세 줄을 채운다.
이 프로젝트(education) 의 실제 이슈 docs/issues/1_prod-csrf-403-forbidden.md 를 보자:
## 전파 (Propagation)
- **일반화 가능?**: Y
- **층 분류**: `3. 도메인 교훈` (deploy) — 리버스 프록시 뒤 Django HTTPS 의
CSRF/scheme 설정은 배포 도메인 공통 함정. wiki/lessons/deploy/ 에 교훈 문서 + INDEX 한 줄 추가.
- **적용 조건(trigger)**: "Apache/nginx 리버스 프록시 뒤 Django 를 HTTPS 로 배포하는데
로그인/POST 가 403 Forbidden 으로 막힘" → SECURE_PROXY_SSL_HEADER +
X-Forwarded-Proto + CSRF_TRUSTED_ORIGINS 3종 점검.
세 줄의 의미:
- 일반화 가능? — Y 면 2·3층 후보, N 이면 4층(전파 안 함).
- 층 분류 — 위 표의 1~4 중 하나 + 이유. 2·3층이면 어디에 남길지(
wiki/lessons/{domain}/)까지 적는다. - 적용 조건(trigger) — 다음 프로젝트가 "내 작업이 이 상황인가?" 를 맞춰볼 수 있는 구체적 조건. 이 한 줄이 전체 시스템의 성패를 가른다(§5 에서 다시).
이 분류는 비자명 버그 수정 시 필수다. 이슈 문서를 커밋할 때 hook 이 강제한다(L13).
4. 2·3층은 어디에 — wiki/lessons 에 교훈 파일 + INDEX 한 줄
2·3층으로 분류했으면, 같은 작업에서 팀 wiki 에 두 가지를 남긴다:
- 교훈 파일 —
wiki/lessons/{domain}/{slug}.md(템플릿 복사). 서사·상황·해결을 담는다. - INDEX 한 줄 —
wiki/lessons/INDEX.md에 아래 형식으로 append:
| 도메인 | 제목 | 적용 조건(trigger) | 출처 | 경로 |
education #1 은 이렇게 한 줄로 색인돼 있다(같은 함정을 ndmarket 도 겪어 출처가 둘이다):
| deploy | 리버스 프록시 뒤 Django HTTPS 는 SECURE_PROXY_SSL_HEADER +
X-Forwarded-Proto + CSRF_TRUSTED_ORIGINS 3종 필수 — prod 만이 아니라 dev 도메인도 동일
| Apache/nginx 가 HTTPS 종단 후 Django 로 http 프록시하는 환경에서 브라우저 POST 만
403 으로 막힐 때 | education#1, ndmarket#2 | deploy/reverse-proxy-https-csrf-403.md |
왜 파일은 항상 새로, INDEX 는 한 줄만 덧붙이나(append) — 동시 작업 충돌 방지다. 여러 프로젝트·여러 세션이 같은 날 각자 교훈을 남긴다. 모두가 한 파일을 같이 고치면 머지 충돌(둘이 같은 자리를 고쳐 부딪힘)이 난다. 교훈은 새 파일로 떼고 INDEX 엔 줄만 더하면 서로 다른 줄이라 충돌이 거의 없다. (이건 standarda 변경 이력 추적 SoT 가 쓰는 것과 같은 설계다 — L27.)
5. trigger 가 전부다
교훈 파일을 아무리 잘 써도, 적용 조건(trigger) 이 부실하면 다음 프로젝트가 그 교훈을 못 찾는다. trigger 는 "언제 이 교훈을 봐야 하나" 를, 다음 프로젝트의 사람/에이전트가 자기 task 와 매칭할 수 있게 쓰는 것이다.
| 예시 | |
|---|---|
| ❌ 나쁨 | "LLM 관련" — 너무 넓어 매번 걸리거나 아무 때도 안 걸린다 |
| ✅ 좋음 | "LLM 에 날짜·시점을 묻거나 출력에 연/월이 들어갈 때" — 내 task 가 여기 해당하는지 바로 안다 |
표면 키워드가 아니라 상황으로 쓴다. 좋은 trigger 는 대개 "~하는 코드를 쓸 때" 또는 "~증상일 때" 형태다. INDEX 의 실제 행들을 몇 개 읽어보면 감이 온다 — 전부 상황 서술로 돼 있다.
6. 다음 프로젝트가 자동으로 소비하는 법 — lesson-finder
여기까지가 "남기는" 쪽이다. 이제 "받는" 쪽.
다른 프로젝트에서 /develop 로 개발을 시작하면, 3단계(컨텍스트 로드)에서 lesson-finder 에이전트(Agent)가 호출된다. 에이전트는 Claude Code 에서 한 가지 일만 맡아 자기 창에서 처리하는 작은 프로그램이다(L12).
이 에이전트가 하는 일은 딱 하나 — 이번 task 에 관련된 교훈만 골라 요약해 오는 것이다.
라이브러리가 수백 개로 커져도 비용이 폭주하지 않도록, "색인만 항상 + 본문은 들어맞는 것만" 원칙으로 동작한다(.claude/agents/lesson-finder.md):
wiki/lessons/INDEX.md색인을 읽는다.- 슬라이스 필터 — 색인 행 중
general또는이번 task 도메인인 행만 후보로 둔다. 나머지 도메인은 아예 안 본다. - trigger 매칭 — 후보 행의
적용 조건을 이번 task 와 대조해, 실제 해당하는 것만 고른다(보통 0~5개). - 본문 읽기 — 선별된 소수의 교훈 본문만 읽는다. 매칭 0개면 본문을 안 읽는다.
- 요약 반환 — "이번 task 에서 무엇을 점검하라 + 원문 경로" 를 메인에 몇 줄로 돌려준다.
그래서 메인 /develop 은 라이브러리 크기와 무관하게 "관련 몇 개" 만 받는다.
색인 스캔(도메인 슬라이스 안, 저렴) + 매칭 본문 소수 + 메인엔 요약 몇 줄 — 이게 스케일의 핵심이다.
그래서 §5 의 trigger 가 결정적이다 — 2단계·3단계가 전적으로 trigger 문구에 의존한다.
7. 한 바퀴 전체 그림
지금까지의 흐름을 education #1 로 끝까지 따라가 보자.
이 그림에서 놓치면 안 되는 마지막 포인트 — 시스템 자체가 전파된다(메타 전파).
이슈 문서 템플릿, lesson-finder 에이전트, /develop 워크플로우가 전부 공통 파일이라 /sync-template 로 모든 프로젝트에 상속된다.
그래서 새 프로젝트는 아무것도 안 해도 "이슈 남기면 전파 섹션 채우고, 개발 시작하면 과거 교훈을 조회하는" 습관을 물려받는다.
8. 층 간 승격 — 2층이 자라면 1층으로 졸업한다
4층 모델은 고정이 아니다.
general/(2층) 교훈이 여러 프로젝트에서 반복되고, 내용이 짧고 강제 가능해질 만큼 무르익으면 → CLAUDE.md ## Code Patterns(1층)로 졸업한다(가능하면 hook 도 붙인다).
즉 2층은 숙성 단계, 1층은 증류된 꼭대기다. 처음부터 1층에 올리려 하지 말고, 대개는 2·3층에 서사와 함께 남긴 뒤, 반복이 확인되면 규칙으로 압축해 올린다.
드물게 버그가 진짜 공유 코드(core/template)에 있으면, 교훈이 아니라 코드 자체를 고쳐야 하므로 #S 백로그로 라우팅한다(L08).
오늘 정리 + 다음
- 정리: 이슈는 프로젝트 안에 갇히지만 교훈은 반복된다. 코드가 아니라 추상화된 교훈을 전파한다. 이슈를 닫을 때 4층(코드규칙 / general / 도메인 / 프로젝트한정)으로 분류 → 2·3층은
wiki/lessons/{domain}/파일 +INDEX.md한 줄 → 다음 프로젝트의/develop이lesson-finder로 general+도메인 슬라이스에서 trigger 매칭된 교훈만 조회해 소비한다. - 흔한 함정: trigger 를 대충 쓰는 것. "LLM 관련" 같은 넓은 trigger 는 매칭이 안 돼 교훈이 조회되지 않는다 — 아무리 좋은 교훈을 써도 lesson-finder 가 못 찾으면 없는 것과 같다. 상황("~하는 코드를 쓸 때", "~증상일 때")으로 구체적으로 쓴다. 또 하나 — 2·3층 교훈을 남기면서 INDEX 한 줄을 빠뜨리는 것(파일만 있고 색인에 없으면 조회 슬라이스에 안 들어온다).
- 다음 시간: 교훈 파일을 실제로 한 편 써보고 trigger 를 다듬는 실습. 그리고 hook 으로 승격한 1층 규칙이 실제로 어떻게 커밋을 막는지(L13)와 연결.
- 자습 권장: 팀 wiki 의
lessons/README.md(시스템 SoT)와lessons/INDEX.md를 훑어보고, 지금 자기 프로젝트에서 최근 고친 비자명 버그 하나를 골라 4층 중 어디에 해당하는지 분류해 본다.