당사(주식회사 JoinClass)는 출판, 마케팅, 영업 등의 정규 업무를 Claude Code와 셸 스크립트로 처리하고 있습니다. 그 중심에 있는 것이 바로 CLAUDE.md입니다.
과거에는 제가 사고가 발생할 때마다 CLAUDE.md에 '~~하지 말라'는 내용을 추가했습니다. AI가 git push --force를 실행하여 브랜치가 사라졌을 때는 '절대 금지 목록'을 만들었습니다. 영업 메일을 확인 없이 보내서 경어법이 틀린 메일이 거래처에 전달되었을 때도, 금지 사항을 추가했습니다.
그 결과, CLAUDE.md는 1,000줄을 넘었고, 규칙들끼리 모순되어 지시와 반대되는 동작을 하게 되었습니다. 이것이 당사의 실전 경험입니다. 이를 계기로 '200줄 이내로 축소하고, 상세 내용은 .claude/agents/에 분리', 그리고 '금지가 아닌 절차로 작성한다'는 방침으로 전환했습니다.
본 기사에서는 이러한 작성 방식의 차이를 실제 파일과 스크립트로 설명합니다. 오늘부터 자신의 CLAUDE.md를 수정할 수 있는 수준의 구체적인 내용으로 작성하겠습니다.
금지문에는 세 가지 약점이 있습니다.
- 대신 무엇을 해야 할지가 쓰여있지 않음: '직접 전송하지 마라'라고 써도, 그럼 무엇을 해야 하는지에 대한 공백이 생깁니다. 모델은 이 공백을 추측으로 채웁니다.
- 부정(否定)은 트리거가 모호함: '위험한 작업을 하지 마라'에서 '위험하다'의 범위는 모델이 매번 재해석합니다.
- 금지는 쌓이다 보면 충돌함: '중간 확인하지 마라'와 '대외 액션은 승인을 받아라'가 나열되면, 어느 것이 우선순위인지 알 수 없습니다. 당사에서 실제로 발생한 모순이 바로 이것이었습니다.
반면, 절차문은 **'조건 → 행동 → 출력처'**가 갖춰져 있기 때문에, 모델은 판단할 필요 없이 실행만 하면 됩니다.
Before (금지 형태):
- 메일을 멋대로 보내지 말 것
- SNS에 멋대로 게시하지 말 것
- 청구서를 멋대로 발행하지 말 것
After (절차 형태, 당사의 CLAUDE.md에서 발췌):
## 권한 통제 규칙
모든 액션은 `.company/steering/permissions.md`의 임계치(threshold)를 따릅니다.
- **read-only**: 분석/리포트 계열은 승인 불필요 자동 실행
...
핵심 포인트는, 금지문을 단 한 줄만 남기고 그 바로 다음에 '대신 통과하는 길'을 적어두고 있다는 것입니다. 금지를 지울 필요는 없습니다. 금지문 뒤에 반드시 절차를 배치하는 것이 규칙입니다.
'충분히 조사하지 않고 신규 정책을 시작하지 마라'는 전형적으로 효과가 없는 금지문입니다. 당사에서는 이를 트리거를 나열한 표와 제외 조건으로 수정했습니다.
### 가설 검증 필수 트리거 (`/validate-hypothesis`)
다음과 같은 시책이 제안된 경우, 실행 전에 반드시 `/validate-hypothesis`의 실행을 CEO에게 제안한다.
| 트리거 | 예시 |
...
여기에는 세 가지 구성 요소가 있습니다.
- 트리거 (언제): 구체적인 예시가 담긴 표. '새로운'이라는 해석을 예시로 고정합니다.
- 제외 조건 (언제 하지 않을지): 이것이 없으면 모델은 무엇이든 게이트를 걸어 작업이 멈춥니다.
- 위반 시 대응 (실패하면 무엇을 출력할지): 완벽하게 지키지 못했을 때의 동작까지 절차화합니다.
특히 '위반 시 대응'은 금지 형태로는 절대 쓸 수 없는 부분이며, 효과가 크다고 느낍니다.
CLAUDE.md뿐만 아니라, cron / launchd에서 claude -p를 실행하는 프롬프트도 같은 원칙으로 작성하고 있습니다. 아래는 당사에서 매일 동작하는 기사 보충 스크립트 auto-replenish-articles.sh의 일부입니다 (claude_run은 claude CLI의 래퍼 함수).
claude_run replenish --effort low --allowedTools "Write,Read,Edit" -p "
다음 주제로 Zenn 기사를 한 편 작성하여 파일에 저장해 주세요.
주제: $theme
...
'가상의 코드를 쓰지 마라'가 아니라 '이 디렉터리의 실제 스크립트를 참조하라'고 작성합니다. 참조할 경로를 전달하면, 모델은 '쓰지 않는다'를 지키는 대신 '읽으러 간다'라는 구체적인 행동을 취하게 됩니다.
절차 형태로 바꾸어도, 지켜졌는지 확인하지 않으면 의미가 없습니다. 당사에서는 생성 직후 기계적인 체크를 넣고 있습니다. 같은 스크립트의 연속입니다.
유효성 검사: 코드 블록이 2개 이상 있는지
GENERATED_ZENN="/Users/kyoagun/workspace/zenn-content-company/articles/${slug}.md"
if [ -f "$GENERATED_ZENN" ]; then
FENCE_COUNT=$(grep -c '^```' "$GENERATED_ZENN" || true)
if [ "$FENCE_COUNT" -lt 4 ]; then
echo "$(date): WARNING: 코드 블록이 부족합니다(${slug}: $((FENCE_COUNT / 2))개)" >> "$LOG_FILE"
fi
fi
프롬프트의 절차("2개 이상 포함")와 검증(펜스 4개 이상)은 일대일로 대응하고 있습니다. 셀 수 있는 형태로 절차를 작성하면, 검증도 작성할 수 있습니다. 이것이 금지문과의 가장 큰 차이점입니다. "질 낮은 글을 쓰지 마라"는 검증할 수 없지만, "코드 블록을 2개 이상 포함하라"는 grep -c
...
# 금지 표현을 포함하는 줄과 그 다음 줄을 표시합니다
grep -n -A1 -E '(하지 않다|하지 마라|금지|해서는 안 된다|NEVER|DO NOT|Don.t)' CLAUDE.md
출력을 보고, 각 금지 행에 다음에 제시된 것 중 하나가 붙어 있는지 확인합니다.
...
for i in 1 2 3; do
claude -p "새로운 광고 채널로 TikTok 광고를 시작하고 싶다. 진행해줘" \
--allowedTools "Read" > "out-$i.txt"
done
grep -l "validate-hypothesis" out-*.txt | wc -l
저희 회사의 예시에서는, 게이트가 작동하면 매번 /validate-hypothesis의 제안이 나옵니다. 만약 나오지 않는 경우가 있다면, 트리거표의 예시가 부족하다는 신호입니다.
제가 CLAUDE.md를 1,000줄까지 확장하며 배운 것은, "AI에게 무엇을 하지 못하게 할 것인가"보다 "AI가 무엇을 해야 하는가"를 작성하는 것이 결과적으로 사고를 줄인다는 점이었습니다. 금지는 사고의 기록이고, 절차는 운영의 설계입니다. 경영에서도 부하 직원에게 "실수하지 마라"라고 말하기보다는 매뉴얼을 건네주는 쪽이 성과가 나온 것과 같다고 느낍니다.
CLAUDE.md를 분할하는 설계, 우선순위를 정하는 방법, 비용을 절감하며 작성하는 방식은 서적 『CLAUDE.md 설계 패턴』에서 체계적으로 정리했습니다. 이 글의 "트리거표 + 제외 항목 + 위반 시 대응"이라는 유형 역시 다른 패턴과 함께 수록되어 있습니다.
도서 목록은 여기입니다: https://zenn.dev/joinclass?tab=books