Claude Code를 팀에 도입하고 나면 곧 한 가지 불편한 진실과 마주친다. 에이전트는 등록된 도구보다 훈련 데이터에서 학습한 습관을 먼저 꺼낸다. MCP 서버를 아무리 잘 설계해도, 에이전트가 그 도구 대신 익숙한 셸 명령을 먼저 집어 든다면 설계는 무력하다. 프롬프트로 "이렇게 해달라"고 부탁하는 것만으로는 부족하다. 이 글은 그 한계를 시스템 레벨에서 돌파하려는 세 가지 실무 기법을 다룬다.
기법 1: PreToolUse 훅으로 나쁜 습관을 차단한다
git-prism v0.7.0이 해결하려는 문제는 단순하다. Claude Code는 git-prism이라는 MCP 서버를 통해 체인지 매니페스트, 함수 컨텍스트, 파일 스냅샷 등 구조화된 다섯 가지 도구를 쓸 수 있다. 그런데 브랜치 리뷰를 시키면 여전히 git diff를 먼저 꺼낸다. 수백만 건의 git 명령 패턴으로 훈련된 근육 기억이 MCP 도구 등록보다 앞서는 것이다.
git-prism이 이 문제를 푸는 방식은 흥미롭다. Claude Code의 PreToolUse 훅—에이전트가 도구를 실행하기 직전에 개입할 수 있는 메커니즘—에 리다이렉트 인터셉터를 심는다. 인터셉터는 Python shlex 토크나이저로 명령을 구조적으로 파싱하고(정규표현식이 아니다—파이프라인, 서브셸, 복합 명령도 정확히 처리한다), 세 가지 중 하나로 판정한다.
- Exit 2 (하드 블록):
gh pr diff같은 명령은 RAW 패치 텍스트를 뱉어 에이전트가 신뢰성 있게 처리할 수 없다. 셸에 도달하기 전에 차단된다. - Exit 0 + JSON (어드바이저리):
git diff main..HEAD는 실행은 허용하되, 다음번에는get_change_manifest를 쓰라는 구조화된 제안을 함께 돌려준다. - Exit 0 (사일런트):
echo hello world같은 무해한 명령은 아무 간섭 없이 통과한다.
여기서 주목할 설계 원칙이 있다. 하드 블록은 에이전트를 막는 게 목적이 아니라, 더 나은 경로로 유도하는 게 목적이다. 새로 추가된 review_change 도구가 그 목적지다. git diff가 PR 하나에 5만 토큰의 비구조적 헝크 노이즈를 뱉는 동안, review_change는 파일별 메타데이터, 함수 레벨 diff, 블라스트-레이디어스 리스크 스코어를 페이지네이션된 JSON으로 반환한다. 컨텍스트 창 예산도 함께 관리된다—기본 8,192 토큰 초과 시 함수 분석을 점진적으로 축소하고, 잘린 항목에는 truncated: true 플래그가 붙어 에이전트가 무엇이 누락됐는지 정확히 안다.
팀 도입 관점에서 실용적인 포인트는 설치 단순성이다. git-prism hooks install 한 줄이 스크립트를 ~/.claude/hooks/에 복사하고 settings.json에 PreToolUse 엔트리를 등록한다. 재설치는 멱등성이 보장된다. 서브에이전트에도 동일하게 적용된다.
기법 2: SCIP 인덱스로 환각의 근원을 제거한다
AI 코딩 어시스턴트가 만드는 버그 중 가장 골치 아픈 유형이 있다. 존재하지 않는 라이브러리 메서드 호출이다. 타입 오류도 아니고 로직 오류도 아니다. 문법적으로 완벽하지만 deprecated됐거나 아예 없는 메서드를 자신감 있게 호출한다. 고치라고 하면 40% 확률로 역시 존재하지 않는 다른 메서드를 제안한다. 15분과 3만 토큰이 날아간다.
원인은 구조적이다. 모델의 훈련 데이터는 스냅샷이고, 라이브러리는 스트림이다. 모델이 알고 있는 StackExchange.Redis는 2.5 버전이고 지금 쓰는 건 2.8이다. grep으로 node_modules를 뒤지는 우회책은 오히려 상황을 악화시킨다—"Connect"가 200군데 나오지만 어떤 게 실제 메서드 정의인지 구분할 수 없다.
dev.to에 공개된 실험 사례(ladislav_sopko)는 이 문제를 MCP로 접근한다. Sourcegraph가 오픈소스로 공개한 SCIP 포맷—IDE의 "참조 찾기" 기능을 구동하는 시맨틱 인덱스—을 Claude Code에 연결하는 방식이다. mcp.example4.ai라는 퍼블릭 MCP 서버가 881개 오픈소스 라이브러리의 SCIP 인덱스를 호스팅한다. React, Vue, Django, FastAPI, Spring Boot, Tokio, Tailwind 등 11개 언어가 커버된다. MCP 클라이언트 설정에 URL 한 줄 추가하면 끝이다.
연결 후 달라지는 건 에이전트의 응답 경로다. 라이브러리 관련 질문이 들어오면 모델 파라미터를 먼저 뒤지는 대신 xmp4_search, xmp4_source, xmp4_usages 등의 도구를 호출해 현재 버전의 실제 소스를 가져온다. 공개된 벤치마크에 따르면 grep 기반 대비 동일한 답변에 70~93% 토큰을 절약했다. 숫자보다 중요한 건 부수 효과다. 검색에 토큰을 덜 쓰면 컨텍스트 창이 오래 유지되고, 긴 리팩터링 세션에서 대화가 표류하지 않는다.
한계는 명확하다. 비공개 레포에는 작동하지 않는다(프라이빗 인덱서가 필요하다). 아직 인덱싱되지 않은 라이브러리도 있다. 하지만 "AI가 이 특정 클래스의 실수를 하지 않게 만드는" 목적 하나만으로도 2분짜리 설정 변경은 충분히 값어치가 있다.
기법 3: 하네스 엔지니어링으로 프롬프트의 한계를 넘는다
요즘IT에 공개된 code-forge 개발기는 세 기법 중 가장 넓은 시야를 다룬다. 저자의 핵심 주장은 하나다: 프롬프트 엔지니어링만으로는 AI 출력을 강제할 수 없다. 굵은 글씨로 "절대 하지 말라"고 써도 에이전트는 가끔 그냥 한다. 이건 프롬프트가 나빠서가 아니라 자연어 지시에는 강제력이 없기 때문이다.
하네스 엔지니어링은 모델 바깥에서 에이전트의 실행 환경 자체를 설계하는 접근이다. OpenAI가 올해 초 이 개념을 소개했고, 마틴 파울러도 다룬 바 있다. code-forge의 하네스는 3층 구조다: 시스템 레벨 훅 → 프롬프트 레벨 컨벤션(AGENTS.md) → 멀티모델 교차 검증.
실행 기록이 솔직해서 참고할 만하다. 처음엔 Claude Code의 hooks가 4줄짜리 exit 0 스텁 5개였다. "나중에 채우자"고 몇 달을 뒀다. 하네스를 설계한다고 했지만 실제로는 프롬프트 레벨에만 의존했던 것이다. 리팩터링 후 11개 실동작 훅, 총 441줄이 됐다.
훅 설계에서 주목할 패턴은 command 훅과 prompt 훅의 레이어링이다. guard.sh(command 훅)는 git add .나 git push --force 같은 알려진 위험 패턴을 즉시 차단한다. 빠르고 확실하다. 하지만 정규표현식은 "내가 생각한 패턴"만 막는다. find / -delete는? 한국어로 "강제 푸시해줘"라고 하면? 여기에 haiku 기반 prompt 훅을 레이어로 쌓는다—느리지만 의미를 이해한다. regex가 놓치는 부분을 LLM이 잡는 구조다.
또 하나 실용적인 설계가 SubagentStop 훅에 TypeScript 타입 체크를 거는 방식이다. /start 워크플로우가 서브에이전트를 순차적으로 spawn할 때, 세션 전체가 끝나는 Stop 이벤트를 기다리면 이미 다음 단계가 진행 중이다. 각 서브에이전트가 끝날 때마다 tsc를 돌려야 오류를 즉시 잡을 수 있다. PreCompact 훅에서는 컨텍스트 압축 직전에 현재 브랜치, 미커밋 파일, 스테이지 상태를 스냅샷으로 주입해 압축 후에도 복귀 지점을 남긴다.
마지막 레이어가 AGENTS.md다. AAIF(Agentic AI Foundation) 표준—OpenAI와 Anthropic이 2025년 Linux Foundation에 공동 기증한 에이전트 설정 표준—으로 작성된 이 파일은 Codex CLI와 Cursor가 네이티브로 읽는다. Claude만 읽는 CLAUDE.md가 아니라 어떤 모델이든 읽을 수 있는 공용 레이어다. Claude로 짠 구현 계획을 Codex에게 교차 검증시키면 다른 편향의 시각에서 허점이 드러난다. 같은 하네스 안에서 여러 모델이 다른 시각을 가지되 같은 규칙으로 움직이는 구조다.
시사점: 세 기법이 가리키는 하나의 방향
세 사례는 서로 다른 레이어를 다루지만 같은 문제의식에서 출발한다. Claude Code는 도구를 등록한다고 쓰지 않는다. 프롬프트로 부탁한다고 반드시 따르지 않는다. 제어는 시스템 레벨에서 설계해야 한다.
git-prism의 PreToolUse 훅은 "에이전트가 나쁜 경로를 선택하기 전에 가로채는" 방식이다. SCIP 인덱스는 "에이전트가 잘못된 정보를 가져오는 근원 자체를 바꾸는" 방식이다. 하네스 엔지니어링은 "프롬프트가 닿지 않는 영역을 시스템으로 채우는" 방식이다. 세 기법을 레이어로 쌓으면 훨씬 신뢰할 수 있는 팀 도구가 된다.
팀 리드로서 내가 가장 주목하는 지점은 학습 곡선이다. 세 기법 모두 설정 복잡도가 낮다—git-prism은 명령어 한 줄, SCIP 인덱스는 JSON 설정 세 줄, 하네스 훅은 작성 비용이 있지만 스텁에서 시작해 점진적으로 채울 수 있다. 반면 팀이 훅 설계를 하지 않으면, 에이전트가 아무리 빠르게 코드를 생성해도 그 코드를 검증하고 수습하는 비용이 속도를 잠식한다.
전망은 분명하다. MCP 생태계가 성숙할수록 "어떤 도구를 연결하느냐"보다 "에이전트가 그 도구를 어떻게 쓰도록 강제하느냐"가 팀 경쟁력의 핵심이 된다. 지금 당장 PreToolUse 훅 하나부터 시작해도 충분하다. 시스템이 에이전트를 통제하는 경험을 한 번 가지면, 그 다음 레이어는 자연스럽게 쌓인다.