오늘도 공부
Claude Skills 이용해서 API 문서화 스킬 만들기(중급) 본문
중급 튜토리얼: API 문서화 스킬 만들기
목표
이 튜토리얼에서는 점진적 공개(Progressive Disclosure) 패턴을 사용하여 여러 파일로 구성된 스킬을 만들어봅니다.
난이도
⭐⭐ 중급 - 여러 파일 구조, 조건부 참조, 도메인별 구성
소요 시간
약 30-40분
단계 1: 문제 정의하기
시나리오
여러분은 REST API 문서를 작성하는 일을 자주 합니다. API는 여러 도메인(사용자, 결제, 상품)으로 나뉘어 있고, 각 도메인마다 다른 스키마와 엔드포인트가 있습니다.
현재 문제점:
- 모든 API 정보를 하나의 파일에 넣으면 너무 길어짐 (1000+ 줄)
- Claude가 필요없는 도메인 정보까지 읽어서 토큰 낭비
- 새로운 도메인을 추가하기 어려움
해결 방법
점진적 공개 패턴을 사용하여 Claude가 필요한 정보만 읽도록 구조화합니다.
단계 2: 디렉토리 구조 설계하기
먼저 스킬의 전체 구조를 설계합니다:
api-documentation/
├── SKILL.md # 메인 가이드 (개요 + 라우팅)
├── domains/ # 도메인별 상세 정보
│ ├── users.md # 사용자 API 스키마
│ ├── payments.md # 결제 API 스키마
│ └── products.md # 상품 API 스키마
├── templates/ # 재사용 가능한 템플릿
│ ├── endpoint.md # 엔드포인트 문서 템플릿
│ └── error-codes.md # 공통 에러 코드
└── examples/ # 실제 사용 예시
└── request-response.md # 요청/응답 예시
설계 원칙
- SKILL.md: 500줄 미만, 개요와 라우팅 로직만
- domains/: 도메인별로 분리하여 필요한 것만 로드
- templates/: 모든 도메인이 공유하는 공통 요소
- examples/: 구체적인 예시는 별도 관리
단계 3: SKILL.md 작성하기
메인 파일을 작성합니다. 여기서는 "라우팅 로직"에 집중합니다.
---
name: Documenting REST APIs
description: REST API 엔드포인트를 명확하고 일관된 형식으로 문서화합니다
---
# REST API 문서화 가이드
이 스킬은 REST API 문서를 작성할 때 일관된 구조와 명확한 설명을 제공합니다.
## 문서화 프로세스
사용자가 API 문서를 요청하면:
1. **도메인 파악**: 어떤 API에 대한 문서인지 확인
2. **적절한 스키마 로드**: 해당 도메인의 상세 정보 읽기
3. **템플릿 적용**: 표준 문서 형식 사용
4. **예시 추가**: 실제 사용 가능한 코드 예시 포함
## 도메인별 라우팅
사용자가 요청한 API에 따라 적절한 파일을 읽으세요:
### 사용자 관련 API
- 회원가입, 로그인, 프로필 등
- 📄 **스키마**: `domains/users.md` 읽기
### 결제 관련 API
- 결제 처리, 환불, 결제 내역 등
- 📄 **스키마**: `domains/payments.md` 읽기
### 상품 관련 API
- 상품 목록, 상세, 검색 등
- 📄 **스키마**: `domains/products.md` 읽기
## 문서 작성 규칙
모든 API 문서는 다음 구조를 따릅니다:
1. **엔드포인트 정보**: HTTP 메서드와 URL
2. **설명**: 무엇을 하는 API인지 명확히
3. **요청 파라미터**: 필수/선택, 타입, 설명
4. **응답 형식**: 성공/실패 케이스
5. **예시**: curl 명령어와 실제 응답
상세한 템플릿은 `templates/endpoint.md`를 참조하세요.
## 공통 요소
### 에러 코드
모든 API가 공유하는 에러 코드는 `templates/error-codes.md`를 참조하세요.
### 요청/응답 예시
실제 예시를 보려면 `examples/request-response.md`를 참조하세요.
핵심 포인트
- ✅ 도메인별로 명확하게 라우팅
- ✅ 필요한 파일만 읽도록 지시
- ✅ 공통 요소는 한 곳에서 관리
- ✅ 500줄 미만 유지
단계 4: 도메인 파일 작성하기
domains/users.md
# 사용자 API 스키마
## 엔드포인트 목록
### POST /api/users/register
회원가입 API
**요청 본문:**
```json
{
"email": "string (required)",
"password": "string (required, min 8 chars)",
"name": "string (required)",
"phone": "string (optional)"
}
응답 (201 Created):
{
"userId": "string",
"email": "string",
"name": "string",
"createdAt": "ISO 8601 timestamp"
}
에러 응답:
- 400: 이메일 형식 오류 또는 비밀번호 길이 부족
- 409: 이미 존재하는 이메일
POST /api/users/login
로그인 API
요청 본문:
{
"email": "string (required)",
"password": "string (required)"
}
응답 (200 OK):
{
"accessToken": "string",
"refreshToken": "string",
"expiresIn": "number (seconds)",
"user": {
"userId": "string",
"email": "string",
"name": "string"
}
}
에러 응답:
- 401: 잘못된 이메일 또는 비밀번호
- 403: 계정이 비활성화됨
GET /api/users/:userId
사용자 프로필 조회
경로 파라미터:
- userId: 조회할 사용자 ID (required)
헤더:
- Authorization: Bearer {accessToken} (required)
응답 (200 OK):
{
"userId": "string",
"email": "string",
"name": "string",
"phone": "string",
"createdAt": "ISO 8601 timestamp",
"updatedAt": "ISO 8601 timestamp"
}
에러 응답:
- 401: 인증 토큰 없음 또는 만료
- 403: 다른 사용자의 정보 조회 시도
- 404: 사용자를 찾을 수 없음
### domains/payments.md
```markdown
# 결제 API 스키마
## 엔드포인트 목록
### POST /api/payments/charge
결제 처리 API
**요청 본문:**
```json
{
"amount": "number (required, positive)",
"currency": "string (required, ISO 4217)",
"paymentMethod": "string (required: 'card' | 'bank' | 'wallet')",
"orderId": "string (required)",
"description": "string (optional)"
}
응답 (200 OK):
{
"paymentId": "string",
"status": "string ('completed' | 'pending' | 'failed')",
"amount": "number",
"currency": "string",
"transactionId": "string",
"paidAt": "ISO 8601 timestamp"
}
에러 응답:
- 400: 잘못된 금액 또는 통화 코드
- 402: 결제 수단 오류 (잔액 부족 등)
- 409: 이미 처리된 주문
POST /api/payments/refund
환불 처리 API
요청 본문:
{
"paymentId": "string (required)",
"amount": "number (optional, 부분 환불 시)",
"reason": "string (required)"
}
응답 (200 OK):
{
"refundId": "string",
"paymentId": "string",
"amount": "number",
"status": "string ('completed' | 'pending')",
"refundedAt": "ISO 8601 timestamp"
}
에러 응답:
- 400: 환불 금액이 결제 금액을 초과
- 404: 결제 정보를 찾을 수 없음
- 409: 이미 환불된 결제
### domains/products.md
```markdown
# 상품 API 스키마
## 엔드포인트 목록
### GET /api/products
상품 목록 조회
**쿼리 파라미터:**
- `page`: 페이지 번호 (default: 1)
- `limit`: 페이지당 항목 수 (default: 20, max: 100)
- `category`: 카테고리 필터 (optional)
- `sort`: 정렬 기준 ('price' | 'name' | 'date', default: 'date')
- `order`: 정렬 순서 ('asc' | 'desc', default: 'desc')
**응답 (200 OK):**
```json
{
"products": [
{
"productId": "string",
"name": "string",
"price": "number",
"category": "string",
"imageUrl": "string",
"stock": "number"
}
],
"pagination": {
"currentPage": "number",
"totalPages": "number",
"totalItems": "number",
"itemsPerPage": "number"
}
}
GET /api/products/:productId
상품 상세 조회
경로 파라미터:
- productId: 상품 ID (required)
응답 (200 OK):
{
"productId": "string",
"name": "string",
"description": "string",
"price": "number",
"category": "string",
"images": ["string"],
"stock": "number",
"specifications": {
"key": "value"
},
"createdAt": "ISO 8601 timestamp",
"updatedAt": "ISO 8601 timestamp"
}
에러 응답:
- 404: 상품을 찾을 수 없음
---
## 단계 5: 템플릿 파일 작성하기
### templates/endpoint.md
```markdown
# API 엔드포인트 문서 템플릿
모든 엔드포인트 문서는 다음 구조를 따릅니다:
## [HTTP 메서드] [경로]
[간단한 한 줄 설명]
### 설명
[이 엔드포인트가 무엇을 하는지 상세히 설명]
### 인증
[필요한 경우 인증 방법 명시]
### 요청
**경로 파라미터:** (해당하는 경우)
- `paramName`: [타입] - [설명]
**쿼리 파라미터:** (해당하는 경우)
- `paramName`: [타입] - [설명] (required/optional, default 값)
**헤더:** (해당하는 경우)
- `HeaderName`: [설명]
**요청 본문:** (해당하는 경우)
```json
{
"field": "type (required/optional) - description"
}
응답
성공 응답 (HTTP 상태 코드):
{
"field": "type - description"
}
에러 응답:
예시
요청:
curl -X [METHOD] \
https://api.example.com/[endpoint] \
-H "Content-Type: application/json" \
-d '{
"field": "value"
}'
응답:
{
"field": "value"
}
참고사항
[추가로 알아야 할 정보가 있다면 작성]
### templates/error-codes.md
```markdown
# 공통 에러 코드
모든 API에서 사용하는 표준 HTTP 상태 코드와 에러 응답 형식입니다.
## 에러 응답 형식
모든 에러 응답은 다음 형식을 따릅니다:
```json
{
"error": {
"code": "string",
"message": "string",
"details": "object (optional)"
}
}
HTTP 상태 코드
2xx - 성공
- 200 OK: 요청 성공
- 201 Created: 리소스 생성 성공
- 204 No Content: 성공했지만 응답 본문 없음
4xx - 클라이언트 에러
- 400 Bad Request: 잘못된 요청 (필수 파라미터 누락, 형식 오류 등)
- 401 Unauthorized: 인증 필요 (토큰 없음 또는 만료)
- 403 Forbidden: 권한 없음 (인증은 됐지만 접근 권한 없음)
- 404 Not Found: 리소스를 찾을 수 없음
- 409 Conflict: 리소스 충돌 (중복된 데이터 등)
- 422 Unprocessable Entity: 요청은 올바르나 처리할 수 없음
- 429 Too Many Requests: 요청 횟수 제한 초과
5xx - 서버 에러
- 500 Internal Server Error: 서버 내부 오류
- 503 Service Unavailable: 서비스 일시 중단 (유지보수 등)
에러 코드 예시
인증 관련
{
"error": {
"code": "AUTH_TOKEN_EXPIRED",
"message": "인증 토큰이 만료되었습니다",
"details": {
"expiredAt": "2024-01-15T10:30:00Z"
}
}
}
유효성 검증 관련
{
"error": {
"code": "VALIDATION_ERROR",
"message": "입력값 검증에 실패했습니다",
"details": {
"fields": {
"email": "올바른 이메일 형식이 아닙니다",
"password": "비밀번호는 최소 8자 이상이어야 합니다"
}
}
}
}
비즈니스 로직 관련
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "잔액이 부족합니다",
"details": {
"required": 10000,
"available": 5000
}
}
}
---
## 단계 6: 예시 파일 작성하기
### examples/request-response.md
```markdown
# 요청/응답 예시
실제 사용 가능한 API 호출 예시입니다.
## 사용자 회원가입 및 로그인 플로우
### 1단계: 회원가입
**요청:**
```bash
curl -X POST https://api.example.com/api/users/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securePassword123",
"name": "홍길동",
"phone": "010-1234-5678"
}'
응답:
{
"userId": "usr_abc123def456",
"email": "user@example.com",
"name": "홍길동",
"createdAt": "2024-01-15T10:30:00Z"
}
2단계: 로그인
요청:
curl -X POST https://api.example.com/api/users/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securePassword123"
}'
응답:
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "def456ghi789jkl...",
"expiresIn": 3600,
"user": {
"userId": "usr_abc123def456",
"email": "user@example.com",
"name": "홍길동"
}
}
3단계: 프로필 조회 (인증 필요)
요청:
curl -X GET https://api.example.com/api/users/usr_abc123def456 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
응답:
{
"userId": "usr_abc123def456",
"email": "user@example.com",
"name": "홍길동",
"phone": "010-1234-5678",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
상품 검색 및 결제 플로우
1단계: 상품 검색
요청:
curl -X GET "https://api.example.com/api/products?category=electronics&sort=price&order=asc&limit=5"
응답:
{
"products": [
{
"productId": "prd_123",
"name": "무선 이어폰",
"price": 89000,
"category": "electronics",
"imageUrl": "https://cdn.example.com/products/prd_123.jpg",
"stock": 50
},
{
"productId": "prd_456",
"name": "블루투스 스피커",
"price": 129000,
"category": "electronics",
"imageUrl": "https://cdn.example.com/products/prd_456.jpg",
"stock": 30
}
],
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalItems": 2,
"itemsPerPage": 5
}
}
2단계: 결제 처리
요청:
curl -X POST https://api.example.com/api/payments/charge \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-d '{
"amount": 89000,
"currency": "KRW",
"paymentMethod": "card",
"orderId": "ord_789xyz",
"description": "무선 이어폰 구매"
}'
응답:
{
"paymentId": "pay_abc123",
"status": "completed",
"amount": 89000,
"currency": "KRW",
"transactionId": "txn_def456",
"paidAt": "2024-01-15T11:00:00Z"
}
에러 처리 예시
인증 토큰 만료
요청:
curl -X GET https://api.example.com/api/users/usr_abc123def456 \
-H "Authorization: Bearer expired_token_here"
응답 (401 Unauthorized):
{
"error": {
"code": "AUTH_TOKEN_EXPIRED",
"message": "인증 토큰이 만료되었습니다",
"details": {
"expiredAt": "2024-01-15T10:00:00Z"
}
}
}
존재하지 않는 리소스
요청:
curl -X GET https://api.example.com/api/products/invalid_id
응답 (404 Not Found):
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "상품을 찾을 수 없습니다",
"details": {
"productId": "invalid_id"
}
}
}
---
## 단계 7: 스킬 테스트하기
### 테스트 케이스 1: 사용자 API 문서 요청
**입력:**
"회원가입 API 문서를 작성해줘"
**예상 동작:**
1. Claude가 "사용자 관련 API"로 인식
2. `domains/users.md` 파일을 읽음
3. `templates/endpoint.md`를 참조하여 형식 맞춤
4. `examples/request-response.md`에서 실제 예시 추가
**확인 사항:**
- ✅ domains/payments.md나 products.md는 읽지 않았는가?
- ✅ 템플릿 형식을 올바르게 따르는가?
- ✅ 실제 사용 가능한 curl 예시가 포함되었는가?
### 테스트 케이스 2: 결제 API 문서 요청
**입력:**
"결제 처리와 환불 API 문서를 만들어줘"
**예상 동작:**
1. Claude가 "결제 관련 API"로 인식
2. `domains/payments.md` 파일만 읽음
3. 공통 에러 코드는 `templates/error-codes.md`에서 참조
**확인 사항:**
- ✅ 불필요한 domains/users.md는 읽지 않았는가?
- ✅ 에러 코드 설명이 포함되었는가?
### 테스트 케이스 3: 전체 API 문서 요청
**입력:**
"모든 API에 대한 종합 문서를 작성해줘"
**예상 동작:**
1. 모든 도메인 파일을 차례로 읽음
2. 도메인별로 섹션을 나눠서 정리
**확인 사항:**
- ✅ 모든 도메인이 포함되었는가?
- ✅ 일관된 형식으로 작성되었는가?
---
## 단계 8: 개선하기
### 개선 포인트 1: 조건부 참조 추가
만약 일부 API만 인증이 필요하다면:
**SKILL.md에 추가:**
```markdown
## 인증 관련
대부분의 API는 인증이 필요합니다. 인증 방법:
- Bearer 토큰을 `Authorization` 헤더에 포함
- 토큰 만료 시 `refreshToken`으로 갱신
예외적으로 인증이 불필요한 엔드포인트:
- GET /api/products (상품 목록 조회)
- GET /api/products/:productId (상품 상세 조회)
개선 포인트 2: 버전 관리
API 버전이 여러 개라면:
디렉토리 구조 변경:
api-documentation/
├── SKILL.md
├── v1/
│ └── domains/
│ ├── users.md
│ └── ...
└── v2/
└── domains/
├── users.md
└── ...
SKILL.md에 라우팅 추가:
## 버전별 라우팅
사용자가 버전을 명시하지 않으면 최신 버전(v2) 사용:
- v1 요청 시: `v1/domains/[domain].md` 읽기
- v2 요청 시: `v2/domains/[domain].md` 읽기
개선 포인트 3: 목차 추가
도메인 파일이 길어지면 상단에 목차 추가:
domains/users.md 상단에:
# 사용자 API 스키마
## 목차
1. [POST /api/users/register](#post-apiusersregister) - 회원가입
2. [POST /api/users/login](#post-apiuserslogin) - 로그인
3. [GET /api/users/:userId](#get-apiusersuserid) - 프로필 조회
4. [PUT /api/users/:userId](#put-apiusersuserid) - 프로필 수정
5. [DELETE /api/users/:userId](#delete-apiusersuserid) - 계정 삭제
---
[나머지 내용...]
단계 9: 점진적 공개의 효과 측정
비교: 단일 파일 vs 점진적 공개
단일 파일 구조 (비추천)
SKILL.md (1500줄)
└── 모든 도메인 정보 포함
문제점:
- 사용자 API 문서 요청 시에도 1500줄 모두 로드
- 토큰 낭비: ~15,000 토큰
- 느린 응답 속도
- 수정 시 전체 파일 검토 필요
점진적 공개 구조 (추천)
SKILL.md (200줄)
├── domains/users.md (300줄)
├── domains/payments.md (250줄)
└── domains/products.md (280줄)
장점:
- 사용자 API 문서 요청 시 200 + 300 = 500줄만 로드
- 토큰 절약: ~5,000 토큰 (67% 절감!)
- 빠른 응답 속도
- 도메인별 독립적 수정 가능
체크리스트
중급 스킬을 완성했다면:
- [ ] SKILL.md가 500줄 미만인가?
- [ ] 도메인별로 파일이 분리되어 있는가?
- [ ] 라우팅 로직이 명확한가?
- [ ] 공통 요소는 별도 파일로 관리하는가?
- [ ] 참조 깊이가 1단계인가? (SKILL.md → 참조 파일)
- [ ] 각 도메인 파일에 목차가 있는가? (100줄 이상인 경우)
- [ ] 모든 테스트 케이스를 통과하는가?
- [ ] 불필요한 파일을 읽지 않는가?
다음 단계
배운 내용
- ✅ 점진적 공개(Progressive Disclosure) 패턴
- ✅ 도메인별 파일 구조
- ✅ 조건부 라우팅
- ✅ 공통 템플릿 활용
- ✅ 토큰 효율성 개선
더 나아가기
- 고급 튜토리얼에서 실행 가능한 스크립트 추가 배우기
- 더 복잡한 라우팅 로직 구현해보기
- MCP 도구와 통합하기
자주 묻는 질문
Q: 언제 파일을 분리해야 하나요?
A: SKILL.md가 500줄에 가까워지거나, 명확하게 구분되는 도메인이 있을 때 분리하세요.
Q: 참조 파일을 2단계 이상 중첩해도 되나요?
A: 안 됩니다. Claude가 부분적으로만 읽을 수 있어 정보가 누락될 수 있습니다. 항상 1단계로 유지하세요.
Q: 공통 요소는 어떻게 관리하나요?
A: templates/ 폴더에 넣고, 모든 도메인에서 참조하도록 SKILL.md에 명시하세요.
Q: 파일이 너무 많아지면 어떻게 하나요?
A: 하위 디렉토리로 그룹핑하세요. 예: domains/users/, domains/payments/
실습 과제
과제 1: 자신만의 도메인 추가
현재 스킬에 "주문(orders)" 도메인을 추가해보세요:
- domains/orders.md 파일 생성
- SKILL.md에 라우팅 로직 추가
- 최소 3개의 엔드포인트 정의
과제 2: 다국어 지원
영어와 한국어 API 문서를 모두 지원하도록 구조를 변경해보세요:
api-documentation/
├── SKILL.md
├── ko/
│ └── domains/
└── en/
└── domains/
과제 3: Swagger/OpenAPI 변환
API 스키마를 OpenAPI 3.0 형식으로 변환하는 기능을 추가해보세요.
축하합니다! 중급 스킬을 완성했습니다! 🎉
이제 점진적 공개 패턴으로 대규모 스킬을 효율적으로 관리할 수 있습니다. 고급 튜토리얼에서는 실행 가능한 Python 스크립트를 포함한 더 복잡한 스킬을 만들어봅니다.
'AI > Claude code' 카테고리의 다른 글
| Claude Code 컴포넌트 완벽 가이드: 실전 워크플로우 (1) | 2025.10.28 |
|---|---|
| Claude Skills를 이용해서 PDF 양식 자동화 스킬 만들기(고급) (0) | 2025.10.26 |
| Claude Skills를 이용해 간단한 블로그 글 작성 스킬 만들기 (초급) (0) | 2025.10.26 |
| Claude Agent Skill 제작 가이드 (심화과정) (0) | 2025.10.26 |
| Claude Code Hooks 고급 가이드: Sub-Agent 동작 제어하기 (0) | 2025.10.26 |
