Claude Code mods를 최소 코드로 읽기: 시도 카운터와 모의 테스트
요약
본 기사는 Claude Code mods를 활용하여 AI의 툴 호출 이벤트를 감지하고 카운팅하는 방법을 다룹니다. `next(event)`를 사용하여 원래 동작을 통과시키면서, `tool.call` 이벤트가 발생할 때마다 시도 횟수를 기록하는 모드 구현 방식을 설명합니다.
핵심 포인트
- Claude Code mods는 코드 레벨에서 AI의 이벤트를 가로채 개입하는 확장 기능입니다.
- `tool.call` 이벤트 핸들러를 통해 툴 호출 시도를 감지할 수 있습니다.
- 카운터 값은 모드 등록 기간 동안의 메모리상 값이므로 재시작 시 초기화됩니다.
- 이벤트 처리는 하류(downstream) 동작에 영향을 주지 않도록 `next()`로 전달해야 합니다.
Claude Code mods는 AI에게 지시하는 문구를 늘리는 메커니즘에만 국한되지 않습니다. Claude Code 자체의 이벤트를 받아 툴 호출이나 화면 표시 등에 코드로 개입하는 확장 기능입니다.
본 기사에서는 '툴을 몇 번 호출하려고 했는지'를 보여주는 작은 mod을 예로 들어, next(event)를 사용하여 원래 동작을 통과시키는 설계와 단위 테스트로 확인할 수 있는 범위를 정리했습니다. 2026년 10월 6일 공식 자료를 확인했으며, 샘플은 Node.js의 모의 환경으로 실행했습니다. Claude Code 내부에서의 로딩, 그리기, 결제는 미검증입니다.
| 필요한 것 | 후보 | 주된 이유 |
|---|---|---|
| 매번 붙여야 하는 절차를 공통화하기 | Skill | 학습시키고자 하는 지식과 절차를 정리할 수 있음 |
| ... | ||
| 공식의 Mods overview는 mods를 plugin 내부의 JavaScript/TypeScript 핸들러로 설명하고 있습니다. plugin은 배포 및 로딩 단위이므로, Skill, MCP, mod의 역할을 동일시하지 않는 것이 구성을 정리하기에 용이합니다. |
예를 들어 '테스트를 실행한 후 완료 보고'라는 지시만 필요하다면 Skill로 충분합니다. 테스트 실행 시도 횟수를 화면에 보여주고 싶다면 mod가 후보입니다. 다만, 시도 횟수 표시가 그 테스트 결과의 정확성을 보장하는 것은 아닙니다.
tool.call 이벤트가 전달되는 시점에는 하류(downstream) 툴이 성공했는지 아직 알 수 없습니다. 그래서 변수명과 표시명 모두 attempts로 합니다.
여기서 세는 것은 이 핸들러에 도달한 이벤트입니다. 실제 네트워크 요청 수, 모든 mod을 포함한 실행 횟수, 모델의 토큰 양, 결과물의 완성 수가 아닙니다. 다른 mod이 먼저 이벤트를 처리하는 경우도 있으므로, 표시명에 측정 대상을 남길 필요가 있습니다.
공식의 Create a mod 구성에 맞춥니다.
tool-attempt-counter/
├── .claude-plugin/plugin.json
└── hooks/
...
plugin.json:
{"name":"tool-attempt-counter","version":"0.1.0","description":"Observe tool call attempts"}
hooks.json:
{"description":"Attempt counter hooks module","modules":["./register.js"]}
register.js:
export function register(on) {
let attempts = 0;
on('tool.call', async ($, event, next) => {
...
이벤트를 그대로 next로 전달하고 있으므로, 이 mod은 툴 이름이나 인수를 수정하지 않습니다. UI 측에서는 복사한 props에 suffix를 추가하여 기존의 label 등을 유지합니다. 카운터는 mod이 등록된 동안의 메모리상의 값이며, 재시작 후의 누적값이 아닙니다.
테스트용 on은 이벤트 핸들러를 Map에 등록하고, next 대신 함수를 전달합니다. 확인한 케이스는 정상적으로 진행되는 호출 2건과 하류에서 실패하는 호출 1건입니다. 테스트는 test.mjs를 plugin의 루트에 저장하여 Node.js로 실행할 수 있습니다.
import assert from 'node:assert/strict';
import { register } from './hooks/register.js';
const handlers = new Map();
...
node test.mjs
2026년 10월 6일, macOS/Node.js v26.9.0에서 실행하여 다음을 확인했습니다.
- 하류로 전달된 이벤트는 원래 객체와 동일합니다.
- 성공 2건, 실패 1건이라도 시도는 3입니다.
- 하류의 예외를 가로채지 않고, 같은 예외를 반환합니다.
- 원래 Spinner의 props를 변경하지 않고, label과 기존 suffix를 유지합니다.
이 테스트는 Claude Code의 API 구현, 핸들러의 순서, 실제 Spinner 표시를 재현하고 있지 않습니다. Node에서 통과한 것과 Claude Code의 실환경에서 작동하는 것은 별개의 검사입니다. 공식적으로 mod의 테스트 절차도 있습니다. 현행 버전으로 검증을 추가할 때는 그 절차와 생성된 타입 정의를 확인합니다.
공식 자료의 대상은 터미널에서는 Claude Code v2.1.287 이후입니다. 데스크톱의 Code 탭에서는 v2.1.286 이후가 표시됩니다. 화면에 그려지는 위치와 핸들러가 실행되는 위치에는 차이가 있으므로, VS Code의 채팅 패널이나 비대화 처리에서 동일한 표시를 기대하지 않도록 합니다.
직접 시도해 볼 경우의 명령어는 다음과 같은 형태입니다. 본 기사 제작 시에는 실행하지 않았습니다.
claude --version
claude plugin validate ./tool-attempt-counter
claude --plugin-dir ./tool-attempt-counter
validate에서는 구성뿐만 아니라 어떤 이벤트를 처리하고 어떤 API를 호출하는지까지 확인합니다. 표시용 mod라 하더라도, mod 전체가 격리되어 있는 것은 아닙니다. 공식적으로는 사용자 권한으로 파일이나 네트워크에 접근할 수 있으며, 모델 호출을 통해 이용량을 소비할 수 있음이 설명되어 있습니다.
우선은 이번처럼 파일 쓰기, 모델 호출, 외부 통신을 포함하지 않는 관측 처리로 한정하면, 검토할 대상을 작게 할 수 있습니다. 이것은 권한을 제한하는 메커니즘이라기보다는 코드의 책무를 줄이는 설계상의 선택입니다.
추가적인 지식이 필요하다면 Skill, 외부 조작이라면 MCP, 기존 스크립트와의 연동이라면 settings hook에서 검토합니다. Claude Code 내부의 UI나 이벤트를 다룰 필요가 있을 때 mod를 선택하면 확장의 역할이 명확해집니다.
mod를 만들 경우에도 '시도', '성공', '완료'를 나누어 기록해야 합니다. 보기 좋은 미터기일수록, 무엇을 측정하지 않았는지까지 표시하고 설명해 두면 개발 판단에 유용합니다.
- Customize Claude Code with mods (발표: 2026년 10월 1일)
- Mods overview
- Create a mod
- Test a mod
사양 확인: 2026년 10월 6일. 코드 실행은 로컬 모의 테스트만 가능합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기