AI 엔지니어링

멀티프로젝트 Claude Code 설정 운영기 4편 — 룰은 상시 로드가 아니라 적중할 때 로드한다

Kir93 2026. 9. 1. 07:44
728x90
반응형

CLAUDE.md에 "Vercel의 react-best-practices를 따라줘"라고 적어두면 에이전트가 그 룰을 지킬 것 같다. 안 지킨다. 이름과 링크는 근거가 아니기 때문이다. 모델은 그 65개 룰의 내용을 이번 컨텍스트에서 보지 못하고, 보지 못한 규칙을 지킬 방법은 없다.

 

여러 프로젝트에 Claude Code와 Codex를 물려 팀 컨벤션을 먹이려던 나는 여기서 막혔다. 이 글은 "AI에 규칙을 어떻게 주입하나?"라는 질문에 대해, 매번 주입에서 출발해 측정 뒤 조건부 로드로 후퇴하기까지의 작업기다. 에이전트에 팀 컨벤션을 강제하려는 사람이라면 같은 벽을 만난다.

이름만 대는 지시는 지켜지지 않는다

문제의 뿌리는 단순하다. 에이전트는 매 요청마다 자기 컨텍스트 창에 들어온 텍스트만 근거로 삼는다. "react-best-practices를 따르라"는 문장은 규칙의 포인터일 뿐, 규칙 자체가 아니다. 사람이라면 링크를 눌러 읽고 오지만, 모델은 그 턴에 주어진 것만 본다. 그러니 규칙을 지키게 하려면 규칙의 내용을 실제로 컨텍스트에 넣어야 한다. 첫 결론은 명확했다. 룰을 글자로 넣자.

65개 룰을 한 화면에 욱여넣기

원본 전문을 그대로 넣으면 수만 토큰이다. 그래서 카테고리별 압축 인덱스를 만들었다. 룰 하나를 번호 이름: 한 줄 요지로 줄이고, 카테고리마다 CRITICAL·HIGH·MEDIUM 우선순위를 붙였다. React 성능 룰 65개가 8개 카테고리로, 웹 인터페이스 가이드라인이 16개 카테고리로 접혔다. 파일 맨 위에는 어떤 파일을 만질 때 이 룰이 필요한지 알려주는 globs: 프런트매터를 달았다.

---
globs: ["**/*.ts", "**/*.tsx", "**/*.jsx"]
---

# React Best Practices (v1.0.0)
# 8개 카테고리 65개 룰의 압축 인덱스. 전문은 원본 링크로.

## 1. Eliminating Waterfalls — CRITICAL
- 1.1 Defer Await Until Needed: 실제 쓰이는 분기 안으로 await를 옮긴다. 안 쓰는 경로를 막지 마라.
- 1.4 Promise.all() for Independent Operations: 상호 의존 없는 작업은 항상 Promise.all().

## 5. Re-render Optimization — MEDIUM
- 5.3 Don't useMemo Simple Primitives: 원시값 반환엔 useMemo 쓰지 마라. 오버헤드가 이득을 넘는다.
- 5.10 Subscribe to Derived State: Zustand/Redux는 필요한 슬라이스만 select. 스토어 전체를 구독하지 마라.

 

한 줄로 접어도 모델은 "1.4는 Promise.all을 쓰라는 거구나"까지 복원한다. 전문이 필요하면 상단의 원본 링크를 따라간다. 압축은 잘 먹혔다. 문제는 이걸 언제 넣느냐였다.

자동 채점 hook은 오탐으로 걷어냈다

처음엔 욕심을 냈다. 매 턴이 끝날 때 산출물이 룰을 지켰는지 자동으로 채점해 한 줄로 띄우는 hook을 붙였다. 의도는 "지키는지 감시하는 눈"이었는데, 실제로는 노이즈였다. UI와 무관한 작업에도 평가가 떴고, 엉뚱한 룰을 지목했다. 자동 판정의 정밀도가 낮으니 매 턴 뜨는 그 한 줄을 나부터 무시하기 시작했고, 무시하는 신호는 있으나 마나다. 그래서 걷어냈다. 첫 교훈은 여기서 나왔다. 자동 트리거는 정밀도가 받쳐주지 않으면 오히려 신뢰를 깎는다.

매번 주입은 대부분의 세션에서 낭비였다

더 큰 문제는 조용히 자라고 있었다. 압축 인덱스를 매 세션 상단에 상시 로드(always-loaded, 매 요청 컨텍스트 최상단에 고정으로 들어가는 텍스트)로 박아뒀는데, 이게 토큰 예산을 갉아먹었다. 두 인덱스만 합쳐서 대략 3.8k 토큰이다(글자 수를 4로 나눈 근사치, ±15%로 실제 토크나이저는 아니다). 그리고 내 세션의 대부분은 UI 작업이 아니다. 설정을 고치고, 스크립트를 짜고, 문서를 만지는 턴에도 React 성능 룰 65개가 꼬박꼬박 상단을 차지했다.

 

다음 편인 5편에서 하네스 전체의 상시 로드 표면을 실측했을 때, 이 룰들을 상시 로드에서 빼는 것이 가장 큰 레버였다. always-loaded가 6.5k에서 4.0k 토큰으로 줄었다(같은 근사 기준). "룰이 중요하니 매번 넣는다"는 첫 믿음이 여기서 흔들렸다. 중요한 것과 매번 필요한 것은 다르다.

룰을 0 토큰으로 대기시킨다

후퇴의 방향은 분명했다. 룰을 컨텍스트에서 빼되, 필요할 때 다시 부를 길을 두 개 낸다.

첫째, 파일을 편집하는 턴은 globs: 프런트매터가 처리한다. **/*.tsx 같은 glob에 걸리는 파일을 만지면 그 룰 파일이 로드된다. React 컴포넌트를 고치는 순간에만 React 룰이 들어오는 것이다. 편집이 glob에 안 걸리면 룰은 0 토큰으로 대기한다.

 

둘째, 여기엔 구멍이 있다. "이 폼 접근성 어떻게 잡지?" 같은 편집 없는 논의 턴은 파일을 안 만지니 glob에 안 걸린다. 그래서 짧은 트리거 문서 하나로 그 구멍을 메웠다.

# UI Rules Loading Trigger

현재 작업이 UI·React·Next.js와 관련되면 — 파일 편집이 없는
순수 설계·논의 턴이라도 — 답하기 전에
`.claude/rules/react-best-practices.md`와
`.claude/rules/web-design-guidelines.md`를 읽는다
(이번 세션에 이미 로드했으면 생략).
파일을 편집하는 턴은 각 룰 파일의 globs 프런트매터가 자동으로 로드한다.
이 트리거는 논의만 하는 턴을 덮는다.

 

이 문서 자체는 백 토큰짜리다. 65개 룰 전체를 대신 대기시키는 값으로는 싸다. 정리하면 룰은 평소 0 토큰으로 자다가, 편집으로 적중하면 glob이, 논의로 적중하면 이 트리거가 깨운다. 놓치기 쉬운 지점은 바로 이 이중 경로다. globs:만 믿으면 편집 없는 UI 논의 턴에서 룰이 조용히 새고, 트리거 문서만 믿으면 매 편집마다 모델이 "지금 읽어야 하나"를 판단하게 만들어 불안정하다. 편집은 기계적인 glob으로, 논의는 문서 지시로 — 성격이 다른 두 적중을 각각 맞는 도구로 잡아야 한다.

주입 빈도는 중요도가 아니라 적중률로 정한다

이 작업기의 한 문장은 이거다. 어떤 룰을 매번 넣을지는 그 룰이 얼마나 중요한지가 아니라, 이번 작업에 걸릴 확률 — 적중률 — 로 정한다. React 성능 룰은 중요하다. 하지만 내 작업 분포에서 그게 적중하는 비율은 낮다. 그러니 상시 배너가 아니라 필요할 때 여는 서랍에 둔다. 반대로 적중률이 높은 규칙이라면 상시 로드가 맞다. 중요도와 적중률을 분리하는 순간, 컨텍스트 예산은 "무엇을 자를까"가 아니라 "무엇을 언제 부를까"의 문제가 된다.

 

정리하면 이렇다. 이름을 대는 지시는 실패한다. 룰은 내용이 컨텍스트에 들어와야 지켜진다. 하지만 매 세션 상시 주입은 대부분의 턴에서 낭비이니, 룰을 0 토큰으로 대기시키고 편집(glob)이나 논의(트리거)에 적중할 때만 깨운다.

 

지금 당신 하네스의 always-loaded 토큰을 한 번 세어보라. 그리고 대부분의 세션에서 안 쓰는 룰부터 globs: 뒤로 밀어내라.

참고 자료

토큰 수치는 글자 수를 4로 나눈 근사치(±15%)로 실제 토크나이저 측정이 아니며, 상시 로드 총량은 시리즈 5편의 측정 환경 기준이다.

 

2026.07.24 - [AI 엔지니어링] - 멀티프로젝트 Claude Code 설정 운영기 3편 - Edit는 줘도 commit은 못 준다

반응형