Recent Posts
Recent Comments
반응형
«   2025/11   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Archives
Today
Total
관리 메뉴

오늘도 공부

Claude Skills 이용해서 API 문서화 스킬 만들기(중급) 본문

AI/Claude code

Claude Skills 이용해서 API 문서화 스킬 만들기(중급)

행복한 수지아빠 2025. 10. 26. 12:00
반응형

중급 튜토리얼: 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 스크립트를 포함한 더 복잡한 스킬을 만들어봅니다.

반응형