본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 22. 19:06

CLAUDE.md가 스스로 작성된다고 말한 지 3주 후, 나도 모르게 4개의 규칙이 추가되었다

요약

코딩 에이전트를 제약하는 CLAUDE.md 파일이 개발 과정 중 발생하는 사건을 통해 스스로 규칙을 업데이트하는 과정을 다룹니다. 기술 부채 감사나 프로덕션 이슈를 통해 새로운 규칙이 어떻게 자연스럽게 프로젝트에 통합되는지 실무적 관점에서 설명합니다.

핵심 포인트

  • CLAUDE.md는 고정된 문서가 아닌 지속적으로 진화하는 규칙 집합임
  • 개발 중 발생하는 기술 부채나 오류가 새로운 에이전트 규칙의 근거가 됨
  • 에이전트의 실수를 방지하기 위한 RBAC 유출 방지 규칙 사례 제시
  • 1인 개발자에게 에이전트 제어 규칙의 자동화된 기록은 매우 중요함

3주 후의 논지. 4월 28일, 나는 매 세션마다 코딩 에이전트 (coding agent)를 제약하는 CLAUDE.md 파일에 대해 네 가지 주장을 담은 글을 DEV.to에 게시하며 다음과 같은 문장으로 끝을 맺었습니다: "CLAUDE.md는 결코 완성되지 않으며, 바로 그 점 때문에 작동합니다" (4가지 사건, 4가지 규칙: 나의 CLAUDE.md가 스스로 작성된 방식). 그것은 은유가 아닌 논지였습니다. 3주가 지났습니다. 그 파일은 내가 개입하지 않았음에도 네 개의 규칙을 추가했습니다. 제 말은, 규칙을 쓰기 위해 자리에 앉은 날에 그것들을 직접 작성하지 않았다는 뜻입니다. 나는 사건(incident)이 규칙을 만들어낸 날에 그것들을 전달받았고, 프로젝트의 흐름 속에서 증발해 버리기 전에 기록하기만 하면 되었습니다. 서류상으로는 그 차이가 미미해 보일 수 있습니다. 하지만 프로덕션 (production) 환경에서 에이전트를 조종하는 1인 개발자 (solo dev)의 실무 관점에서는 교리적인 차이입니다. 목록을 나열하기 전 한 가지 명확히 하자면, 이 기사의 제목은 거의 "5개의 규칙"이라고 할 뻔했습니다. live-snapshot-cache.md는 전환점이 된 기사가 게시되기 3일 전인 4월 25일에 커밋되었습니다. 그것은 포함하지 않겠습니다. 나는 편한 반올림보다는 정직한 숫자를 택하겠습니다.

git으로 측정한 감사 (audit)
가공되지 않은 자료 없이는 서사도 없습니다. 다음은 2026년 4월 28일(전환점 기사 게시일)부터 2026년 5월 21일(오늘) 사이에 .claude/rules/ 디렉토리에서 git log --diff-filter=A --follow 명령어를 실행했을 때 반환되는 결과입니다. 즉, 게시 이후에 엄격히 추가된 네 개의 새로운 파일입니다.

cache-auth-contract.md — 5월 2일 커밋됨. 프로덕션 충돌 (production crash)이 아닌 기술 부채 (technical debt) 감사에서 탄생했습니다. 금요일 늦은 오후입니다. 니란 (Niran)은 두 책상 너머에 앉아 헤드폰을 쓰고 있고, 구석에는 다 먹은 버거 상자가 놓여 있습니다. 나는 오른쪽 화면의 docs/dette/AUDIT-2026-04-30.md 섹션 D-20을 보고 있고, 왼쪽에는 코드를 띄워 놓았습니다. getCachedFormateurs를 읽어 내려가며, 나는 unstable_cache가 모든 사용자 간에 공유되며 세션 (session)이 전파될 수 없다는 것을 이해합니다. 만약 누군가 가드 (guard) 없이 API 경로를 통해 이 함수를 노출한다면, 이는 소리 없는 RBAC (Role-Based Access Control) 유출입니다. 나는 니란에게 이 문제를 말하기 위해 고개를 들었습니다. 그는 헤드폰을 벗고 듣더니, "음, 그거 골치 아프겠네요."라고 말하고는 다시 헤드폰을 씁니다. 그날 저녁, 규칙이 작성되었습니다.

// .claude/rules/cache-auth-contract.md — anti-pattern to prohibit // Flaw: no guard export async function GET () { const formateurs = await getCachedFormateurs () return Response . json ( formateurs ) } // Correct export async function GET ( req : NextRequest ) { const supabase = await createSupabaseServer () const { data : { user } } = await supabase . auth . getUser () if ( ! user ) return new Response ( ' Unauthorized ' , { status : 401 }) const profile = await getUserProfile ( user ) if ( ! canAccess ( profile , ' communication ' )) return new Response ( ' Forbidden ' , { status : 403 }) const formateurs = await getCachedFormateurs () return Response . json ( formateurs ) } // inscrit-nom-prenom-required.md — committed May 14th. "Hm, it's buggy." — Catherine, two hours earlier. "But it's a quick fix." The daily drift probe sonde_contacts_orphelins_inscrits surfaced an inscrit -status contact with an empty first name — a child named Loubna, imported from Airtable where the first name lived in a separate unmapped column. The grep that followed found sixteen similar cases. What would have broken regular attendance tracking ( Cannot read properties of undefined ) gets caught by a Postgres CHECK constraint that closes the incident class at the root. -- .claude/rules/inscrit-nom-prenom-required.md CHECK ( statut <> 'inscrit' OR ( nom IS NOT NULL AND nom <> '' AND prenom IS NOT NULL AND prenom <> '' ) ) Without this CHECK, the rule stays textual in CLAUDE.md and the next import brings back a seventeenth case before the next probe. With it, the INSERT fails, and the import surfaces the problem at the source. contrat-formation.md — committed May 16th, in the wake of ADR-0068. It's the longest rule, because the professional training contract is a Snapshot where every column carries its guarantee of immutability. motivation_code , text_version , cases_cochees , pdf_storage_path — frozen at generation, never recalculated retroactively.

계약(contract)의 진화는 결코 스냅샷(Snapshot)을 다시 쓰는 것이 아니라, 새로운 text_version을 가진 새로운 이벤트입니다. 이 규칙이 존재하는 이유는 3년 주기의 Qualiopi 심사가 생성된 PDF와 관련 수강생 서명의 불변성(immutability)에 전적으로 의존하기 때문입니다. 소급하여 재계산하는 행위는 해당 파일을 방어 불가능한 상태로 만들기에 충분합니다. hybrid-snapshot-live-reset.md — 이 기사가 작성되기 이틀 전인 5월 19일에 커밋되었습니다. 53개의 2단계(Phase 2) 재등록 SMS 메시지를 발송하기 전, 사전 점검(pre-flight audit) 과정에서 53개 중 하나의 토큰이 이미 소비되었다는 사실이 드러났습니다 — 그 토큰은 그날 아침 테스트 모드에서 생성되었고, 클릭되었으며, used_at 값이 null이 아니었습니다. 만약 2단계 SMS가 그대로 발송되었다면, 링크 /r/<short_code>는 410 Gone을 반환했을 것이고, 고객은 전환(conversion) 기회를 놓치게 되며, 지원 티켓(support ticket)이 업무 종료 시점에 접수되었을 것입니다. generateTokenForContact 헬퍼 함수가 객체(frozen identity Snapshot)는 부활시키면서도, Live 사용 마커(Live usage marker)를 리셋하는 것을 잊었던 것입니다. 수정 커밋 07ed02d. 이 규칙은 해당 패턴의 이름을 명명하고, 이를 툴킷의 R6 Live / Snapshot / Cache 체계에 대입하며, 이는 해당 체계의 프로젝트별 확장(extension)이 됩니다. 이전에는 작성될 수 없었을 규칙. 이제 Catherine과 Loubna의 두 번째 규칙으로 돌아가 봅시다. 물론, 첫 번째 CLAUDE.md가 생성된 날인 2026년 3월 21일에

해당 규칙의 '이유(Why)' 단락(16개의 패치된 연락처, Airtable의 기원, case-zero를 드러낸 조사 도구를 명시하는 부분)을 작성하려면, 당신은 반드시 '확장(the widening)'의 시기를 직접 겪었어야만 합니다. "나는 첫날부터 나의 교리를 작성한다"라는 식의 말로는 결코 그 정도의 구체성을 만들어낼 수 없습니다. 이 규칙은 초안으로 작성된 원칙이 아니라, 단단하게 굳은 흉터입니다.

퇴적(sedimentation)에 관한 네 가지 관찰
네 개의 규칙을 나란히 세워두고 멀리서 바라보면 네 가지 사실이 보입니다. 첫째, 네 가지 규칙 중 그 어떤 것도 해당 사건(incident)이 발생하기 전에는 작성될 수 없었습니다. 이는 3월 21일에 제 상상력이 부족했기 때문이 아니라, 유용한 규칙이 갖는 실질적인 정밀함은 구체적인 사례와의 조우에서 나오기 때문입니다. CHECK 제약 조건(constraint), DREETS 논문의 박스들과 매핑되는 터널, SELECT와 동일한 트랜잭션 내에서의 used_at 리셋 — 이러한 작동하는 세부 사항들은 추상화만으로는 결코 만들어낼 수 없는 것들입니다.

둘째, 이 규칙들은 모두 커밋(commit), 마이그레이션(migration), 세션 로그(session log), 또는 ADR(Architecture Decision Record)을 인용합니다. 부유하는 규칙은 없습니다. 저는 피벗(pivot) 기사에서 이러한 추적 가능성(traceability)을 배웠지만, 이를 하나의 메커니즘으로서 측정해 보지는 않았습니다. 오늘날 저는 이를 측정합니다. 만약 규칙이 그 실질적인 닻(material anchor)을 지니고 있지 않다면, 제 역할을 수행하지 못합니다. 에이전트(agent)도 읽을 수 있고, 인간 독자도 읽을 수 있으며, 필요하다면 양쪽 모두 사건을 역추적할 수 있습니다.

셋째, 네 가지 중 세 가지는 안티 패턴(anti-pattern)을 금지하며, 네 번째는 스냅샷(Snapshot)을 고정합니다. #20에서 예측한 바와 같이 부정적인 규칙이 지배적입니다. 추상적인 긍정 규칙(기본적으로 Server Components를 사용하라)은 읽히고 잊혀집니다. 하지만 닻이 내려진 부정적 규칙(Live 마커를 조용히 리셋하지 않고 Snapshot을 반환하는 getOr* 헬퍼 함수는 다음 재사용 시 데드 링크(dead link)를 유발한다)은 그 결과(consequence)를 수반하기 때문에 읽히고 기억됩니다.

넷째, 그리고 이것이 CLAUDE.md의 상태를 변화시키는 관찰입니다. 저는 이 규칙들을 발명한 것이 아니라, 수신(received)한 것입니다. 이 구분은 수사적으로 들릴지 모르지만, 그렇지 않습니다. 규칙을 발명한다는 것은 당신이 그것을 상상한 뒤 작성한다는 것을 전제로 합니다. 규칙을 수신한다는 것은 어떤 사건이 규칙을 만들어냈고, 당신은 그것이 증발하기 전에 기록했다는 것을 전제로 합니다.

첫 번째 체제에서 이 파일은 포괄성을 주장하는 단독적인 기록 행위입니다. 두 번째 체제에서 이 파일은 유지 공간을 요구하는 퇴적 장치(sedimentation device)입니다. 즉, 열린 노트, 정기적인 검토 세션, 짧은 간격의 git log grep과 같은 공간 말입니다. 이제 작업은 쓰는 것이 아니라 포착하는 것이 됩니다.

왜 이것이 지금 작동하는가: 문서화의 모범 사례(best practices)에 대해 우리가 어떻게 생각하든, 프로젝트 시작 시점에 한 번에 작성된 CLAUDE.md는 지속되지 않습니다. 그것은 2주 만에 노후화되고, 아무도 호출하지 않는 규칙들이 쌓이며, 에이전트는 이를 작업 메모리(working memory)에 로드하지 않은 채 기계적으로 읽게 됩니다. 지속되는 CLAUDE.md는 노후화되지 않고 퇴적됩니다. 각 사건(incident)이 층을 쌓으며, 파일은 상상된 문서화가 아닌 프로젝트의 실질적인 기억을 담아냅니다.

#20 이후 3주간의 실천은 전환점(pivot)이 된 글의 논지를 실질적으로 확인해 줍니다. 하지만 그것은 #20에서 공식화하지 못했던 미묘한 차이를 추가합니다. 파일이 퇴적되려면, 그것을 유도하는 무언가가 있어야 한다는 점입니다. 매일의 표류 탐사(drift probe), 매월의 부채 감사(debt audit)

만약 답이 아무것도 아니라면, 아마도 고갈된 것은 교리(doctrine)가 아니라 사건을 발생시키는 장치(device)일 것입니다. 4건의 사건에 따른 후속 조치로서 4개의 규칙을 제시합니다: 나의 CLAUDE.md가 어떻게 스스로 작성되었는가 (2026년 4월 28일). 23일 경과 시점의 측정값이며, Rembrandt 리포지토리의 .claude/rules/에서 확인되었습니다 — 현재 18개의 규칙 파일이 활성화되어 있으며, 그중 4개는 피벗(pivot) 이후에 엄격히 추가되었습니다. 이번 후속편에는 Counterpart Toolkit을 다루지 않습니다 — 해당 주제는 별도의 시리즈에서 다룹니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0