본문으로 건너뛰기

[Claude Code Plugin 만들기 #2] 스킬과 커맨드 — AI에게 새 능력 가르치기

· 약 6분
Nam Young Kim
Data Fullstack Engineer

이 글은 Claude Code Plugin 만들기 시리즈의 두 번째 편입니다. Skill과 Command를 직접 만드는 방법, SKILL.md frontmatter 옵션, 그리고 실수 사례를 다룹니다.


TL;DR​

  • Skill = SKILL.md 파일 하나로 Claude에게 배경 지식을 가르치는 것
  • Command와 Skill은 공식적으로 통합됨 — 둘 다 /skill-name으로 호출
  • frontmatter의 user-invocable, disable-model-invocation으로 호출 방식 제어
  • references/ 패턴으로 긴 내용을 분리하여 컨텍스트 효율 극대화

Skill 만들기: AI의 참고서​

Skill은 Claude Code가 자동으로 참고하는 참고서입니다. 특정 상황에서 "이 규칙이 있었지"라고 Claude가 알아서 떠올리게 만듭니다.

SKILL.md 기본 구조​

모든 Skill은 디렉토리 안의 SKILL.md 파일로 정의됩니다.

skills/
└── python-standards/
└── SKILL.md # 필수

SKILL.md는 YAML frontmatter로 시작합니다.

skills/python-standards/SKILL.md
---
name: python-standards
description: "Python 프로젝트 감지 시 uv + ruff + pytest 규칙 적용"
user-invocable: false
---

Python 프로젝트를 다룰 때는 항상:
- uv를 패키지 매니저로 사용합니다.
- ruff로 코드 스타일을 검사합니다.
- pytest를 테스트 프레임워크로 사용합니다.

description은 Claude가 "이 Skill을 언제 써야 하는지" 판단하는 기준입니다. 명확하게 작성할수록 적절한 시점에 적용됩니다.


SKILL.md Frontmatter 완전 정복​

공식 문서 기준으로, 사용 가능한 frontmatter 필드를 모두 정리합니다.

필드 목록​

필드필수설명
name아니오스킬 이름. 생략하면 디렉토리명 사용. 소문자, 숫자, 하이픈만
description권장무엇을 하는 스킬인지, 언제 사용하는지
user-invocable아니오false면 / 메뉴에서 숨김. 기본값: true
disable-model-invocation아니오true면 Claude가 자동 로딩하지 않음. 기본값: false
argument-hint아니오자동완성 시 표시되는 힌트. 예: [issue-number]
allowed-tools아니오스킬 활성 시 허용할 도구 목록
model아니오스킬 활성 시 사용할 모델
context아니오fork으로 설정하면 서브에이전트에서 실행
agent아니오context: fork 시 사용할 에이전트 타입
hooks아니오스킬 라이프사이클에 스코프된 hooks

호출 제어: 3가지 모드​

frontmatter 조합에 따라 Skill의 동작이 달라집니다.

설정사용자 호출Claude 자동 호출용도
기본값 (설정 없음)OO범용 스킬
disable-model-invocation: trueOX수동 전용 워크플로우
user-invocable: falseXO배경 지식, 자동 규칙

모드 1: 기본값 — 누구나 부를 수 있는 스킬​

---
name: code-review
description: "코드 변경사항에 대한 리뷰"
---

사용자가 /code-review로 호출할 수도 있고, Claude가 상황에 맞다고 판단하면 자동으로 참고할 수도 있습니다.

모드 2: 수동 전용 — 명시적으로 불러야 하는 스킬​

---
name: product-planning
description: "인터뷰 기반 제품 기획 및 설계"
disable-model-invocation: true
---

복잡한 워크플로우처럼 "사용자가 의도적으로 시작해야 하는" 스킬에 적합합니다. Claude가 임의로 시작하지 않습니다.

모드 3: 배경 지식 — Claude만 참고하는 스킬​

---
name: python-standards
description: "Python 프로젝트 감지 시 uv + ruff + pytest 규칙 적용"
user-invocable: false
---

/python-standards로 호출할 수 없고, Claude가 Python 코드를 다룰 때 자동으로 참고합니다. 우리 Plugin에서는 python-standards, go-standards, rust-standards, typescript-standards 4개 언어 표준 스킬이 모두 이 모드를 씁니다.


변수 치환: 동적 컨텍스트​

SKILL.md에서 사용할 수 있는 변수들이 있습니다.

인자 치환​

---
name: coding-problem-solver
description: "코딩 문제 풀이 정리"
argument-hint: "[문제 URL 또는 이름]"
---

$ARGUMENTS를 분석하여 풀이를 정리합니다.

사용자가 /coding-problem-solver https://leetcode.com/problems/two-sum이라고 입력하면, $ARGUMENTS가 URL로 치환됩니다.

변수설명
$ARGUMENTS전달된 모든 인자
$0, $1, ...인자를 인덱스로 접근
${CLAUDE_SESSION_ID}현재 세션 ID

동적 컨텍스트 주입: !`command`​

셸 명령어의 결과를 스킬 컨텍스트에 직접 주입할 수 있습니다.

skills/git-master/SKILL.md
---
name: git-master
description: "커밋 아키텍트 + 히스토리 전문가"
---

## 현재 상태
- 변경 파일: !`git status -s`
- 최근 커밋: !`git log --oneline -5`
- 현재 브랜치: !`git branch --show-current`

!`command` 구문은 스킬이 로딩될 때 실행되어 결과를 Claude에게 전달합니다. 이를 통해 매번 "git status 먼저 확인해줘"라고 말하지 않아도 됩니다.


references/ 패턴: 길어지면 분리하기​

SKILL.md가 너무 길어지면 references/ 디렉토리에 분리합니다.

skills/
└── typescript-standards/
├── SKILL.md # 핵심 규칙 (간결하게)
└── references/
├── claude-snippet.md # CLAUDE.md용 코드 조각
├── conventions-snippet.md # 컨벤션 상세
└── settings-snippet.json # 설정 파일 템플릿

Claude는 SKILL.md를 먼저 로딩하고, 필요할 때만 references/ 파일을 읽습니다. 이렇게 하면 컨텍스트 윈도우를 효율적으로 사용할 수 있습니다.

컨텍스트 예산

Skill description은 컨텍스트 윈도우의 2% 이내로 제한됩니다. SKILL.md 본문은 500줄 이내로 유지하고, 나머지는 references/로 분리하는 것이 좋습니다.


서브에이전트에서 실행하기​

복잡한 스킬은 독립된 컨텍스트에서 실행할 수 있습니다.

---
name: deep-research
description: "주제에 대한 깊은 리서치"
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---

$ARGUMENTS에 대해 철저히 조사하세요.

context: fork를 설정하면 메인 대화와 분리된 서브에이전트에서 실행됩니다. 리서치가 끝나면 결과만 메인 대화에 반환합니다.


실수 사례 1: 네이티브 명령어 충돌​

뭐가 문제였나​

처음 Plugin을 만들 때 /review라고 커맨드 이름을 지었는데, Claude Code에 이미 같은 이름이 있었습니다.

어떻게 고쳤나​

/review → /code-review로 이름을 변경했습니다. 하지만 한 파일만 바꾸면 되는 게 아니었습니다.

  1. 커맨드 파일: commands/code-review.md
  2. 에이전트 파일: agents/code-reviewer.md에서 커맨드 호출 부분
  3. CLAUDE.md: 스킬 목록 업데이트
  4. README.md: 사용법 업데이트
교훈

새 커맨드를 만들기 전에 /help로 Claude Code 네이티브 명령어 목록을 먼저 확인하세요. 이미 존재하는 이름과 충돌하면 예상치 못한 동작이 발생합니다.


실수 사례 2: 경로 참조 문제​

뭐가 문제였나​

Plugin 스킬은 "사용자의 프로젝트 폴더"에서 실행됩니다. 그런데 Plugin 파일 자체는 다른 경로에 있습니다.

사용자 프로젝트:  /home/leo/my-project/
Plugin 파일: ~/.claude/plugins/cache/.../my-plugin/

SKILL.md에서 Plugin 내부 파일을 참조하려면 경로를 알아야 하는데, 하드코딩하면 안 됩니다.

해결 방법​

hooks.json이나 스크립트에서는 ${CLAUDE_PLUGIN_ROOT} 환경 변수를 사용합니다.

{
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh"
}

SKILL.md에서는 상대 경로로 references/ 파일을 참조합니다. Claude가 SKILL.md를 읽을 때, 같은 디렉토리의 파일을 자연스럽게 접근할 수 있습니다.

주의

${CLAUDE_PLUGIN_ROOT}는 Markdown 파일 내에서 변수 치환이 되지 않습니다. 셸 환경(hooks.json, scripts/)에서만 동작합니다.


Skill vs Command 정리​

공식 문서에서 Commands와 Skills는 통합되었습니다. 실제로 .claude/commands/review.md와 .claude/skills/review/SKILL.md는 동일하게 /review를 생성합니다.

구분SkillsCommands (레거시)
파일 위치skills/name/SKILL.mdcommands/name.md
frontmatter전체 옵션 지원기본 옵션
referencesreferences/ 패턴 지원지원 안 함
호출 방식동일 (/name)동일 (/name)

신규 개발 시에는 skills/ 디렉토리에 SKILL.md로 만드는 것을 권장합니다. commands/도 계속 동작하지만, frontmatter 옵션이나 references 패턴을 활용하려면 skills가 더 유연합니다.


정리​

  • Skill 파일: skills/<name>/SKILL.md 하나면 충분
  • 호출 제어: user-invocable과 disable-model-invocation 조합으로 3가지 모드
  • 변수 치환: $ARGUMENTS, !`command` 로 동적 컨텍스트
  • references/: 긴 내용은 분리하여 컨텍스트 효율 극대화
  • 실수 방지: 네이티브 명령어 충돌 확인, 경로는 ${CLAUDE_PLUGIN_ROOT} 사용

다음 편 예고​

다음 편에서는 Hooks로 자동화를 다룹니다. hooks.json의 구조, 이벤트 종류, 그리고 가장 중요한 exit code의 의미(exit 0, exit 1, exit 2의 차이)를 실제 디버깅 사례와 함께 설명합니다.


Claude Code Plugin 만들기 시리즈