byteforce

CPN 한국어 자습서 · 러닝패스 1 / 4 — Agent Skills

3장

고급 설정과 멀티파일 스킬

Introduction to agent skills · Configuration and multi-file skills

스킬은 이름과 설명만 있어도 작동합니다. 하지만 스킬이 커지고 더 민감한 일을 맡게 되면, 세 가지 고급 설정이 필요해집니다. 쓸 수 있는 도구를 제한하고(allowed-tools), 설명을 더 잘 써서 제때 발동하게 하고, 파일이 비대해지지 않게 나눠 두는 것입니다. 이 장에서 하나씩 살펴봅니다.

이 장에서 배우는 것What you'll learn

약 20분
1

고급 메타데이터 필드 — allowed-tools와 model까지 다루기

2

제때 발동하는 효과적인 description 쓰는 법

3

allowed-tools로 스킬이 켜졌을 때 할 수 있는 일 제한하기

4

점진적 공개·멀티파일로 복잡한 스킬 구조 잡기

이 자습서를 보는 법

영어 영상을 먼저 본 뒤, 여기서 한국어로 따라 읽고 손으로 익히는 교재입니다. 점선 친 단어는 올리거나 탭하면 뜻이 나오고, 아래쪽엔 스킬이 작동하는 모습을 직접 눌러 보는 시뮬레이터가 있습니다. 읽은 만큼 ‘완료’를 누르면 위 진도 바가 찹니다.

실습 환경

먼저, 이 장에 나오는 낯선 단어
프론트매터 (Frontmatter) · 메타데이터
파일 맨 위 --- 사이에 적는 요약 정보입니다. 스킬의 이름·설명, 그리고 아래 옵션들이 여기에 들어갑니다.
도구 (Tool)
Claude가 일하며 쓰는 기능입니다. 예: Read(읽기), Edit(수정), Write(쓰기), Bash(명령 실행) 등.
allowed-tools (허용 도구)
스킬이 켜져 있는 동안 Claude가 쓸 수 있는 도구를 ‘정해진 것만’으로 제한하는 설정입니다.
점진적 공개 (Progressive disclosure)
핵심만 SKILL.md에 두고, 자세한 자료는 따로 두어 필요할 때만 불러오는 방식입니다.
스크립트 (Script)
정해진 일을 자동으로 처리하는 작은 프로그램(코드)입니다.

기본을 넘어서면 생기는 것

이름과 설명만 있는 기본 스킬도 잘 작동합니다. 하지만 스킬을 많이 쓰고 키우다 보면 세 가지 고민이 생깁니다.

이 장의 고급 설정은 정확히 이 셋을 해결합니다. 좋은 설명, allowed-tools, 그리고 점진적 공개입니다.

영상 내용, 한국어로

Video walkthrough

먼저 영상을 보세요. 영어가 어렵다면 아래 한국어를 같이 읽으면 됩니다. 자동 번역과 달리, 낯선 용어를 풀어서 옮겼습니다.

영상 · Configuration and multi-file skills약 4분 · 영어코스에서 영상 보기 →

영상 대본 — 영어 영상을 보며 같이 읽으세요 (타임스탬프 기준).

0:02스킬은 namedescription만 있어도 작동하지만, 여기에 몇 가지 고급 기법을 더하면 Claude Code에서 훨씬 강력해집니다.
0:12agentskills.io 공개 표준에는 쓸 수 있는 필드가 여럿 있습니다. name은 스킬을 가리키는 이름으로, 소문자·숫자·하이픈만 쓰고 최대 64자, 폴더 이름과 같게 맞춥니다.
0:28description도 필수이며, Claude에게 언제 이 스킬을 쓸지 알려줍니다. 최대 1,024자이고, 가장 중요한 필드입니다 — Claude가 매칭에 이걸 씁니다.
0:37선택 필드도 있습니다. allowed-tools는 스킬이 켜져 있을 때 쓸 수 있는 도구를 제한하고, model은 그 스킬에 어떤 Claude 모델을 쓸지 지정합니다.
0:45지침은 구체적으로 적으세요. 누군가 제게 “당신 일은 문서를 돕는 것”이라고만 하면 뭘 해야 할지 모를 겁니다. Claude도 똑같습니다.
1:02좋은 설명은 두 질문에 답합니다. 이 스킬은 무엇을 하는가? 그리고 Claude는 언제 써야 하는가?
1:14스킬이 잘 발동하지 않으면, 평소 요청하는 말투에 맞는 키워드를 설명에 더 넣어 보세요.
1:20때로는 파일을 읽기만 하고 고치지는 못하게 하고 싶을 때가 있습니다 — 보안이 민감하거나 읽기 전용 작업처럼요. allowed-tools가 이를 가능하게 합니다. 켜져 있으면 거기 적힌 도구만 (권한 묻지 않고) 쓸 수 있고, 수정·쓰기·bash는 막힙니다. 비워 두면 아무것도 제한하지 않습니다.
1:49스킬은 대화와 같은 컨텍스트 윈도우를 나눠 씁니다. Claude가 스킬을 쓰기로 하면 그 내용을 컨텍스트로 불러옵니다. 그런데 참고 자료·예시·보조 스크립트가 필요할 때가 있죠.
2:06전부 한 파일(2만 줄)에 욱여넣으면 컨텍스트를 많이 잡아먹고 관리도 고됩니다. 여기서 점진적 공개가 등장합니다. 핵심 지침은 SKILL.md에, 자세한 참고 자료는 별도 파일에 두어 필요할 때만 읽게 합니다.
2:21공개 표준은 실행 코드용 scripts, 문서용 references, 이미지·템플릿용 assets 폴더를 권합니다.
2:39SKILL.md에서 보조 파일로 링크를 겁니다. 그러면 시스템 설계를 물을 때만 architecture.md를 읽고, “어디에 컴포넌트를 넣지?” 같은 질문엔 아예 안 불러옵니다. 문서 전체 대신 목차만 두는 셈입니다.
2:56SKILL.md는 500줄 아래로 유지하세요. 넘으면 내용을 나눌지 고민해 보세요.
3:08스킬 폴더의 스크립트는 내용을 컨텍스트에 올리지 않고 실행할 수 있습니다. 결과만 토큰을 씁니다. “읽지 말고 실행하라”고 알려주세요 — 환경 점검, 일관된 데이터 변환 등에 유용합니다.
3:31정리하면, 메타데이터 필드 중 name·description은 필수, allowed-tools는 도구 제한, model은 모델 지정입니다.
3:40같은 설명을 반복하지 않게, 설명엔 구체적 동작과 발동 문구를 넣으세요. 큰 스킬은 점진적 공개로 — SKILL.md는 500줄 아래, 보조 파일은 필요할 때만. 스크립트는 내용을 안 올리고 실행돼 컨텍스트를 아낍니다.

메타데이터 필드

Skill metadata fields

스킬 맨 위 프론트매터(--- 사이)에는 여러 필드를 적을 수 있습니다. 둘은 필수, 나머지는 선택입니다. 아래는 네 가지를 다 쓴 예시입니다. (원문은 영어입니다.)

SKILL.md — codebase-onboarding
.claude › skills › codebase-onboarding › SKILL.md
1
2
3
4
5
6
7
8
---
name: codebase-onboarding
description: Helps new developers understand how the system works.
allowed-tools: Read, Grep, Glob, Bash
model: sonnet
---

# Codebase Guide
직접 해보기 · 메타데이터 필드

선택 필드를 켜고 끄면, 오른쪽 SKILL.md 머리말이 실시간으로 바뀝니다. 필수 필드는 끌 수 없습니다.

name필수
description필수
SKILL.md
---name: codebase-onboardingdescription: Helps new developers understand how the system works.allowed-tools: Read, Grep, Glob, Bashmodel: sonnet---

allowed-tools · 스킬이 켜져 있을 때 쓸 수 있는 도구를 제한합니다. 끄면 제한이 없어져 평소 권한대로 동작합니다.

name·description은 항상 있어야 합니다. allowed-tools·model은 필요할 때만 더하면 됩니다.

좋은 설명(description) 쓰기

Writing effective descriptions

설명은 구체적으로 적어야 합니다. 누군가에게 “당신 일은 문서를 돕는 거예요”라고만 하면 막막하겠죠. Claude도 똑같습니다. 좋은 설명은 두 질문에 답합니다.

스킬이 제때 안 불려 오면, 평소 쓰는 말투의 키워드를 설명에 더 넣어 보세요. 예: “PR 리뷰” 외에 “코드 변경 점검”, “수정 사항 확인”처럼요. 설명의 표현이 곧 발동 정확도입니다.

allowed-tools로 도구 제한

Restricting tools

때로는 파일을 읽기만 하고 고치지는 못하게 하고 싶습니다. 보안이 민감한 작업, 읽기 전용 점검처럼 가드레일이 필요할 때죠. 위 예시처럼 allowed-tools: Read, Grep, Glob, Bash로 두면, 그 스킬이 켜져 있는 동안 Claude는 그 도구만 (권한 묻지 않고) 쓸 수 있습니다. 수정·쓰기는 막힙니다.

allowed-tools를 아예 비워 두면 아무것도 제한하지 않고, Claude의 평소 권한 방식대로 동작합니다.

스킬이 작동하는 모습

See it work

그럼 allowed-tools가 실제로 어떻게 막을까요? 아래에서 직접 도구를 켜고 끄며, 읽기·수정 요청이 어떻게 처리되는지 확인해 보세요. 기준은 allowed-tools스킬이 켜져 있을 때 쓸 수 있는 도구를 정해진 것만으로 제한하는 설정.가 걸린 codebase-onboarding 스킬입니다.

권한 시뮬레이터 — allowed-tools에 어떤 도구를 넣는지에 따라 요청이 통과되거나 막힙니다.

권한 시뮬레이터 · allowed-tools

스킬에 넣을 도구를 켜고 끈 다음, 요청을 골라 [실행]하세요. 요청이 필요로 하는 도구가 목록에 없으면 막힙니다.

allowed-tools에 넣을 도구

allowed-tools: Read, Grep, Glob, Bash

요청

팁. Edit를 켜고 ‘수정’ 요청을 다시 실행해 보세요. 막히던 요청이 통과됩니다.

기본 상태에선 수정 요청이 막힙니다 — Edit가 목록에 없으니까요. 이렇게 스킬마다 ‘할 수 있는 일의 범위’를 미리 정해 둘 수 있습니다.

핵심

읽기 전용 스킬은 실수로라도 파일을 건드리지 않습니다. 권한을 좁혀 두는 것만으로 안전장치가 됩니다.

직접 해보기 내 Claude로 제한 걸어 보기

스킬의 프론트매터에 allowed-tools 줄을 넣어 두고, 수정이 필요한 요청을 해보세요.

그 스킬이 켜진 동안에는 수정·쓰기 도구가 막혀, 읽기 작업만 권한 없이 진행됩니다.

스킬은 PC(Claude Code) 전용이라 모바일에서는 발동하지 않습니다. 위 시뮬레이터로 흐름을 익혀 두고, PC에서 직접 확인해 보세요.

점진적 공개로 큰 스킬 나누기

Progressive disclosure

스킬이 켜지면 그 SKILL.md 내용이 통째로 컨텍스트Claude가 한 대화에서 한 번에 다루는 정보의 양. 가득 찰수록 효율이 떨어집니다.로 들어옵니다. 그런데 참고 자료·예시·스크립트까지 한 파일에 다 넣으면 — 2만 줄짜리 파일을 떠올려 보세요 — 공간을 너무 많이 차지하고 관리도 힘듭니다.

점진적 공개가 해법입니다. 핵심 지침만 SKILL.md에 두고, 자세한 자료는 별도 파일로 빼서 필요할 때만 읽게 합니다. 폴더는 보통 이렇게 나눕니다.

멀티파일 스킬 구조 — SKILL.md는 목차, 나머지는 필요할 때만.

codebase-onboarding/
├─ SKILL.md            ← 핵심 지침 · 목차
├─ references/          ← 자세한 문서 (필요할 때만)
│  ├─ architecture-guide.md
│  └─ deep-dive-guide.md
├─ scripts/             ← 실행 코드
└─ assets/              ← 이미지·템플릿

그리고 SKILL.md 안에서, 언제 그 파일을 읽을지 조건과 함께 링크를 겁니다.

SKILL.md — codebase-onboarding
.claude › skills › codebase-onboarding › SKILL.md
1
2
3
4
5
6
7
8
9
10
# Codebase Onboarding

## Progressive Disclosure Levels

### Level 2: Architecture Overview
**Only load when user requests more detail.**
See [architecture-guide.md](references/architecture-guide.md).

### Level 3: Deep Dives
See [deep-dive-guide.md](references/deep-dive-guide.md).

이렇게 해 두면 시스템 설계를 물을 때만 architecture-guide.md를 읽고, “어디에 컴포넌트를 넣지?” 같은 질문엔 아예 불러오지 않습니다.

직접 해보기 · 점진적 공개

질문을 골라 [보내기]를 누르면, 그 질문에 필요한 참고 파일만 컨텍스트로 들어옵니다. 나머지는 목차로만 남습니다.

스킬 폴더SKILL.md는 항상, 참고 파일은 필요할 때만

SKILL.md

핵심 지침 · 목차 (항상 로드)

references/architecture-guide.md

시스템 설계 · 구조

필요할 때만 로드

references/deep-dive-guide.md

특정 주제 심화

필요할 때만 로드

컨텍스트 윈도우지금 작업 기억에 올라온 것

SKILL.md · 목차
참고 파일은 아직 올라오지 않음

질문:

‘어디에 추가하지?’는 목차(SKILL.md)만으로 답할 수 있어, 참고 파일을 아예 안 불러옵니다. 깊은 질문일 때만 해당 파일이 로드돼 컨텍스트가 가볍게 유지됩니다.

쉽게 말하면

두꺼운 설명서를 통째로 들고 다니는 대신, 목차만 들고 다니다 필요한 장만 펴 보는 것입니다. 기준은 간단해요 — SKILL.md는 500줄 아래로.

스크립트는 ‘읽지 말고 실행’

Using scripts efficiently

스킬 폴더에 둔 스크립트정해진 일을 자동으로 처리하는 작은 프로그램(코드).는, 내용을 컨텍스트에 올리지 않고도 실행할 수 있습니다. 스크립트가 실행되고 그 결과(출력)만 토큰을 씁니다. 그래서 SKILL.md에는 “이 스크립트를 읽지 말고 실행하라”고 적어 둡니다.

이 방식은 다음에 특히 좋습니다.

한 줄 정리

스스로 점검

Check yourself

정답을 먼저 떠올려 본 뒤 골라 보세요. 맞히면 설명이 나옵니다.

Q1스킬 프론트매터에서 ‘필수’ 필드는 무엇인가요?

Q2allowed-tools: Read, Grep, Glob으로 설정하면 어떻게 되나요?

Q3스킬이 커질 때 ‘점진적 공개’의 핵심은?

생각해보기

LAB · 실습 콘솔SKILL.MD FRONTMATTER

SKILL.md 검사기

SKILL.md 맨 위 라벨의 이름·설명·도구 항목이 agentskills.io 공개 표준에 맞는지 점검하고, 규칙을 벗어난 곳을 항목별로 짚어 봅니다.

index.js
// SKILL.md 검사기 — SKILL.md 맨 위 라벨(name·description·allowed-tools·model)이
// agentskills.io 공개 표준 규칙에 맞는지 점검한다. 규칙은 3장에서 배운 것 그대로다.

// (a) 맨 위 라벨 블록을 잘라 읽는다. 여는 --- 와 닫는 --- 는 각자 제 줄에 있어야 한다.
//     한 줄로 붕괴하면(---name: ... model: ... ---) 라벨을 읽지 못한다.
function parseFrontmatter(src) {
  const lines = src.replace(/\r\n/g, "\n").split("\n");
  const first = lines[0].trim();
  if (first !== "---") {
    if (first.indexOf("---") === 0) {
      return { ok: false, reason: "여는 --- 뒤에 내용이 붙어 라벨이 한 줄로 붕괴했습니다." };
    }
    return { ok: false, reason: "맨 위 라벨이 --- 로 시작하지 않습니다." };
  }
  let end = -1;
  for (let i = 1; i < lines.length; i++) {
    if (lines[i].trim() === "---") { end = i; break; }
  }
  if (end === -1) {
    return { ok: false, reason: "닫는 --- 가 제 줄에 없습니다." };
  }
  const fm = {};
  for (let i = 1; i < end; i++) {
    const c = lines[i].indexOf(":");
    if (c === -1) continue;
    fm[lines[i].slice(0, c).trim()] = lines[i].slice(c + 1).trim();
  }
  return { ok: true, fm: fm, body: lines.slice(end + 1).join("\n").trim() };
}

// (b) 라벨과 본문을 규칙대로 점검한다.
function validate(parsed) {
  const problems = [];
  if (!parsed.ok) { problems.push(parsed.reason); return problems; }
  const fm = parsed.fm;

  // name — 필수 · 소문자·숫자·하이픈만 · 최대 64자
  if (!fm.name) {
    problems.push("name 이 없습니다(필수).");
  } else {
    if (!/^[a-z0-9-]+$/.test(fm.name)) {
      problems.push("name 은 소문자·숫자·하이픈만 됩니다: " + fm.name);
    }
    if (fm.name.length > 64) {
      problems.push("name 이 64자를 넘습니다(" + fm.name.length + "자).");
    }
  }

  // description — 필수 · 최대 1,024자 · 매칭에 가장 중요한 항목
  if (!fm.description) {
    problems.push("description 이 없습니다(필수 · 매칭에 가장 중요).");
  } else if (fm.description.length > 1024) {
    problems.push("description 이 1,024자를 넘습니다(" + fm.description.length + "자).");
  }

  // allowed-tools — 있으면 쉼표로 나눈 목록이어야 한다
  const at = fm["allowed-tools"];
  if (at && at.indexOf(",") === -1 && at.split(/\s+/).length > 1) {
    problems.push("allowed-tools 는 쉼표로 나눈 목록이어야 합니다.");
  }

  // 라벨 아래 본문(지침) 존재
  if (!parsed.body) {
    problems.push("라벨 아래 본문(지침)이 없습니다.");
  }
  return problems;
}

// ── 점검할 SKILL.md 세 벌(전부 3장 예시에서 가져옴) ──
const GOOD = [
  "---",
  "name: codebase-onboarding",
  "description: Helps new developers understand how the system works.",
  "allowed-tools: Read, Grep, Glob, Bash",
  "model: sonnet",
  "---",
  "",
  "# Codebase Guide",
].join("\n");

// 여닫는 --- 가 제 줄을 잃고 한 줄로 붕괴한 라벨
const COLLAPSED =
  "---name: codebase-onboardingdescription: Helps new developers " +
  "understand how the system works.allowed-tools: Read, Grep, Glob, Bashmodel: sonnet---";

// 이름에 대문자·공백이 섞이고 description 이 빠진 라벨
const BAD = [
  "---",
  "name: PR Review",
  "model: sonnet",
  "---",
  "",
  "When writing a PR description, run git diff first.",
].join("\n");

[
  ["통과 · codebase-onboarding", GOOD],
  ["위반A · 한 줄로 붕괴한 라벨", COLLAPSED],
  ["위반B · 이름 규칙 위반 + 설명 누락", BAD],
].forEach(function (pair) {
  const problems = validate(parseFrontmatter(pair[1]));
  console.log(pair[0] + " → " + (problems.length ? problems.join(" / ") : "표준과 일치"));
});