> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 모드 만들기

> Claude가 설명으로부터 Claude Code 모드를 작성하도록 하거나, 도구 호출을 세고 명령을 추가하는 모드를 직접 작성하세요. 다시 로드 및 검증 루프를 배웁니다.

모드는 Claude Code [플러그인](/docs/ko/plugins/overview)으로, 이벤트가 발생할 때 Claude Code가 호출하는 함수를 포함한 hooks 모듈이라는 항목 파일을 가집니다. 이는 JavaScript 또는 TypeScript 파일입니다. 모드를 만드는 방법은 두 가지입니다:

* **Claude에게 작성하도록 요청**: Claude Code 세션에서 [원하는 것을 설명](#ask-claude-for-a-mod)하세요
* **직접 작성**: [튜토리얼을 따르세요](#write-a-mod-yourself) 모드의 코드가 어떻게 작동하는지 배우세요. Node.js, 번들러 또는 빌드 단계가 필요하지 않습니다. Claude Code는 `.js` 및 `.ts` 파일을 직접 로드하기 때문입니다.

모드가 올바른 도구인지 아직 결정하지 못했다면, 먼저 [개요의 비교](/docs/ko/plugins/mods/overview#compare-mods-settings-hooks-skills-and-mcp-servers)를 읽으세요.

<Note>
  모드는 Claude Code v2.1.287 이상이 필요합니다. 셸에서 `claude --version`을 실행하여 확인하세요. 모드가 로드될 수 있는지 확인하려면 [모드가 로드될 수 있는지 확인](/docs/ko/plugins/mods/troubleshoot#check-whether-mods-can-load)을 참조하세요.
</Note>

<h2 id="ask-claude-for-a-mod">
  Claude에게 모드 작성 요청
</h2>

대화형 Claude Code 세션에서 원하는 모드를 설명하면 Claude가 작성합니다. Claude는 `plugin-authoring`이라는 내장 [스킬](/docs/ko/skills)에서 작동하며, 이는 모드를 작성할 위치, 버전이 가진 이벤트 및 메서드, 모드가 로드되는 방식을 알려줍니다. Claude는 모드를 요청할 때 스킬을 로드할 수 있거나, Claude Code 프롬프트에서 `/plugin-authoring`을 실행하여 직접 로드할 수 있습니다.

모드는 승인하면 실행됩니다. 단, [Claude가 작성한 모드가 로드될 수 없는 세션](#sessions-that-skip-the-approval)은 제외됩니다.

<Steps>
  <Step title="모드 설명">
    자신의 말로 모드를 요청하세요. 예를 들어 `현재 git 브랜치를 프롬프트 위에 표시하는 모드를 만들어`라고 할 수 있습니다. Claude는 세션의 모드 폴더에 있는 자신의 디렉토리에 모드를 작성합니다. 이는 `~/.claude/dev-mods/` 다음에 세션의 ID가 옵니다. 모드의 전체 경로는 `~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/`와 같습니다.

    <Note>
      `default` 및 `acceptEdits` [권한 모드](/docs/ko/permission-modes#protected-paths)에서 Claude Code는 Claude가 모드의 각 파일을 생성하기 전에 요청합니다. `~/.claude`는 보호된 경로이기 때문입니다. 각 파일이 나타나면 승인하세요.
    </Note>
  </Step>

  <Step title="모드 승인">
    Claude가 첫 번째 파일을 저장하면 Claude Code는 세션에 대해 핫 리로딩을 활성화할지 묻습니다. 핫 리로딩은 이 세션에서 Claude가 작성한 모드를 실행하고 나중에 변경 사항을 선택합니다.

    다음 중 하나를 선택하세요:

    * **이 세션에 대해 활성화**: 세션의 모드 폴더에 있는 모드는 턴이 끝날 때 로드되고, 변경 사항이 있는 각 턴의 끝에 다시 로드됩니다. 답변은 세션 동안 지속되며, 재개한 후에도 지속됩니다.
    * **지금은 아님**: 지금은 아무것도 로드되지 않습니다. 파일은 Claude가 작성한 위치에 남아 있으며, 모드는 해당 세션이 다음에 시작될 때 로드됩니다. 모드가 로드되지 않도록 하려면 해당 디렉토리를 삭제하세요.
  </Step>

  <Step title="모드가 로드되었는지 확인">
    Claude Code 프롬프트에서 `/plugin`을 실행하고 **Installed** 탭이 선택될 때까지 Tab을 누르세요. 모드가 나열되며, 여기서 끌 수 있습니다.
  </Step>

  <Step title="모드 시도">
    요청한 것을 사용하세요. 예제 프롬프트의 경우 현재 브랜치 이름이 프롬프트 상자 위에 나타납니다. 모드가 원하는 작업을 수행하지 않으면 Claude에게 변경할 사항을 알려주세요. 모드는 파일을 변경하는 각 턴의 끝에 다시 로드되므로 Claude가 완료되는 즉시 변경 사항을 시도할 수 있습니다.
  </Step>
</Steps>

<h3 id="use-the-mod-in-other-sessions">
  다른 세션에서 모드 사용
</h3>

Claude가 작성한 모드는 모드를 만든 세션에서만 로드되며, Claude Code는 [`cleanupPeriodDays`](/docs/ko/settings-reference#cleanupperioddays)보다 오래되면 해당 세션의 모드 폴더를 삭제합니다. 모드를 유지하려면 모드 폴더에서 디렉토리를 `~/mods/git-branch`와 같은 자신의 위치로 복사하세요. 그런 다음 로드 방법을 선택하세요:

* **시작하는 세션에서**: 셸에서 `claude --plugin-dir ~/mods/git-branch`를 실행하세요
* **다른 사람들을 위해**: [마켓플레이스에 추가](#share-your-mod)하여 설치할 수 있도록 하세요

<h3 id="sessions-that-skip-the-approval">
  Claude가 작성한 모드가 로드될 수 없는 세션
</h3>

Claude가 작성한 모드는 승인 후에만 로드되며, 모드가 실행될 수 있는 신뢰할 수 있는 작업 공간에서만 로드됩니다. 이 세션에서는 로드되지 않습니다:

* **승인할 사람이 없음**: `claude -p` 실행 또는 [`dontAsk` 모드](/docs/ko/permission-modes)와 같이 세션이 프롬프트를 표시할 수 없습니다
* **작업 공간을 신뢰하지 않음**: 디렉토리에 대한 신뢰 프롬프트를 수락하지 않았습니다
* **모드가 중지됨**: `--safe-mode` 또는 `--bare`로 시작했거나, `disableAllHooks`를 설정했거나, 조직의 [관리 설정이 차단](/docs/ko/plugins/mods/admin#choose-how-much-to-allow)했습니다

<h2 id="write-a-mod-yourself">
  모드 직접 작성
</h2>

이 튜토리얼에서는 Claude가 수행하는 도구 호출을 세고, Claude가 작동하는 동안 스피너 옆에 개수를 표시하고, 개수를 인쇄하는 `/tally` 명령을 추가하는 `first-mod`라는 모드를 빌드합니다. 그런 다음 Claude Code가 모드 옆에 작성한 타입 선언을 읽고 `claude plugin validate`를 실행합니다. 함께 버전이 제공하는 이벤트 및 메서드와 Claude Code가 코드에서 읽는 것을 보여줍니다.

이 녹화는 완성된 모드를 보여줍니다. 스피너는 도구 호출을 세고, `/tally`는 개수를 인쇄하며, 코드 편집은 세션이 실행되는 동안 적용됩니다:

<Frame>
  <video autoPlay muted loop playsInline controls className="w-full dark:hidden" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-first-mod-light.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=eb561134afa90375777408453ba51c77" aria-label="Claude Code 세션에서 '여기 파일을 나열하고 README를 읽어'라는 프롬프트가 입력되고 전송됩니다. 스피너는 '생각 중 · 도구 호출: 1'을 읽고 Claude가 작동하면서 개수가 증가합니다. /tally 명령은 'first-mod: Claude가 이 모드가 로드된 이후 3개의 도구 호출을 했습니다'를 인쇄합니다. 한 줄은 first-mod가 다시 로드되었고 네 개의 hooks를 나열합니다. 다음 프롬프트에서 스피너는 '생각 중 · 사용된 도구: 1'을 읽습니다." data-path="images/mods-first-mod-light.mp4" />

  <video autoPlay muted loop playsInline controls className="w-full hidden dark:block" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-first-mod-dark.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=09779dadc7ef66c2b1e2da0c2e31ac72" aria-label="Claude Code 세션에서 '여기 파일을 나열하고 README를 읽어'라는 프롬프트가 입력되고 전송됩니다. 스피너는 '생각 중 · 도구 호출: 1'을 읽고 Claude가 작동하면서 개수가 증가합니다. /tally 명령은 'first-mod: Claude가 이 모드가 로드된 이후 3개의 도구 호출을 했습니다'를 인쇄합니다. 한 줄은 first-mod가 다시 로드되었고 네 개의 hooks를 나열합니다. 다음 프롬프트에서 스피너는 '생각 중 · 사용된 도구: 1'을 읽습니다." data-path="images/mods-first-mod-dark.mp4" />
</Frame>

세 개의 파일을 작성합니다:

```text theme={null}
first-mod/
├── .claude-plugin/
│   └── plugin.json
└── hooks/
    ├── hooks.json
    └── register.js
```

* **`plugin.json`**: 플러그인의 [매니페스트](/docs/ko/plugins/manifest-reference)
* **`hooks.json`**: [코드 파일을 가리킵니다](/docs/ko/plugins/mods/reference#files)
* **`register.js`**: 코드, hooks 모듈이라고 불립니다

<Steps>
  <Step title="플러그인 디렉토리 생성">
    파일을 보관할 두 디렉토리를 생성하세요:

    <Tabs>
      <Tab title="Bash or Zsh">
        ```bash theme={null}
        mkdir -p first-mod/.claude-plugin first-mod/hooks
        ```
      </Tab>

      <Tab title="PowerShell">
        ```powershell theme={null}
        New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="매니페스트 작성">
    모드는 플러그인이며, 모드는 [매니페스트](/docs/ko/plugins/manifest-reference)가 필요합니다. 이 모드의 매니페스트에는 특별한 필드가 없습니다. 이를 `first-mod/.claude-plugin/plugin.json`으로 저장하세요:

    ```json first-mod/.claude-plugin/plugin.json theme={null}
    {
      "name": "first-mod",
      "version": "0.1.0",
      "description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
      "author": { "name": "Your Name" }
    }
    ```
  </Step>

  <Step title="Claude Code에 코드 위치 알리기">
    Claude Code가 플러그인을 로드할 때, 플러그인의 `hooks/hooks.json`을 읽습니다. 해당 파일의 `modules` 키는 코드의 경로를 제공하며, 이를 가지는 것이 플러그인을 모드로 만드는 것입니다. 한 경로를 나열하세요. `hooks.json`에 상대적입니다. 여기서는 다음 단계에서 작성할 `register.js`를 가리킵니다.

    이를 `first-mod/hooks/hooks.json`으로 저장하세요:

    ```json first-mod/hooks/hooks.json theme={null}
    {
      "description": "The first-mod hooks module",
      "modules": ["./register.js"]
    }
    ```
  </Step>

  <Step title="코드 작성">
    이 파일은 모드의 코드이며, hooks 모듈이라고 불립니다. 모드가 로드될 때, Claude Code는 파일이 내보내는 `register` 함수를 호출하고 [`on`](/docs/ko/plugins/mods/reference#the-hook-function)이라는 함수를 전달합니다. `on`에 대한 각 호출은 이벤트 핸들러(hook이라고 불림)를 이름이 지정한 이벤트에 등록합니다.

    이를 `first-mod/hooks/register.js`로 저장하세요:

    ```javascript first-mod/hooks/register.js theme={null}
    // The count, shared by the hooks below
    let calls = 0

    // Claude Code calls this once when the mod loads
    export function register(on) {
      // Runs when the session starts, before your first prompt
      on('session.start', async ($, e, next) => {
        // Add the /tally command
        await $.command.register({
          name: 'tally',
          description: 'Show how many tool calls Claude has made',
        })
        // Let the session start as usual
        return next(e)
      })

      // Runs each time Claude is about to use a tool
      on('tool.call', async ($, e, next) => {
        calls += 1
        // Ask Claude Code to draw the interface again, so the new count shows
        $.ui.invalidate('ui.render')
        // Let the tool run as usual
        return next(e)
      })

      // Runs when you type /tally, and only then, because of the matcher
      on('command.run', { command: 'tally' }, async () => {
        // The text to print in the transcript
        return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
      })

      // Runs each time Claude Code draws the spinner
      on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
        // Keep Claude Code's spinner, with the count added after its word
        return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
      })
    }
    ```

    파일은 `calls`에 개수를 유지하고 네 개의 hooks를 등록합니다:

    * \*\*[`session.start`](/docs/ko/plugins/mods/reference#session)\*\*는 세션이 시작될 때, 첫 번째 프롬프트 전에 실행되며, 모드가 다시 로드될 때마다 실행됩니다. Claude Code에 `/tally` 명령을 추가합니다.
    * \*\*[`tool.call`](/docs/ko/plugins/mods/reference#tools)\*\*는 Claude가 도구를 사용하려고 할 때마다 실행됩니다. `calls`에 1을 더하고 Claude Code에 인터페이스를 다시 그리도록 요청합니다.
    * \*\*[`command.run`](/docs/ko/plugins/mods/reference#commands-and-configuration)\*\*은 `/tally`를 입력할 때 실행됩니다. 인쇄할 텍스트를 반환합니다.
    * \*\*[`ui.render`](/docs/ko/plugins/mods/reference#interface)\*\*는 Claude Code가 스피너를 그릴 때마다 실행됩니다. 스피너의 단어 뒤에 개수를 추가합니다.

    [예제 모드가 어떻게 작동하는지](#how-the-example-mod-works)는 각 hook이 취하는 세 개의 인수와 각각이 반환하는 것을 설명합니다.
  </Step>

  <Step title="모드 로드">
    `--plugin-dir` 플래그로 Claude Code를 시작하세요. 이는 설치하지 않고 한 세션에 대해 플러그인 디렉토리를 로드합니다:

    ```bash theme={null}
    claude --plugin-dir ./first-mod
    ```
  </Step>

  <Step title="모드 시도">
    Claude에게 몇 가지 도구 호출을 수행하는 작업을 요청하세요. 예를 들어 `여기 파일을 나열하고 README를 읽어`. Claude가 작동하는 동안 스피너의 단어 뒤에 증가하는 개수가 나타납니다. 예를 들어 `생각 중 · 도구 호출: 2…`. Claude가 완료되면 `/tally`를 입력하고 Enter를 누르세요. 트랜스크립트는 `first-mod: Claude가 이 모드가 로드된 이후 2개의 도구 호출을 했습니다`를 표시하며, 자신의 개수가 있습니다. Claude Code는 플러그인의 이름을 명령의 텍스트 앞에 놓습니다.

    대화형 세션 없이 명령을 확인하려면 비대화형 모드에서 실행하세요:

    ```bash theme={null}
    claude -p "/tally" --plugin-dir ./first-mod
    ```

    ```text theme={null}
    first-mod: Claude has made 0 tool calls since this mod loaded
    ```

    `/tally`가 명령 목록에 없으면 모듈이 로드되지 않았습니다. [모드가 아무것도 하지 않는 이유 찾기](/docs/ko/plugins/mods/troubleshoot#find-out-why-a-mod-does-nothing)를 참조하세요.
  </Step>

  <Step title="세션이 실행되는 동안 코드 변경">
    세션을 열어 두세요. `register.js`에서 `ui.render` hook의 `' · tool calls: '`를 `' · tools used: '`로 변경하고 저장하세요. 강조된 줄이 변경되는 줄입니다:

    ```javascript first-mod/hooks/register.js {4} theme={null}
      // Runs each time Claude Code draws the spinner
      on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
        // Keep Claude Code's spinner, with the count added after its word
        return next({ ...e, props: { ...e.props, suffix: ' · tools used: ' + calls + '…' } })
      })
    ```

    트랜스크립트의 한 줄은 `first-mod`가 다시 로드되었고 hooks를 나열하며, 다음 스피너는 새 텍스트를 사용합니다. 예를 들어 `생각 중 · 사용된 도구: 1…`.
  </Step>
</Steps>

<h3 id="how-the-example-mod-works">
  예제 모드가 어떻게 작동하는지
</h3>

`on`에 전달하는 각 함수는 hook이며, 이는 이벤트 핸들러입니다. Claude Code는 모든 hook에 동일한 세 개의 인수를 전달합니다:

* **mods API**, `$`라고 이름 지어짐: 모드가 자신 외부에 도달하기 위해 호출할 수 있는 모든 메서드. `$.ui` 및 `$.command`와 같은 [네임스페이스](/docs/ko/plugins/mods/reference#mods-api-methods)에 있습니다
* **이벤트**, `e`라고 이름 지어짐: [이벤트의 입력](/docs/ko/plugins/mods/reference#events). 도구 호출의 이름 및 인수와 같은 일반 데이터
* **다음 핸들러**, [`next`](/docs/ko/plugins/mods/events#how-a-hook-handles-an-event)라고 이름 지어짐: 이벤트를 다른 모드로 전달한 다음 Claude Code의 자체 동작으로 전달하고 결과를 반환하는 함수

`first-mod`의 hooks는 hook이 할 수 있는 세 가지 방식으로 이벤트를 처리합니다:

* **관찰**: `session.start` hook은 명령을 등록하고, `tool.call` hook은 호출을 세고 다시 그리도록 요청합니다. 둘 다 `next(e)`를 반환하므로 세션이 시작되고 도구가 평소대로 실행됩니다.
* **답변**: `command.run` hook은 자신의 결과를 반환하고 `next`를 호출하지 않습니다. `on`의 두 번째 인수인 `{ command: 'tally' }`는 [matcher](/docs/ko/plugins/mods/events#filter-which-events-a-hook-handles)라고 불리는 필터이므로 hook은 `/tally`에 대해서만 실행됩니다.
* **다시 쓰기**: `ui.render` hook은 `e`의 복사본과 함께 `next`를 호출하며, 그 `suffix`는 개수를 보유하므로 Claude Code는 단어 뒤에 텍스트가 있는 일반적인 스피너를 그립니다

Claude Code는 `--plugin-dir`으로 로드된 디렉토리를 감시하고 파일이 변경될 때 hooks 모듈을 핫 리로드합니다. 각 리로드는 `register`를 다시 실행하므로 `calls`는 `0`으로 돌아가고 `/tally`는 다시 세기 시작합니다. 리로드 전체에서 값을 유지하려면 [상태 유지](/docs/ko/plugins/mods/interface#keep-state)를 참조하세요.

<h2 id="keep-working-on-a-mod">
  모드에서 계속 작업하기
</h2>

모드가 로드되면 Claude가 변경하도록 할 수 있으며, 버전의 타입 정의에 대해 코드를 확인하고, Claude Code가 찾은 이벤트 및 호출을 나열하고, 테스트할 수 있습니다.

<h3 id="change-a-mod-with-claude">
  Claude로 모드 변경하기
</h3>

이미 가지고 있는 모드를 변경하려면 `--plugin-dir`이 모드의 디렉토리를 가리키도록 하여 세션을 시작하세요. 그러면 Claude가 작성한 것이 동일한 세션에서 로드됩니다:

```bash theme={null}
claude --plugin-dir ./first-mod
```

그런 다음 변경을 요청하세요. 예를 들어 `이 모드에 /tally-reset 명령을 추가하여 tally를 0으로 설정하세요`. Claude는 hooks 모듈을 편집하고, `claude plugin validate`를 실행하고, 보고하는 것을 수정합니다. `--plugin-dir`으로 로드하는 디렉토리는 [보호된 경로](/docs/ko/permission-modes#protected-paths)이므로 `default` 및 `acceptEdits` 모드에서 모드에 대한 Claude의 각 편집을 승인하도록 요청받습니다. 보호된 경로 테이블은 다른 권한 모드의 결과를 제공합니다.

Claude가 턴 중에 저장한 파일은 턴이 끝날 때 다시 로드되므로 Claude가 완료되는 즉시 `/tally-reset`을 시도할 수 있습니다.

<h3 id="get-the-types-for-your-build">
  버전의 타입 정의 가져오기
</h3>

Claude Code가 `--plugin-dir`에 전달한 디렉토리에서 모드를 로드하거나 다시 로드할 때마다, 또는 [Claude가 작성한 모드](#ask-claude-for-a-mod)일 때마다, `.d.ts`로 끝나는 TypeScript 선언 파일을 모드의 디렉토리 내 `.claude-plugin/types/`에 작성합니다. 이들은 실행 중인 Claude Code 버전의 정확한 이벤트, mods API 메서드 및 요소를 설명하므로 편집기는 hooks를 자동 완성하고 타입 확인할 수 있습니다. 선언을 온라인으로 탐색하려면 Claude Code 저장소의 [`mods/types/claude-code.d.ts`](https://github.com/anthropics/claude-code/blob/main/mods/types/claude-code.d.ts)를 읽으세요. 첫 번째 줄은 이를 작성한 버전의 이름을 지정합니다. 디렉토리는 다음 파일을 보유합니다:

| 경로 | 선언하는 것 |
| :- | :- |
| `claude-code/index.d.ts` | 모든 이벤트 및 입력과 결과, 모든 mods API 네임스페이스 및 메서드, 각 표면이 그릴 수 있는 요소 |
| `claude-code-tools/index.d.ts` | 내장 도구의 입력 및 결과. `e.tool === 'Bash'` 확인이 `e`를 좁히도록 |
| `claude-code-mcp/index.d.ts` | 모드에서 파일을 마지막으로 저장했을 때 연결된 MCP 도구의 입력 |
| 플러그인 이름의 디렉토리에 있는 `index.d.ts` | 해당 플러그인이 mods API에 추가하는 것. `plugin.json`이 `dependencies` 아래에 나열하는 각 플러그인에 대해 하나의 디렉토리가 있습니다. |
| `tsconfig.json` | hooks 모듈에 맞는 컴파일러 옵션 |

모드에 자신의 `tsconfig.json`이 없으면 Claude Code는 생성된 것을 확장하는 모드의 루트에 하나를 추가하므로 편집기와 `tsc -p ./first-mod`는 추가 설정 없이 모드를 타입 확인합니다.

이벤트 및 메서드는 릴리스 간에 변경될 수 있으므로 불일치할 때 이 페이지를 포함한 모든 페이지보다 이 파일을 신뢰하세요.

`claude-code/index.d.ts`는 모든 mods API 메서드에 대한 주석 및 예제가 있는 빌드의 가장 완전한 참조입니다. 무언가를 찾으려면 파일에서 이름(예: `'tool.call'`)을 검색하세요.

<h3 id="check-what-claude-code-reads-from-your-mod">
  Claude Code가 모드에서 읽는 것 확인하기
</h3>

모드를 Claude Code가 보는 방식으로 보려면, 코드를 실행하거나 세션을 시작하지 않고 `claude plugin validate`를 사용하세요. 매니페스트를 확인하고 Claude Code가 모드를 로드할 때 실행하는 hooks 모듈의 소스에 대해 동일한 정적 분석을 실행합니다. 셸에서 모드의 디렉토리에서 실행하세요:

```bash theme={null}
claude plugin validate ./first-mod
```

`first-mod`의 경우 출력에는 다음 줄이 포함됩니다.

```text theme={null}
  ❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
  ❯ ./register.js calls: $.command.register, $.ui.invalidate

✔ Validation passed
```

`hooks:` 줄은 모듈이 hook하는 이벤트를 나열하며, 각각은 중괄호에 필터가 있습니다. `calls:` 줄은 호출하는 모든 mods API 메서드를 나열합니다. 환경 변수를 읽거나 설정하는 모듈도 `env reads:` 및 `env writes:` 줄을 가지며, [`$.state`](/docs/ko/plugins/mods/interface#keep-state)를 사용하는 모듈은 `state reads:` 및 `state writes:` 줄을 가집니다.

hook하려고 한 이벤트가 첫 번째 줄에서 누락되면 Claude Code도 해당 hook을 호출하지 않습니다. 일반적인 원인은 철자가 잘못된 이벤트 이름이며, 명령은 `"tool.calls" is not an event`와 같은 오류로 보고합니다.

정적 분석이 모든 hook과 호출을 찾을 수 있도록 다음 규칙을 따르세요:

* 각 mods API 호출을 완전히 철자하세요: `$`, 네임스페이스, 메서드. 예를 들어 `$.store.get('notes')`. `$`를 동일한 파일의 최상위 수준에서 선언된 함수로 전달할 수 있으며, `loadNotes`라는 함수의 경우 `calls:` 줄은 `$.store.get (via loadNotes)`를 읽습니다. `$`를 메서드, 함수 내부에서 정의된 함수, 또는 파일의 다른 부분에서 가져온 함수로 전달하면 검증이 실패합니다. [`$.state`](/docs/ko/plugins/mods/interface#keep-state)가 사용하는 `read` 및 `update` 함수는 이를 취할 수 있는 가져오기입니다. `$` 또는 네임스페이스 중 하나를 변수에 할당하거나, 구조 분해하거나, 계산된 이름으로 인덱싱하지 마세요. `const ui = $.ui`는 `$.ui is used as a value`로 실패합니다.
* 각 `on` 호출에서 이벤트 이름을 문자열 리터럴로 작성하세요. 예를 들어 `'tool.call'`. 변수 또는 이름 목록에 대한 루프는 `the event name passed to on() is not a string literal`로 실패합니다.
* `register` 내부에서 `on`이라는 두 번째 변수 또는 매개변수를 선언하지 마세요. 검증은 `"on" is declared again (shadowed)`로 실패합니다.
* 상대 경로로 플러그인 디렉토리 내 파일에서만 가져오세요. 허용되는 유일한 베어 가져오기는 타입 및 몇 가지 도우미를 위한 `claude-code`입니다.
* 파일의 맨 위에 `import` 선언을 사용하세요. 예를 들어 `import { name } from './file.js'`. 동적 `import()`는 `a dynamic import(); a hooks module imports its own files with an import declaration`로 실패합니다.
* 모든 파일을 ES 모듈로 작성하세요. `import`를 사용하고 `require`는 사용하지 마세요. [참조](/docs/ko/plugins/mods/reference#files)는 Claude Code가 로드하는 파일 확장자를 나열합니다.

<h3 id="test-the-mod">
  모드 테스트하기
</h3>

모드에 대한 자동화된 테스트를 작성하고 세션, 로그인 또는 네트워크 없이 셸에서 `claude plugin test`로 실행할 수 있습니다. 테스트는 hooks가 처리하는 이벤트를 발생시키고 hooks가 수행한 작업을 확인합니다.

이 테스트는 두 개의 도구 호출을 발생시키고, `/tally`를 실행하고, 회신이 둘 다 세는지 확인합니다. 이를 `first-mod/tests/first-mod.test.ts`로 저장하세요:

```typescript first-mod/tests/first-mod.test.ts theme={null}
import { expect, test } from 'claude-code/testing'

test('/tally reports the tool calls the mod has seen', async ($, on) => {
  // Answer each tool call in Claude Code's place, so no tool runs
  on('tool.call', () => ({ result: 'ok' }))

  // Raise two tool calls, which the mod's tool.call hook counts
  await $.tool.call({ tool: 'Bash', command: 'ls' })
  await $.tool.call({ tool: 'Read', file_path: 'README.md' })

  // Run /tally and check the text its hook returns
  const answer = await $.command.run({ command: 'tally', args: '' })
  expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
```

셸에서 `first-mod` 디렉토리에서 테스트를 실행하세요:

```bash theme={null}
claude plugin test
```

출력은 각 테스트와 통과 여부를 이름으로 지정하며, 실행 간에 다양한 타이밍이 있습니다:

```text theme={null}
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]

 1 pass
 0 fail
Ran 1 test across 1 file. [0.19s]
```

[모드 테스트](/docs/ko/plugins/mods/test)는 모델 호출 또는 저장소를 스텁하고, 타이머 및 그리기를 테스트하는 것을 다룹니다.

<h2 id="share-your-mod">
  모드 공유
</h2>

모드는 플러그인이므로 매니페스트에서 버전을 지정하고 사람들은 `/plugin` 명령으로 설치하고 업데이트합니다. 다른 사람들에게 제공하려면 [마켓플레이스에 추가](/docs/ko/plugins/publish)하세요.

그 전에 플러그인의 `name`을 확인하세요: `claude plugin validate`는 [Anthropic의 자체 것처럼 보이는](/docs/ko/plugins/manifest-reference#name) 이름(예: `claude-`로 시작하는 이름)을 실패합니다. 이벤트 및 메서드는 릴리스 간에 변경될 수 있으므로 README는 테스트한 Claude Code 버전을 말하는 곳입니다.

설치된 복사본이 아닌 `--plugin-dir`이 있는 디렉토리에 대해 계속 개발하세요. Claude Code는 설치된 플러그인을 버전별로 캐시하므로 버전을 올리고 다시 설치할 때까지 편집 사항이 설치된 복사본에 도달하지 않습니다.

<h2 id="next-steps">
  다음 단계
</h2>

* [인터페이스에 그리기](/docs/ko/plugins/mods/interface): 창을 열고, 프롬프트 위에 그리고, 버튼 및 텍스트 필드 추가
* [이벤트에 반응](/docs/ko/plugins/mods/events): 도구 호출, 프롬프트 및 턴 hook
* [mods API 사용](/docs/ko/plugins/mods/api): 명령 및 도구 추가, 모델 호출, 타이머에서 작업 실행
* [모드 테스트](/docs/ko/plugins/mods/test): Claude Code가 답변할 것을 스텁하고, 타이머 및 그리기 테스트
* [모드 문제 해결](/docs/ko/plugins/mods/troubleshoot): 모드가 아무것도 하지 않는 이유 및 디버그 로그
* [내장 모드의 소스 읽기](/docs/ko/plugins/mods/overview#read-the-source-of-built-in-mods): 완전한 플러그인. 각각 hooks 모듈 및 테스트 포함
