사내 Claude Code 세미나 4주차 회고: Custom Skills로 만드는 재사용 가능한 표준 절차
이번 주차 발표를 준비하면서 가장 흥미로웠던 깨달음은, Skill의 본질이 라이브러리화가 아니라 컨텍스트 비용의 분할이었다는 점이다. "재사용 가능한 프롬프트"라는 표면적 정의 뒤에 훨씬 더 영리한 자원 관리 모델이 숨어 있었다.
세미나 4주차를 마쳤다. 1주차의 "API 요청 한 덩어리" 멘탈 모델, 2주차의 CLAUDE.md/메모리, 3주차의 Hooks를 차곡차곡 쌓아 올린 자리에서, 이번 주는 Skill을 다뤘다. Skill은 1주차 발표 슬라이드에서 "Plugin Agents와 헷갈리지 말 것"이라고 짧게 짚고 넘어갔던 그 개념이다. 이번 주차에 작정하고 깊이 들어갔고, 끝에는 사내에서 바로 쓸 수 있는 Spring Boot 서비스 생성기를 함께 만들어 보는 시간으로 마무리했다. 이 글은 그날 발표의 핵심을 시니어 개발자의 시선으로 다시 한 번 정리한 회고다.
1. Skill이란 무엇인가: "필요할 때 펼쳐 드는 표준 절차"
발표 첫 슬라이드에 박은 정의는 이것이었다.
Skill은 Claude Code에서 재사용 가능한 프롬프트 모듈이다. 특정 작업에 대한 지시사항, 규칙, 템플릿, 스크립트를 묶어서
/스킬명으로 호출하거나, Claude가 자동으로 트리거한다.
여기서 챕터원 한 분이 좋은 질문을 했다. "그러면 CLAUDE.md와 뭐가 다른 거예요?" 이 질문이 이번 주차 발표의 출발점이었다.
| CLAUDE.md | Skill | |
|---|---|---|
| 적용 시점 | 항상 로드됨 | 필요할 때만 로드됨 |
| 적용 범위 | 프로젝트 전체에 항상 | 특정 작업·파일·요청에만 |
| 비용 | 매 요청마다 토큰 소비 | 트리거되어야 비용 발생 |
| 비유 | 사무실 벽에 붙은 표준 가이드 | 책장에서 꺼내 펼치는 매뉴얼 |
이 표 한 장을 두고 5분을 보냈다. 답이 정해진다: 항상 지켜야 하는 기본 규칙은 CLAUDE.md, 특정 작업에서만 펼쳐지는 표준 절차는 Skill. 발표 후 받은 피드백 중 가장 자주 인용된 한 줄이 이거였다.
2. 3단계 로딩: Skill의 본질은 컨텍스트 비용 관리다
여기가 이번 발표의 가장 중요한 그림이었다. Skill은 단순히 "재사용 가능한 프롬프트"가 아니다. Claude의 컨텍스트 윈도우를 영리하게 쓰기 위해 만들어진 3단계 로딩 모델이다.
- Level 1: Metadata (
name + description): 세션 시작 시점에 항상 로드. ~100 단어, description은 최대 250자. 전체 컨텍스트의 1% 정도만 차지하며, 이 한 줄 설명이 Claude가 "이 스킬을 써야 하나?"를 판단하는 유일한 근거가 된다. - Level 2: SKILL.md 본문: 스킬이 트리거된 시점에만 로드. 500줄 권장. 실제 절차·규칙·출력 포맷이 들어간다.
- Level 3: Bundle Resources (
references/,templates/,scripts/,assets/): Claude가 필요할 때만 읽음. 스크립트는 실행만 되고 컨텍스트엔 들어가지 않는다.
1주차의 Deferred Tools와 정확히 같은 발상이다. 항상 들고 다니지 말고, 알 수 있는 만큼만 들고 다니다가, 필요할 때 가져온다. 발표장에서 이 그림을 띄우자 한 분이 "lazy loading의 LLM 버전이네요"라고 정확히 짚어주셨다. 그렇다.
3. Frontmatter: 트리거의 정확도가 description에 달렸다
Skill의 frontmatter는 YAML이다. 그 안에 들어가는 필드 중 무엇이 중요한지 우선순위로 정리했다.
---
name: my-skill # 슬래시 커맨드명 (소문자·하이픈·64자 이내)
description: 스킬이 하는 일과 언제 트리거되어야 하는지 설명 (250자)
argument-hint: [인자-설명] # 자동완성 시 표시
allowed-tools: Read, Grep, Glob, Edit, Write
paths: "**/*.java" # 특정 파일 매칭 시에만 활성화
disable-model-invocation: false # true면 Claude 자동 호출 불가 (수동 전용)
user-invocable: true # false면 / 메뉴에서 숨김
model: claude-opus-4-7 # 스킬 실행용 모델 오버라이드
effort: high # 추론 노력 수준
context: fork # fork면 서브에이전트에서 격리 실행
agent: Explore # fork 시 에이전트 타입
---여기서 발표 시간을 가장 많이 쓴 필드는 description 이었다. Claude의 자동 트리거 판단의 99%가 이 한 줄에서 결정된다. 발표에서 던진 한 슬라이드.
❌ 소극적: 트리거 빈도 낮음
description: 대시보드를 만드는 방법
✅ 적극적(pushy): 트리거 빈도 높음
description: 대시보드를 만드는 방법.
사용자가 대시보드, 데이터 시각화,
내부 지표를 언급하거나 데이터를 표시하려
할 때 반드시 이 스킬을 사용한다.Skill description은 광고 카피처럼 적어야 한다. "이 작업이라면 무조건 나를 불러" 라고 적극적으로 외쳐야 자동 트리거가 잘 잡힌다. "소극적인 description은 결국 안 쓰이는 Skill로 끝난다"는 한 줄이 발표장에서 가장 자주 인용되었다.
4. 치환 변수: 동적 값을 본문에 주입한다
스킬 본문에서 호출 인자와 환경 정보를 받아쓰는 변수들.
| 변수 | 설명 |
|---|---|
$ARGUMENTS | 호출 시 전달된 전체 인자 |
$ARGUMENTS[N] 또는 $N | N번째 인자 (0-based) |
${CLAUDE_SESSION_ID} | 현재 세션 ID (로깅용) |
${CLAUDE_SKILL_DIR} | SKILL.md가 위치한 디렉토리 절대 경로 |
---
name: migrate-component
argument-hint: [컴포넌트명] [원본] [대상]
---
$0 컴포넌트를 $1에서 $2로 마이그레이션하라.
검증 스크립트 실행:
!`bash ${CLAUDE_SKILL_DIR}/scripts/validate.sh $0`호출하면 /migrate-component SearchBar React Vue → $0=SearchBar, $1=React, $2=Vue로 치환된다. 백엔드 시각으로 보면 템플릿 엔진이다. Mustache, JSP, Thymeleaf의 LLM 버전.
여기서 한 가지 영리한 패턴이 등장한다: shell command injection.
## 현재 파일 히스토리
!`git log --oneline -20 $ARGUMENTS`이 !`...` 구문은 스킬이 로드되는 시점에 명령이 실행되어, 출력이 프롬프트에 끼워 넣어진다. Claude가 보는 프롬프트엔 이미 git log 결과가 박혀 있는 셈이다. 컨텍스트 주입 패턴의 가장 우아한 형태다.
5. 저장 위치와 우선순위: Skill도 4단 계층
Skill은 4가지 스코프에 저장할 수 있다. 충돌 시 상위 스코프가 이긴다.
1. Enterprise : managed settings (조직 전체, 최우선)
2. Personal : ~/.claude/skills/<name>/SKILL.md
3. Project : .claude/skills/<name>/SKILL.md (git 공유)
4. Plugin : <plugin>/skills/<name>/SKILL.md발표에서 monorepo 자동 탐색 예시도 보여줬다.
my-monorepo/
├── .claude/skills/ # root skills (모든 패키지)
│ └── deploy/SKILL.md
└── packages/
├── frontend/
│ └── .claude/skills/ # 프론트엔드 전용
│ └── react-patterns/SKILL.md
└── backend/
└── .claude/skills/ # 백엔드 전용
└── api-conventions/SKILL.mdpackages/frontend/src/Component.tsx를 작업 중이면 packages/frontend/.claude/skills/의 스킬도 자동 탐색된다. 2주차의 CLAUDE.md 멀티모듈 패턴과 똑같은 발상이다. 컨텍스트는 작업이 있는 곳 가까이에 둔다.
6. Single-File vs Multi-File: 언제 어떻게 만들 것인가
발표에서 두 형태를 나란히 비교했다.
Single-File: 디렉토리 하나에 SKILL.md 한 개.
~/.claude/skills/my-skill/
└── SKILL.md빠른 프로토타입, 개인용 컨벤션, 가벼운 체크리스트에 적합. 단, 500줄을 넘어가면 관리가 어려워진다.
Multi-File: SKILL.md가 허브, 자료는 분리.
~/.claude/skills/spring-service-generator/
├── SKILL.md # 진입점 (네비게이션 허브)
├── references/
│ └── conventions.md # 코딩 컨벤션 (lazy-loaded)
├── templates/
│ ├── entity.java.md
│ ├── service.java.md
│ ├── controller.java.md
│ ├── dto.java.md
│ └── test.java.md
└── scripts/
└── validate.sh # 안티패턴 검출핵심 설계 원칙:
- SKILL.md는 500줄 이하, 넘으면 하위 파일로 분리하고 참조 포인터만 둔다.
- 선택적 로딩:
references/,templates/는 Claude가 필요할 때만 읽음. - 스크립트는 실행만: 컨텍스트에 로드되지 않고 결과만 반환.
7. 라이브 데모: Spring Boot 서비스 생성기
발표 후반부에서 실제로 만들어 본 스킬이다. 한 줄 호출로 7개 파일이 일관된 컨벤션으로 생성된다.
---
name: spring-service
description: Spring Boot 서비스 레이어 보일러플레이트 생성기.
Entity, Repository, Service, Controller, DTO, 테스트를 한번에 생성.
새 도메인/리소스 생성 시, CRUD 추가 시, 모듈 스캐폴딩 시 사용.
argument-hint: [엔티티명] [필드1:타입 필드2:타입 ...]
paths: "**/*.java"
allowed-tools: Read, Grep, Glob, Edit, Write, Bash
---
## 생성 파일 목록
엔티티 $0에 대해 7개 파일 생성:
| Layer | File | Location |
|--------------|------------------------|--------------------|
| Entity | $0.java | domain/ |
| Repository | $0Repository.java | repository/ |
| Service | $0Service.java | service/ |
| ServiceImpl | $0ServiceImpl.java | service/impl/ |
| Controller | $0Controller.java | controller/ |
| RequestDTO | $0Request.java | dto/request/ |
| ResponseDTO | $0Response.java | dto/response/ |
## 사용 방법
1. $ARGUMENTS 파싱 (엔티티명 + 필드:타입 쌍)
2. 기존 Java 파일 스캔으로 base package 자동 감지
3. [references/conventions.md]를 읽고 컨벤션 확인
4. [templates/] 디렉토리의 가이드로 각 파일 생성
5. 검증 스크립트 실행:
!`bash ${CLAUDE_SKILL_DIR}/scripts/validate.sh`/spring-service Product name:string price:decimal stockQuantity:int description:text
# 결과: 7개 파일 자동 생성
# - domain/Product.java
# - repository/ProductRepository.java
# - service/ProductService.java
# - service/impl/ProductServiceImpl.java
# - controller/ProductController.java
# - dto/request/ProductRequest.java
# - dto/response/ProductResponse.java
# + validate.sh 자동 실행templates/entity.java.md 같은 가이드 파일에는 이런 내용이 들어간다.
# entity.java.md
- @Entity + @Table(name = 복수형 snake_case)
- @Getter + @NoArgsConstructor(access = PROTECTED) + @Builder
- Setter 금지: Builder로 생성, 도메인 메서드로 수정
- 금액 필드: BigDecimal(precision=19, scale=4)
- 모든 필드에 Bean Validation 어노테이션
- BaseEntity 상속 (createdAt, updatedAt 자동 관리)이걸 만들어두면 신입이 첫 도메인을 만들 때 시니어가 짠 컨벤션이 그대로 적용된 코드 7개가 나온다. 발표장에서 라이브 데모로 보여줬을 때 가장 큰 반응이 나왔다. 한 챕터원이 "이게 사내 표준이 되면 좋겠네요"라고 말한 게 인상적이었다. 정확히 그 자리에서 사내 공통 스킬 레포 만들자는 합의가 나왔다.
8. skill-creator: 품질이 검증된 스킬을 만드는 방법
수동으로 만든 스킬은 "내가 써보니 잘 된다" 수준의 검증이다. 팀에 배포할 거라면 skill-creator 로 정교화하는 게 정답이다.
1. Intent (목적·트리거·출력)
2. Interview (엣지 케이스·포맷)
3. Draft (+ evals.json)
4. A/B Run (with vs without skill)
5. Eval (정량 + 정성)
6. Iterate (만족할 때까지)
7. Description Optimization
- should-trigger 쿼리 20개
- should-not-trigger 쿼리 20개
- 60% train / 40% test
- 자동으로 최적 description 도출
8. Package & Deploy (.skill 파일)이 워크플로우의 가장 정교한 부분은 Step 7: Description 최적화 다. 트리거 정확도를 정량적으로 측정하기 위해 train/test 분리까지 한다. 백엔드 개발자에겐 이 한 줄로 충분한 설명이다: "description을 모델로 보고 fine-tuning한다." 머신러닝의 보편 원리가 그대로 적용된 것이다.
발표에서 정리한 선택 가이드:
| 상황 | 수동 생성 | skill-creator |
|---|---|---|
| 빨리 만들어 써보고 싶을 때 | ✓ | |
| 개인용 스킬 | ✓ | |
| 프로토타입 단계 | ✓ | |
| 팀에 배포할 스킬 | ✓ | |
| 출력 품질이 중요할 때 | ✓ | |
| 트리거 정확도가 중요할 때 | ✓ |
결론: 프로토타입은 수동으로 빠르게, 검증 후 skill-creator로 정교화한다. 두 방식은 경쟁자가 아니라 단계의 차이다.
9. 호출 제어: Claude만 호출 vs 사용자만 호출
disable-model-invocation과 user-invocable 조합으로 호출 권한을 세밀하게 통제할 수 있다.
| 설정 | 사용자 호출 | Claude 자동 호출 | 적합한 용도 |
|---|---|---|---|
| 기본값 | ✓ | ✓ | 일반 스킬 |
disable-model-invocation: true | ✓ | ✗ | 배포 · 위험 작업 (수동 전용) |
user-invocable: false | ✗ | ✓ | 배경 지식 · 컨텍스트 정보 |
배포 스킬이나 DROP TABLE 비슷한 위험한 작업은 disable-model-invocation: true로 Claude의 자동 트리거를 막아야 한다. 사람이 명시적으로 호출했을 때만 동작하는 안전장치다. 반대로 사용자가 직접 부를 일 없는 배경 지식 스킬은 user-invocable: false로 / 메뉴에서 숨길 수 있다.
10. 시니어의 시선으로 정리하는 인사이트
① Skill의 본질은 "재사용 가능한 프롬프트"가 아니라 "비용 관리"다
표면적으로는 라이브러리화처럼 보이지만, 진짜 가치는 컨텍스트 윈도우를 영리하게 쪼개 쓰는 데에 있다. 항상 들고 다닐 필요 없는 절차·자료를 트리거 가능하게 분리하는 것. 백엔드 시각으로 보면 lazy initialization과 같은 원리다.
② description은 광고 카피처럼 적어라
Skill 동작의 95%는 description의 정밀도에서 결정된다. 모호한 description은 트리거되지 않는 Skill로 귀결되고, 안 쓰이는 Skill은 없는 Skill과 같다. 시니어 입장에서 Skill을 만들 때 가장 시간을 많이 들여야 하는 부분이 본문이 아니라 description이라는 점은 처음엔 낯설지만, 한 번 익히면 본능이 된다.
③ CLAUDE.md vs Skill vs Hook의 역할 분담
세미나 시리즈 전체에서 가장 자주 받는 질문이다. 단순한 라우팅 규칙을 깔아두면 머릿속이 깨끗해진다.
- 항상 지켜야 하는 규칙 → CLAUDE.md
- 특정 작업에서만 펼치는 표준 절차 → Skill
- 모든 동작 시점에 자동으로 끼어드는 검증·자동화 → Hook
세 개는 서로 보완 관계지 대체 관계가 아니다. 이 라우팅을 팀에 정착시키면, "이 규칙을 어디 적어야 하지?"라는 질문이 사라진다.
④ Multi-File Skill의 디렉토리 구조는 곧 그 스킬의 아키텍처다
references/, templates/, scripts/의 분리는 단순한 정리가 아니다. 컨텍스트에 들어갈 것과 들어가지 않을 것의 경계, 정적 자료와 실행 자료의 경계 를 명시하는 일이다. 잘 설계된 Multi-File Skill은 그 디렉토리 트리만 봐도 무엇을 하는 스킬인지가 읽힌다.
⑤ skill-creator는 "Skill의 단위 테스트"다
evals.json과 benchmark.json은 결국 스킬의 회귀 테스트다. 백엔드 개발자라면 매우 익숙한 그림이다: 코드에 단위 테스트가 필요하듯, 프롬프트에도 단위 테스트가 필요하다. 팀에 배포할 거라면 이 과정 없이는 신뢰할 수 없는 자산이 되어 버린다.
마무리: 우리 팀의 표준 절차를 자산으로 남기는 일
발표를 마치고 한 챕터원이 이런 말을 했다.
"신입에게 '여기서는 이렇게 짭니다'라고 PR마다 댓글로 다는 시간이 다 모이면 결국 시니어들의 가장 비싼 시간을 까먹는 일이잖아요. 그 댓글들을 한 번 Skill로 옮기면, 시니어가 사라져도 그 컨벤션은 남는다는 거네요."
정확히 그게 핵심이다. Skill은 우리 팀의 표준 절차를 자산으로 남기는 도구다. CLAUDE.md가 헌법이라면, Skill은 그 헌법을 구체적으로 집행하는 표준 작업 절차(SOP)다. 한 번 잘 만들어두면 사람이 들고 나도 그 자산은 남는다.
이번 주차의 발표를 준비하며 나도 처음으로 사내에서 쓸 만한 Spring Boot 서비스 생성기 스킬을 직접 만들었다. 발표가 끝난 다음 날, 그 스킬의 첫 PR이 챕터원 손에서 올라온 걸 보고 묘한 기분이 들었다. 누군가의 시간을 한 시간 줄여주는 자산이 정말 만들어진 것이다. 다음 주는 시리즈의 마지막, Sub-Agents 편이다. 메인 컨텍스트의 격리, 도구·권한·모델의 제약, 그리고 "외주 전문가에게 일을 통째로 맡기는" 그 그림으로 시리즈를 닫게 된다. 잘 마무리 짓고 싶다.