
CLAUDE.md에 쓰는 것을 그만두다 — AI에 대한 지식은, 기계가 '그 순간'에 전달한다
요약
AI 모델의 지시 이행력을 높이기 위해 지식의 추상도(A1~A3)에 따라 전달 방식을 차별화하는 전략을 제안합니다. 특히 반복적인 규칙(A3)은 프롬프트 대신 Claude Code의 hooks와 ast-grep을 활용해 기계적으로 주입하는 구조적 해결책을 다룹니다.
핵심 포인트
- 태스크 추상도에 따라 규칙(Rule), 렌즈(Lens), 사정(Context)으로 전달 방식 분리
- A3(저추상도) 작업은 프롬프트가 아닌 기계적 체크와 에러 메시지 주입으로 해결
- 에러 메시지 자체가 AI를 위한 효과적인 프롬프트 역할을 수행
- DEVIATE 마커를 통해 AI의 예외 상황을 기록하고 관리하는 워크플로우 구축
AI가 말을 듣지 않는다. 이 문제를 우리 팀은 구조적인 측면에서 해결해 왔습니다.
첫 번째 답은 지식의 외치(外置, 외부 배치)였습니다. 지켜야 할 지식은 SKILL(필요할 때 읽어들이는 절차서)로서 등록한다. 본체는 두껍게 써도 좋다. CLAUDE.md에는 「언제 어떤 SKILL을 사용할지」만을 적는다. 프롬프트(Prompt)를 가볍게 유지하는 설계입니다.
그럼에도 지켜지지 않았습니다. 이 방식은 「AI가 올바른 순간에, 올바른 SKILL을 호출해 줄 것」에 도박을 걸고 있습니다. 지식을 외부로 빼내는 것까지는 맞았지만, 전달하는 타이밍의 판단이 모델 내부에 남아 있었다——즉, 운에 맡기는 것이었습니다.
재정비할 때 깨달은 것은, 「지켜야 할 지식」이 애초에 한 종류가 아니라는 점이었습니다. 태스크(Task)의 추상도에 따라 3가지로 나뉘며, 각각 과제가 다릅니다. 그래서 전달 방식도 따로 만들어야 합니다. 우리 팀에서는 이 3가지를 A1·A2·A3라고 부릅니다 (A는 Abstraction. 숫자가 작을수록 추상도가 높습니다).

추상도가 낮은 순서대로:
A3: 정해진 반복— 쓰고 버리는 디버그 코드가 남아 있지 않은가. as 캐스팅을 하고 있지는 않은가. 「이럴 때는 이렇게」를 사전에 전부 작성할 수 있는 업무. 과제는 종류가 무척 많다는 것 -
A2: 그 자리에서의 판단— 이 구조로 나중에 곤란해지지 않을까. 이쪽 구조가 더 심플하지 않을까. 케이스가 무한하여 절차로 다 적을 수 없는 업무 -
A1: 배경이 필요한 업무— 왜 이 사양인가. 무엇을 양보할 수 없는가. 사정을 모르면 애초에 목표를 벗어나는 업무
전달해야 할 것은 순서대로, **규칙(Rule)·렌즈(Lens)·사정(Context)**입니다. 별개의 것입니다.
A3 — 전부 작성할 수 있다면, 프롬프트가 아니라 기계에 전달한다
A3의 규약은 과거에 CLAUDE.md에 직접 작성했습니다. 적어두면 한동안은 지켜집니다. 다만, 컨텍스트(Context) 양이 늘어날수록 지켜지지 않는 횟수가 늘어나고, 지적하면 「죄송합니다, 놓치고 있었습니다」라는 답변이 돌아오는 것이 결말입니다. 이를 규약의 수만큼 인간이 계속 잡아내야 합니다.
그래서 A3의 규약은 프롬프트에서 끌어올려, 편집할 때마다 기계로 체크하고, 위반했을 때만 알려주는 형태로 바꾸었습니다. 1개의 규칙을 1개의 YAML로 만들어 .claude/ast-rules/에서 관리하고, Claude Code의 hooks(파일 편집 직후에 발화)를 통해 ast-grep으로 매칭시켜, 히트(Hit)한 규칙의 message를 AI의 컨텍스트에 inject(주입)하고 있습니다.
실제 사례 하나를 보겠습니다. React.memo를 검지하는 규칙입니다.
id: no-react-memo-in-new-code
language: Tsx
severity: warning
...
요점은 message를 작성하는 방식입니다. 독자는 AI입니다. 「~는 금지」라고만 적으면, 받은 AI가 다른 악수(Bad move)를 둘 수 있습니다. 그러므로 왜 안 되는지·대신 무엇을 사용하는지·이 패턴이 올바른 것은 어떤 케이스인지, 이 3점을 반드시 채워 넣습니다. 즉 에러 메시지는 프롬프트이다——A3에서 가장 효과적이었던 발상이었습니다.
의도적으로 벗어날 때는 DEVIATE 마커를 남깁니다.
// AI: DEVIATE(no-react-memo-in-new-code): useForm을 사용하기 위해 Compiler 비호환
이것이 있으면 엔진은 재경고하지 않습니다. disable 주석과의 차이점은 이유의 신고가 필수라는 점이며, 마커는 리뷰 시에 정밀 조사하여 타당하면 영구 코멘트로 승격시키고, 부당하면 코드를 수정합니다. 일탈은 자유입니다. 입을 다문 일탈만이 금지됩니다.
요컨대, 이것은 AI를 위한 Linter입니다. 흑백이 분명한 규칙뿐이라면 기존의 Linter를 hooks에서 실행하는 구성으로 대신할 수 있습니다. 다만, 읽는 사람이 AI로 바뀌면 오검지(False positive)의 가성비가 달라집니다. 인간용 Linter에서는 오검지가 많은 규칙은 무시되거나 무효화되어 끝납니다. 그래서 「위반이라고 단정할 수 있는 것」밖에 실질적으로 올릴 수 없습니다. 읽는 사람이 AI라면, 정오를 단정할 수 없는 질문을 던져도 검토되고, 빗나갔다면 버려질 뿐입니다. Linter에는 올릴 수 없었던 종류의 지적까지 전달할 수 있습니다——실제 사례는 후반부에 나옵니다.
A3의 과제는 궁극적으로 "종류가 많다"는 것뿐입니다. 그리고 이 구성이 효과를 발휘하는 지점이 바로 그 부분입니다. 프롬프트에 규약을 한 줄 추가하는 것은 모든 턴(turn)에 대한 과세이며, 늘어날수록 서로에 대한 주의력이 분산됩니다. 반대로 삭제하려고 해도, 상주하고 있던 지식은 영향 범위를 예측할 수 없습니다. YAML 한 장이라면 해당 편집의 순간에만 나타나며, 삭제하는 것도 파일 하나면 충분합니다. 추가와 삭제가 저렴해져야 비로소 그 많은 종류를 따라잡을 수 있습니다.
지금은 리뷰 지적이 재발할 때마다 하나씩 추가하여 28개가 되었습니다. 같은 문제를 겪는 팀이라면 비슷한 메커니즘에 도달했을 것이며, 유사한 실천 사례도 공개되어 있습니다.
A2 — 설계 판단은 코드의 표면에 드러나지 않는다
A3가 성공했기에 설계 판단도 같은 발상으로 공략하려 했습니다. 하지만 벽이 있었습니다. "React.memo가 있다"는 구문으로 알 수 있습니다. "이 구조로 나중에 곤란해지지 않을까"는 해당 행을 아무리 들여다봐도 알 수 없습니다. A2는 구조의 문제이며, 코드의 표면에 드러나지 않습니다. AST(Abstract Syntax Tree)로는 검지할 방법이 없는 것입니다.
기계적 검지가 불가능한 이상, 놓을 수 있는 곳은 컨텍스트(context)뿐입니다. 그래서 "이럴 때는 이렇게 하라"를 규칙 형태의 문장으로 쌓아 나갔습니다. "반환값의 union을 선언할 때는 실제로 반환되는 variant와 일치시켜라", "공통화할 때는 묶이는 각 케이스가 정말로 같은 것인지 확인하라"——.
이것은 꽤나 실패했습니다. 실패하는 방식은 세 가지입니다.
- 응용이 되지 않는다. 규칙은 작성된 상황에만 유효하며, 조금이라도 어긋난 케이스에는 침묵한다.
- 잘못 해석된다. 규칙은 단정형이므로, 해당하지 않는 문맥에서도 AI는 단정적으로 적용한다. 잘못된 결론이 그대로 도출된다.
- 스케일(scale)하지 않는다. 컨텍스트에 쌓는 방식이기에, 추가할수록 하나당 할당되는 주의력이 옅어진다.
지금 되돌아보면 경계의 정의는 심플했습니다.
"이럴 때는 이렇게"가 규칙으로서 작성될 수 있고 기계로 검지할 수 있다면, 그것은 A2가 아니라 A3입니다. 글로 써 내려가지 못하고 남은 것이 A2의 본체입니다.
재정의 — A2는 절차가 아니라 관점으로 전달한다
실패하는 방식을 살펴보며 깨달은 것은, 같은 판단 내용이라도 절차로서 전달하면 실패하고, 관점으로서 전달하면 통한다는 것이었습니다.
"반환값의 union을 선언했다면 일치 여부를 확인하라"는 절차입니다. 대신 이렇게 전달합니다—— "그 타입의 상태 공간은 현실에 존재하는 상태와 1:1인가. 존재할 수 없는 상태를 타입이 허용하고 있지는 않은가. 존재할 수 있는 상태를 타입이 뭉개고 있지는 않은가". 절차는 사라지고, "무엇을 중요하게 볼 것인가"만이 남은 형태입니다. 저희는 이것을 **렌즈(lens)**라고 부르고 있으며, 상태 표현·경계의 어휘·계층의 배치·시간축·구조적 방어 등 현재 8개가 있습니다.

렌즈 문서에는 정해진 절이 있습니다. 핵심 질문(무엇을 볼 것인가), 결정적인 타입(무엇이 확인되면 결론을 내려도 좋은가), 망치화(hammering)의 형태(이 렌즈 자체가 오적용될 때의 전형적인 모습). 핵심은 마지막입니다. A3의 예외는 "useForm과 공존한다면 React.memo가 옳다"와 같이 열거할 수 있었습니다. A2의 오적용은 열거할 수 없습니다. 그래서 "오적용될 때의 형태" 그 자체를 렌즈에 내장시킵니다.
규칙과 렌즈의 본질적인 차이는, 떼어냈을 때의 고장 방식입니다.
- 규칙은 결론을 내린다. 오적용하면 잘못된 결론이 그대로 도출된다 (fail-hard).
- 렌즈는 질문을 던진다. 잘못 선택해도 무관한 질문이 던져질 뿐이며, "이 쟁점은 다른 관점이다"라는 라우팅 정보가 되기도 한다 (fail-soft).
운용은 렌즈를 하나씩 사용합니다. N개의 관점을 하나의 컨텍스트에 공존시키면, 각 관점은 1/N의 주의력만 받게 됩니다. "모든 관점에서 꼼꼼히 봐줘"는 "전부 얕게 봐줘"와 같은 의미입니다.
또 다른 실패 — 관점은 "전달하는 타이밍"에 결정된다
렌즈를 정비하고 나서도 여전히 실패했습니다. 이번에는 "관점은 가지고 있는데, 사용해야 할 순간에 나타나지 않는다"였습니다. 구현 후에 렌즈를 적용하면 지적은 나옵니다. 다만 돌아오는 대답은 또 그 예의 그 대답입니다—— "죄송합니다, 놓쳤습니다". 관점은 처음부터 전달되어 있었습니다. 사라진 것이 아니라, 사용하는 순간에 상기되지 않는 것입니다.
그래서 편집 루프에 「지금 어떤 관점으로 보아야 하는가」만을 판단하는 가벼운 **감시자 (Watchdog)**를 상주 시켰습니다. A3와 동일한 편집 시 hooks이지만, 포착하고 싶은 것은 구문(Syntax)으로 쓸 수 없는 「판단의 형태」이기에 ast-grep으로는 대조할 수 없습니다. 대신, 편집할 때마다 diff를 소형 모델 (Claude Haiku)에 전달합니다. 구조 그 자체는 diff에 나타나지 않지만, 구조적인 문제가 diff에 배어 나올 때의 「증상의 모습」에는 일정한 패턴이 있습니다. 그 패턴의 카탈로그와 대조하게 합니다.
편집할 때마다 모델을 돌린다고 해도, Haiku이기에 비용이나 지연 시간은 체감상 느껴지지 않습니다. 다소 비용을 지불하더라도, 인간이 리뷰에서 동일한 지적을 쓰는 시간이 줄어든다면 저렴하다——그런 채산성을 고려했습니다.
카탈로그는 1증상 1파일의 YAML 형식으로 .claude/lens-signals/에 두고 있습니다. 예를 들어 「플래그의 병렬 추가」라는 증상입니다.
id: parallel-flags
paths: ["**/*.ts", "**/*.tsx"]
detect: The diff adds parallel boolean flags or individually nullable fields
...
isLoading과 isError를 별개의 useState로 가지는 것과 같은 형태입니다. 두 플래그가 정말로 독립적일 때도 있고, 실제로는 배타적(둘 다 true가 될 수 없음)일 때도 있습니다. 어느 쪽인지는 구문만으로는 결정할 수 없습니다. 그래서 단정 짓지 않고, 질문으로서 흘려보냅니다. 매칭되면 구현 중인 AI에게는 다음과 같이 전달됩니다.
[감시자] 이 편집에서 「플래그·nullable의 병렬 추가」 형태가 보입니다 (추측 — 빗나갔다면 무시해도 좋음):
- 상태의 수가 현실과 일치하는가. 현실에서 일어날 수 없는 조합이 타입 상에서 만들어진다면, 하나의 union으로 묶을 것.
...

감시자의 설계 원칙은 거의 모두 실측을 통해 결정되었습니다. 실패 사례를 포함하여 작성합니다.
- 판단하게 하지 않는다. 명령이 아닌 속삭임. 「빗나갔다면 무시해도 좋음」을 매번 명시한다. 확신 없는 탐지를 명령 형태로 내보내면, A2를 규칙화하려다 실패했던 상황의 재연이 된다.
- 추상적으로 묻지 않는다. 「이 편집에 설계 판단이 포함되어 있는가」라고 물으면, 소형 모델은 자신의 상식에 따라 PASS를 내보낸다. 구체적인 증상 기술과의 대조만을 수행하게 한다.
- 관측은 내보내게 하되, 의견은 내보내지 않는다. 「관측: 이 diff에서는 ~」라는 사실을 한 줄 덧붙이게 한다. 질문뿐이라면 일반론으로 흘려보낼 수 있지만, 관측 사실이 나열되면 흘려보낼 수 없게 된다.
- 집약은 코드로 한다. 소형 모델은 대조(열거)는 정확해도, 집약 과정에서 자기 모순을 일으킨다.
- 대조 카탈로그는 영어로 작성한다. 일본어로 대조하게 했더니 정밀도가 양방향으로 무너졌다. 주입하는 일본어 문장은 사전에 인간 측에서 작성해 두고, 모델에는 분류 결과만을 내보내게 한다 (위의 YAML에서
detect만 영어인 이유가 이것입니다).
시그널은 「싸게 죽어도 되는」 설계입니다. 실제로 올바른 코드에 하루 3번 오작동하여 퇴역한 사례가 있습니다 (「변환 처리를 소비 측에 작성하지 않았는가」——나쁜 형태와 좋은 형태가 diff 상에서는 똑같은 모습이며, 차이는 파일이 어느 계층에 있느냐에 달려 있습니다. diff 단독으로는 원리적으로 판정할 수 없었습니다). 특정 사고의 표면에 너무 치우쳐서, 유사 사례로 일반화하지 못하고 퇴역한 것도 있습니다. 사고마다 시그널을 추가하는 것은 「이럴 때는 이렇게」를 쌓아 올리는 것과 같은 실패입니다.
계층을 넘나드는 이동도 일어납니다. 시그널이 엄격한 정규 표현식을 원하기 시작한다면, 그것은 「AST 규칙이 되고 싶어 한다」는 자백——A3로 옮깁니다. 반대로, 렌즈의 질문에서 기계 판정이 가능한 파편이 발견되면, 그 부분만 A3로 졸업시킵니다. 3계층은 고정된 분류표라기보다, 이러한 승격·강등을 포함한 하나의 메커니즘입니다.
A1 — 정보가 압도적으로 부족하다. 그래서 묻게 한다
가장 상위 계층. 무엇을 하고 싶은가. 반드시 회피하고 싶은 것, 반드시 달성하고 싶은 것은 무엇인가. 여기서 초점을 놓치면, 코드는 맞지만 업무로서는 틀린 결과물이 나옵니다. 과제는, AI에게 전달되는 정보량이 압도적으로 부족하다는 점입니다.
모든 상황을 전달할 수 있는 환경——모든 회의록, Slack의 광범위한 대화, 사무실에서의 대화까지 AI가 참조할 수 있는 환경과 권한——이 가장 이상적이겠지만, 지금은 아직 현실적이지 않고 가성비도 떨어집니다. 사양(Specification)의 배경에는 구두 합의나 과거의 실패, 사업의 온도감이 섞여 있어, 이를 망라하여 글로 써 내려간 적도 없습니다. 그래서 방향을 반대로 바꾸어, AI 측에서 질문 공세를 퍼붓게 하기로 했습니다. 설계를 확정하기 전에 AI가 인간을 인터뷰하여, 양보할 수 없는 요구사항, 피하고 싶은 결과, 판단이 갈리는 분기점을 먼저 해결합니다(/grill-me를 사용하고 있습니다). 자율 에이전트(Autonomous Agent)에게는 "판단이 갈리면 안 A(잃는 것) / 안 B(잃는 것) / 권장 형태로 질문하고, 그 논점에 의존하는 작업은 중단하라"는 계약을 맺었습니다.
다 써 내려갈 수 없는 것을 억지로 쓰는 것보다, 질문을 통해 끌어내는 것이 더 빠르다——그것이 이 계층(Layer)의 현시점에서의 해답입니다.
요약 — "말을 듣지 않는다"는 것은, 전달하는 대상의 착오
| 계층 | 업무의 성격 | 과제 | 전달하는 것 | 메커니즘 |
|---|---|---|---|---|
| A3 | 정해진 반복 업무 | 종류가 많음 | 규칙 (결론을 내기) | ast-grep + 편집 시 hook. 메시지는 AI를 위해 작성 |
| A2 | 그 자리에서의 판단 | 표면화되지 않음·절차화할 수 없음 | 렌즈 (질문을 던지기) | 렌즈 문서 + 편집 시 감시자의 속삭임 |
| A1 | 배경이 필요한 업무 | 정보량이 부족함 | 상황 (질문을 통해 끌어내기) | AI의 질문 공세 · 구조화된 질문 시 중단하는 계약 |
"AI가 말을 듣지 않는다"고 느꼈을 때, 모델의 능력을 의심하기 전에 전달하고 있는 대상의 종류를 의심해 볼 가치가 있습니다. 기계로 탐지할 수 있는 규약을 프롬프트(Prompt)에 적어 품질을 저하시키고 있지는 않은가. 절차로 써 내려갈 수 없는 판단을 규칙의 형태로 만들어 잘못 적용하게 하고 있지는 않은가. 계층이 맞지 않으면 아무리 말을 다해도 효과가 없습니다.
운용 원칙은 두 가지로 압축할 수 있습니다——지식을 전달하는 방법을 모델에게 맡기지 않는다. 그리고 AI에게 판단하게 하기 전에, 기계로 탐지할 수 있는 것은 모두 기계로 넘긴다. LLM의 주의력(Attention)과 판단력은 유한한 자원입니다. 규칙으로 끝낼 수 있는 업무에 낭비하지 말고, 규칙으로 만들 수 없는 판단을 위해 아껴두어야 합니다. 이를 위한 3개 계층이었습니다.
Discussion

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