Claude Code 스킬 만들기: SKILL.md 작성부터 호출 확인까지
REVIEW 주석이 몇 개 남았는지 세는 스킬을 하나 만들어 두고, 스킬 이름은 빼고 How many review notes are left in this project?라고만 물어봤습니다. 답은 2개로 맞았어요.
그런데 답만 봐서는 스킬이 불린 건지, Claude가 파일을 직접 뒤진 건지 알 수가 없더라고요. 도구 호출 기록을 열어 보고서야 Skill 도구가 한 번 호출된 걸 확인했습니다.
스킬은 이렇게 description과 본문으로 된 Markdown 파일 하나입니다. /이름으로 직접 부를 수도 있고, 요청이 설명과 맞으면 Claude가 스스로 부르기도 해요.
같은 지시나 체크리스트를 대화마다 붙여넣고 있다면 .claude/skills/<이름>/SKILL.md로 뽑아내 보세요. 설명만 늘 컨텍스트에 있고 본문은 쓸 때만 로드되니, 참고 자료가 길어져도 평소 비용은 거의 늘지 않습니다.
배포처럼 타이밍이 중요한 절차라면 disable-model-invocation: true를 붙여 /이름으로만 부르게 하시고요.
2026년 9월 12일 Claude Code 2.1.269에서 스킬을 직접 만들어 두 방식으로 호출해 확인했고, 공식 문서도 같은 날 다시 확인했습니다.
10월 12일에는 Windows 11 · PowerShell 5.1 · Claude Code 2.1.296에서 같은 스킬로 ! 명령이 도는 셸, shell: powershell, 정션으로 연결한 스킬 폴더를 확인했습니다. 자동 호출이 안 되거나 권한을 다시 묻는 사례는 공개 이슈에서 가져온 것입니다.
확인한 버전·환경
- Claude Code 2.1.296 · Windows 11 · Windows PowerShell 5.1 · 확인일 2026-10-12
- Claude Code 2.1.269 · 확인일 2026-09-12
붙여넣는 체크리스트가 있다면 스킬 후보
리뷰를 부탁할 때마다 같은 체크리스트를 붙여넣고 있다면, 그 체크리스트가 스킬 후보입니다. CLAUDE.md의 한 절이 “사실”이 아니라 여러 단계짜리 “절차”로 자라났을 때도 마찬가지고요.
그냥 CLAUDE.md에 다 적으면 안 되느냐고 물을 수 있는데, 비용이 다릅니다. CLAUDE.md는 매 세션 전체가 컨텍스트에 올라가서, 가끔만 필요한 자료를 거기 쌓으면 평소 대화 전부가 그 비용을 냅니다. 스킬은 세션 시작 때 짧은 설명만 올라가고, 실제로 불릴 때만 본문 전체가 로드돼요.
CLAUDE.md |
스킬 | 훅 | |
|---|---|---|---|
| 로드 | 매 세션 전체 | 설명만 상시, 본문은 호출 시 | 없음(밖에서 실행) |
| 담는 것 | “항상 이렇게” 규칙 | 참고 지식, /이름 절차 |
셸 명령·HTTP·MCP 호출 |
| 실행 보장 | 아니오 | 아니오 | 예 |
그래서 API 문서나 긴 체크리스트처럼 길어도 되는 참고 자료, 배포·릴리스처럼 /이름으로 부를 절차는 스킬에 둡니다. CLAUDE.md는 파일당 200줄 아래로 유지하고, 늘어나는 내용은 스킬이나 .claude/rules/로 나누는 법을 쓰면 됩니다.
count-todos 스킬을 만들어 불러 봤다
도입에서 말한 그 스킬입니다. 만드는 데는 파일 하나면 충분했어요.
1. 폴더와 SKILL.md 만들기
.claude/skills/count-todos/SKILL.md:
---
description: Counts REVIEW comments across the project's source files. Use when asked how many review notes are left or where they are.
---
## REVIEW scan
!`grep -rn "REVIEW" src/ 2>/dev/null || echo "(no matches)"`
## Instructions
Using only the scan result above, report the total number of REVIEW comments and list each file:line. Do not search the filesystem yourself.
폴더 이름 count-todos가 /count-todos라는 명령이 됩니다. !`명령`은 Claude가 내용을 읽기 전에 셸 명령을 먼저 돌려 결과를 끼워 넣는 문법이에요.
description에는 무엇을 하고 언제 쓰는지를 적습니다. Claude는 이 설명을 보고 스킬을 부를지 정하거든요.
2. /이름으로 불러 보기
REVIEW 주석 2개가 든 예제 파일을 두고 /count-todos를 실행한 출력입니다.
Total REVIEW comments: 2
- src/app.py:2
- src/app.py:6
스크립트에서 claude -p "/count-todos"처럼 부를 때 Windows의 Git Bash라면 조심하세요. Git Bash가 /로 시작하는 인자를 경로로 바꿔서, Claude에게는 C:/Program Files/Git/count-todos가 전달되더라고요. 답 끝에 그 경로를 언급하는 걸 보고서야 알았습니다.
Git Bash에서는 경로 변환을 끄고 부르면 됩니다. PowerShell에서는 그대로 불러도 문제가 없었어요.
MSYS_NO_PATHCONV=1 claude -p "/count-todos"
3. 자동으로 불렸는지 확인하기
이번에는 이름 없이 How many review notes are left in this project?라고만 물었습니다.
There are **2** review notes left:
- `src/app.py:2` — validate input types
- `src/app.py:6` — handle division by zero
앞에서 말했듯 이 답만으로는 알 수 없어서, 도구 호출 기록을 열어 Skill 도구가 한 번 호출된 걸 확인했습니다. 대화형 세션에서는 Ctrl+O로 자세한 기록을 열면 도구 호출이 보여요. 자동 호출이 됐는지 궁금할 때는 답이 아니라 이 기록을 보세요.
disable-model-invocation: true도 같은 방법으로 확인했습니다. 최근 커밋으로 릴리스 노트를 쓰는 release-notes 스킬에 이 옵션을 켜고 “릴리스 노트를 써 줄 수 있나요?“라고 물었더니, Skill 호출은 한 번도 없었고 Claude가 Bash·Read로 커밋 이력을 직접 뒤졌습니다. /release-notes로 부르자 스킬이 정상으로 로드돼, 커밋이 하나뿐인 저장소에서 항목 3개짜리 초안을 바로 내놓았고요.
4. Windows에서 ! 명령은 어느 셸에서 도나
Windows에서도 위 count-todos는 고치지 않고 그대로 돌았습니다. ! 명령의 기본 셸이 Git Bash라서 grep이 그대로 실행되거든요. 공식 문서 기준으로 Git Bash가 없는 Windows에서는 shell을 적지 않은 스킬의 ! 명령이 PowerShell로 돌고, shell: bash를 명시한 스킬만 명령을 돌리기 전에 Git Bash was not found 오류로 멈춥니다.
PowerShell 명령을 쓰고 싶으면 frontmatter에 shell: powershell을 넣고 명령도 PowerShell로 바꿉니다.
---
description: Counts REVIEW comments using PowerShell.
shell: powershell
---
## REVIEW scan
!`Select-String -Path src\*.py -Pattern "REVIEW" | ForEach-Object { "$($_.Filename):$($_.LineNumber)" }`
부르면 스캔 결과로 app.py:2, app.py:6 두 줄이 들어가고, 답도 2개로 맞았습니다. 이 필드는 PowerShell 도구가 켜져 있을 때만 통합니다. Amazon Bedrock 같은 클라우드 공급자 경유 세션에서는 CLAUDE_CODE_USE_POWERSHELL_TOOL=1을 따로 켜야 하고요.
shell: powershell만 넣고 명령은 bash 그대로 두면, 실패해도 멈추지 않고 답이 맞게 나올 수 있습니다.
실제로 grep … || echo 줄을 그대로 둔 채 shell: powershell을 넣어 봤습니다. PowerShell 5.1이 ||를 해석하지 못해 스캔은 실패했어요. 그런데 Claude가 Select-String으로 직접 다시 검색해, 스킬 본문의 “직접 검색하지 말라”는 지시를 어긴 채 2개라는 맞는 답을 내더라고요.
셸을 바꿀 때는 명령도 같이 바꾸고, 한 번은 스캔 결과를 그대로 출력하게 해서 확인해 보세요.
어디에 두고, 누가 부르게 할까
어디에 두느냐에 따라 누가 어디서 쓰는지가 정해집니다.
| 위치 | 경로 | 범위 |
|---|---|---|
| 개인 | ~/.claude/skills/<이름>/SKILL.md |
내 모든 프로젝트(커밋 안 됨) |
| 프로젝트 | .claude/skills/<이름>/SKILL.md |
이 저장소, 커밋하면 팀도 |
| 중첩 | <하위폴더>/.claude/skills/<이름>/SKILL.md |
그 폴더 파일을 처음 다룰 때 로드 |
| 플러그인 | <플러그인>/skills/<이름>/SKILL.md |
/플러그인이름:스킬이름으로 |
같은 이름이 개인·프로젝트에 다 있으면 개인 쪽이 이깁니다(관리자 정책 > 개인 > 프로젝트).
여러 프로젝트가 같은 스킬 폴더를 나눠 쓰려면 링크로 연결하면 됩니다. Windows에서 심볼릭 링크는 관리자 권한이나 개발자 모드가 필요해서 저는 정션을 썼어요.
cmd /c mklink /J .claude\skills\say-hello C:\shared-skills\say-hello
이렇게 연결한 스킬은 /say-hello로 정상 호출됐습니다. 정션을 지울 때는 cmd /c rmdir로 링크만 지우세요.
누가 부를 수 있는지는 frontmatter 두 필드로 정해요.
| frontmatter | /이름으로 호출 |
Claude가 스스로 호출 |
|---|---|---|
| (기본값) | 됨 | 됨 |
disable-model-invocation: true |
됨 | 안 됨(설명도 안 올라감) |
user-invocable: false |
안 됨(메뉴에서 숨김) | 됨 |
“설명도 안 올라감”은 /context의 Skills 표로 확인했습니다. 이 확인을 위해 disable-model-invocation: true를 붙여 만든 테스트 스킬 세 개는 표에 아예 없었고, 붙이지 않은 count-todos만 약 50토큰으로 올라와 있었어요.
저는 배포·커밋·메시지 발송처럼 부수 효과가 있는 절차라면 disable-model-invocation: true로 타이밍을 사용자가 쥐게 하길 권합니다. 사용자가 직접 부를 일이 없는 배경 지식은 user-invocable: false로 숨기면 되고요.
불린 스킬은 대화에 얼마나 남나
한 번 불리면 SKILL.md 내용이 메시지로 들어가 이후 턴에도 남고, 같은 내용으로 다시 부르면 중복해서 넣지 않습니다. 대화가 요약(컴팩션)될 때는 가장 최근에 부른 스킬부터 스킬당 최대 5,000토큰, 전체 합쳐 25,000토큰까지만 남고 나머지는 빠질 수 있어요.
스킬 폴더를 지워도 마찬가지입니다. /skills 목록에서는 바로 빠지지만, 이미 불려 대화에 들어간 내용은 이 컴팩션 규칙대로 남았다가 요약 때 정리됩니다.
꼭 돌아야 하는 일은 스킬 말고 훅에
스킬은 부르지 않으면 아무 일도 하지 않고, 불려도 Claude가 해석합니다. “.env를 절대 건드리지 마라”를 스킬이나 CLAUDE.md에 적는 건 부탁일 뿐이고, 실제로 막는 건 그 편집을 차단하는 PreToolUse 훅이에요.
같은 지시를 세 곳에 넣고 실행 횟수를 센 결과는 훅·스킬·CLAUDE.md를 고르는 기준에, 훅을 거는 법은 훅 가이드에 있습니다.
그럼 훅이 스킬을 자동으로 불러오게 하면 되지 않을까요? 지금은 안 됩니다. 훅의 출력은 텍스트로 컨텍스트에 들어갈 뿐이고, 반대 방향으로 스킬 frontmatter의 hooks 필드로 스킬이 불릴 때 훅을 등록하는 것만 됩니다.
훅이 규칙·스킬·파일을 직접 불러오게 해 달라는 기능 요청이 공개 이슈로 올라와 있어요(Adrian333Dev, 2026-09-10, #93252).
스킬이 맞지 않는 경우도 짚어 둡니다. 매번 반드시 돌아야 하는 검사, 사용자 승인이 필요한 명령을 승인 없이 돌리려는 목적이라면 스킬로는 해결되지 않습니다. 아래 사례처럼 스킬 안의 명령도 권한 확인을 받거든요.
다른 사람들이 겪은 문제
아래 네 가지는 공개된 GitHub 이슈의 사례입니다. 네 건 모두 확정된 원인은 밝혀지지 않았고 이슈도 열려 있습니다.
설명에 맞는 요청인데 스킬이 자동으로 불리지 않음
vanek-21-code는 “shadcn, UI components, component installation”을 언급하는 설명을 넣었는데, shadcn 컴포넌트를 물으면 Claude가 스킬 대신 파일을 직접 읽거나 검색했다고 보고했습니다(2025-10-17, #9716, Claude Code 2.0.21, macOS). 유지보수자 답변도 없었어요.
같은 일을 겪는다면 설명을 실제 요청 문구에 가깝게 다듬고, 꼭 그 스킬을 써야 하는 자리에서는 /이름으로 직접 부르세요.
스킬 안에서는 permissions.allow의 명령도 매번 승인을 물음
thomast73의 보고입니다. ~/.claude/settings.json의 permissions.allow에 넣은 명령(git rebase team/master, mvn spring-boot:run 등)이 일반 대화에서는 자동 승인되는데, 스킬 안에서는 매번 승인을 물었다고 해요(2026-01-18, #18950, Claude Code 2.1.12, macOS 26.2).
보고자는 프로젝트 단위 권한으로 옮기는 임시 우회를 적었지만, 그러면 사용자 단위 설정의 장점이 사라진다고 지적했습니다.
allowed-tools에 적은 패턴과 같은 명령인데 거부됨
KyussCaesar는 frontmatter에 allowed-tools: Bash(say -v "Samantha":*)를 적고 Skill(speak)도 허용했는데, 실제 say -v "Samantha" "..." 실행은 승인이 필요하다며 거부됐다고 보고했습니다(2025-12-21, #14956, Claude Code 2.0.75, macOS).
전역 허용 목록에 Bash(say:*)를 넣으면 되지만, 그러면 스킬 단위로 도구를 묶는 취지가 사라진다고 적었고요.
심볼릭 링크로 연결한 스킬 폴더를 /skills가 못 찾음
kchristidis는 ~/.claude/skills/ 밖의 폴더를 심볼릭 링크로 연결했더니 /skills가 “No skills found”를 보여 줬다고 보고했습니다. 그 상태에서도 모델은 스킬을 쓸 수 있었다고 해요(2025-12-20, #14836, Claude Code 2.0.73, macOS).
이후 문서는 심볼릭 링크로 연결한 스킬 폴더를 지원한다고 설명합니다.
마치며
스킬을 만들어 보니 어려운 건 파일 작성이 아니라 “정말 불렸는지”를 확인하는 쪽이었습니다. 답이 맞아도 스킬 덕인지 알 수 없으니, 새 스킬을 만들면 한 번은 도구 호출 기록에서 Skill이 찍혔는지 보시길 권합니다. 설명은 실제 요청 문구에 가깝게 쓰고, 부수 효과가 있는 절차는 /이름으로만 부르게 두는 편이 안전합니다.
시작은 최근 대화에서 두 번 이상 붙여넣은 지시 하나를 골라 .claude/skills/<이름>/SKILL.md로 옮겨 보는 것이면 충분합니다. 스킬로 부족해 외부 도구를 붙이고 싶다면 Claude Code에 MCP 서버를 붙이고 범위를 고르는 법을 이어서 보세요.
참고한 공식 자료
Extend Claude with skills는 2026년 10월 12일에 다시 확인했고, Extend Claude Code는 9월 12일에 확인했습니다.
- Extend Claude with skills — SKILL.md 작성법, frontmatter 필드, 저장 위치별 범위, 호출 제어, 컨텍스트 수명
- Extend Claude Code — CLAUDE.md·스킬·훅·서브에이전트를 고르는 기준
인용한 공개 사례
- vanek-21-code, Claude Code assistant not aware of available skills in .claude/skills/ directory (2025-10-17) — 설명이 맞아도 자동 호출이 안 되는 사례
- thomast73, Skills/subagents do not inherit user-level permissions from settings.json (2026-01-18) — 스킬 안에서 사용자 단위 허용 목록이 적용되지 않는 사례
- KyussCaesar, Skill allowed-tools doesn’t grant permission for Bash commands (2025-12-21) —
allowed-tools패턴이 일치해도 거부되는 사례 - kchristidis, /skills command doesn’t find skills in symlinked directories (2025-12-20) — 심볼릭 링크 스킬 폴더가 메뉴에 안 잡히는 사례
- Adrian333Dev, Hooks can inject text but cannot load a rule, a skill, or a file (2026-09-10) — 훅이 스킬·규칙·파일을 직접 불러오게 해 달라는 기능 요청
변경 이력
- 2026-10-14 — Git Bash가 없는 Windows에서 ! 명령이 모두 멈춘다고 적었던 것을, 공식 문서대로 shell을 적지 않은 스킬은 PowerShell로 돌고 shell: bash를 적은 스킬만 멈춘다로 고쳤습니다.
- 2026-10-12 — Windows 11에서 직접 확인한 결과를 더했습니다. ! 명령이 Git Bash에서 도는 것, shell: powershell 사용법과 명령을 바꾸지 않았을 때 생기는 일, Git Bash에서 /이름 인자가 경로로 바뀌는 문제, 정션으로 연결한 스킬 폴더 호출입니다.