From cb56d1a22d708fa898552d3442ca7c41474810f8 Mon Sep 17 00:00:00 2001 From: hahmee Date: Sun, 8 Mar 2026 17:58:02 +0900 Subject: [PATCH 1/9] docs: add Korean (ko-KR) README and CONTRIBUTING translation --- README.md | 4 +- README.zh-CN.md | 2 +- docs/ja-JP/README.md | 2 +- docs/ko-KR/CONTRIBUTING.md | 324 +++++++++++++++++ docs/ko-KR/README.md | 729 +++++++++++++++++++++++++++++++++++++ docs/zh-TW/README.md | 2 +- 6 files changed, 1058 insertions(+), 5 deletions(-) create mode 100644 docs/ko-KR/CONTRIBUTING.md create mode 100644 docs/ko-KR/README.md diff --git a/README.md b/README.md index 05e33b99..8c03737d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**Language:** English | [繁體中文](docs/zh-TW/README.md) +**Language:** English | [繁體中文](docs/zh-TW/README.md) | [한국어](docs/ko-KR/README.md) # Everything Claude Code @@ -24,7 +24,7 @@ **🌐 Language / 语言 / 語言** -[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) +[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) diff --git a/README.zh-CN.md b/README.zh-CN.md index ef72d195..7ce9043d 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -13,7 +13,7 @@ **🌐 Language / 语言 / 語言** -[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) +[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) diff --git a/docs/ja-JP/README.md b/docs/ja-JP/README.md index 9781f955..e11da683 100644 --- a/docs/ja-JP/README.md +++ b/docs/ja-JP/README.md @@ -1,4 +1,4 @@ -**言語:** English | [简体中文](../../README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) +**言語:** English | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](README.md) | [한국어](../ko-KR/README.md) # Everything Claude Code diff --git a/docs/ko-KR/CONTRIBUTING.md b/docs/ko-KR/CONTRIBUTING.md new file mode 100644 index 00000000..8f2f5614 --- /dev/null +++ b/docs/ko-KR/CONTRIBUTING.md @@ -0,0 +1,324 @@ +# Everything Claude Code에 기여하기 + +기여에 관심을 가져주셔서 감사합니다! 이 저장소는 Claude Code 사용자를 위한 커뮤니티 리소스입니다. + +## 목차 + +- [우리가 찾는 것](#우리가-찾는-것) +- [빠른 시작](#빠른-시작) +- [스킬 기여하기](#스킬-기여하기) +- [에이전트 기여하기](#에이전트-기여하기) +- [훅 기여하기](#훅-기여하기) +- [커맨드 기여하기](#커맨드-기여하기) +- [Pull Request 프로세스](#pull-request-프로세스) + +--- + +## 우리가 찾는 것 + +### 에이전트 +특정 작업을 잘 처리하는 새로운 에이전트: +- 언어별 리뷰어 (Python, Go, Rust) +- 프레임워크 전문가 (Django, Rails, Laravel, Spring) +- DevOps 전문가 (Kubernetes, Terraform, CI/CD) +- 도메인 전문가 (ML 파이프라인, 데이터 엔지니어링, 모바일) + +### 스킬 +워크플로우 정의와 도메인 지식: +- 언어 모범 사례 +- 프레임워크 패턴 +- 테스팅 전략 +- 아키텍처 가이드 + +### 훅 +유용한 자동화: +- 린팅/포매팅 훅 +- 보안 검사 +- 유효성 검증 훅 +- 알림 훅 + +### 커맨드 +유용한 워크플로우를 호출하는 슬래시 커맨드: +- 배포 커맨드 +- 테스팅 커맨드 +- 코드 생성 커맨드 + +--- + +## 빠른 시작 + +```bash +# 1. 포크 및 클론 +gh repo fork affaan-m/everything-claude-code --clone +cd everything-claude-code + +# 2. 브랜치 생성 +git checkout -b feat/my-contribution + +# 3. 기여 항목 추가 (아래 섹션 참고) + +# 4. 로컬 테스트 +cp -r skills/my-skill ~/.claude/skills/ # 스킬의 경우 +# 그런 다음 Claude Code로 테스트 + +# 5. PR 제출 +git add . && git commit -m "feat: add my-skill" && git push -u origin feat/my-contribution +``` + +--- + +## 스킬 기여하기 + +스킬은 Claude Code가 컨텍스트에 따라 로드하는 지식 모듈입니다. + +### 디렉토리 구조 + +``` +skills/ +└── your-skill-name/ + └── SKILL.md +``` + +### SKILL.md 템플릿 + +```markdown +--- +name: your-skill-name +description: 스킬 목록에 표시되는 간단한 설명 +origin: ECC +--- + +# 스킬 제목 + +이 스킬이 다루는 내용에 대한 간단한 개요. + +## 핵심 개념 + +주요 패턴과 가이드라인 설명. + +## 코드 예제 + +\`\`\`typescript +// 실용적이고 테스트된 예제 포함 +function example() { + // 잘 주석 처리된 코드 +} +\`\`\` + +## 모범 사례 + +- 실행 가능한 가이드라인 +- 해야 할 것과 하지 말아야 할 것 +- 흔한 실수 방지 + +## 사용 시점 + +이 스킬이 적용되는 시나리오 설명. +``` + +### 스킬 체크리스트 + +- [ ] 하나의 도메인/기술에 집중 +- [ ] 실용적인 코드 예제 포함 +- [ ] 500줄 미만 +- [ ] 명확한 섹션 헤더 사용 +- [ ] Claude Code에서 테스트 완료 + +### 스킬 예시 + +| 스킬 | 용도 | +|------|------| +| `coding-standards/` | TypeScript/JavaScript 패턴 | +| `frontend-patterns/` | React와 Next.js 모범 사례 | +| `backend-patterns/` | API와 데이터베이스 패턴 | +| `security-review/` | 보안 체크리스트 | + +--- + +## 에이전트 기여하기 + +에이전트는 Task 도구를 통해 호출되는 전문 어시스턴트입니다. + +### 파일 위치 + +``` +agents/your-agent-name.md +``` + +### 에이전트 템플릿 + +```markdown +--- +name: your-agent-name +description: 이 에이전트가 하는 일과 Claude가 언제 호출해야 하는지. 구체적으로 작성! +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: sonnet +--- + +당신은 [역할] 전문가입니다. + +## 역할 + +- 주요 책임 +- 부차적 책임 +- 하지 않는 것 (경계) + +## 워크플로우 + +### 1단계: 이해 +작업에 접근하는 방법. + +### 2단계: 실행 +작업을 수행하는 방법. + +### 3단계: 검증 +결과를 검증하는 방법. + +## 출력 형식 + +사용자에게 반환하는 것. + +## 예제 + +### 예제: [시나리오] +입력: [사용자가 제공하는 것] +행동: [수행하는 것] +출력: [반환하는 것] +``` + +### 에이전트 필드 + +| 필드 | 설명 | 옵션 | +|------|------|------| +| `name` | 소문자, 하이픈 연결 | `code-reviewer` | +| `description` | 호출 시점 결정에 사용 | 구체적으로 작성! | +| `tools` | 필요한 것만 포함 | `Read, Write, Edit, Bash, Grep, Glob, WebFetch, Task` | +| `model` | 복잡도 수준 | `haiku` (단순), `sonnet` (코딩), `opus` (복잡) | + +--- + +## 훅 기여하기 + +훅은 Claude Code 이벤트에 의해 트리거되는 자동 동작입니다. + +### 파일 위치 + +``` +hooks/hooks.json +``` + +### 훅 유형 + +| 유형 | 트리거 시점 | 사용 사례 | +|------|-----------|----------| +| `PreToolUse` | 도구 실행 전 | 유효성 검증, 경고, 차단 | +| `PostToolUse` | 도구 실행 후 | 포매팅, 검사, 알림 | +| `SessionStart` | 세션 시작 시 | 컨텍스트 로딩 | +| `Stop` | 세션 종료 시 | 정리, 감사 | + +### 훅 체크리스트 + +- [ ] Matcher가 구체적 (너무 광범위하지 않게) +- [ ] 명확한 오류/정보 메시지 포함 +- [ ] 올바른 종료 코드 사용 (`exit 1`은 차단, `exit 0`은 허용) +- [ ] 충분한 테스트 완료 +- [ ] 설명 포함 + +--- + +## 커맨드 기여하기 + +커맨드는 `/command-name`으로 사용자가 호출하는 액션입니다. + +### 파일 위치 + +``` +commands/your-command.md +``` + +### 커맨드 예시 + +| 커맨드 | 용도 | +|--------|------| +| `commit.md` | Git 커밋 생성 | +| `code-review.md` | 코드 변경사항 리뷰 | +| `tdd.md` | TDD 워크플로우 | +| `e2e.md` | E2E 테스팅 | + +--- + +## Pull Request 프로세스 + +### 1. PR 제목 형식 + +``` +feat(skills): add rust-patterns skill +feat(agents): add api-designer agent +feat(hooks): add auto-format hook +fix(skills): update React patterns +docs: improve contributing guide +``` + +### 2. PR 설명 + +```markdown +## 요약 +무엇을 추가했고 왜 필요한지. + +## 유형 +- [ ] 스킬 +- [ ] 에이전트 +- [ ] 훅 +- [ ] 커맨드 + +## 테스트 +어떻게 테스트했는지. + +## 체크리스트 +- [ ] 형식 가이드라인 준수 +- [ ] Claude Code에서 테스트 완료 +- [ ] 민감한 정보 없음 (API 키, 경로) +- [ ] 명확한 설명 포함 +``` + +### 3. 리뷰 프로세스 + +1. 메인테이너가 48시간 이내에 리뷰 +2. 피드백이 있으면 수정 반영 +3. 승인되면 main에 머지 + +--- + +## 가이드라인 + +### 해야 할 것 +- 기여를 집중적이고 모듈화되게 유지 +- 명확한 설명 포함 +- 제출 전 테스트 +- 기존 패턴 따르기 +- 의존성 문서화 + +### 하지 말아야 할 것 +- 민감한 데이터 포함 (API 키, 토큰, 경로) +- 지나치게 복잡하거나 특수한 설정 추가 +- 테스트하지 않은 기여 제출 +- 기존 기능과 중복되는 것 생성 + +--- + +## 파일 이름 규칙 + +- 소문자에 하이픈 사용: `python-reviewer.md` +- 설명적으로 작성: `workflow.md`가 아닌 `tdd-workflow.md` +- name과 파일명을 일치시키기 + +--- + +## 질문이 있으신가요? + +- **이슈:** [github.com/affaan-m/everything-claude-code/issues](https://github.com/affaan-m/everything-claude-code/issues) +- **X/Twitter:** [@affaanmustafa](https://x.com/affaanmustafa) + +--- + +기여해 주셔서 감사합니다! 함께 훌륭한 리소스를 만들어 갑시다. diff --git a/docs/ko-KR/README.md b/docs/ko-KR/README.md new file mode 100644 index 00000000..70a1098a --- /dev/null +++ b/docs/ko-KR/README.md @@ -0,0 +1,729 @@ +**언어:** [English](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | 한국어 + +# Everything Claude Code + +[![Stars](https://img.shields.io/github/stars/affaan-m/everything-claude-code?style=flat)](https://github.com/affaan-m/everything-claude-code/stargazers) +[![Forks](https://img.shields.io/github/forks/affaan-m/everything-claude-code?style=flat)](https://github.com/affaan-m/everything-claude-code/network/members) +[![Contributors](https://img.shields.io/github/contributors/affaan-m/everything-claude-code?style=flat)](https://github.com/affaan-m/everything-claude-code/graphs/contributors) +[![npm ecc-universal](https://img.shields.io/npm/dw/ecc-universal?label=ecc-universal%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-universal) +[![npm ecc-agentshield](https://img.shields.io/npm/dw/ecc-agentshield?label=ecc-agentshield%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-agentshield) +[![GitHub App Install](https://img.shields.io/badge/GitHub%20App-150%20installs-2ea44f?logo=github)](https://github.com/marketplace/ecc-tools) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) +![Shell](https://img.shields.io/badge/-Shell-4EAA25?logo=gnu-bash&logoColor=white) +![TypeScript](https://img.shields.io/badge/-TypeScript-3178C6?logo=typescript&logoColor=white) +![Python](https://img.shields.io/badge/-Python-3776AB?logo=python&logoColor=white) +![Go](https://img.shields.io/badge/-Go-00ADD8?logo=go&logoColor=white) +![Java](https://img.shields.io/badge/-Java-ED8B00?logo=openjdk&logoColor=white) +![Markdown](https://img.shields.io/badge/-Markdown-000000?logo=markdown&logoColor=white) + +> **50K+ stars** | **6K+ forks** | **30 contributors** | **6개 언어 지원** | **Anthropic 해커톤 우승** + +--- + +
+ +**🌐 Language / 语言 / 語言 / 언어** + +[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](README.md) + +
+ +--- + +**AI 에이전트 하네스를 위한 성능 최적화 시스템. Anthropic 해커톤 우승자가 만들었습니다.** + +단순한 설정 파일 모음이 아닙니다. 스킬, 직관(Instinct), 메모리 최적화, 지속적 학습, 보안 스캐닝, 리서치 우선 개발을 아우르는 완전한 시스템입니다. 10개월 이상 실제 프로덕트를 만들며 매일 집중적으로 사용해 발전시킨 프로덕션 레벨의 에이전트, 훅, 커맨드, 룰, MCP 설정이 포함되어 있습니다. + +**Claude Code**, **Codex**, **Cowork** 등 다양한 AI 에이전트 하네스에서 사용할 수 있습니다. + +--- + +## 가이드 + +이 저장소는 코드만 포함하고 있습니다. 가이드에서 모든 것을 설명합니다. + + + + + + + + + + +
+ +The Shorthand Guide to Everything Claude Code + + + +The Longform Guide to Everything Claude Code + +
요약 가이드
설정, 기초, 철학. 이것부터 읽으세요.
상세 가이드
토큰 최적화, 메모리 영속성, 평가, 병렬 처리.
+ +| 주제 | 배울 수 있는 것 | +|------|----------------| +| 토큰 최적화 | 모델 선택, 시스템 프롬프트 최적화, 백그라운드 프로세스 | +| 메모리 영속성 | 세션 간 컨텍스트를 자동으로 저장/불러오는 훅 | +| 지속적 학습 | 세션에서 패턴을 자동 추출하여 재사용 가능한 스킬로 변환 | +| 검증 루프 | 체크포인트 vs 연속 평가, 채점 유형, pass@k 메트릭 | +| 병렬 처리 | Git worktree, 캐스케이드 방식, 인스턴스 확장 시점 | +| 서브에이전트 오케스트레이션 | 컨텍스트 문제, 반복 검색 패턴 | + +--- + +## 새로운 소식 + +### v1.8.0 — 하네스 성능 시스템 (2026년 3월) + +- **하네스 중심 릴리스** — ECC는 이제 단순 설정 모음이 아닌, 에이전트 하네스 성능 시스템으로 명시됩니다. +- **훅 안정성 개선** — SessionStart 루트 폴백, Stop 단계 세션 요약, 취약한 인라인 원라이너를 스크립트 기반 훅으로 교체. +- **훅 런타임 제어** — `ECC_HOOK_PROFILE=minimal|standard|strict`와 `ECC_DISABLED_HOOKS=...`로 훅 파일 수정 없이 런타임 제어. +- **새 하네스 커맨드** — `/harness-audit`, `/loop-start`, `/loop-status`, `/quality-gate`, `/model-route`. +- **NanoClaw v2** — 모델 라우팅, 스킬 핫로드, 세션 분기/검색/내보내기/압축/메트릭. +- **크로스 하네스 호환성** — Claude Code, Cursor, OpenCode, Codex 간 동작 일관성 강화. +- **997개 내부 테스트 통과** — 훅/런타임 리팩토링 및 호환성 업데이트 후 전체 테스트 통과. + +### v1.7.0 — 크로스 플랫폼 확장 & 프레젠테이션 빌더 (2026년 2월) + +- **Codex 앱 + CLI 지원** — AGENTS.md 기반의 직접적인 Codex 지원 +- **`frontend-slides` 스킬** — 의존성 없는 HTML 프레젠테이션 빌더 +- **5개 신규 비즈니스/콘텐츠 스킬** — `article-writing`, `content-engine`, `market-research`, `investor-materials`, `investor-outreach` +- **992개 내부 테스트** — 확장된 검증 및 회귀 테스트 범위 + +### v1.6.0 — Codex CLI, AgentShield & 마켓플레이스 (2026년 2월) + +- **Codex CLI 지원** — OpenAI Codex CLI 호환성을 위한 `/codex-setup` 커맨드 +- **7개 신규 스킬** — `search-first`, `swift-actor-persistence`, `swift-protocol-di-testing` 등 +- **AgentShield 통합** — `/security-scan`으로 Claude Code에서 직접 AgentShield 실행; 1282개 테스트, 102개 규칙 +- **GitHub 마켓플레이스** — [github.com/marketplace/ecc-tools](https://github.com/marketplace/ecc-tools)에서 무료/프로/엔터프라이즈 티어 제공 +- **30명 이상의 커뮤니티 기여** — 6개 언어에 걸친 30명의 기여자 +- **978개 내부 테스트** — 에이전트, 스킬, 커맨드, 훅, 룰 전반에 걸친 검증 + +전체 변경 내역은 [Releases](https://github.com/affaan-m/everything-claude-code/releases)에서 확인하세요. + +--- + +## 🚀 빠른 시작 + +2분 안에 설정 완료: + +### 1단계: 플러그인 설치 + +```bash +# 마켓플레이스 추가 +/plugin marketplace add affaan-m/everything-claude-code + +# 플러그인 설치 +/plugin install everything-claude-code@everything-claude-code +``` + +### 2단계: 룰 설치 (필수) + +> ⚠️ **중요:** Claude Code 플러그인은 `rules`를 자동으로 배포할 수 없습니다. 수동으로 설치해야 합니다: + +```bash +# 먼저 저장소 클론 +git clone https://github.com/affaan-m/everything-claude-code.git +cd everything-claude-code + +# 권장: 설치 스크립트 사용 (common + 언어별 룰을 안전하게 처리) +./install.sh typescript # 또는 python, golang +# 여러 언어를 한번에 설치할 수 있습니다: +# ./install.sh typescript python golang +# Cursor를 대상으로 설치: +# ./install.sh --target cursor typescript +``` + +수동 설치 방법은 `rules/` 폴더의 README를 참고하세요. + +### 3단계: 사용 시작 + +```bash +# 커맨드 실행 (플러그인 설치 시 네임스페이스 형태 사용) +/everything-claude-code:plan "사용자 인증 추가" + +# 수동 설치(옵션 2) 시에는 짧은 형태를 사용: +# /plan "사용자 인증 추가" + +# 사용 가능한 커맨드 확인 +/plugin list everything-claude-code@everything-claude-code +``` + +✨ **끝!** 이제 16개 에이전트, 65개 스킬, 40개 커맨드를 사용할 수 있습니다. + +--- + +## 🌐 크로스 플랫폼 지원 + +이 플러그인은 **Windows, macOS, Linux**를 완벽하게 지원하며, 주요 IDE(Cursor, OpenCode, Antigravity) 및 CLI 하네스와 긴밀하게 통합됩니다. 모든 훅과 스크립트는 최대 호환성을 위해 Node.js로 작성되었습니다. + +### 패키지 매니저 감지 + +플러그인이 선호하는 패키지 매니저(npm, pnpm, yarn, bun)를 자동으로 감지합니다: + +1. **환경 변수**: `CLAUDE_PACKAGE_MANAGER` +2. **프로젝트 설정**: `.claude/package-manager.json` +3. **package.json**: `packageManager` 필드 +4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb에서 감지 +5. **글로벌 설정**: `~/.claude/package-manager.json` +6. **폴백**: 사용 가능한 첫 번째 패키지 매니저 + +패키지 매니저 설정 방법: + +```bash +# 환경 변수로 설정 +export CLAUDE_PACKAGE_MANAGER=pnpm + +# 글로벌 설정 +node scripts/setup-package-manager.js --global pnpm + +# 프로젝트 설정 +node scripts/setup-package-manager.js --project bun + +# 현재 설정 확인 +node scripts/setup-package-manager.js --detect +``` + +또는 Claude Code에서 `/setup-pm` 커맨드를 사용하세요. + +### 훅 런타임 제어 + +런타임 플래그로 엄격도를 조절하거나 특정 훅을 임시로 비활성화할 수 있습니다: + +```bash +# 훅 엄격도 프로필 (기본값: standard) +export ECC_HOOK_PROFILE=standard + +# 비활성화할 훅 ID (쉼표로 구분) +export ECC_DISABLED_HOOKS="pre:bash:tmux-reminder,post:edit:typecheck" +``` + +--- + +## 📦 구성 요소 + +이 저장소는 **Claude Code 플러그인**입니다 - 직접 설치하거나 컴포넌트를 수동으로 복사할 수 있습니다. + +``` +everything-claude-code/ +|-- .claude-plugin/ # 플러그인 및 마켓플레이스 매니페스트 +| |-- plugin.json # 플러그인 메타데이터와 컴포넌트 경로 +| |-- marketplace.json # /plugin marketplace add용 마켓플레이스 카탈로그 +| +|-- agents/ # 위임을 위한 전문 서브에이전트 +| |-- planner.md # 기능 구현 계획 +| |-- architect.md # 시스템 설계 의사결정 +| |-- tdd-guide.md # 테스트 주도 개발 +| |-- code-reviewer.md # 품질 및 보안 리뷰 +| |-- security-reviewer.md # 취약점 분석 +| |-- build-error-resolver.md +| |-- e2e-runner.md # Playwright E2E 테스팅 +| |-- refactor-cleaner.md # 사용하지 않는 코드 정리 +| |-- doc-updater.md # 문서 동기화 +| |-- go-reviewer.md # Go 코드 리뷰 +| |-- go-build-resolver.md # Go 빌드 에러 해결 +| |-- python-reviewer.md # Python 코드 리뷰 +| |-- database-reviewer.md # 데이터베이스/Supabase 리뷰 +| +|-- skills/ # 워크플로우 정의와 도메인 지식 +| |-- coding-standards/ # 언어 모범 사례 +| |-- backend-patterns/ # API, 데이터베이스, 캐싱 패턴 +| |-- frontend-patterns/ # React, Next.js 패턴 +| |-- continuous-learning/ # 세션에서 패턴 자동 추출 +| |-- continuous-learning-v2/ # 신뢰도 점수가 있는 직관 기반 학습 +| |-- tdd-workflow/ # TDD 방법론 +| |-- security-review/ # 보안 체크리스트 +| |-- 그 외 다수... +| +|-- commands/ # 빠른 실행을 위한 슬래시 커맨드 +| |-- tdd.md # /tdd - 테스트 주도 개발 +| |-- plan.md # /plan - 구현 계획 +| |-- e2e.md # /e2e - E2E 테스트 생성 +| |-- code-review.md # /code-review - 품질 리뷰 +| |-- build-fix.md # /build-fix - 빌드 에러 수정 +| |-- 그 외 다수... +| +|-- rules/ # 항상 따르는 가이드라인 (~/.claude/rules/에 복사) +| |-- common/ # 언어 무관 원칙 +| |-- typescript/ # TypeScript/JavaScript 전용 +| |-- python/ # Python 전용 +| |-- golang/ # Go 전용 +| +|-- hooks/ # 트리거 기반 자동화 +| |-- hooks.json # 모든 훅 설정 +| |-- memory-persistence/ # 세션 라이프사이클 훅 +| +|-- scripts/ # 크로스 플랫폼 Node.js 스크립트 +|-- tests/ # 테스트 모음 +|-- contexts/ # 동적 시스템 프롬프트 주입 컨텍스트 +|-- examples/ # 예제 설정 및 세션 +|-- mcp-configs/ # MCP 서버 설정 +``` + +--- + +## 🛠️ 에코시스템 도구 + +### Skill Creator + +저장소에서 Claude Code 스킬을 생성하는 두 가지 방법: + +#### 옵션 A: 로컬 분석 (내장) + +외부 서비스 없이 로컬에서 분석하려면 `/skill-create` 커맨드를 사용하세요: + +```bash +/skill-create # 현재 저장소 분석 +/skill-create --instincts # 직관(instincts)도 함께 생성 +``` + +git 히스토리를 로컬에서 분석하여 SKILL.md 파일을 생성합니다. + +#### 옵션 B: GitHub 앱 (고급) + +고급 기능(10k+ 커밋, 자동 PR, 팀 공유)이 필요한 경우: + +[GitHub 앱 설치](https://github.com/apps/skill-creator) | [ecc.tools](https://ecc.tools) + +### AgentShield — 보안 감사 도구 + +> Claude Code 해커톤(Cerebral Valley x Anthropic, 2026년 2월)에서 개발. 1282개 테스트, 98% 커버리지, 102개 정적 분석 규칙. + +Claude Code 설정에서 취약점, 잘못된 구성, 인젝션 위험을 스캔합니다. + +```bash +# 빠른 스캔 (설치 불필요) +npx ecc-agentshield scan + +# 안전한 문제 자동 수정 +npx ecc-agentshield scan --fix + +# 3개의 Opus 4.6 에이전트로 정밀 분석 +npx ecc-agentshield scan --opus --stream + +# 안전한 설정을 처음부터 생성 +npx ecc-agentshield init +``` + +**스캔 대상:** CLAUDE.md, settings.json, MCP 설정, 훅, 에이전트 정의, 스킬 — 시크릿 감지(14개 패턴), 권한 감사, 훅 인젝션 분석, MCP 서버 위험 프로파일링, 에이전트 설정 검토의 5가지 카테고리. + +**`--opus` 플래그**는 레드팀/블루팀/감사관 파이프라인으로 3개의 Claude Opus 4.6 에이전트를 실행합니다. 공격자가 익스플로잇 체인을 찾고, 방어자가 보호 조치를 평가하며, 감사관이 양쪽의 결과를 종합하여 우선순위가 매겨진 위험 평가를 작성합니다. + +Claude Code에서 `/security-scan`을 사용하거나, [GitHub Action](https://github.com/affaan-m/agentshield)으로 CI에 추가하세요. + +[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield) + +### 🧠 지속적 학습 v2 + +직관(Instinct) 기반 학습 시스템이 여러분의 패턴을 자동으로 학습합니다: + +```bash +/instinct-status # 학습된 직관과 신뢰도 확인 +/instinct-import # 다른 사람의 직관 가져오기 +/instinct-export # 내 직관 내보내기 +/evolve # 관련 직관을 스킬로 클러스터링 +``` + +자세한 내용은 `skills/continuous-learning-v2/`를 참고하세요. + +--- + +## 📋 요구 사항 + +### Claude Code CLI 버전 + +**최소 버전: v2.1.0 이상** + +이 플러그인은 훅 시스템 변경으로 인해 Claude Code CLI v2.1.0 이상이 필요합니다. + +버전 확인: +```bash +claude --version +``` + +### 중요: 훅 자동 로딩 동작 + +> ⚠️ **기여자 참고:** `.claude-plugin/plugin.json`에 `"hooks"` 필드를 추가하지 **마세요**. 회귀 테스트로 이를 강제합니다. + +Claude Code v2.1+는 설치된 플러그인의 `hooks/hooks.json`을 **자동으로 로드**합니다. 명시적으로 선언하면 중복 감지 오류가 발생합니다. + +--- + +## 📥 설치 + +### 옵션 1: 플러그인으로 설치 (권장) + +```bash +# 마켓플레이스 추가 +/plugin marketplace add affaan-m/everything-claude-code + +# 플러그인 설치 +/plugin install everything-claude-code@everything-claude-code +``` + +또는 `~/.claude/settings.json`에 직접 추가: + +```json +{ + "extraKnownMarketplaces": { + "everything-claude-code": { + "source": { + "source": "github", + "repo": "affaan-m/everything-claude-code" + } + } + }, + "enabledPlugins": { + "everything-claude-code@everything-claude-code": true + } +} +``` + +> **참고:** Claude Code 플러그인 시스템은 `rules`를 플러그인으로 배포하는 것을 지원하지 않습니다. 룰은 수동으로 설치해야 합니다: +> +> ```bash +> git clone https://github.com/affaan-m/everything-claude-code.git +> +> # 옵션 A: 사용자 레벨 룰 (모든 프로젝트에 적용) +> mkdir -p ~/.claude/rules +> cp -r everything-claude-code/rules/common/* ~/.claude/rules/ +> cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 사용하는 스택 선택 +> +> # 옵션 B: 프로젝트 레벨 룰 (현재 프로젝트에만 적용) +> mkdir -p .claude/rules +> cp -r everything-claude-code/rules/common/* .claude/rules/ +> ``` + +--- + +### 🔧 옵션 2: 수동 설치 + +설치할 항목을 직접 선택하고 싶다면: + +```bash +# 저장소 클론 +git clone https://github.com/affaan-m/everything-claude-code.git + +# 에이전트 복사 +cp everything-claude-code/agents/*.md ~/.claude/agents/ + +# 룰 복사 (common + 언어별) +cp -r everything-claude-code/rules/common/* ~/.claude/rules/ +cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 사용하는 스택 선택 + +# 커맨드 복사 +cp everything-claude-code/commands/*.md ~/.claude/commands/ + +# 스킬 복사 +cp -r everything-claude-code/.agents/skills/* ~/.claude/skills/ +cp -r everything-claude-code/skills/search-first ~/.claude/skills/ +``` + +--- + +## 🎯 핵심 개념 + +### 에이전트 + +서브에이전트가 제한된 범위 내에서 위임된 작업을 처리합니다. 예시: + +```markdown +--- +name: code-reviewer +description: 코드의 품질, 보안, 유지보수성을 리뷰합니다 +tools: ["Read", "Grep", "Glob", "Bash"] +model: opus +--- + +당신은 시니어 코드 리뷰어입니다... +``` + +### 스킬 + +스킬은 커맨드나 에이전트에 의해 호출되는 워크플로우 정의입니다: + +```markdown +# TDD 워크플로우 + +1. 인터페이스를 먼저 정의 +2. 실패하는 테스트 작성 (RED) +3. 최소한의 코드 구현 (GREEN) +4. 리팩토링 (IMPROVE) +5. 80% 이상 커버리지 확인 +``` + +### 훅 + +훅은 도구 이벤트에 반응하여 실행됩니다. 예시 - console.log 경고: + +```json +{ + "matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\\\.(ts|tsx|js|jsx)$\"", + "hooks": [{ + "type": "command", + "command": "#!/bin/bash\ngrep -n 'console\\.log' \"$file_path\" && echo '[Hook] console.log를 제거하세요' >&2" + }] +} +``` + +### 룰 + +룰은 항상 따라야 하는 가이드라인으로, `common/`(언어 무관) + 언어별 디렉토리로 구성됩니다: + +``` +rules/ + common/ # 보편적 원칙 (항상 설치) + typescript/ # TS/JS 전용 패턴과 도구 + python/ # Python 전용 패턴과 도구 + golang/ # Go 전용 패턴과 도구 +``` + +자세한 내용은 [`rules/README.md`](../../rules/README.md)를 참고하세요. + +--- + +## 🗺️ 어떤 에이전트를 사용해야 할까? + +어디서 시작해야 할지 모르겠다면 이 참고표를 보세요: + +| 하고 싶은 것 | 사용할 커맨드 | 사용되는 에이전트 | +|-------------|-------------|-----------------| +| 새 기능 계획하기 | `/everything-claude-code:plan "인증 추가"` | planner | +| 시스템 아키텍처 설계 | `/everything-claude-code:plan` + architect 에이전트 | architect | +| 테스트를 먼저 작성하며 코딩 | `/tdd` | tdd-guide | +| 방금 작성한 코드 리뷰 | `/code-review` | code-reviewer | +| 빌드 실패 수정 | `/build-fix` | build-error-resolver | +| E2E 테스트 실행 | `/e2e` | e2e-runner | +| 보안 취약점 찾기 | `/security-scan` | security-reviewer | +| 사용하지 않는 코드 제거 | `/refactor-clean` | refactor-cleaner | +| 문서 업데이트 | `/update-docs` | doc-updater | +| Go 코드 리뷰 | `/go-review` | go-reviewer | +| Python 코드 리뷰 | `/python-review` | python-reviewer | + +### 일반적인 워크플로우 + +**새로운 기능 시작:** +``` +/everything-claude-code:plan "OAuth를 사용한 사용자 인증 추가" + → planner가 구현 청사진 작성 +/tdd → tdd-guide가 테스트 먼저 작성 강제 +/code-review → code-reviewer가 코드 검토 +``` + +**버그 수정:** +``` +/tdd → tdd-guide: 버그를 재현하는 실패 테스트 작성 + → 수정 구현, 테스트 통과 확인 +/code-review → code-reviewer: 회귀 검사 +``` + +**프로덕션 준비:** +``` +/security-scan → security-reviewer: OWASP Top 10 감사 +/e2e → e2e-runner: 핵심 사용자 흐름 테스트 +/test-coverage → 80% 이상 커버리지 확인 +``` + +--- + +## ❓ FAQ + +
+설치된 에이전트/커맨드 확인은 어떻게 하나요? + +```bash +/plugin list everything-claude-code@everything-claude-code +``` + +플러그인에서 사용할 수 있는 모든 에이전트, 커맨드, 스킬을 보여줍니다. +
+ +
+훅이 작동하지 않거나 "Duplicate hooks file" 오류가 보여요 + +가장 흔한 문제입니다. `.claude-plugin/plugin.json`에 `"hooks"` 필드를 **추가하지 마세요.** Claude Code v2.1+는 설치된 플러그인의 `hooks/hooks.json`을 자동으로 로드합니다. +
+ +
+컨텍스트 윈도우가 줄어들어요 / Claude가 컨텍스트가 부족해요 + +MCP 서버가 너무 많으면 컨텍스트를 잡아먹습니다. 각 MCP 도구 설명이 200k 윈도우에서 토큰을 소비하여 ~70k까지 줄어들 수 있습니다. + +**해결:** 프로젝트별로 사용하지 않는 MCP를 비활성화하세요: +```json +// 프로젝트의 .claude/settings.json에서 +{ + "disabledMcpServers": ["supabase", "railway", "vercel"] +} +``` + +10개 미만의 MCP와 80개 미만의 도구를 활성화 상태로 유지하세요. +
+ +
+일부 컴포넌트만 사용할 수 있나요? (예: 에이전트만) + +네. 옵션 2(수동 설치)를 사용하여 필요한 것만 복사하세요: + +```bash +# 에이전트만 +cp everything-claude-code/agents/*.md ~/.claude/agents/ + +# 룰만 +cp -r everything-claude-code/rules/common/* ~/.claude/rules/ +``` + +각 컴포넌트는 완전히 독립적입니다. +
+ +
+Cursor / OpenCode / Codex / Antigravity에서도 작동하나요? + +네. ECC는 크로스 플랫폼입니다: +- **Cursor**: `.cursor/`에 변환된 설정 제공 +- **OpenCode**: `.opencode/`에 전체 플러그인 지원 +- **Codex**: macOS 앱과 CLI 모두 퍼스트클래스 지원 +- **Antigravity**: `.agent/`에 워크플로우, 스킬, 평탄화된 룰 통합 +- **Claude Code**: 네이티브 — 이것이 주 타겟입니다 +
+ +
+새 스킬이나 에이전트를 기여하고 싶어요 + +[CONTRIBUTING.md](../../CONTRIBUTING.md)를 참고하세요. 간단히 말하면: +1. 저장소를 포크 +2. `skills/your-skill-name/SKILL.md`에 스킬 생성 (YAML frontmatter 포함) +3. 또는 `agents/your-agent.md`에 에이전트 생성 +4. 명확한 설명과 함께 PR 제출 +
+ +--- + +## 🧪 테스트 실행 + +```bash +# 모든 테스트 실행 +node tests/run-all.js + +# 개별 테스트 파일 실행 +node tests/lib/utils.test.js +node tests/lib/package-manager.test.js +node tests/hooks/hooks.test.js +``` + +--- + +## 🤝 기여하기 + +**기여를 환영합니다.** + +이 저장소는 커뮤니티 리소스로 만들어졌습니다. 가지고 계신 것이 있다면: +- 유용한 에이전트나 스킬 +- 멋진 훅 +- 더 나은 MCP 설정 +- 개선된 룰 + +기여해 주세요! 가이드라인은 [CONTRIBUTING.md](../../CONTRIBUTING.md)를 참고하세요. + +### 기여 아이디어 + +- 언어별 스킬 (Rust, C#, Swift, Kotlin) — Go, Python, Java는 이미 포함 +- 프레임워크별 설정 (Rails, Laravel, FastAPI, NestJS) — Django, Spring Boot는 이미 포함 +- DevOps 에이전트 (Kubernetes, Terraform, AWS, Docker) +- 테스팅 전략 (다양한 프레임워크, 비주얼 리그레션) +- 도메인별 지식 (ML, 데이터 엔지니어링, 모바일) + +--- + +## 토큰 최적화 + +Claude Code 사용 비용이 부담된다면 토큰 소비를 관리해야 합니다. 이 설정으로 품질 저하 없이 비용을 크게 줄일 수 있습니다. + +### 권장 설정 + +`~/.claude/settings.json`에 추가: + +```json +{ + "model": "sonnet", + "env": { + "MAX_THINKING_TOKENS": "10000", + "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "50" + } +} +``` + +| 설정 | 기본값 | 권장값 | 효과 | +|------|--------|--------|------| +| `model` | opus | **sonnet** | ~60% 비용 절감; 80% 이상의 코딩 작업 처리 가능 | +| `MAX_THINKING_TOKENS` | 31,999 | **10,000** | 요청당 숨겨진 사고 비용 ~70% 절감 | +| `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` | 95 | **50** | 더 일찍 압축 — 긴 세션에서 더 나은 품질 | + +깊은 아키텍처 추론이 필요할 때만 Opus로 전환: +``` +/model opus +``` + +### 일상 워크플로우 커맨드 + +| 커맨드 | 사용 시점 | +|--------|----------| +| `/model sonnet` | 대부분의 작업에서 기본값 | +| `/model opus` | 복잡한 아키텍처, 디버깅, 깊은 추론 | +| `/clear` | 관련 없는 작업 사이 (무료, 즉시 초기화) | +| `/compact` | 논리적 작업 전환 시점 (리서치 완료, 마일스톤 달성) | +| `/cost` | 세션 중 토큰 지출 모니터링 | + +### 컨텍스트 윈도우 관리 + +**중요:** 모든 MCP를 한꺼번에 활성화하지 마세요. 각 MCP 도구 설명이 200k 윈도우에서 토큰을 소비하여 ~70k까지 줄어들 수 있습니다. + +- 프로젝트당 10개 미만의 MCP 활성화 +- 80개 미만의 도구 활성화 유지 +- 프로젝트 설정에서 `disabledMcpServers`로 사용하지 않는 것 비활성화 + +--- + +## ⚠️ 중요 참고 사항 + +### 커스터마이징 + +이 설정은 제 워크플로우에 맞게 만들어졌습니다. 여러분은: +1. 공감되는 것부터 시작하세요 +2. 여러분의 스택에 맞게 수정하세요 +3. 사용하지 않는 것은 제거하세요 +4. 여러분만의 패턴을 추가하세요 + +--- + +## 💜 스폰서 + +이 프로젝트는 무료 오픈소스입니다. 스폰서의 지원으로 유지보수와 성장이 이루어집니다. + +[**스폰서 되기**](https://github.com/sponsors/affaan-m) | [스폰서 티어](../../SPONSORS.md) | [스폰서십 프로그램](../../SPONSORING.md) + +--- + +## 🌟 Star 히스토리 + +[![Star History Chart](https://api.star-history.com/svg?repos=affaan-m/everything-claude-code&type=Date)](https://star-history.com/#affaan-m/everything-claude-code&Date) + +--- + +## 🔗 링크 + +- **요약 가이드 (여기서 시작):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795) +- **상세 가이드 (고급):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352) +- **팔로우:** [@affaanmustafa](https://x.com/affaanmustafa) +- **zenith.chat:** [zenith.chat](https://zenith.chat) + +--- + +## 📄 라이선스 + +MIT - 자유롭게 사용하고, 필요에 따라 수정하고, 가능하다면 기여해 주세요. + +--- + +**이 저장소가 도움이 되었다면 Star를 눌러주세요. 두 가이드를 모두 읽어보세요. 멋진 것을 만드세요.** diff --git a/docs/zh-TW/README.md b/docs/zh-TW/README.md index 2b84fe9a..d976bd11 100644 --- a/docs/zh-TW/README.md +++ b/docs/zh-TW/README.md @@ -13,7 +13,7 @@ **🌐 Language / 语言 / 語言** -[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](README.md) | [日本語](../../docs/ja-JP/README.md) +[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](README.md) | [日本語](../../docs/ja-JP/README.md) | [한국어](../ko-KR/README.md) From b390fd141dd8dac71f68eb58825a6efeaddae84c Mon Sep 17 00:00:00 2001 From: hahmee Date: Sun, 8 Mar 2026 18:00:43 +0900 Subject: [PATCH 2/9] docs(ko-KR): add Korean translation for rules --- docs/ko-KR/rules/agents.md | 49 ++++++++++++++++++++++++++++ docs/ko-KR/rules/coding-style.md | 48 ++++++++++++++++++++++++++++ docs/ko-KR/rules/git-workflow.md | 24 ++++++++++++++ docs/ko-KR/rules/hooks.md | 30 +++++++++++++++++ docs/ko-KR/rules/patterns.md | 31 ++++++++++++++++++ docs/ko-KR/rules/performance.md | 55 ++++++++++++++++++++++++++++++++ docs/ko-KR/rules/security.md | 29 +++++++++++++++++ docs/ko-KR/rules/testing.md | 29 +++++++++++++++++ 8 files changed, 295 insertions(+) create mode 100644 docs/ko-KR/rules/agents.md create mode 100644 docs/ko-KR/rules/coding-style.md create mode 100644 docs/ko-KR/rules/git-workflow.md create mode 100644 docs/ko-KR/rules/hooks.md create mode 100644 docs/ko-KR/rules/patterns.md create mode 100644 docs/ko-KR/rules/performance.md create mode 100644 docs/ko-KR/rules/security.md create mode 100644 docs/ko-KR/rules/testing.md diff --git a/docs/ko-KR/rules/agents.md b/docs/ko-KR/rules/agents.md new file mode 100644 index 00000000..c26b8c53 --- /dev/null +++ b/docs/ko-KR/rules/agents.md @@ -0,0 +1,49 @@ +# 에이전트 오케스트레이션 + +## 사용 가능한 에이전트 + +`~/.claude/agents/`에 위치: + +| 에이전트 | 용도 | 사용 시점 | +|---------|------|----------| +| planner | 구현 계획 | 복잡한 기능, 리팩토링 | +| architect | 시스템 설계 | 아키텍처 의사결정 | +| tdd-guide | 테스트 주도 개발 | 새 기능, 버그 수정 | +| code-reviewer | 코드 리뷰 | 코드 작성 후 | +| security-reviewer | 보안 분석 | 커밋 전 | +| build-error-resolver | 빌드 에러 수정 | 빌드 실패 시 | +| e2e-runner | E2E 테스팅 | 핵심 사용자 흐름 | +| refactor-cleaner | 사용하지 않는 코드 정리 | 코드 유지보수 | +| doc-updater | 문서 관리 | 문서 업데이트 | + +## 즉시 에이전트 사용 + +사용자 프롬프트 불필요: +1. 복잡한 기능 요청 - **planner** 에이전트 사용 +2. 코드 작성/수정 직후 - **code-reviewer** 에이전트 사용 +3. 버그 수정 또는 새 기능 - **tdd-guide** 에이전트 사용 +4. 아키텍처 의사결정 - **architect** 에이전트 사용 + +## 병렬 Task 실행 + +독립적인 작업에는 항상 병렬 Task 실행 사용: + +```markdown +# 좋음: 병렬 실행 +3개 에이전트를 병렬로 실행: +1. 에이전트 1: 인증 모듈 보안 분석 +2. 에이전트 2: 캐시 시스템 성능 리뷰 +3. 에이전트 3: 유틸리티 타입 검사 + +# 나쁨: 불필요하게 순차 실행 +먼저 에이전트 1, 그다음 에이전트 2, 그다음 에이전트 3 +``` + +## 다중 관점 분석 + +복잡한 문제에는 역할 분리 서브에이전트 사용: +- 사실 검증 리뷰어 +- 시니어 엔지니어 +- 보안 전문가 +- 일관성 검토자 +- 중복 검사자 diff --git a/docs/ko-KR/rules/coding-style.md b/docs/ko-KR/rules/coding-style.md new file mode 100644 index 00000000..bb6b68ca --- /dev/null +++ b/docs/ko-KR/rules/coding-style.md @@ -0,0 +1,48 @@ +# 코딩 스타일 + +## 불변성 (중요) + +항상 새 객체를 생성하고, 기존 객체를 절대 변경하지 마세요: + +``` +// 의사 코드 +잘못된 예: modify(original, field, value) → 원본을 직접 변경 +올바른 예: update(original, field, value) → 변경 사항이 반영된 새 복사본 반환 +``` + +근거: 불변 데이터는 숨겨진 사이드 이펙트를 방지하고, 디버깅을 쉽게 하며, 안전한 동시성을 가능하게 합니다. + +## 파일 구성 + +많은 작은 파일 > 적은 큰 파일: +- 높은 응집도, 낮은 결합도 +- 200-400줄이 일반적, 최대 800줄 +- 큰 모듈에서 유틸리티를 분리 +- 타입이 아닌 기능/도메인별로 구성 + +## 에러 처리 + +항상 에러를 포괄적으로 처리: +- 모든 레벨에서 에러를 명시적으로 처리 +- UI 코드에서는 사용자 친화적인 에러 메시지 제공 +- 서버 측에서는 상세한 에러 컨텍스트 로깅 +- 에러를 절대 조용히 무시하지 않기 + +## 입력 유효성 검증 + +항상 시스템 경계에서 유효성 검증: +- 처리 전에 모든 사용자 입력을 검증 +- 가능한 경우 스키마 기반 유효성 검증 사용 +- 명확한 에러 메시지와 함께 빠르게 실패 +- 외부 데이터를 절대 신뢰하지 않기 (API 응답, 사용자 입력, 파일 내용) + +## 코드 품질 체크리스트 + +작업 완료 전 확인: +- [ ] 코드가 읽기 쉽고 이름이 적절한가 +- [ ] 함수가 작은가 (<50줄) +- [ ] 파일이 집중적인가 (<800줄) +- [ ] 깊은 중첩이 없는가 (>4단계) +- [ ] 적절한 에러 처리가 되어 있는가 +- [ ] 하드코딩된 값이 없는가 (상수나 설정 사용) +- [ ] 변이가 없는가 (불변 패턴 사용) diff --git a/docs/ko-KR/rules/git-workflow.md b/docs/ko-KR/rules/git-workflow.md new file mode 100644 index 00000000..f3efb92b --- /dev/null +++ b/docs/ko-KR/rules/git-workflow.md @@ -0,0 +1,24 @@ +# Git 워크플로우 + +## 커밋 메시지 형식 +``` +: + +<선택적 본문> +``` + +타입: feat, fix, refactor, docs, test, chore, perf, ci + +참고: 어트리뷰션은 ~/.claude/settings.json에서 전역적으로 비활성화되어 있습니다. + +## Pull Request 워크플로우 + +PR을 만들 때: +1. 전체 커밋 히스토리를 분석 (최신 커밋만이 아닌) +2. `git diff [base-branch]...HEAD`로 모든 변경사항 확인 +3. 포괄적인 PR 요약 작성 +4. TODO가 포함된 테스트 계획 포함 +5. 새 브랜치인 경우 `-u` 플래그와 함께 push + +> git 작업 전 전체 개발 프로세스(계획, TDD, 코드 리뷰)는 +> [development-workflow.md](./development-workflow.md)를 참고하세요. diff --git a/docs/ko-KR/rules/hooks.md b/docs/ko-KR/rules/hooks.md new file mode 100644 index 00000000..1f0c2dac --- /dev/null +++ b/docs/ko-KR/rules/hooks.md @@ -0,0 +1,30 @@ +# 훅 시스템 + +## 훅 유형 + +- **PreToolUse**: 도구 실행 전 (유효성 검증, 매개변수 수정) +- **PostToolUse**: 도구 실행 후 (자동 포맷, 검사) +- **Stop**: 세션 종료 시 (최종 검증) + +## 자동 수락 권한 + +주의하여 사용: +- 신뢰할 수 있는, 잘 정의된 계획에서만 활성화 +- 탐색적 작업에서는 비활성화 +- dangerously-skip-permissions 플래그를 절대 사용하지 않기 +- 대신 `~/.claude.json`에서 `allowedTools`를 설정 + +## TodoWrite 모범 사례 + +TodoWrite 도구 활용: +- 다단계 작업의 진행 상황 추적 +- 지시사항 이해도 검증 +- 실시간 방향 조정 가능 +- 세부 구현 단계 표시 + +Todo 목록으로 확인 가능한 것: +- 순서가 맞지 않는 단계 +- 누락된 항목 +- 불필요한 추가 항목 +- 잘못된 세분화 수준 +- 잘못 해석된 요구사항 diff --git a/docs/ko-KR/rules/patterns.md b/docs/ko-KR/rules/patterns.md new file mode 100644 index 00000000..5a3147b2 --- /dev/null +++ b/docs/ko-KR/rules/patterns.md @@ -0,0 +1,31 @@ +# 공통 패턴 + +## 스켈레톤 프로젝트 + +새 기능을 구현할 때: +1. 검증된 스켈레톤 프로젝트를 검색 +2. 병렬 에이전트로 옵션 평가: + - 보안 평가 + - 확장성 분석 + - 관련성 점수 + - 구현 계획 +3. 가장 적합한 것을 기반으로 클론 +4. 검증된 구조 내에서 반복 개선 + +## 디자인 패턴 + +### 리포지토리 패턴 + +일관된 인터페이스 뒤에 데이터 접근을 캡슐화: +- 표준 작업 정의: findAll, findById, create, update, delete +- 구체적 구현이 저장소 세부사항 처리 (데이터베이스, API, 파일 등) +- 비즈니스 로직은 저장소 메커니즘이 아닌 추상 인터페이스에 의존 +- 데이터 소스의 쉬운 교체 및 모킹을 통한 테스트 단순화 가능 + +### API 응답 형식 + +모든 API 응답에 일관된 엔벨로프 사용: +- 성공/상태 표시자 포함 +- 데이터 페이로드 포함 (에러 시 null) +- 에러 메시지 필드 포함 (성공 시 null) +- 페이지네이션 응답에 메타데이터 포함 (total, page, limit) diff --git a/docs/ko-KR/rules/performance.md b/docs/ko-KR/rules/performance.md new file mode 100644 index 00000000..3252da8e --- /dev/null +++ b/docs/ko-KR/rules/performance.md @@ -0,0 +1,55 @@ +# 성능 최적화 + +## 모델 선택 전략 + +**Haiku 4.5** (Sonnet 능력의 90%, 3배 비용 절감): +- 자주 호출되는 경량 에이전트 +- 페어 프로그래밍과 코드 생성 +- 멀티 에이전트 시스템의 워커 에이전트 + +**Sonnet 4.6** (최고의 코딩 모델): +- 주요 개발 작업 +- 멀티 에이전트 워크플로우 오케스트레이션 +- 복잡한 코딩 작업 + +**Opus 4.5** (가장 깊은 추론): +- 복잡한 아키텍처 의사결정 +- 최대 추론 요구사항 +- 리서치 및 분석 작업 + +## 컨텍스트 윈도우 관리 + +컨텍스트 윈도우의 마지막 20%에서는 다음을 피하세요: +- 대규모 리팩토링 +- 여러 파일에 걸친 기능 구현 +- 복잡한 상호작용 디버깅 + +컨텍스트 민감도가 낮은 작업: +- 단일 파일 수정 +- 독립적인 유틸리티 생성 +- 문서 업데이트 +- 단순한 버그 수정 + +## 확장 사고 + 계획 모드 + +확장 사고는 기본적으로 활성화되어 있으며, 내부 추론을 위해 최대 31,999 토큰을 예약합니다. + +확장 사고 제어 방법: +- **전환**: Option+T (macOS) / Alt+T (Windows/Linux) +- **설정**: `~/.claude/settings.json`에서 `alwaysThinkingEnabled` 설정 +- **예산 제한**: `export MAX_THINKING_TOKENS=10000` +- **상세 모드**: Ctrl+O로 사고 출력 확인 + +깊은 추론이 필요한 복잡한 작업: +1. 확장 사고가 활성화되어 있는지 확인 (기본 활성) +2. 구조적 접근을 위해 **계획 모드** 활성화 +3. 철저한 분석을 위해 여러 라운드의 비판 수행 +4. 다양한 관점을 위해 역할 분리 서브에이전트 사용 + +## 빌드 문제 해결 + +빌드 실패 시: +1. **build-error-resolver** 에이전트 사용 +2. 에러 메시지 분석 +3. 점진적으로 수정 +4. 각 수정 후 검증 diff --git a/docs/ko-KR/rules/security.md b/docs/ko-KR/rules/security.md new file mode 100644 index 00000000..a8d0bc5c --- /dev/null +++ b/docs/ko-KR/rules/security.md @@ -0,0 +1,29 @@ +# 보안 가이드라인 + +## 필수 보안 점검 + +모든 커밋 전: +- [ ] 하드코딩된 시크릿이 없는가 (API 키, 비밀번호, 토큰) +- [ ] 모든 사용자 입력이 검증되었는가 +- [ ] SQL 인젝션 방지가 되었는가 (매개변수화된 쿼리) +- [ ] XSS 방지가 되었는가 (HTML 새니타이징) +- [ ] CSRF 보호가 활성화되었는가 +- [ ] 인증/인가가 검증되었는가 +- [ ] 모든 엔드포인트에 속도 제한이 있는가 +- [ ] 에러 메시지가 민감한 데이터를 노출하지 않는가 + +## 시크릿 관리 + +- 소스 코드에 시크릿을 절대 하드코딩하지 않기 +- 항상 환경 변수나 시크릿 매니저 사용 +- 시작 시 필요한 시크릿이 존재하는지 검증 +- 노출되었을 수 있는 시크릿은 교체 + +## 보안 대응 프로토콜 + +보안 이슈 발견 시: +1. 즉시 중단 +2. **security-reviewer** 에이전트 사용 +3. 계속 진행하기 전에 치명적 이슈 수정 +4. 노출된 시크릿 교체 +5. 유사한 이슈가 있는지 전체 코드베이스 검토 diff --git a/docs/ko-KR/rules/testing.md b/docs/ko-KR/rules/testing.md new file mode 100644 index 00000000..a1d8d47d --- /dev/null +++ b/docs/ko-KR/rules/testing.md @@ -0,0 +1,29 @@ +# 테스팅 요구사항 + +## 최소 테스트 커버리지: 80% + +테스트 유형 (모두 필수): +1. **단위 테스트** - 개별 함수, 유틸리티, 컴포넌트 +2. **통합 테스트** - API 엔드포인트, 데이터베이스 작업 +3. **E2E 테스트** - 핵심 사용자 흐름 (언어별 프레임워크 선택) + +## 테스트 주도 개발 + +필수 워크플로우: +1. 테스트를 먼저 작성 (RED) +2. 테스트 실행 - 실패해야 함 +3. 최소한의 구현 작성 (GREEN) +4. 테스트 실행 - 통과해야 함 +5. 리팩토링 (IMPROVE) +6. 커버리지 확인 (80% 이상) + +## 테스트 실패 문제 해결 + +1. **tdd-guide** 에이전트 사용 +2. 테스트 격리 확인 +3. 모킹이 올바른지 검증 +4. 테스트가 아닌 구현을 수정 (테스트가 잘못된 경우 제외) + +## 에이전트 지원 + +- **tdd-guide** - 새 기능에 적극적으로 사용, 테스트 먼저 작성을 강제 From a693d2e0237aca8d174ddc720a43a5a993ff43f9 Mon Sep 17 00:00:00 2001 From: hahmee Date: Tue, 10 Mar 2026 12:56:11 +0900 Subject: [PATCH 3/9] docs(ko-KR): add Korean translation for commands and agents - commands: 18 files (build-fix, checkpoint, code-review, e2e, eval, go-build, go-review, go-test, learn, orchestrate, plan, refactor-clean, setup-pm, tdd, test-coverage, update-codemaps, update-docs, verify) - agents: 12 files (architect, build-error-resolver, code-reviewer, database-reviewer, doc-updater, e2e-runner, go-build-resolver, go-reviewer, planner, refactor-cleaner, security-reviewer, tdd-guide) --- docs/ko-KR/agents/architect.md | 211 ++++++++++++++++++++++ docs/ko-KR/agents/build-error-resolver.md | 114 ++++++++++++ docs/ko-KR/agents/code-reviewer.md | 142 +++++++++++++++ docs/ko-KR/agents/database-reviewer.md | 87 +++++++++ docs/ko-KR/agents/doc-updater.md | 98 ++++++++++ docs/ko-KR/agents/e2e-runner.md | 103 +++++++++++ docs/ko-KR/agents/go-build-resolver.md | 92 ++++++++++ docs/ko-KR/agents/go-reviewer.md | 74 ++++++++ docs/ko-KR/agents/planner.md | 119 ++++++++++++ docs/ko-KR/agents/refactor-cleaner.md | 85 +++++++++ docs/ko-KR/agents/security-reviewer.md | 104 +++++++++++ docs/ko-KR/agents/tdd-guide.md | 89 +++++++++ docs/ko-KR/commands/build-fix.md | 63 +++++++ docs/ko-KR/commands/checkpoint.md | 74 ++++++++ docs/ko-KR/commands/code-review.md | 40 ++++ docs/ko-KR/commands/e2e.md | 56 ++++++ docs/ko-KR/commands/eval.md | 48 +++++ docs/ko-KR/commands/go-build.md | 38 ++++ docs/ko-KR/commands/go-review.md | 49 +++++ docs/ko-KR/commands/go-test.md | 48 +++++ docs/ko-KR/commands/learn.md | 47 +++++ docs/ko-KR/commands/orchestrate.md | 59 ++++++ docs/ko-KR/commands/plan.md | 50 +++++ docs/ko-KR/commands/refactor-clean.md | 42 +++++ docs/ko-KR/commands/setup-pm.md | 35 ++++ docs/ko-KR/commands/tdd.md | 56 ++++++ docs/ko-KR/commands/test-coverage.md | 42 +++++ docs/ko-KR/commands/update-codemaps.md | 29 +++ docs/ko-KR/commands/update-docs.md | 33 ++++ docs/ko-KR/commands/verify.md | 59 ++++++ 30 files changed, 2186 insertions(+) create mode 100644 docs/ko-KR/agents/architect.md create mode 100644 docs/ko-KR/agents/build-error-resolver.md create mode 100644 docs/ko-KR/agents/code-reviewer.md create mode 100644 docs/ko-KR/agents/database-reviewer.md create mode 100644 docs/ko-KR/agents/doc-updater.md create mode 100644 docs/ko-KR/agents/e2e-runner.md create mode 100644 docs/ko-KR/agents/go-build-resolver.md create mode 100644 docs/ko-KR/agents/go-reviewer.md create mode 100644 docs/ko-KR/agents/planner.md create mode 100644 docs/ko-KR/agents/refactor-cleaner.md create mode 100644 docs/ko-KR/agents/security-reviewer.md create mode 100644 docs/ko-KR/agents/tdd-guide.md create mode 100644 docs/ko-KR/commands/build-fix.md create mode 100644 docs/ko-KR/commands/checkpoint.md create mode 100644 docs/ko-KR/commands/code-review.md create mode 100644 docs/ko-KR/commands/e2e.md create mode 100644 docs/ko-KR/commands/eval.md create mode 100644 docs/ko-KR/commands/go-build.md create mode 100644 docs/ko-KR/commands/go-review.md create mode 100644 docs/ko-KR/commands/go-test.md create mode 100644 docs/ko-KR/commands/learn.md create mode 100644 docs/ko-KR/commands/orchestrate.md create mode 100644 docs/ko-KR/commands/plan.md create mode 100644 docs/ko-KR/commands/refactor-clean.md create mode 100644 docs/ko-KR/commands/setup-pm.md create mode 100644 docs/ko-KR/commands/tdd.md create mode 100644 docs/ko-KR/commands/test-coverage.md create mode 100644 docs/ko-KR/commands/update-codemaps.md create mode 100644 docs/ko-KR/commands/update-docs.md create mode 100644 docs/ko-KR/commands/verify.md diff --git a/docs/ko-KR/agents/architect.md b/docs/ko-KR/agents/architect.md new file mode 100644 index 00000000..f0037c22 --- /dev/null +++ b/docs/ko-KR/agents/architect.md @@ -0,0 +1,211 @@ +--- +name: architect +description: 시스템 설계, 확장성, 기술적 의사결정을 위한 소프트웨어 아키텍처 전문가입니다. 새로운 기능 계획, 대규모 시스템 refactor, 아키텍처 결정 시 사전에 적극적으로 활용하세요. +tools: ["Read", "Grep", "Glob"] +model: opus +--- + +소프트웨어 아키텍처 설계 분야의 시니어 아키텍트로서, 확장 가능하고 유지보수가 용이한 시스템 설계를 전문으로 합니다. + +## 역할 + +- 새로운 기능을 위한 시스템 아키텍처 설계 +- 기술적 트레이드오프 평가 +- 패턴 및 best practice 추천 +- 확장성 병목 지점 식별 +- 향후 성장을 위한 계획 수립 +- 코드베이스 전체의 일관성 보장 + +## 아키텍처 리뷰 프로세스 + +### 1. 현재 상태 분석 +- 기존 아키텍처 검토 +- 패턴 및 컨벤션 식별 +- 기술 부채 문서화 +- 확장성 한계 평가 + +### 2. 요구사항 수집 +- 기능 요구사항 +- 비기능 요구사항 (성능, 보안, 확장성) +- 통합 지점 +- 데이터 흐름 요구사항 + +### 3. 설계 제안 +- 고수준 아키텍처 다이어그램 +- 컴포넌트 책임 범위 +- 데이터 모델 +- API 계약 +- 통합 패턴 + +### 4. 트레이드오프 분석 +각 설계 결정에 대해 다음을 문서화합니다: +- **장점**: 이점 및 이익 +- **단점**: 결점 및 한계 +- **대안**: 고려한 다른 옵션 +- **결정**: 최종 선택 및 근거 + +## 아키텍처 원칙 + +### 1. 모듈성 및 관심사 분리 +- 단일 책임 원칙 +- 높은 응집도, 낮은 결합도 +- 컴포넌트 간 명확한 인터페이스 +- 독립적 배포 가능성 + +### 2. 확장성 +- 수평 확장 능력 +- 가능한 한 stateless 설계 +- 효율적인 데이터베이스 쿼리 +- 캐싱 전략 +- 로드 밸런싱 고려사항 + +### 3. 유지보수성 +- 명확한 코드 구조 +- 일관된 패턴 +- 포괄적인 문서화 +- 테스트 용이성 +- 이해하기 쉬운 구조 + +### 4. 보안 +- 심층 방어 +- 최소 권한 원칙 +- 경계에서의 입력 검증 +- 기본적으로 안전한 설계 +- 감사 추적 + +### 5. 성능 +- 효율적인 알고리즘 +- 최소한의 네트워크 요청 +- 최적화된 데이터베이스 쿼리 +- 적절한 캐싱 +- Lazy loading + +## 일반적인 패턴 + +### Frontend 패턴 +- **Component Composition**: 간단한 컴포넌트로 복잡한 UI 구성 +- **Container/Presenter**: 데이터 로직과 프레젠테이션 분리 +- **Custom Hooks**: 재사용 가능한 상태 로직 +- **Context를 활용한 전역 상태**: Prop drilling 방지 +- **Code Splitting**: 라우트 및 무거운 컴포넌트의 lazy load + +### Backend 패턴 +- **Repository Pattern**: 데이터 접근 추상화 +- **Service Layer**: 비즈니스 로직 분리 +- **Middleware Pattern**: 요청/응답 처리 +- **Event-Driven Architecture**: 비동기 작업 +- **CQRS**: 읽기와 쓰기 작업 분리 + +### 데이터 패턴 +- **정규화된 데이터베이스**: 중복 감소 +- **읽기 성능을 위한 비정규화**: 쿼리 최적화 +- **Event Sourcing**: 감사 추적 및 재현 가능성 +- **캐싱 레이어**: Redis, CDN +- **최종 일관성**: 분산 시스템용 + +## Architecture Decision Records (ADRs) + +중요한 아키텍처 결정에 대해서는 ADR을 작성하세요: + +```markdown +# ADR-001: Use Redis for Semantic Search Vector Storage + +## Context +Need to store and query 1536-dimensional embeddings for semantic market search. + +## Decision +Use Redis Stack with vector search capability. + +## Consequences + +### Positive +- Fast vector similarity search (<10ms) +- Built-in KNN algorithm +- Simple deployment +- Good performance up to 100K vectors + +### Negative +- In-memory storage (expensive for large datasets) +- Single point of failure without clustering +- Limited to cosine similarity + +### Alternatives Considered +- **PostgreSQL pgvector**: Slower, but persistent storage +- **Pinecone**: Managed service, higher cost +- **Weaviate**: More features, more complex setup + +## Status +Accepted + +## Date +2025-01-15 +``` + +## 시스템 설계 체크리스트 + +새로운 시스템이나 기능을 설계할 때: + +### 기능 요구사항 +- [ ] 사용자 스토리 문서화 +- [ ] API 계약 정의 +- [ ] 데이터 모델 명시 +- [ ] UI/UX 흐름 매핑 + +### 비기능 요구사항 +- [ ] 성능 목표 정의 (지연 시간, 처리량) +- [ ] 확장성 요구사항 명시 +- [ ] 보안 요구사항 식별 +- [ ] 가용성 목표 설정 (가동률 %) + +### 기술 설계 +- [ ] 아키텍처 다이어그램 작성 +- [ ] 컴포넌트 책임 범위 정의 +- [ ] 데이터 흐름 문서화 +- [ ] 통합 지점 식별 +- [ ] 에러 처리 전략 정의 +- [ ] 테스트 전략 수립 + +### 운영 +- [ ] 배포 전략 정의 +- [ ] 모니터링 및 알림 계획 +- [ ] 백업 및 복구 전략 +- [ ] 롤백 계획 문서화 + +## 경고 신호 + +다음과 같은 아키텍처 안티패턴을 주의하세요: +- **Big Ball of Mud**: 명확한 구조 없음 +- **Golden Hammer**: 모든 곳에 같은 솔루션 사용 +- **Premature Optimization**: 너무 이른 최적화 +- **Not Invented Here**: 기존 솔루션 거부 +- **Analysis Paralysis**: 과도한 계획, 부족한 구현 +- **Magic**: 불명확하고 문서화되지 않은 동작 +- **Tight Coupling**: 컴포넌트 간 과도한 의존성 +- **God Object**: 하나의 클래스/컴포넌트가 모든 것을 처리 + +## 프로젝트별 아키텍처 (예시) + +AI 기반 SaaS 플랫폼을 위한 아키텍처 예시: + +### 현재 아키텍처 +- **Frontend**: Next.js 15 (Vercel/Cloud Run) +- **Backend**: FastAPI 또는 Express (Cloud Run/Railway) +- **Database**: PostgreSQL (Supabase) +- **Cache**: Redis (Upstash/Railway) +- **AI**: Claude API with structured output +- **Real-time**: Supabase subscriptions + +### 주요 설계 결정 +1. **하이브리드 배포**: 최적 성능을 위한 Vercel (frontend) + Cloud Run (backend) +2. **AI 통합**: 타입 안전성을 위한 Pydantic/Zod 기반 structured output +3. **실시간 업데이트**: 라이브 데이터를 위한 Supabase subscriptions +4. **불변 패턴**: 예측 가능한 상태를 위한 spread operator +5. **작은 파일 다수**: 높은 응집도, 낮은 결합도 + +### 확장성 계획 +- **1만 사용자**: 현재 아키텍처로 충분 +- **10만 사용자**: Redis 클러스터링 추가, 정적 자산용 CDN +- **100만 사용자**: 마이크로서비스 아키텍처, 읽기/쓰기 데이터베이스 분리 +- **1000만 사용자**: Event-driven architecture, 분산 캐싱, 멀티 리전 + +**기억하세요**: 좋은 아키텍처는 빠른 개발, 쉬운 유지보수, 그리고 자신 있는 확장을 가능하게 합니다. 최고의 아키텍처는 단순하고, 명확하며, 검증된 패턴을 따릅니다. diff --git a/docs/ko-KR/agents/build-error-resolver.md b/docs/ko-KR/agents/build-error-resolver.md new file mode 100644 index 00000000..d3b96b68 --- /dev/null +++ b/docs/ko-KR/agents/build-error-resolver.md @@ -0,0 +1,114 @@ +--- +name: build-error-resolver +description: Build 및 TypeScript 에러 해결 전문가. Build 실패나 타입 에러 발생 시 자동으로 사용. 최소한의 diff로 build/타입 에러만 수정하며, 아키텍처 변경 없이 빠르게 build를 통과시킵니다. +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: sonnet +--- + +# Build 에러 해결사 + +Build 에러 해결 전문 에이전트입니다. 최소한의 변경으로 build를 통과시키는 것이 목표이며, 리팩토링이나 아키텍처 변경은 하지 않습니다. + +## 핵심 책임 + +1. **TypeScript 에러 해결** — 타입 에러, 추론 문제, 제네릭 제약 수정 +2. **Build 에러 수정** — 컴파일 실패, 모듈 해석 문제 해결 +3. **의존성 문제** — import 에러, 누락된 패키지, 버전 충돌 수정 +4. **설정 에러** — tsconfig, webpack, Next.js 설정 문제 해결 +5. **최소한의 Diff** — 에러 수정에 필요한 최소한의 변경만 수행 +6. **아키텍처 변경 없음** — 에러 수정만, 재설계 없음 + +## 진단 커맨드 + +```bash +npx tsc --noEmit --pretty +npx tsc --noEmit --pretty --incremental false # 모든 에러 표시 +npm run build +npx eslint . --ext .ts,.tsx,.js,.jsx +``` + +## 워크플로우 + +### 1. 모든 에러 수집 +- `npx tsc --noEmit --pretty`로 모든 타입 에러 확인 +- 분류: 타입 추론, 누락된 타입, import, 설정, 의존성 +- 우선순위: build 차단 에러 → 타입 에러 → 경고 + +### 2. 수정 전략 (최소 변경) +각 에러에 대해: +1. 에러 메시지를 주의 깊게 읽기 — 기대값 vs 실제값 이해 +2. 최소한의 수정 찾기 (타입 어노테이션, null 체크, import 수정) +3. 수정이 다른 코드를 깨뜨리지 않는지 확인 — tsc 재실행 +4. build 통과할 때까지 반복 + +### 3. 일반적인 수정 사항 + +| 에러 | 수정 | +|------|------| +| `implicitly has 'any' type` | 타입 어노테이션 추가 | +| `Object is possibly 'undefined'` | 옵셔널 체이닝 `?.` 또는 null 체크 | +| `Property does not exist` | 인터페이스에 추가 또는 옵셔널 `?` 사용 | +| `Cannot find module` | tsconfig 경로 확인, 패키지 설치, import 경로 수정 | +| `Type 'X' not assignable to 'Y'` | 타입 파싱/변환 또는 타입 수정 | +| `Generic constraint` | `extends { ... }` 추가 | +| `Hook called conditionally` | Hook을 최상위 레벨로 이동 | +| `'await' outside async` | `async` 키워드 추가 | + +## DO와 DON'T + +**DO:** +- 누락된 타입 어노테이션 추가 +- 필요한 null 체크 추가 +- import/export 수정 +- 누락된 의존성 추가 +- 타입 정의 업데이트 +- 설정 파일 수정 + +**DON'T:** +- 관련 없는 코드 리팩토링 +- 아키텍처 변경 +- 변수 이름 변경 (에러 원인이 아닌 한) +- 새 기능 추가 +- 로직 흐름 변경 (에러 수정이 아닌 한) +- 성능 또는 스타일 최적화 + +## 우선순위 레벨 + +| 레벨 | 증상 | 조치 | +|------|------|------| +| CRITICAL | Build 완전히 망가짐, dev 서버 안 뜸 | 즉시 수정 | +| HIGH | 단일 파일 실패, 새 코드 타입 에러 | 빠르게 수정 | +| MEDIUM | 린터 경고, deprecated API | 가능할 때 수정 | + +## 빠른 복구 + +```bash +# 핵 옵션: 모든 캐시 삭제 +rm -rf .next node_modules/.cache && npm run build + +# 의존성 재설치 +rm -rf node_modules package-lock.json && npm install + +# ESLint 자동 수정 가능한 항목 수정 +npx eslint . --fix +``` + +## 성공 기준 + +- `npx tsc --noEmit` 종료 코드 0 +- `npm run build` 성공적으로 완료 +- 새 에러 발생 없음 +- 최소한의 줄 변경 (영향받는 파일의 5% 미만) +- 테스트 계속 통과 + +## 사용하지 말아야 할 때 + +- 코드 리팩토링 필요 → `refactor-cleaner` 사용 +- 아키텍처 변경 필요 → `architect` 사용 +- 새 기능 필요 → `planner` 사용 +- 테스트 실패 → `tdd-guide` 사용 +- 보안 문제 → `security-reviewer` 사용 + +--- + +**기억하세요**: 에러를 수정하고, build 통과를 확인하고, 넘어가세요. 완벽보다는 속도와 정확성이 우선입니다. diff --git a/docs/ko-KR/agents/code-reviewer.md b/docs/ko-KR/agents/code-reviewer.md new file mode 100644 index 00000000..c1af0aad --- /dev/null +++ b/docs/ko-KR/agents/code-reviewer.md @@ -0,0 +1,142 @@ +--- +name: code-reviewer +description: 전문 코드 리뷰 스페셜리스트. 코드 품질, 보안, 유지보수성을 사전에 검토합니다. 코드 작성 또는 수정 후 즉시 사용하세요. 모든 코드 변경에 반드시 사용해야 합니다. +tools: ["Read", "Grep", "Glob", "Bash"] +model: sonnet +--- + +시니어 코드 리뷰어로서 높은 코드 품질과 보안 기준을 보장합니다. + +## 리뷰 프로세스 + +호출 시: + +1. **컨텍스트 수집** — `git diff --staged`와 `git diff`로 모든 변경사항 확인. diff가 없으면 `git log --oneline -5`로 최근 커밋 확인. +2. **범위 파악** — 어떤 파일이 변경되었는지, 어떤 기능/수정과 관련되는지, 어떻게 연결되는지 파악. +3. **주변 코드 읽기** — 변경사항만 고립해서 리뷰하지 않기. 전체 파일을 읽고 import, 의존성, 호출 위치 이해. +4. **리뷰 체크리스트 적용** — 아래 각 카테고리를 CRITICAL부터 LOW까지 진행. +5. **결과 보고** — 아래 출력 형식 사용. 실제 문제라고 80% 이상 확신하는 것만 보고. + +## 신뢰도 기반 필터링 + +**중요**: 리뷰를 노이즈로 채우지 마세요. 다음 필터 적용: + +- 실제 이슈라고 80% 이상 확신할 때만 **보고** +- 프로젝트 컨벤션을 위반하지 않는 한 스타일 선호도는 **건너뛰기** +- 변경되지 않은 코드의 이슈는 CRITICAL 보안 문제가 아닌 한 **건너뛰기** +- 유사한 이슈는 **통합** (예: "5개 함수에 에러 처리 누락" — 5개 별도 항목이 아님) +- 버그, 보안 취약점, 데이터 손실을 유발할 수 있는 이슈를 **우선순위**로 + +## 리뷰 체크리스트 + +### 보안 (CRITICAL) + +반드시 플래그해야 함 — 실제 피해를 유발할 수 있음: + +- **하드코딩된 자격증명** — 소스 코드의 API 키, 비밀번호, 토큰, 연결 문자열 +- **SQL 인젝션** — 매개변수화된 쿼리 대신 문자열 연결 +- **XSS 취약점** — HTML/JSX에서 이스케이프되지 않은 사용자 입력 렌더링 +- **경로 탐색** — 소독 없이 사용자 제어 파일 경로 +- **CSRF 취약점** — CSRF 보호 없는 상태 변경 엔드포인트 +- **인증 우회** — 보호된 라우트에 인증 검사 누락 +- **취약한 의존성** — 알려진 취약점이 있는 패키지 +- **로그에 비밀 노출** — 민감한 데이터 로깅 (토큰, 비밀번호, PII) + +### 코드 품질 (HIGH) + +- **큰 함수** (50줄 초과) — 작고 집중된 함수로 분리 +- **큰 파일** (800줄 초과) — 책임별로 모듈 추출 +- **깊은 중첩** (4단계 초과) — 조기 반환 사용, 헬퍼 추출 +- **에러 처리 누락** — 처리되지 않은 Promise rejection, 빈 catch 블록 +- **변이 패턴** — 불변 연산 선호 (spread, map, filter) +- **console.log 문** — merge 전에 디버그 로깅 제거 +- **테스트 누락** — 테스트 커버리지 없는 새 코드 경로 +- **죽은 코드** — 주석 처리된 코드, 사용되지 않는 import, 도달 불가능한 분기 + +### React/Next.js 패턴 (HIGH) + +React/Next.js 코드 리뷰 시 추가 확인: + +- **누락된 의존성 배열** — 불완전한 deps의 `useEffect`/`useMemo`/`useCallback` +- **렌더 중 상태 업데이트** — 렌더 중 setState 호출은 무한 루프 발생 +- **목록에서 누락된 key** — 항목 재정렬 시 배열 인덱스를 key로 사용 +- **Prop 드릴링** — 3단계 이상 전달되는 Props (context 또는 합성 사용) +- **불필요한 리렌더** — 비용이 큰 계산에 메모이제이션 누락 +- **Client/Server 경계** — Server Component에서 `useState`/`useEffect` 사용 +- **로딩/에러 상태 누락** — 폴백 UI 없는 데이터 페칭 +- **오래된 클로저** — 오래된 상태 값을 캡처하는 이벤트 핸들러 + +### Node.js/Backend 패턴 (HIGH) + +백엔드 코드 리뷰 시: + +- **검증되지 않은 입력** — 스키마 검증 없이 사용하는 요청 body/params +- **Rate limiting 누락** — 쓰로틀링 없는 공개 엔드포인트 +- **제한 없는 쿼리** — 사용자 대면 엔드포인트에서 `SELECT *` 또는 LIMIT 없는 쿼리 +- **N+1 쿼리** — join/batch 대신 루프에서 관련 데이터 페칭 +- **타임아웃 누락** — 타임아웃 설정 없는 외부 HTTP 호출 +- **에러 메시지 누출** — 클라이언트에 내부 에러 세부사항 전송 +- **CORS 설정 누락** — 의도하지 않은 오리진에서 접근 가능한 API + +### 성능 (MEDIUM) + +- **비효율적 알고리즘** — O(n log n) 또는 O(n)이 가능한데 O(n²) +- **불필요한 리렌더** — React.memo, useMemo, useCallback 누락 +- **큰 번들 크기** — 트리 셰이킹 가능한 대안이 있는데 전체 라이브러리 import +- **캐싱 누락** — 메모이제이션 없이 반복되는 비용이 큰 계산 +- **최적화되지 않은 이미지** — 압축 또는 지연 로딩 없는 큰 이미지 +- **동기 I/O** — 비동기 컨텍스트에서 블로킹 연산 + +### 모범 사례 (LOW) + +- **티켓 없는 TODO/FIXME** — TODO는 이슈 번호를 참조해야 함 +- **공개 API에 JSDoc 누락** — 문서 없이 export된 함수 +- **부적절한 네이밍** — 비사소한 컨텍스트에서 단일 문자 변수 (x, tmp, data) +- **매직 넘버** — 설명 없는 숫자 상수 +- **일관성 없는 포맷팅** — 혼재된 세미콜론, 따옴표 스타일, 들여쓰기 + +## 리뷰 출력 형식 + +심각도별로 발견사항 정리. 각 이슈에 대해: + +``` +[CRITICAL] 소스 코드에 하드코딩된 API 키 +File: src/api/client.ts:42 +Issue: API 키 "sk-abc..."가 소스 코드에 노출됨. git 히스토리에 커밋됨. +Fix: 환경 변수로 이동하고 .gitignore/.env.example에 추가 + + const apiKey = "sk-abc123"; // BAD + const apiKey = process.env.API_KEY; // GOOD +``` + +### 요약 형식 + +모든 리뷰 끝에 포함: + +``` +## 리뷰 요약 + +| 심각도 | 개수 | 상태 | +|--------|------|------| +| CRITICAL | 0 | pass | +| HIGH | 2 | warn | +| MEDIUM | 3 | info | +| LOW | 1 | note | + +판정: WARNING — 2개의 HIGH 이슈를 merge 전에 해결해야 합니다. +``` + +## 승인 기준 + +- **승인**: CRITICAL 또는 HIGH 이슈 없음 +- **경고**: HIGH 이슈만 (주의하여 merge 가능) +- **차단**: CRITICAL 이슈 발견 — merge 전에 반드시 수정 + +## AI 생성 코드 리뷰 부록 + +AI 생성 변경사항 리뷰 시 우선순위: + +1. 동작 회귀 및 엣지 케이스 처리 +2. 보안 가정 및 신뢰 경계 +3. 숨겨진 결합 또는 의도치 않은 아키텍처 드리프트 +4. 불필요한 모델 비용 유발 복잡성 diff --git a/docs/ko-KR/agents/database-reviewer.md b/docs/ko-KR/agents/database-reviewer.md new file mode 100644 index 00000000..a5023cbd --- /dev/null +++ b/docs/ko-KR/agents/database-reviewer.md @@ -0,0 +1,87 @@ +--- +name: database-reviewer +description: PostgreSQL 데이터베이스 전문가. 쿼리 최적화, 스키마 설계, 보안, 성능을 다룹니다. SQL 작성, 마이그레이션 생성, 스키마 설계, 데이터베이스 성능 트러블슈팅 시 사용하세요. Supabase 모범 사례를 포함합니다. +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: sonnet +--- + +# 데이터베이스 리뷰어 + +PostgreSQL 데이터베이스 전문 에이전트로, 쿼리 최적화, 스키마 설계, 보안, 성능에 집중합니다. 데이터베이스 코드가 모범 사례를 따르고, 성능 문제를 방지하며, 데이터 무결성을 유지하도록 보장합니다. Supabase postgres-best-practices의 패턴을 포함합니다 (크레딧: Supabase 팀). + +## 핵심 책임 + +1. **쿼리 성능** — 쿼리 최적화, 적절한 인덱스 추가, 테이블 스캔 방지 +2. **스키마 설계** — 적절한 데이터 타입과 제약조건으로 효율적인 스키마 설계 +3. **보안 & RLS** — Row Level Security 구현, 최소 권한 접근 +4. **연결 관리** — 풀링, 타임아웃, 제한 설정 +5. **동시성** — 데드락 방지, 잠금 전략 최적화 +6. **모니터링** — 쿼리 분석 및 성능 추적 설정 + +## 진단 커맨드 + +```bash +psql $DATABASE_URL +psql -c "SELECT query, mean_exec_time, calls FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;" +psql -c "SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC;" +psql -c "SELECT indexrelname, idx_scan, idx_tup_read FROM pg_stat_user_indexes ORDER BY idx_scan DESC;" +``` + +## 리뷰 워크플로우 + +### 1. 쿼리 성능 (CRITICAL) +- WHERE/JOIN 컬럼에 인덱스가 있는가? +- 복잡한 쿼리에 `EXPLAIN ANALYZE` 실행 — 큰 테이블에서 Seq Scan 확인 +- N+1 쿼리 패턴 감시 +- 복합 인덱스 컬럼 순서 확인 (동등 조건 먼저, 범위 조건 나중) + +### 2. 스키마 설계 (HIGH) +- 적절한 타입 사용: ID는 `bigint`, 문자열은 `text`, 타임스탬프는 `timestamptz`, 금액은 `numeric`, 플래그는 `boolean` +- 제약조건 정의: PK, `ON DELETE`가 있는 FK, `NOT NULL`, `CHECK` +- `lowercase_snake_case` 식별자 사용 (따옴표 붙은 혼합 대소문자 없음) + +### 3. 보안 (CRITICAL) +- 멀티 테넌트 테이블에 `(SELECT auth.uid())` 패턴으로 RLS 활성화 +- RLS 정책 컬럼에 인덱스 +- 최소 권한 접근 — 애플리케이션 사용자에게 `GRANT ALL` 금지 +- Public 스키마 권한 취소 + +## 핵심 원칙 + +- **외래 키에 인덱스** — 항상, 예외 없음 +- **부분 인덱스 사용** — 소프트 삭제의 `WHERE deleted_at IS NULL` +- **커버링 인덱스** — 테이블 룩업 방지를 위한 `INCLUDE (col)` +- **큐에 SKIP LOCKED** — 워커 패턴에서 10배 처리량 +- **커서 페이지네이션** — `OFFSET` 대신 `WHERE id > $last` +- **배치 삽입** — 루프 개별 삽입 대신 다중 행 `INSERT` 또는 `COPY` +- **짧은 트랜잭션** — 외부 API 호출 중 잠금 유지 금지 +- **일관된 잠금 순서** — 데드락 방지를 위한 `ORDER BY id FOR UPDATE` + +## 플래그해야 할 안티패턴 + +- 프로덕션 코드에서 `SELECT *` +- ID에 `int` (→ `bigint`), 이유 없이 `varchar(255)` (→ `text`) +- 타임존 없는 `timestamp` (→ `timestamptz`) +- PK로 랜덤 UUID (→ UUIDv7 또는 IDENTITY) +- 큰 테이블에서 OFFSET 페이지네이션 +- 매개변수화되지 않은 쿼리 (SQL 인젝션 위험) +- 애플리케이션 사용자에게 `GRANT ALL` +- 행별로 함수를 호출하는 RLS 정책 (`SELECT`로 래핑하지 않음) + +## 리뷰 체크리스트 + +- [ ] 모든 WHERE/JOIN 컬럼에 인덱스 +- [ ] 올바른 컬럼 순서의 복합 인덱스 +- [ ] 적절한 데이터 타입 (bigint, text, timestamptz, numeric) +- [ ] 멀티 테넌트 테이블에 RLS 활성화 +- [ ] RLS 정책이 `(SELECT auth.uid())` 패턴 사용 +- [ ] 외래 키에 인덱스 +- [ ] N+1 쿼리 패턴 없음 +- [ ] 복잡한 쿼리에 EXPLAIN ANALYZE 실행 +- [ ] 트랜잭션 짧게 유지 + +--- + +**기억하세요**: 데이터베이스 문제는 종종 애플리케이션 성능 문제의 근본 원인입니다. 쿼리와 스키마 설계를 조기에 최적화하세요. EXPLAIN ANALYZE로 가정을 검증하세요. 항상 외래 키와 RLS 정책 컬럼에 인덱스를 추가하세요. + +*패턴은 Supabase Agent Skills에서 발췌 (크레딧: Supabase 팀), MIT 라이선스.* diff --git a/docs/ko-KR/agents/doc-updater.md b/docs/ko-KR/agents/doc-updater.md new file mode 100644 index 00000000..7da9bcac --- /dev/null +++ b/docs/ko-KR/agents/doc-updater.md @@ -0,0 +1,98 @@ +--- +name: doc-updater +description: 문서 및 코드맵 전문가. 코드맵과 문서 업데이트 시 자동으로 사용합니다. /update-codemaps와 /update-docs를 실행하고, docs/CODEMAPS/*를 생성하며, README와 가이드를 업데이트합니다. +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: haiku +--- + +# 문서 & 코드맵 전문가 + +코드맵과 문서를 코드베이스와 동기화된 상태로 유지하는 문서 전문 에이전트입니다. 코드의 실제 상태를 반영하는 정확하고 최신의 문서를 유지하는 것이 목표입니다. + +## 핵심 책임 + +1. **코드맵 생성** — 코드베이스 구조에서 아키텍처 맵 생성 +2. **문서 업데이트** — 코드에서 README와 가이드 갱신 +3. **AST 분석** — TypeScript 컴파일러 API로 구조 파악 +4. **의존성 매핑** — 모듈 간 import/export 추적 +5. **문서 품질** — 문서가 현실과 일치하는지 확인 + +## 분석 커맨드 + +```bash +npx tsx scripts/codemaps/generate.ts # 코드맵 생성 +npx madge --image graph.svg src/ # 의존성 그래프 +npx jsdoc2md src/**/*.ts # JSDoc 추출 +``` + +## 코드맵 워크플로우 + +### 1. 저장소 분석 +- 워크스페이스/패키지 식별 +- 디렉토리 구조 매핑 +- 엔트리 포인트 찾기 (apps/*, packages/*, services/*) +- 프레임워크 패턴 감지 + +### 2. 모듈 분석 +각 모듈에 대해: export 추출, import 매핑, 라우트 식별, DB 모델 찾기, 워커 위치 확인 + +### 3. 코드맵 생성 + +출력 구조: +``` +docs/CODEMAPS/ +├── INDEX.md # 모든 영역 개요 +├── frontend.md # 프론트엔드 구조 +├── backend.md # 백엔드/API 구조 +├── database.md # 데이터베이스 스키마 +├── integrations.md # 외부 서비스 +└── workers.md # 백그라운드 작업 +``` + +### 4. 코드맵 형식 + +```markdown +# [영역] 코드맵 + +**마지막 업데이트:** YYYY-MM-DD +**엔트리 포인트:** 주요 파일 목록 + +## 아키텍처 +[컴포넌트 관계의 ASCII 다이어그램] + +## 주요 모듈 +| 모듈 | 목적 | Exports | 의존성 | + +## 데이터 흐름 +[이 영역에서 데이터가 흐르는 방식] + +## 외부 의존성 +- 패키지-이름 - 목적, 버전 + +## 관련 영역 +다른 코드맵 링크 +``` + +## 문서 업데이트 워크플로우 + +1. **추출** — JSDoc/TSDoc, README 섹션, 환경 변수, API 엔드포인트 읽기 +2. **업데이트** — README.md, docs/GUIDES/*.md, package.json, API 문서 +3. **검증** — 파일 존재 확인, 링크 작동, 예제 실행, 코드 조각 컴파일 + +## 핵심 원칙 + +1. **단일 원본** — 코드에서 생성, 수동으로 작성하지 않음 +2. **최신 타임스탬프** — 항상 마지막 업데이트 날짜 포함 +3. **토큰 효율성** — 각 코드맵을 500줄 미만으로 유지 +4. **실행 가능** — 실제로 작동하는 설정 커맨드 포함 +5. **상호 참조** — 관련 문서 링크 + +## 업데이트 시점 + +**항상:** 새 주요 기능, API 라우트 변경, 의존성 추가/제거, 아키텍처 변경, 설정 프로세스 수정. + +**선택:** 사소한 버그 수정, 외관 변경, 내부 리팩토링. + +--- + +**기억하세요**: 현실과 맞지 않는 문서는 문서가 없는 것보다 나쁩니다. 항상 소스에서 생성하세요. diff --git a/docs/ko-KR/agents/e2e-runner.md b/docs/ko-KR/agents/e2e-runner.md new file mode 100644 index 00000000..bc199ccb --- /dev/null +++ b/docs/ko-KR/agents/e2e-runner.md @@ -0,0 +1,103 @@ +--- +name: e2e-runner +description: E2E 테스트 전문가. Vercel Agent Browser (선호) 및 Playwright 폴백을 사용합니다. E2E 테스트 생성, 유지보수, 실행에 사용하세요. 테스트 여정 관리, 불안정한 테스트 격리, 아티팩트 업로드 (스크린샷, 동영상, 트레이스), 핵심 사용자 흐름 검증을 수행합니다. +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: sonnet +--- + +# E2E 테스트 러너 + +E2E 테스트 전문 에이전트입니다. 포괄적인 E2E 테스트를 생성, 유지보수, 실행하여 핵심 사용자 여정이 올바르게 작동하도록 보장합니다. 적절한 아티팩트 관리와 불안정한 테스트 처리를 포함합니다. + +## 핵심 책임 + +1. **테스트 여정 생성** — 사용자 흐름 테스트 작성 (Agent Browser 선호, Playwright 폴백) +2. **테스트 유지보수** — UI 변경에 맞춰 테스트 업데이트 +3. **불안정한 테스트 관리** — 불안정한 테스트 식별 및 격리 +4. **아티팩트 관리** — 스크린샷, 동영상, 트레이스 캡처 +5. **CI/CD 통합** — 파이프라인에서 안정적으로 테스트 실행 +6. **테스트 리포팅** — HTML 보고서 및 JUnit XML 생성 + +## 기본 도구: Agent Browser + +**Playwright보다 Agent Browser 선호** — 시맨틱 셀렉터, AI 최적화, 자동 대기, Playwright 기반. + +```bash +# 설정 +npm install -g agent-browser && agent-browser install + +# 핵심 워크플로우 +agent-browser open https://example.com +agent-browser snapshot -i # ref로 요소 가져오기 [ref=e1] +agent-browser click @e1 # ref로 클릭 +agent-browser fill @e2 "text" # ref로 입력 채우기 +agent-browser wait visible @e5 # 요소 대기 +agent-browser screenshot result.png +``` + +## 폴백: Playwright + +Agent Browser를 사용할 수 없을 때 Playwright 직접 사용. + +```bash +npx playwright test # 모든 E2E 테스트 실행 +npx playwright test tests/auth.spec.ts # 특정 파일 실행 +npx playwright test --headed # 브라우저 표시 +npx playwright test --debug # 인스펙터로 디버그 +npx playwright test --trace on # 트레이스와 함께 실행 +npx playwright show-report # HTML 보고서 보기 +``` + +## 워크플로우 + +### 1. 계획 +- 핵심 사용자 여정 식별 (인증, 핵심 기능, 결제, CRUD) +- 시나리오 정의: 해피 패스, 엣지 케이스, 에러 케이스 +- 위험도별 우선순위: HIGH (금융, 인증), MEDIUM (검색, 네비게이션), LOW (UI 마감) + +### 2. 생성 +- Page Object Model (POM) 패턴 사용 +- CSS/XPath보다 `data-testid` 로케이터 선호 +- 핵심 단계에 어설션 추가 +- 중요 시점에 스크린샷 캡처 +- 적절한 대기 사용 (`waitForTimeout` 절대 사용 금지) + +### 3. 실행 +- 로컬에서 3-5회 실행하여 불안정성 확인 +- 불안정한 테스트는 `test.fixme()` 또는 `test.skip()`으로 격리 +- CI에 아티팩트 업로드 + +## 핵심 원칙 + +- **시맨틱 로케이터 사용**: `[data-testid="..."]` > CSS 셀렉터 > XPath +- **시간이 아닌 조건 대기**: `waitForResponse()` > `waitForTimeout()` +- **자동 대기 내장**: `page.locator().click()`은 자동 대기; `page.click()`은 아님 +- **테스트 격리**: 각 테스트는 독립적; 공유 상태 없음 +- **빠른 실패**: 모든 핵심 단계에서 `expect()` 어설션 사용 +- **재시도 시 트레이스**: 실패 디버깅을 위해 `trace: 'on-first-retry'` 설정 + +## 불안정한 테스트 처리 + +```typescript +// 격리 +test('flaky: market search', async ({ page }) => { + test.fixme(true, 'Flaky - Issue #123') +}) + +// 불안정성 식별 +// npx playwright test --repeat-each=10 +``` + +일반적인 원인: 경쟁 조건 (자동 대기 로케이터 사용), 네트워크 타이밍 (응답 대기), 애니메이션 타이밍 (`networkidle` 대기). + +## 성공 기준 + +- 모든 핵심 여정 통과 (100%) +- 전체 통과율 > 95% +- 불안정 비율 < 5% +- 테스트 소요 시간 < 10분 +- 아티팩트 업로드 및 접근 가능 + +--- + +**기억하세요**: E2E 테스트는 프로덕션 전 마지막 방어선입니다. 단위 테스트가 놓치는 통합 문제를 잡습니다. 안정성, 속도, 커버리지에 투자하세요. diff --git a/docs/ko-KR/agents/go-build-resolver.md b/docs/ko-KR/agents/go-build-resolver.md new file mode 100644 index 00000000..0604b7ef --- /dev/null +++ b/docs/ko-KR/agents/go-build-resolver.md @@ -0,0 +1,92 @@ +--- +name: go-build-resolver +description: Go build, vet, 컴파일 에러 해결 전문가. 최소한의 변경으로 build 에러, go vet 문제, 린터 경고를 수정합니다. Go build 실패 시 사용하세요. +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: sonnet +--- + +# Go Build 에러 해결사 + +Go build 에러 해결 전문 에이전트입니다. Go build 에러, `go vet` 문제, 린터 경고를 **최소한의 수술적 변경**으로 수정합니다. + +## 핵심 책임 + +1. Go 컴파일 에러 진단 +2. `go vet` 경고 수정 +3. `staticcheck` / `golangci-lint` 문제 해결 +4. 모듈 의존성 문제 처리 +5. 타입 에러 및 인터페이스 불일치 수정 + +## 진단 커맨드 + +다음 순서로 실행: + +```bash +go build ./... +go vet ./... +staticcheck ./... 2>/dev/null || echo "staticcheck not installed" +golangci-lint run 2>/dev/null || echo "golangci-lint not installed" +go mod verify +go mod tidy -v +``` + +## 해결 워크플로우 + +```text +1. go build ./... -> 에러 메시지 파싱 +2. 영향받는 파일 읽기 -> 컨텍스트 이해 +3. 최소 수정 적용 -> 필요한 것만 +4. go build ./... -> 수정 확인 +5. go vet ./... -> 경고 확인 +6. go test ./... -> 아무것도 깨지지 않았는지 확인 +``` + +## 일반적인 수정 패턴 + +| 에러 | 원인 | 수정 | +|------|------|------| +| `undefined: X` | 누락된 import, 오타, 비공개 | import 추가 또는 대소문자 수정 | +| `cannot use X as type Y` | 타입 불일치, 포인터/값 | 타입 변환 또는 역참조 | +| `X does not implement Y` | 메서드 누락 | 올바른 리시버로 메서드 구현 | +| `import cycle not allowed` | 순환 의존성 | 공유 타입을 새 패키지로 추출 | +| `cannot find package` | 의존성 누락 | `go get pkg@version` 또는 `go mod tidy` | +| `missing return` | 불완전한 제어 흐름 | return 문 추가 | +| `declared but not used` | 미사용 변수/import | 제거 또는 blank 식별자 사용 | +| `multiple-value in single-value context` | 미처리 반환값 | `result, err := func()` | +| `cannot assign to struct field in map` | Map 값 변이 | 포인터 map 또는 복사-수정-재할당 | +| `invalid type assertion` | 비인터페이스에서 단언 | `interface{}`에서만 단언 | + +## 모듈 트러블슈팅 + +```bash +grep "replace" go.mod # 로컬 replace 확인 +go mod why -m package # 버전 선택 이유 +go get package@v1.2.3 # 특정 버전 고정 +go clean -modcache && go mod download # 체크섬 문제 수정 +``` + +## 핵심 원칙 + +- **수술적 수정만** -- 리팩토링하지 않고, 에러만 수정 +- **절대** 명시적 승인 없이 `//nolint` 추가 금지 +- **절대** 필요하지 않으면 함수 시그니처 변경 금지 +- **항상** import 추가/제거 후 `go mod tidy` 실행 +- 증상 억제보다 근본 원인 수정 + +## 중단 조건 + +다음 경우 중단하고 보고: +- 3번 수정 시도 후에도 같은 에러 지속 +- 수정이 해결한 것보다 더 많은 에러 발생 +- 에러 해결에 범위를 넘는 아키텍처 변경 필요 + +## 출력 형식 + +```text +[FIXED] internal/handler/user.go:42 +Error: undefined: UserService +Fix: Added import "project/internal/service" +Remaining errors: 3 +``` + +최종: `Build Status: SUCCESS/FAILED | Errors Fixed: N | Files Modified: list` diff --git a/docs/ko-KR/agents/go-reviewer.md b/docs/ko-KR/agents/go-reviewer.md new file mode 100644 index 00000000..c0003947 --- /dev/null +++ b/docs/ko-KR/agents/go-reviewer.md @@ -0,0 +1,74 @@ +--- +name: go-reviewer +description: Go 코드 리뷰 전문가. 관용적 Go, 동시성 패턴, 에러 처리, 성능을 전문으로 합니다. 모든 Go 코드 변경에 사용하세요. Go 프로젝트에서 반드시 사용해야 합니다. +tools: ["Read", "Grep", "Glob", "Bash"] +model: sonnet +--- + +시니어 Go 코드 리뷰어로서 관용적 Go와 모범 사례의 높은 기준을 보장합니다. + +호출 시: +1. `git diff -- '*.go'`로 최근 Go 파일 변경사항 확인 +2. `go vet ./...`과 `staticcheck ./...` 실행 (가능한 경우) +3. 수정된 `.go` 파일에 집중 +4. 즉시 리뷰 시작 + +## 리뷰 우선순위 + +### CRITICAL -- 보안 +- **SQL 인젝션**: `database/sql` 쿼리에서 문자열 연결 +- **커맨드 인젝션**: `os/exec`에서 검증되지 않은 입력 +- **경로 탐색**: `filepath.Clean` + 접두사 확인 없이 사용자 제어 파일 경로 +- **경쟁 조건**: 동기화 없이 공유 상태 +- **Unsafe 패키지**: 정당한 이유 없이 사용 +- **하드코딩된 비밀**: 소스의 API 키, 비밀번호 +- **안전하지 않은 TLS**: `InsecureSkipVerify: true` + +### CRITICAL -- 에러 처리 +- **무시된 에러**: `_`로 에러 폐기 +- **에러 래핑 누락**: `fmt.Errorf("context: %w", err)` 없이 `return err` +- **복구 가능한 에러에 Panic**: 에러 반환 사용 +- **errors.Is/As 누락**: `err == target` 대신 `errors.Is(err, target)` 사용 + +### HIGH -- 동시성 +- **고루틴 누수**: 취소 메커니즘 없음 (`context.Context` 사용) +- **버퍼 없는 채널 데드락**: 수신자 없이 전송 +- **sync.WaitGroup 누락**: 조율 없는 고루틴 +- **Mutex 오용**: `defer mu.Unlock()` 미사용 + +### HIGH -- 코드 품질 +- **큰 함수**: 50줄 초과 +- **깊은 중첩**: 4단계 초과 +- **비관용적**: 조기 반환 대신 `if/else` +- **패키지 레벨 변수**: 가변 전역 상태 +- **인터페이스 과다**: 사용되지 않는 추상화 정의 + +### MEDIUM -- 성능 +- **루프에서 문자열 연결**: `strings.Builder` 사용 +- **슬라이스 사전 할당 누락**: `make([]T, 0, cap)` +- **N+1 쿼리**: 루프에서 데이터베이스 쿼리 +- **불필요한 할당**: 핫 패스에서 객체 생성 + +### MEDIUM -- 모범 사례 +- **Context 우선**: `ctx context.Context`가 첫 번째 매개변수여야 함 +- **테이블 주도 테스트**: 테스트는 테이블 주도 패턴 사용 +- **에러 메시지**: 소문자, 구두점 없음 +- **패키지 네이밍**: 짧고, 소문자, 밑줄 없음 +- **루프에서 defer 호출**: 리소스 누적 위험 + +## 진단 커맨드 + +```bash +go vet ./... +staticcheck ./... +golangci-lint run +go build -race ./... +go test -race ./... +govulncheck ./... +``` + +## 승인 기준 + +- **승인**: CRITICAL 또는 HIGH 이슈 없음 +- **경고**: MEDIUM 이슈만 +- **차단**: CRITICAL 또는 HIGH 이슈 발견 diff --git a/docs/ko-KR/agents/planner.md b/docs/ko-KR/agents/planner.md new file mode 100644 index 00000000..4acad03c --- /dev/null +++ b/docs/ko-KR/agents/planner.md @@ -0,0 +1,119 @@ +--- +name: planner +description: 복잡한 기능 및 리팩토링을 위한 전문 계획 스페셜리스트. 기능 구현, 아키텍처 변경, 복잡한 리팩토링 요청 시 자동으로 활성화됩니다. +tools: ["Read", "Grep", "Glob"] +model: opus +--- + +포괄적이고 실행 가능한 구현 계획을 만드는 전문 계획 스페셜리스트입니다. + +## 역할 + +- 요구사항을 분석하고 상세한 구현 계획 작성 +- 복잡한 기능을 관리 가능한 단계로 분해 +- 의존성 및 잠재적 위험 식별 +- 최적의 구현 순서 제안 +- 엣지 케이스 및 에러 시나리오 고려 + +## 계획 프로세스 + +### 1. 요구사항 분석 +- 기능 요청을 완전히 이해 +- 필요시 명확한 질문 +- 성공 기준 식별 +- 가정 및 제약사항 나열 + +### 2. 아키텍처 검토 +- 기존 코드베이스 구조 분석 +- 영향받는 컴포넌트 식별 +- 유사한 구현 검토 +- 재사용 가능한 패턴 고려 + +### 3. 단계 분해 +다음을 포함한 상세 단계 작성: +- 명확하고 구체적인 액션 +- 파일 경로 및 위치 +- 단계 간 의존성 +- 예상 복잡도 +- 잠재적 위험 + +### 4. 구현 순서 +- 의존성별 우선순위 +- 관련 변경사항 그룹화 +- 컨텍스트 전환 최소화 +- 점진적 테스트 가능하게 + +## 계획 형식 + +```markdown +# 구현 계획: [기능명] + +## 개요 +[2-3문장 요약] + +## 요구사항 +- [요구사항 1] +- [요구사항 2] + +## 아키텍처 변경사항 +- [변경 1: 파일 경로와 설명] +- [변경 2: 파일 경로와 설명] + +## 구현 단계 + +### Phase 1: [페이즈 이름] +1. **[단계명]** (File: path/to/file.ts) + - Action: 수행할 구체적 액션 + - Why: 이 단계의 이유 + - Dependencies: 없음 / 단계 X 필요 + - Risk: Low/Medium/High + +### Phase 2: [페이즈 이름] +... + +## 테스트 전략 +- 단위 테스트: [테스트할 파일] +- 통합 테스트: [테스트할 흐름] +- E2E 테스트: [테스트할 사용자 여정] + +## 위험 및 완화 +- **위험**: [설명] + - 완화: [해결 방법] + +## 성공 기준 +- [ ] 기준 1 +- [ ] 기준 2 +``` + +## 모범 사례 + +1. **구체적으로** — 정확한 파일 경로, 함수명, 변수명 사용 +2. **엣지 케이스 고려** — 에러 시나리오, null 값, 빈 상태 생각 +3. **변경 최소화** — 재작성보다 기존 코드 확장 선호 +4. **패턴 유지** — 기존 프로젝트 컨벤션 따르기 +5. **테스트 가능하게** — 쉽게 테스트할 수 있도록 변경 구조화 +6. **점진적으로** — 각 단계가 검증 가능해야 함 +7. **결정 문서화** — 무엇만이 아닌 왜를 설명 + +## 리팩토링 계획 시 + +1. 코드 스멜과 기술 부채 식별 +2. 필요한 구체적 개선사항 나열 +3. 기존 기능 보존 +4. 가능하면 하위 호환 변경 생성 +5. 필요시 점진적 마이그레이션 계획 + +## 크기 조정 및 단계화 + +기능이 클 때, 독립적으로 전달 가능한 단계로 분리: + +- **Phase 1**: 최소 실행 가능 — 가치를 제공하는 가장 작은 단위 +- **Phase 2**: 핵심 경험 — 완전한 해피 패스 +- **Phase 3**: 엣지 케이스 — 에러 처리, 마감 +- **Phase 4**: 최적화 — 성능, 모니터링, 분석 + +각 Phase는 독립적으로 merge 가능해야 합니다. 모든 Phase가 완료되어야 작동하는 계획은 피하세요. + +--- + +**기억하세요**: 좋은 계획은 구체적이고, 실행 가능하며, 해피 패스와 엣지 케이스 모두를 고려합니다. 최고의 계획은 자신감 있고 점진적인 구현을 가능하게 합니다. diff --git a/docs/ko-KR/agents/refactor-cleaner.md b/docs/ko-KR/agents/refactor-cleaner.md new file mode 100644 index 00000000..f13db83c --- /dev/null +++ b/docs/ko-KR/agents/refactor-cleaner.md @@ -0,0 +1,85 @@ +--- +name: refactor-cleaner +description: 데드 코드 정리 및 통합 전문가. 미사용 코드, 중복 제거, 리팩토링에 사용하세요. 분석 도구(knip, depcheck, ts-prune)를 실행하여 데드 코드를 식별하고 안전하게 제거합니다. +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: sonnet +--- + +# 리팩토링 & 데드 코드 클리너 + +코드 정리와 통합에 집중하는 리팩토링 전문 에이전트입니다. 데드 코드, 중복, 미사용 export를 식별하고 제거하는 것이 목표입니다. + +## 핵심 책임 + +1. **데드 코드 감지** -- 미사용 코드, export, 의존성 찾기 +2. **중복 제거** -- 중복 코드 식별 및 통합 +3. **의존성 정리** -- 미사용 패키지와 import 제거 +4. **안전한 리팩토링** -- 변경이 기능을 깨뜨리지 않도록 보장 + +## 감지 커맨드 + +```bash +npx knip # 미사용 파일, export, 의존성 +npx depcheck # 미사용 npm 의존성 +npx ts-prune # 미사용 TypeScript export +npx eslint . --report-unused-disable-directives # 미사용 eslint 지시자 +``` + +## 워크플로우 + +### 1. 분석 +- 감지 도구를 병렬로 실행 +- 위험도별 분류: **SAFE** (미사용 export/의존성), **CAREFUL** (동적 import), **RISKY** (공개 API) + +### 2. 확인 +제거할 각 항목에 대해: +- 모든 참조를 grep (문자열 패턴을 통한 동적 import 포함) +- 공개 API의 일부인지 확인 +- git 히스토리에서 컨텍스트 확인 + +### 3. 안전하게 제거 +- SAFE 항목부터 시작 +- 한 번에 한 카테고리씩 제거: 의존성 → export → 파일 → 중복 +- 각 배치 후 테스트 실행 +- 각 배치 후 커밋 + +### 4. 중복 통합 +- 중복 컴포넌트/유틸리티 찾기 +- 최선의 구현 선택 (가장 완전하고, 가장 잘 테스트된) +- 모든 import 업데이트, 중복 삭제 +- 테스트 통과 확인 + +## 안전 체크리스트 + +제거 전: +- [ ] 감지 도구가 미사용 확인 +- [ ] Grep이 참조 없음 확인 (동적 포함) +- [ ] 공개 API의 일부가 아님 +- [ ] 제거 후 테스트 통과 + +각 배치 후: +- [ ] Build 성공 +- [ ] 테스트 통과 +- [ ] 설명적 메시지로 커밋 + +## 핵심 원칙 + +1. **작게 시작** -- 한 번에 한 카테고리 +2. **자주 테스트** -- 모든 배치 후 +3. **보수적으로** -- 확신이 없으면 제거하지 않기 +4. **문서화** -- 배치별 설명적 커밋 메시지 +5. **절대 제거 금지** -- 활발한 기능 개발 중 또는 배포 전 + +## 사용하지 말아야 할 때 + +- 활발한 기능 개발 중 +- 프로덕션 배포 직전 +- 적절한 테스트 커버리지 없이 +- 이해하지 못하는 코드에 + +## 성공 기준 + +- 모든 테스트 통과 +- Build 성공 +- 회귀 없음 +- 번들 크기 감소 diff --git a/docs/ko-KR/agents/security-reviewer.md b/docs/ko-KR/agents/security-reviewer.md new file mode 100644 index 00000000..49dcff92 --- /dev/null +++ b/docs/ko-KR/agents/security-reviewer.md @@ -0,0 +1,104 @@ +--- +name: security-reviewer +description: 보안 취약점 감지 및 수정 전문가. 사용자 입력 처리, 인증, API 엔드포인트, 민감한 데이터를 다루는 코드 작성 후 사용하세요. 시크릿, SSRF, 인젝션, 안전하지 않은 암호화, OWASP Top 10 취약점을 플래그합니다. +tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +model: sonnet +--- + +# 보안 리뷰어 + +웹 애플리케이션의 취약점을 식별하고 수정하는 보안 전문 에이전트입니다. 보안 문제가 프로덕션에 도달하기 전에 방지하는 것이 목표입니다. + +## 핵심 책임 + +1. **취약점 감지** — OWASP Top 10 및 일반적인 보안 문제 식별 +2. **시크릿 감지** — 하드코딩된 API 키, 비밀번호, 토큰 찾기 +3. **입력 유효성 검사** — 모든 사용자 입력이 적절히 소독되는지 확인 +4. **인증/인가** — 적절한 접근 제어 확인 +5. **의존성 보안** — 취약한 npm 패키지 확인 +6. **보안 모범 사례** — 안전한 코딩 패턴 강제 + +## 분석 커맨드 + +```bash +npm audit --audit-level=high +npx eslint . --plugin security +``` + +## 리뷰 워크플로우 + +### 1. 초기 스캔 +- `npm audit`, `eslint-plugin-security` 실행, 하드코딩된 시크릿 검색 +- 고위험 영역 검토: 인증, API 엔드포인트, DB 쿼리, 파일 업로드, 결제, 웹훅 + +### 2. OWASP Top 10 점검 +1. **인젝션** — 쿼리 매개변수화? 사용자 입력 소독? ORM 안전 사용? +2. **인증 취약** — 비밀번호 해시(bcrypt/argon2)? JWT 검증? 세션 안전? +3. **민감 데이터** — HTTPS 강제? 시크릿이 환경 변수? PII 암호화? 로그 소독? +4. **XXE** — XML 파서 안전 설정? 외부 엔터티 비활성화? +5. **접근 제어 취약** — 모든 라우트에 인증 확인? CORS 적절히 설정? +6. **잘못된 설정** — 기본 자격증명 변경? 프로덕션에서 디버그 모드 끔? 보안 헤더 설정? +7. **XSS** — 출력 이스케이프? CSP 설정? 프레임워크 자동 이스케이프? +8. **안전하지 않은 역직렬화** — 사용자 입력 안전하게 역직렬화? +9. **알려진 취약점** — 의존성 최신? npm audit 깨끗? +10. **불충분한 로깅** — 보안 이벤트 로깅? 알림 설정? + +### 3. 코드 패턴 리뷰 +다음 패턴 즉시 플래그: + +| 패턴 | 심각도 | 수정 | +|------|--------|------| +| 하드코딩된 시크릿 | CRITICAL | `process.env` 사용 | +| 사용자 입력으로 셸 커맨드 | CRITICAL | 안전한 API 또는 execFile 사용 | +| 문자열 연결 SQL | CRITICAL | 매개변수화된 쿼리 | +| `innerHTML = userInput` | HIGH | `textContent` 또는 DOMPurify 사용 | +| `fetch(userProvidedUrl)` | HIGH | 허용 도메인 화이트리스트 | +| 평문 비밀번호 비교 | CRITICAL | `bcrypt.compare()` 사용 | +| 라우트에 인증 검사 없음 | CRITICAL | 인증 미들웨어 추가 | +| 잠금 없는 잔액 확인 | CRITICAL | 트랜잭션에서 `FOR UPDATE` 사용 | +| Rate limiting 없음 | HIGH | `express-rate-limit` 추가 | +| 비밀번호/시크릿 로깅 | MEDIUM | 로그 출력 소독 | + +## 핵심 원칙 + +1. **심층 방어** — 여러 보안 계층 +2. **최소 권한** — 필요한 최소 권한 +3. **안전한 실패** — 에러가 데이터를 노출하지 않아야 함 +4. **입력 불신** — 모든 것을 검증하고 소독 +5. **정기 업데이트** — 의존성을 최신으로 유지 + +## 일반적인 오탐지 + +- `.env.example`의 환경 변수 (실제 시크릿이 아님) +- 테스트 파일의 테스트 자격증명 (명확히 표시된 경우) +- 공개 API 키 (실제로 공개 의도인 경우) +- 체크섬용 SHA256/MD5 (비밀번호용이 아님) + +**플래그 전에 항상 컨텍스트를 확인하세요.** + +## 긴급 대응 + +CRITICAL 취약점 발견 시: +1. 상세 보고서로 문서화 +2. 프로젝트 소유자에게 즉시 알림 +3. 안전한 코드 예제 제공 +4. 수정이 작동하는지 확인 +5. 자격증명 노출 시 시크릿 교체 + +## 실행 시점 + +**항상:** 새 API 엔드포인트, 인증 코드 변경, 사용자 입력 처리, DB 쿼리 변경, 파일 업로드, 결제 코드, 외부 API 연동, 의존성 업데이트. + +**즉시:** 프로덕션 인시던트, 의존성 CVE, 사용자 보안 보고, 주요 릴리스 전. + +## 성공 기준 + +- CRITICAL 이슈 없음 +- 모든 HIGH 이슈 해결 +- 코드에 시크릿 없음 +- 의존성 최신 +- 보안 체크리스트 완료 + +--- + +**기억하세요**: 보안은 선택 사항이 아닙니다. 하나의 취약점이 사용자에게 실제 금전적 손실을 줄 수 있습니다. 철저하게, 편집증적으로, 사전에 대응하세요. diff --git a/docs/ko-KR/agents/tdd-guide.md b/docs/ko-KR/agents/tdd-guide.md new file mode 100644 index 00000000..2ea68c4b --- /dev/null +++ b/docs/ko-KR/agents/tdd-guide.md @@ -0,0 +1,89 @@ +--- +name: tdd-guide +description: 테스트 주도 개발 전문가. 테스트 먼저 작성 방법론을 강제합니다. 새 기능 작성, 버그 수정, 코드 리팩토링 시 사용하세요. 80% 이상 테스트 커버리지를 보장합니다. +tools: ["Read", "Write", "Edit", "Bash", "Grep"] +model: sonnet +--- + +테스트 주도 개발(TDD) 전문가로서 모든 코드가 테스트 우선으로 개발되고 포괄적인 커버리지를 갖추도록 보장합니다. + +## 역할 + +- 테스트 먼저 작성 방법론 강제 +- Red-Green-Refactor 사이클 가이드 +- 80% 이상 테스트 커버리지 보장 +- 포괄적인 테스트 스위트 작성 (단위, 통합, E2E) +- 구현 전에 엣지 케이스 포착 + +## TDD 워크플로우 + +### 1. 테스트 먼저 작성 (RED) +기대 동작을 설명하는 실패하는 테스트 작성. + +### 2. 테스트 실행 -- 실패 확인 +```bash +npm test +``` + +### 3. 최소한의 구현 작성 (GREEN) +테스트를 통과하기에 충분한 코드만. + +### 4. 테스트 실행 -- 통과 확인 + +### 5. 리팩토링 (IMPROVE) +중복 제거, 이름 개선, 최적화 -- 테스트는 그린 유지. + +### 6. 커버리지 확인 +```bash +npm run test:coverage +# 필수: branches, functions, lines, statements 80% 이상 +``` + +## 필수 테스트 유형 + +| 유형 | 테스트 대상 | 시점 | +|------|------------|------| +| **단위** | 개별 함수를 격리하여 | 항상 | +| **통합** | API 엔드포인트, 데이터베이스 연산 | 항상 | +| **E2E** | 핵심 사용자 흐름 (Playwright) | 핵심 경로 | + +## 반드시 테스트해야 할 엣지 케이스 + +1. **Null/Undefined** 입력 +2. **빈** 배열/문자열 +3. **잘못된 타입** 전달 +4. **경계값** (최소/최대) +5. **에러 경로** (네트워크 실패, DB 에러) +6. **경쟁 조건** (동시 작업) +7. **대량 데이터** (10k+ 항목으로 성능) +8. **특수 문자** (유니코드, 이모지, SQL 문자) + +## 테스트 안티패턴 + +- 동작 대신 구현 세부사항(내부 상태) 테스트 +- 서로 의존하는 테스트 (공유 상태) +- 너무 적은 어설션 (아무것도 검증하지 않는 통과 테스트) +- 외부 의존성 목킹 안 함 (Supabase, Redis, OpenAI 등) + +## 품질 체크리스트 + +- [ ] 모든 공개 함수에 단위 테스트 +- [ ] 모든 API 엔드포인트에 통합 테스트 +- [ ] 핵심 사용자 흐름에 E2E 테스트 +- [ ] 엣지 케이스 커버 (null, empty, invalid) +- [ ] 에러 경로 테스트 (해피 패스만 아닌) +- [ ] 외부 의존성에 mock 사용 +- [ ] 테스트가 독립적 (공유 상태 없음) +- [ ] 어설션이 구체적이고 의미 있음 +- [ ] 커버리지 80% 이상 + +## Eval 주도 TDD 부록 + +TDD 흐름에 eval 주도 개발 통합: + +1. 구현 전에 capability + regression eval 정의. +2. 베이스라인 실행 및 실패 시그니처 캡처. +3. 최소한의 통과 변경 구현. +4. 테스트와 eval 재실행; pass@1과 pass@3 보고. + +릴리스 핵심 경로는 merge 전에 pass^3 안정성을 목표로 해야 합니다. diff --git a/docs/ko-KR/commands/build-fix.md b/docs/ko-KR/commands/build-fix.md new file mode 100644 index 00000000..c113cc80 --- /dev/null +++ b/docs/ko-KR/commands/build-fix.md @@ -0,0 +1,63 @@ +# Build 오류 수정 + +최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다. + +## 1단계: Build 시스템 감지 + +프로젝트의 build 도구를 식별하고 build를 실행합니다: + +| 식별 기준 | Build 명령어 | +|-----------|---------------| +| `package.json`에 `build` 스크립트 포함 | `npm run build` 또는 `pnpm build` | +| `tsconfig.json` (TypeScript 전용) | `npx tsc --noEmit` | +| `Cargo.toml` | `cargo build 2>&1` | +| `pom.xml` | `mvn compile` | +| `build.gradle` | `./gradlew compileJava` | +| `go.mod` | `go build ./...` | +| `pyproject.toml` | `python -m py_compile` 또는 `mypy .` | + +## 2단계: 오류 파싱 및 그룹화 + +1. Build 명령어를 실행하고 stderr를 캡처합니다 +2. 파일 경로별로 오류를 그룹화합니다 +3. 의존성 순서에 따라 정렬합니다 (import/타입 오류를 로직 오류보다 먼저 수정) +4. 진행 상황 추적을 위해 전체 오류 수를 셉니다 + +## 3단계: 수정 루프 (한 번에 하나의 오류씩) + +각 오류에 대해: + +1. **파일 읽기** — Read 도구를 사용하여 오류 전후 10줄의 컨텍스트를 확인합니다 +2. **진단** — 근본 원인을 식별합니다 (누락된 import, 잘못된 타입, 구문 오류) +3. **최소한으로 수정** — Edit 도구를 사용하여 오류를 해결하는 최소한의 변경을 적용합니다 +4. **Build 재실행** — 오류가 해결되었고 새로운 오류가 발생하지 않았는지 확인합니다 +5. **다음으로 이동** — 남은 오류를 계속 처리합니다 + +## 4단계: 안전장치 + +다음 경우 사용자에게 확인을 요청합니다: + +- 수정이 **해결하는 것보다 더 많은 오류를 발생**시키는 경우 +- **동일한 오류가 3번 시도 후에도 지속**되는 경우 (더 깊은 문제일 가능성) +- 수정에 **아키텍처 변경이 필요**한 경우 (단순 build 수정이 아님) +- Build 오류가 **누락된 의존성**에서 비롯된 경우 (`npm install`, `cargo add` 등이 필요) + +## 5단계: 요약 + +결과를 표시합니다: +- 수정된 오류 (파일 경로 포함) +- 남아있는 오류 (있는 경우) +- 새로 발생한 오류 (0이어야 함) +- 미해결 문제에 대한 다음 단계 제안 + +## 복구 전략 + +| 상황 | 조치 | +|-----------|--------| +| 모듈/import 누락 | 패키지가 설치되어 있는지 확인하고 설치 명령어를 제안합니다 | +| 타입 불일치 | 양쪽 타입 정의를 확인하고 더 좁은 타입을 수정합니다 | +| 순환 의존성 | import 그래프로 순환을 식별하고 분리를 제안합니다 | +| 버전 충돌 | `package.json` / `Cargo.toml`의 버전 제약 조건을 확인합니다 | +| Build 도구 설정 오류 | 설정 파일을 확인하고 정상 동작하는 기본값과 비교합니다 | + +안전을 위해 한 번에 하나의 오류씩 수정하세요. 리팩토링보다 최소한의 diff를 선호합니다. diff --git a/docs/ko-KR/commands/checkpoint.md b/docs/ko-KR/commands/checkpoint.md new file mode 100644 index 00000000..e8d5e578 --- /dev/null +++ b/docs/ko-KR/commands/checkpoint.md @@ -0,0 +1,74 @@ +# Checkpoint 명령어 + +워크플로우에서 checkpoint를 생성하거나 검증합니다. + +## 사용법 + +`/checkpoint [create|verify|list] [name]` + +## Checkpoint 생성 + +Checkpoint를 생성할 때: + +1. `/verify quick`를 실행하여 현재 상태가 깨끗한지 확인합니다 +2. Checkpoint 이름으로 git stash 또는 commit을 생성합니다 +3. `.claude/checkpoints.log`에 checkpoint를 기록합니다: + +```bash +echo "$(date +%Y-%m-%d-%H:%M) | $CHECKPOINT_NAME | $(git rev-parse --short HEAD)" >> .claude/checkpoints.log +``` + +4. Checkpoint 생성 완료를 보고합니다 + +## Checkpoint 검증 + +Checkpoint와 대조하여 검증할 때: + +1. 로그에서 checkpoint를 읽습니다 +2. 현재 상태를 checkpoint와 비교합니다: + - Checkpoint 이후 추가된 파일 + - Checkpoint 이후 수정된 파일 + - 현재와 당시의 테스트 통과율 + - 현재와 당시의 커버리지 + +3. 보고: +``` +CHECKPOINT COMPARISON: $NAME +============================ +Files changed: X +Tests: +Y passed / -Z failed +Coverage: +X% / -Y% +Build: [PASS/FAIL] +``` + +## Checkpoint 목록 + +모든 checkpoint를 다음 정보와 함께 표시합니다: +- 이름 +- 타임스탬프 +- Git SHA +- 상태 (current, behind, ahead) + +## 워크플로우 + +일반적인 checkpoint 흐름: + +``` +[시작] --> /checkpoint create "feature-start" + | +[구현] --> /checkpoint create "core-done" + | +[테스트] --> /checkpoint verify "core-done" + | +[리팩토링] --> /checkpoint create "refactor-done" + | +[PR] --> /checkpoint verify "feature-start" +``` + +## 인자 + +$ARGUMENTS: +- `create ` - 이름이 지정된 checkpoint를 생성합니다 +- `verify ` - 이름이 지정된 checkpoint와 검증합니다 +- `list` - 모든 checkpoint를 표시합니다 +- `clear` - 이전 checkpoint를 제거합니다 (최근 5개만 유지) diff --git a/docs/ko-KR/commands/code-review.md b/docs/ko-KR/commands/code-review.md new file mode 100644 index 00000000..3f4c115e --- /dev/null +++ b/docs/ko-KR/commands/code-review.md @@ -0,0 +1,40 @@ +# 코드 리뷰 + +커밋되지 않은 변경사항에 대한 포괄적인 보안 및 품질 리뷰를 수행합니다: + +1. 변경된 파일 목록 조회: git diff --name-only HEAD + +2. 각 변경된 파일에 대해 다음을 검사합니다: + +**보안 이슈 (CRITICAL):** +- 하드코딩된 인증 정보, API 키, 토큰 +- SQL 인젝션 취약점 +- XSS 취약점 +- 누락된 입력 유효성 검사 +- 안전하지 않은 의존성 +- 경로 탐색(Path Traversal) 위험 + +**코드 품질 (HIGH):** +- 50줄 초과 함수 +- 800줄 초과 파일 +- 4단계 초과 중첩 깊이 +- 누락된 에러 처리 +- console.log 문 +- TODO/FIXME 주석 +- 공개 API에 대한 JSDoc 누락 + +**모범 사례 (MEDIUM):** +- 변이(Mutation) 패턴 (불변 패턴을 사용하세요) +- 코드/주석의 이모지 사용 +- 새 코드에 대한 테스트 누락 +- 접근성(a11y) 문제 + +3. 다음을 포함한 보고서를 생성합니다: + - 심각도: CRITICAL, HIGH, MEDIUM, LOW + - 파일 위치 및 줄 번호 + - 이슈 설명 + - 수정 제안 + +4. CRITICAL 또는 HIGH 이슈가 발견되면 commit을 차단합니다 + +보안 취약점이 있는 코드는 절대 승인하지 마세요! diff --git a/docs/ko-KR/commands/e2e.md b/docs/ko-KR/commands/e2e.md new file mode 100644 index 00000000..74a36bd9 --- /dev/null +++ b/docs/ko-KR/commands/e2e.md @@ -0,0 +1,56 @@ +--- +description: Playwright로 E2E 테스트를 생성하고 실행합니다. 테스트 여정을 만들고, 테스트를 실행하며, 스크린샷/비디오/트레이스를 캡처하고, 아티팩트를 업로드합니다. +--- + +# E2E 커맨드 + +이 커맨드는 **e2e-runner** 에이전트를 호출하여 Playwright를 사용한 E2E 테스트를 생성, 유지, 실행합니다. + +## 이 커맨드가 하는 것 + +1. **테스트 여정 생성** - 사용자 흐름에 대한 Playwright 테스트 생성 +2. **E2E 테스트 실행** - 여러 브라우저에서 테스트 실행 +3. **아티팩트 캡처** - 실패 시 스크린샷, 비디오, 트레이스 +4. **결과 업로드** - HTML 보고서 및 JUnit XML +5. **불안정한 테스트 식별** - 불안정한 테스트를 격리 + +## 사용 시점 + +`/e2e`를 사용해야 할 때: +- 핵심 사용자 여정 테스트 (로그인, 거래, 결제) +- 다단계 흐름이 E2E로 작동하는지 검증 +- UI 인터랙션 및 네비게이션 테스트 +- 프론트엔드와 백엔드 간 통합 검증 +- 프로덕션 배포 준비 + +## 작동 방식 + +e2e-runner 에이전트가 수행하는 작업: + +1. **사용자 흐름 분석** 및 테스트 시나리오 식별 +2. Page Object Model 패턴을 사용한 **Playwright 테스트 생성** +3. 여러 브라우저(Chrome, Firefox, Safari)에서 **테스트 실행** +4. 스크린샷, 비디오, 트레이스로 **실패 캡처** +5. 결과와 아티팩트로 **보고서 생성** +6. **불안정한 테스트 식별** 및 수정 권장 + +## 모범 사례 + +**해야 할 것:** +- Page Object Model을 사용하여 유지보수성 향상 +- data-testid 속성을 셀렉터로 사용 +- 임의의 타임아웃 대신 API 응답을 대기 +- 핵심 사용자 여정을 E2E로 테스트 +- main에 merge하기 전에 테스트 실행 + +**하지 말아야 할 것:** +- 취약한 셀렉터 사용 (CSS 클래스는 변경될 수 있음) +- 구현 세부사항 테스트 +- 프로덕션에 대해 테스트 실행 +- 불안정한 테스트 무시 +- E2E로 모든 엣지 케이스 테스트 (단위 테스트 사용) + +## 관련 에이전트 + +이 커맨드는 `e2e-runner` 에이전트를 호출합니다: +`~/.claude/agents/e2e-runner.md` diff --git a/docs/ko-KR/commands/eval.md b/docs/ko-KR/commands/eval.md new file mode 100644 index 00000000..49f15df2 --- /dev/null +++ b/docs/ko-KR/commands/eval.md @@ -0,0 +1,48 @@ +# Eval 커맨드 + +평가 기반 개발 워크플로우를 관리합니다. + +## 사용법 + +`/eval [define|check|report|list] [feature-name]` + +## 평가 정의 + +`/eval define feature-name` + +새로운 평가 정의를 생성합니다: + +1. `.claude/evals/feature-name.md`에 템플릿 생성 +2. 사용자에게 구체적인 기준을 입력하도록 안내 + +## 평가 확인 + +`/eval check feature-name` + +기능에 대한 평가를 실행합니다: + +1. `.claude/evals/feature-name.md`에서 평가 정의 읽기 +2. 각 기능 평가: 기준 검증 시도, PASS/FAIL 기록 +3. 각 회귀 평가: 관련 테스트 실행, 기준선과 비교 +4. 현재 상태 보고 + +## 평가 보고 + +`/eval report feature-name` + +포괄적인 평가 보고서를 생성합니다. + +## 평가 목록 + +`/eval list` + +모든 평가 정의를 표시합니다. + +## 인자 + +$ARGUMENTS: +- `define ` - 새 평가 정의 생성 +- `check ` - 평가 실행 및 확인 +- `report ` - 전체 보고서 생성 +- `list` - 모든 평가 표시 +- `clean` - 오래된 평가 로그 제거 (최근 10회 유지) diff --git a/docs/ko-KR/commands/go-build.md b/docs/ko-KR/commands/go-build.md new file mode 100644 index 00000000..824240f8 --- /dev/null +++ b/docs/ko-KR/commands/go-build.md @@ -0,0 +1,38 @@ +--- +description: Go build 에러, go vet 경고, 린터 이슈를 점진적으로 수정합니다. 최소한의 정밀한 수정을 위해 go-build-resolver 에이전트를 호출합니다. +--- + +# Go Build and Fix + +이 커맨드는 **go-build-resolver** 에이전트를 호출하여 최소한의 변경으로 Go build 에러를 점진적으로 수정합니다. + +## 이 커맨드가 하는 것 + +1. **진단 실행**: `go build`, `go vet`, `staticcheck` 실행 +2. **에러 분석**: 파일별로 그룹화하고 심각도순 정렬 +3. **점진적 수정**: 한 번에 하나의 에러씩 +4. **각 수정 검증**: 각 변경 후 build 재실행 +5. **요약 보고**: 수정된 것과 남은 것 표시 + +## 사용 시점 + +`/go-build`를 사용해야 할 때: +- `go build ./...`가 에러로 실패할 때 +- `go vet ./...`가 이슈를 보고할 때 +- `golangci-lint run`이 경고를 보여줄 때 +- 모듈 의존성이 깨졌을 때 +- 변경사항을 pull한 후 build가 깨졌을 때 + +## 수정 전략 + +1. **Build 에러 먼저** - 코드가 컴파일되어야 함 +2. **Vet 경고 두 번째** - 의심스러운 구조 수정 +3. **Lint 경고 세 번째** - 스타일과 모범 사례 +4. **한 번에 하나씩** - 각 변경 검증 +5. **최소한의 변경** - 리팩토링이 아닌 수정만 + +## 관련 커맨드 + +- `/go-test` - build 성공 후 테스트 실행 +- `/go-review` - 코드 품질 리뷰 +- `/verify` - 전체 검증 루프 diff --git a/docs/ko-KR/commands/go-review.md b/docs/ko-KR/commands/go-review.md new file mode 100644 index 00000000..395f9f56 --- /dev/null +++ b/docs/ko-KR/commands/go-review.md @@ -0,0 +1,49 @@ +--- +description: 관용적 패턴, 동시성 안전성, 에러 처리, 보안에 대한 포괄적인 Go 코드 리뷰. go-reviewer 에이전트를 호출합니다. +--- + +# Go 코드 리뷰 + +이 커맨드는 **go-reviewer** 에이전트를 호출하여 Go 전용 포괄적 코드 리뷰를 수행합니다. + +## 이 커맨드가 하는 것 + +1. **Go 변경사항 식별**: `git diff`로 수정된 `.go` 파일 찾기 +2. **정적 분석 실행**: `go vet`, `staticcheck`, `golangci-lint` 실행 +3. **보안 스캔**: SQL 인젝션, 커맨드 인젝션, 레이스 컨디션 검사 +4. **동시성 리뷰**: 고루틴 안전성, 채널 사용, 뮤텍스 패턴 분석 +5. **관용적 Go 검사**: Go 컨벤션과 모범 사례 준수 여부 확인 +6. **보고서 생성**: 심각도별 이슈 분류 + +## 사용 시점 + +`/go-review`를 사용해야 할 때: +- Go 코드를 작성하거나 수정한 후 +- Go 변경사항을 커밋하기 전 +- Go 코드가 포함된 PR 리뷰 시 +- 새 Go 코드베이스에 온보딩할 때 + +## 리뷰 카테고리 + +### CRITICAL (반드시 수정) +- SQL/커맨드 인젝션 취약점 +- 동기화 없는 레이스 컨디션 +- 고루틴 누수 +- 하드코딩된 인증 정보 + +### HIGH (수정 권장) +- 컨텍스트 없는 에러 래핑 누락 +- 에러 반환 대신 panic 사용 +- 컨텍스트 전파 누락 +- 데드락을 유발하는 버퍼 없는 채널 + +### MEDIUM (고려) +- 비관용적 코드 패턴 +- 공개 항목에 godoc 주석 누락 +- 비효율적인 문자열 연결 + +## 관련 커맨드 + +- `/go-test` - 먼저 테스트 통과 확인 +- `/go-build` - build 에러 발생 시 +- `/code-review` - Go 외 일반적인 관심사항 diff --git a/docs/ko-KR/commands/go-test.md b/docs/ko-KR/commands/go-test.md new file mode 100644 index 00000000..a30a1648 --- /dev/null +++ b/docs/ko-KR/commands/go-test.md @@ -0,0 +1,48 @@ +--- +description: Go용 TDD 워크플로우 강제. 테이블 기반 테스트를 먼저 작성한 후 구현. go test -cover로 80% 이상 커버리지 검증. +--- + +# Go TDD 커맨드 + +이 커맨드는 관용적 Go 테스팅 패턴을 사용하여 Go 코드에 테스트 주도 개발 방법론을 강제합니다. + +## 이 커맨드가 하는 것 + +1. **타입/인터페이스 정의**: 함수 시그니처를 먼저 스캐폴딩 +2. **테이블 기반 테스트 작성**: 포괄적인 테스트 케이스 생성 (RED) +3. **테스트 실행**: 올바른 이유로 테스트가 실패하는지 확인 +4. **코드 구현**: 통과하기 위한 최소한의 코드 작성 (GREEN) +5. **리팩토링**: 테스트를 통과시키면서 개선 +6. **커버리지 확인**: 80% 이상 커버리지 확보 + +## 사용 시점 + +`/go-test`를 사용해야 할 때: +- 새로운 Go 함수 구현 +- 기존 코드에 테스트 커버리지 추가 +- 버그 수정 (실패하는 테스트를 먼저 작성) +- 핵심 비즈니스 로직 구현 + +## TDD 사이클 + +``` +RED → 실패하는 테이블 기반 테스트 작성 +GREEN → 통과하기 위한 최소한의 코드 구현 +REFACTOR → 코드 개선, 테스트는 통과 유지 +REPEAT → 다음 테스트 케이스 +``` + +## 커버리지 목표 + +| 코드 유형 | 목표 | +|-----------|------| +| 핵심 비즈니스 로직 | 100% | +| 공개 API | 90%+ | +| 일반 코드 | 80%+ | +| 생성된 코드 | 제외 | + +## 관련 커맨드 + +- `/go-build` - build 에러 수정 +- `/go-review` - 구현 후 코드 리뷰 +- `/verify` - 전체 검증 루프 diff --git a/docs/ko-KR/commands/learn.md b/docs/ko-KR/commands/learn.md new file mode 100644 index 00000000..3ded15a0 --- /dev/null +++ b/docs/ko-KR/commands/learn.md @@ -0,0 +1,47 @@ +# /learn - 재사용 가능한 패턴 추출 + +현재 세션을 분석하고 스킬로 저장할 가치가 있는 패턴을 추출합니다. + +## 트리거 + +세션 중 중요한 문제를 해결했을 때 `/learn`을 실행합니다. + +## 추출 대상 + +다음을 찾습니다: + +1. **에러 해결 패턴** + - 어떤 에러가 발생했는가? + - 근본 원인은 무엇이었는가? + - 무엇이 해결했는가? + - 유사한 에러에 재사용 가능한가? + +2. **디버깅 기법** + - 직관적이지 않은 디버깅 단계 + - 효과적인 도구 조합 + - 진단 패턴 + +3. **해결 방법** + - 라이브러리 특이 사항 + - API 제한 사항 + - 버전별 수정 사항 + +4. **프로젝트 특화 패턴** + - 발견된 코드베이스 컨벤션 + - 내려진 아키텍처 결정 + - 통합 패턴 + +## 프로세스 + +1. 세션에서 추출 가능한 패턴 검토 +2. 가장 가치 있고 재사용 가능한 인사이트 식별 +3. 스킬 파일 초안 작성 +4. 저장 전 사용자 확인 요청 +5. `~/.claude/skills/learned/`에 저장 + +## 참고 사항 + +- 사소한 수정은 추출하지 않기 (오타, 단순 구문 에러) +- 일회성 이슈는 추출하지 않기 (특정 API 장애 등) +- 향후 세션에서 시간을 절약할 수 있는 패턴에 집중 +- 스킬은 집중적으로 - 스킬당 하나의 패턴 diff --git a/docs/ko-KR/commands/orchestrate.md b/docs/ko-KR/commands/orchestrate.md new file mode 100644 index 00000000..37c73151 --- /dev/null +++ b/docs/ko-KR/commands/orchestrate.md @@ -0,0 +1,59 @@ +# Orchestrate 커맨드 + +복잡한 작업을 위한 순차적 에이전트 워크플로우입니다. + +## 사용법 + +`/orchestrate [workflow-type] [task-description]` + +## 워크플로우 유형 + +### feature +전체 기능 구현 워크플로우: +``` +planner -> tdd-guide -> code-reviewer -> security-reviewer +``` + +### bugfix +버그 조사 및 수정 워크플로우: +``` +planner -> tdd-guide -> code-reviewer +``` + +### refactor +안전한 리팩토링 워크플로우: +``` +architect -> code-reviewer -> tdd-guide +``` + +### security +보안 중심 리뷰: +``` +security-reviewer -> code-reviewer -> architect +``` + +## 실행 패턴 + +워크플로우의 각 에이전트에 대해: + +1. 이전 에이전트의 컨텍스트로 **에이전트 호출** +2. 구조화된 핸드오프 문서로 **출력 수집** +3. 체인의 **다음 에이전트에 전달** +4. **결과를 종합**하여 최종 보고서 작성 + +## 인자 + +$ARGUMENTS: +- `feature ` - 전체 기능 워크플로우 +- `bugfix ` - 버그 수정 워크플로우 +- `refactor ` - 리팩토링 워크플로우 +- `security ` - 보안 리뷰 워크플로우 +- `custom ` - 사용자 정의 에이전트 순서 + +## 팁 + +1. 복잡한 기능에는 **planner부터 시작** +2. merge 전에는 **항상 code-reviewer 포함** +3. 인증/결제/개인정보에는 **security-reviewer 사용** +4. **핸드오프는 간결하게** - 다음 에이전트에 필요한 것에 집중 +5. 필요하면 에이전트 사이에 **검증 실행** diff --git a/docs/ko-KR/commands/plan.md b/docs/ko-KR/commands/plan.md new file mode 100644 index 00000000..512e0d93 --- /dev/null +++ b/docs/ko-KR/commands/plan.md @@ -0,0 +1,50 @@ +--- +description: 요구사항을 재확인하고, 위험을 평가하며, 단계별 구현 계획을 작성합니다. 코드를 건드리기 전에 사용자 확인을 기다립니다. +--- + +# Plan 커맨드 + +이 커맨드는 **planner** 에이전트를 호출하여 코드를 작성하기 전에 포괄적인 구현 계획을 만듭니다. + +## 이 커맨드가 하는 것 + +1. **요구사항 재확인** - 무엇을 만들어야 하는지 명확히 함 +2. **위험 식별** - 잠재적 이슈와 차단 요소 도출 +3. **단계별 계획 작성** - 구현을 단계로 분해 +4. **확인 대기** - 진행 전 반드시 사용자 승인을 받음 + +## 사용 시점 + +`/plan`을 사용해야 할 때: +- 새 기능 시작 +- 중요한 아키텍처 변경 +- 복잡한 리팩토링 작업 +- 여러 파일/컴포넌트에 영향을 미칠 때 +- 요구사항이 불명확하거나 모호할 때 + +## 작동 방식 + +planner 에이전트가 수행하는 작업: + +1. 요청을 **분석**하고 요구사항을 명확하게 재확인 +2. 구체적이고 실행 가능한 단계로 **분해** +3. 컴포넌트 간 **의존성 식별** +4. **위험 평가** 및 잠재적 차단 요소 파악 +5. **복잡도 추정** (High/Medium/Low) +6. 계획을 **제시**하고 명시적 확인을 **대기** + +## 중요 참고 사항 + +**핵심**: planner 에이전트는 "yes"나 "proceed" 같은 긍정적 응답으로 명시적으로 확인하기 전까지 코드를 **작성하지 않습니다.** + +## 다른 커맨드와의 연계 + +계획 수립 후: +- `/tdd`를 사용하여 테스트 주도 개발로 구현 +- `/build-fix`로 build 에러 발생 시 수정 +- `/code-review`로 완성된 구현 리뷰 + +## 관련 에이전트 + +이 커맨드는 `planner` 에이전트를 호출합니다: +`~/.claude/agents/planner.md` diff --git a/docs/ko-KR/commands/refactor-clean.md b/docs/ko-KR/commands/refactor-clean.md new file mode 100644 index 00000000..6923c8f7 --- /dev/null +++ b/docs/ko-KR/commands/refactor-clean.md @@ -0,0 +1,42 @@ +# Refactor Clean + +사용하지 않는 코드를 안전하게 식별하고 매 단계마다 테스트 검증을 수행합니다. + +## 1단계: 사용하지 않는 코드 감지 + +프로젝트 유형에 따라 분석 도구 실행: + +| 도구 | 감지 대상 | 커맨드 | +|------|----------|--------| +| knip | 미사용 exports, 파일, 의존성 | `npx knip` | +| depcheck | 미사용 npm 의존성 | `npx depcheck` | +| ts-prune | 미사용 TypeScript exports | `npx ts-prune` | +| vulture | 미사용 Python 코드 | `vulture src/` | +| deadcode | 미사용 Go 코드 | `deadcode ./...` | + +## 2단계: 결과 분류 + +안전 등급별 분류: + +| 등급 | 예시 | 조치 | +|------|------|------| +| **안전** | 미사용 유틸리티, 테스트 헬퍼, 내부 함수 | 확신을 가지고 삭제 | +| **주의** | 컴포넌트, API 라우트, 미들웨어 | 동적 임포트나 외부 소비자가 없는지 확인 | +| **위험** | 설정 파일, 엔트리 포인트, 타입 정의 | 건드리기 전에 조사 | + +## 3단계: 안전한 삭제 루프 + +각 안전 항목에 대해: + +1. **전체 테스트 실행** — 기준선 확립 (모두 통과) +2. **사용하지 않는 코드 삭제** — Edit 도구로 정밀 삭제 +3. **테스트 재실행** — 깨진 것이 없는지 확인 +4. **테스트 실패 시** — 즉시 `git checkout -- `로 되돌리고 건너뜀 +5. **테스트 통과 시** — 다음 항목으로 이동 + +## 규칙 + +- **테스트를 먼저 실행하지 않고 절대 삭제하지 않기** +- **한 번에 하나씩 삭제** — 원자적 변경으로 롤백 용이 +- **확실하지 않으면 건너뛰기** — 프로덕션을 깨뜨리는 것보다 사용하지 않는 코드를 유지하는 것이 나음 +- **정리하면서 리팩토링하지 않기** — 관심사 분리 (먼저 정리, 나중에 리팩토링) diff --git a/docs/ko-KR/commands/setup-pm.md b/docs/ko-KR/commands/setup-pm.md new file mode 100644 index 00000000..5e9d54b1 --- /dev/null +++ b/docs/ko-KR/commands/setup-pm.md @@ -0,0 +1,35 @@ +--- +description: 선호하는 패키지 매니저(npm/pnpm/yarn/bun) 설정 +disable-model-invocation: true +--- + +# 패키지 매니저 설정 + +프로젝트 또는 전역으로 선호하는 패키지 매니저를 설정합니다. + +## 사용법 + +```bash +# 현재 패키지 매니저 감지 +node scripts/setup-package-manager.js --detect + +# 전역 설정 +node scripts/setup-package-manager.js --global pnpm + +# 프로젝트 설정 +node scripts/setup-package-manager.js --project bun + +# 사용 가능한 패키지 매니저 목록 +node scripts/setup-package-manager.js --list +``` + +## 감지 우선순위 + +패키지 매니저를 결정할 때 다음 순서로 확인합니다: + +1. **환경 변수**: `CLAUDE_PACKAGE_MANAGER` +2. **프로젝트 설정**: `.claude/package-manager.json` +3. **package.json**: `packageManager` 필드 +4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb +5. **전역 설정**: `~/.claude/package-manager.json` +6. **폴백**: 사용 가능한 첫 번째 패키지 매니저 (pnpm > bun > yarn > npm) diff --git a/docs/ko-KR/commands/tdd.md b/docs/ko-KR/commands/tdd.md new file mode 100644 index 00000000..197bd8bf --- /dev/null +++ b/docs/ko-KR/commands/tdd.md @@ -0,0 +1,56 @@ +--- +description: 테스트 주도 개발 워크플로우 강제. 인터페이스를 스캐폴딩하고, 테스트를 먼저 생성한 후 통과할 최소한의 코드를 구현합니다. 80% 이상 커버리지를 보장합니다. +--- + +# TDD 커맨드 + +이 커맨드는 **tdd-guide** 에이전트를 호출하여 테스트 주도 개발 방법론을 강제합니다. + +## 이 커맨드가 하는 것 + +1. **인터페이스 스캐폴딩** - 타입/인터페이스를 먼저 정의 +2. **테스트 먼저 생성** - 실패하는 테스트 작성 (RED) +3. **최소한의 코드 구현** - 통과하기에 충분한 코드만 작성 (GREEN) +4. **리팩토링** - 테스트를 통과시키면서 코드 개선 (REFACTOR) +5. **커버리지 확인** - 80% 이상 테스트 커버리지 보장 + +## 사용 시점 + +`/tdd`를 사용해야 할 때: +- 새 기능 구현 +- 새 함수/컴포넌트 추가 +- 버그 수정 (버그를 재현하는 테스트를 먼저 작성) +- 기존 코드 리팩토링 +- 핵심 비즈니스 로직 구현 + +## TDD 사이클 + +``` +RED → GREEN → REFACTOR → REPEAT + +RED: 실패하는 테스트 작성 +GREEN: 통과할 최소한의 코드 작성 +REFACTOR: 코드 개선, 테스트 계속 통과 유지 +REPEAT: 다음 기능/시나리오 +``` + +## 모범 사례 + +**해야 할 것:** +- 구현 전에 테스트를 먼저 작성 +- 각 변경 후 테스트 실행 및 실패 확인 +- 테스트를 통과하기 위한 최소한의 코드 작성 +- 테스트가 통과한 후에만 리팩토링 +- 80% 이상 커버리지 목표 (핵심 코드는 100%) + +**하지 말아야 할 것:** +- 테스트 전에 구현 작성 +- RED 단계 건너뛰기 +- 한 번에 너무 많은 코드 작성 +- 실패하는 테스트 무시 +- 구현 세부사항 테스트 (동작을 테스트) + +## 관련 에이전트 + +이 커맨드는 `tdd-guide` 에이전트를 호출합니다: +`~/.claude/agents/tdd-guide.md` diff --git a/docs/ko-KR/commands/test-coverage.md b/docs/ko-KR/commands/test-coverage.md new file mode 100644 index 00000000..ec507e46 --- /dev/null +++ b/docs/ko-KR/commands/test-coverage.md @@ -0,0 +1,42 @@ +# 테스트 커버리지 + +테스트 커버리지를 분석하고, 갭을 식별하며, 80% 이상 커버리지 달성을 위해 누락된 테스트를 생성합니다. + +## 1단계: 테스트 프레임워크 감지 + +| 지표 | 커버리지 커맨드 | +|------|----------------| +| `jest.config.*` 또는 `package.json` jest | `npx jest --coverage --coverageReporters=json-summary` | +| `vitest.config.*` | `npx vitest run --coverage` | +| `pytest.ini` / `pyproject.toml` pytest | `pytest --cov=src --cov-report=json` | +| `Cargo.toml` | `cargo llvm-cov --json` | +| `go.mod` | `go test -coverprofile=coverage.out ./...` | + +## 2단계: 커버리지 보고서 분석 + +1. 커버리지 커맨드 실행 +2. 출력 파싱 (JSON 요약 또는 터미널 출력) +3. **80% 미만인 파일**을 최저순으로 정렬하여 목록화 +4. 각 미달 파일에서 미테스트 함수, 누락된 분기 커버리지, 데드 코드 식별 + +## 3단계: 누락된 테스트 생성 + +우선순위에 따라 테스트 생성: + +1. **Happy path** — 유효한 입력의 핵심 기능 +2. **에러 처리** — 잘못된 입력, 누락된 데이터, 네트워크 실패 +3. **엣지 케이스** — 빈 배열, null/undefined, 경계값 (0, -1, MAX_INT) +4. **분기 커버리지** — 각 if/else, switch case, 삼항 연산자 + +## 4단계: 검증 + +1. 전체 테스트 실행 — 모든 테스트 통과 확인 +2. 커버리지 재실행 — 개선 확인 +3. 여전히 80% 미만이면 나머지 갭에 대해 3단계 반복 + +## 집중 영역 + +- 복잡한 분기가 있는 함수 (높은 순환 복잡도) +- 에러 핸들러와 catch 블록 +- 코드베이스 전반에서 사용되는 유틸리티 함수 +- API 엔드포인트 핸들러 (요청 → 응답 흐름) diff --git a/docs/ko-KR/commands/update-codemaps.md b/docs/ko-KR/commands/update-codemaps.md new file mode 100644 index 00000000..4f71bd8f --- /dev/null +++ b/docs/ko-KR/commands/update-codemaps.md @@ -0,0 +1,29 @@ +# 코드맵 업데이트 + +코드베이스 구조를 분석하고 토큰 효율적인 아키텍처 문서를 생성합니다. + +## 1단계: 프로젝트 구조 스캔 + +1. 프로젝트 유형 식별 (모노레포, 단일 앱, 라이브러리, 마이크로서비스) +2. 모든 소스 디렉토리 찾기 (src/, lib/, app/, packages/) +3. 엔트리 포인트 매핑 (main.ts, index.ts, app.py, main.go 등) + +## 2단계: 코드맵 생성 + +`docs/CODEMAPS/`에 코드맵 생성 또는 업데이트: + +| 파일 | 내용 | +|------|------| +| `architecture.md` | 상위 시스템 다이어그램, 서비스 경계, 데이터 흐름 | +| `backend.md` | API 라우트, 미들웨어 체인, 서비스 → 리포지토리 매핑 | +| `frontend.md` | 페이지 트리, 컴포넌트 계층, 상태 관리 흐름 | +| `data.md` | 데이터베이스 테이블, 관계, 마이그레이션 히스토리 | +| `dependencies.md` | 외부 서비스, 서드파티 통합, 공유 라이브러리 | + +## 팁 + +- **구현 세부사항이 아닌 상위 구조**에 집중 +- 전체 코드 블록 대신 **파일 경로와 함수 시그니처** 사용 +- 효율적인 컨텍스트 로딩을 위해 각 코드맵을 **1000 토큰 미만**으로 유지 +- 장황한 설명 대신 데이터 흐름에 ASCII 다이어그램 사용 +- 주요 기능 추가 또는 리팩토링 세션 후 실행 diff --git a/docs/ko-KR/commands/update-docs.md b/docs/ko-KR/commands/update-docs.md new file mode 100644 index 00000000..49b3062f --- /dev/null +++ b/docs/ko-KR/commands/update-docs.md @@ -0,0 +1,33 @@ +# 문서 업데이트 + +문서를 코드베이스와 동기화하고, 원본 소스 파일에서 생성합니다. + +## 1단계: 원본 소스 식별 + +| 소스 | 생성 대상 | +|------|----------| +| `package.json` scripts | 사용 가능한 커맨드 참조 | +| `.env.example` | 환경 변수 문서 | +| `openapi.yaml` / 라우트 파일 | API 엔드포인트 참조 | +| 소스 코드 exports | 공개 API 문서 | +| `Dockerfile` / `docker-compose.yml` | 인프라 설정 문서 | + +## 2단계: 스크립트 참조 생성 + +1. `package.json` (또는 `Makefile`, `Cargo.toml`, `pyproject.toml`) 읽기 +2. 모든 스크립트/커맨드와 설명 추출 +3. 참조 테이블 생성 + +## 3단계: 환경 변수 문서 생성 + +1. `.env.example` 읽기 +2. 모든 변수와 용도 추출 +3. 필수 vs 선택으로 분류 +4. 예상 형식과 유효 값 문서화 + +## 규칙 + +- **단일 원본**: 항상 코드에서 생성, 생성된 섹션을 수동으로 편집하지 않기 +- **수동 섹션 보존**: 생성된 섹션만 업데이트; 수기 작성 내용은 그대로 유지 +- **생성된 콘텐츠 표시**: 생성된 섹션 주변에 `` 마커 사용 +- **요청 없이 문서 생성하지 않기**: 커맨드가 명시적으로 요청한 경우에만 새 문서 파일 생성 diff --git a/docs/ko-KR/commands/verify.md b/docs/ko-KR/commands/verify.md new file mode 100644 index 00000000..41ca5cbe --- /dev/null +++ b/docs/ko-KR/commands/verify.md @@ -0,0 +1,59 @@ +# 검증 커맨드 + +현재 코드베이스 상태에 대한 포괄적인 검증을 실행합니다. + +## 지시사항 + +정확히 이 순서로 검증을 실행하세요: + +1. **Build 검사** + - 이 프로젝트의 build 커맨드 실행 + - 실패 시 에러를 보고하고 중단 + +2. **타입 검사** + - TypeScript/타입 체커 실행 + - 모든 에러를 파일:줄번호로 보고 + +3. **Lint 검사** + - 린터 실행 + - 경고와 에러 보고 + +4. **테스트 실행** + - 모든 테스트 실행 + - 통과/실패 수 보고 + - 커버리지 비율 보고 + +5. **Console.log 감사** + - 소스 파일에서 console.log 검색 + - 위치 보고 + +6. **Git 상태** + - 커밋되지 않은 변경사항 표시 + - 마지막 커밋 이후 수정된 파일 표시 + +## 출력 + +간결한 검증 보고서를 생성합니다: + +``` +VERIFICATION: [PASS/FAIL] + +Build: [OK/FAIL] +Types: [OK/X errors] +Lint: [OK/X issues] +Tests: [X/Y passed, Z% coverage] +Secrets: [OK/X found] +Logs: [OK/X console.logs] + +Ready for PR: [YES/NO] +``` + +치명적 이슈가 있으면 수정 제안과 함께 목록화합니다. + +## 인자 + +$ARGUMENTS: +- `quick` - build + 타입만 +- `full` - 모든 검사 (기본값) +- `pre-commit` - 커밋에 관련된 검사 +- `pre-pr` - 전체 검사 + 보안 스캔 From b3f8206d4708b6f6d5a4502a2cdac5709a10e32d Mon Sep 17 00:00:00 2001 From: hahmee Date: Tue, 10 Mar 2026 13:29:00 +0900 Subject: [PATCH 4/9] docs(ko-KR): add Korean translation for skills - 15 skill categories (17 files): coding-standards, tdd-workflow, frontend-patterns, backend-patterns, security-review (2 files), postgres-patterns, verification-loop, continuous-learning, continuous-learning-v2, eval-harness, iterative-retrieval, strategic-compact, golang-patterns, golang-testing, clickhouse-io, project-guidelines-example --- docs/ko-KR/skills/backend-patterns/SKILL.md | 598 +++++++++++++++ docs/ko-KR/skills/clickhouse-io/SKILL.md | 439 +++++++++++ docs/ko-KR/skills/coding-standards/SKILL.md | 530 +++++++++++++ .../skills/continuous-learning-v2/SKILL.md | 363 +++++++++ .../ko-KR/skills/continuous-learning/SKILL.md | 119 +++ docs/ko-KR/skills/eval-harness/SKILL.md | 270 +++++++ docs/ko-KR/skills/frontend-patterns/SKILL.md | 642 ++++++++++++++++ docs/ko-KR/skills/golang-patterns/SKILL.md | 674 ++++++++++++++++ docs/ko-KR/skills/golang-testing/SKILL.md | 720 ++++++++++++++++++ .../ko-KR/skills/iterative-retrieval/SKILL.md | 211 +++++ docs/ko-KR/skills/postgres-patterns/SKILL.md | 147 ++++ .../project-guidelines-example/SKILL.md | 349 +++++++++ docs/ko-KR/skills/security-review/SKILL.md | 495 ++++++++++++ .../cloud-infrastructure-security.md | 361 +++++++++ docs/ko-KR/skills/strategic-compact/SKILL.md | 103 +++ docs/ko-KR/skills/tdd-workflow/SKILL.md | 410 ++++++++++ docs/ko-KR/skills/verification-loop/SKILL.md | 126 +++ 17 files changed, 6557 insertions(+) create mode 100644 docs/ko-KR/skills/backend-patterns/SKILL.md create mode 100644 docs/ko-KR/skills/clickhouse-io/SKILL.md create mode 100644 docs/ko-KR/skills/coding-standards/SKILL.md create mode 100644 docs/ko-KR/skills/continuous-learning-v2/SKILL.md create mode 100644 docs/ko-KR/skills/continuous-learning/SKILL.md create mode 100644 docs/ko-KR/skills/eval-harness/SKILL.md create mode 100644 docs/ko-KR/skills/frontend-patterns/SKILL.md create mode 100644 docs/ko-KR/skills/golang-patterns/SKILL.md create mode 100644 docs/ko-KR/skills/golang-testing/SKILL.md create mode 100644 docs/ko-KR/skills/iterative-retrieval/SKILL.md create mode 100644 docs/ko-KR/skills/postgres-patterns/SKILL.md create mode 100644 docs/ko-KR/skills/project-guidelines-example/SKILL.md create mode 100644 docs/ko-KR/skills/security-review/SKILL.md create mode 100644 docs/ko-KR/skills/security-review/cloud-infrastructure-security.md create mode 100644 docs/ko-KR/skills/strategic-compact/SKILL.md create mode 100644 docs/ko-KR/skills/tdd-workflow/SKILL.md create mode 100644 docs/ko-KR/skills/verification-loop/SKILL.md diff --git a/docs/ko-KR/skills/backend-patterns/SKILL.md b/docs/ko-KR/skills/backend-patterns/SKILL.md new file mode 100644 index 00000000..5118b328 --- /dev/null +++ b/docs/ko-KR/skills/backend-patterns/SKILL.md @@ -0,0 +1,598 @@ +--- +name: backend-patterns +description: Node.js, Express, Next.js API 라우트를 위한 백엔드 아키텍처 패턴, API 설계, 데이터베이스 최적화 및 서버 사이드 모범 사례. +origin: ECC +--- + +# 백엔드 개발 패턴 + +확장 가능한 서버 사이드 애플리케이션을 위한 백엔드 아키텍처 패턴과 모범 사례. + +## 활성화 시점 + +- REST 또는 GraphQL API 엔드포인트를 설계할 때 +- Repository, Service 또는 Controller 레이어를 구현할 때 +- 데이터베이스 쿼리를 최적화할 때 (N+1, 인덱싱, 커넥션 풀링) +- 캐싱을 추가할 때 (Redis, 인메모리, HTTP 캐시 헤더) +- 백그라운드 작업이나 비동기 처리를 설정할 때 +- API를 위한 에러 처리 및 유효성 검사를 구조화할 때 +- 미들웨어를 구축할 때 (인증, 로깅, 요청 제한) + +## API 설계 패턴 + +### RESTful API 구조 + +```typescript +// ✅ Resource-based URLs +GET /api/markets # List resources +GET /api/markets/:id # Get single resource +POST /api/markets # Create resource +PUT /api/markets/:id # Replace resource +PATCH /api/markets/:id # Update resource +DELETE /api/markets/:id # Delete resource + +// ✅ Query parameters for filtering, sorting, pagination +GET /api/markets?status=active&sort=volume&limit=20&offset=0 +``` + +### Repository 패턴 + +```typescript +// Abstract data access logic +interface MarketRepository { + findAll(filters?: MarketFilters): Promise + findById(id: string): Promise + create(data: CreateMarketDto): Promise + update(id: string, data: UpdateMarketDto): Promise + delete(id: string): Promise +} + +class SupabaseMarketRepository implements MarketRepository { + async findAll(filters?: MarketFilters): Promise { + let query = supabase.from('markets').select('*') + + if (filters?.status) { + query = query.eq('status', filters.status) + } + + if (filters?.limit) { + query = query.limit(filters.limit) + } + + const { data, error } = await query + + if (error) throw new Error(error.message) + return data + } + + // Other methods... +} +``` + +### Service 레이어 패턴 + +```typescript +// Business logic separated from data access +class MarketService { + constructor(private marketRepo: MarketRepository) {} + + async searchMarkets(query: string, limit: number = 10): Promise { + // Business logic + const embedding = await generateEmbedding(query) + const results = await this.vectorSearch(embedding, limit) + + // Fetch full data + const markets = await this.marketRepo.findByIds(results.map(r => r.id)) + + // Sort by similarity + return markets.sort((a, b) => { + const scoreA = results.find(r => r.id === a.id)?.score || 0 + const scoreB = results.find(r => r.id === b.id)?.score || 0 + return scoreA - scoreB + }) + } + + private async vectorSearch(embedding: number[], limit: number) { + // Vector search implementation + } +} +``` + +### 미들웨어 패턴 + +```typescript +// Request/response processing pipeline +export function withAuth(handler: NextApiHandler): NextApiHandler { + return async (req, res) => { + const token = req.headers.authorization?.replace('Bearer ', '') + + if (!token) { + return res.status(401).json({ error: 'Unauthorized' }) + } + + try { + const user = await verifyToken(token) + req.user = user + return handler(req, res) + } catch (error) { + return res.status(401).json({ error: 'Invalid token' }) + } + } +} + +// Usage +export default withAuth(async (req, res) => { + // Handler has access to req.user +}) +``` + +## 데이터베이스 패턴 + +### 쿼리 최적화 + +```typescript +// ✅ GOOD: Select only needed columns +const { data } = await supabase + .from('markets') + .select('id, name, status, volume') + .eq('status', 'active') + .order('volume', { ascending: false }) + .limit(10) + +// ❌ BAD: Select everything +const { data } = await supabase + .from('markets') + .select('*') +``` + +### N+1 쿼리 방지 + +```typescript +// ❌ BAD: N+1 query problem +const markets = await getMarkets() +for (const market of markets) { + market.creator = await getUser(market.creator_id) // N queries +} + +// ✅ GOOD: Batch fetch +const markets = await getMarkets() +const creatorIds = markets.map(m => m.creator_id) +const creators = await getUsers(creatorIds) // 1 query +const creatorMap = new Map(creators.map(c => [c.id, c])) + +markets.forEach(market => { + market.creator = creatorMap.get(market.creator_id) +}) +``` + +### 트랜잭션 패턴 + +```typescript +async function createMarketWithPosition( + marketData: CreateMarketDto, + positionData: CreatePositionDto +) { + // Use Supabase transaction + const { data, error } = await supabase.rpc('create_market_with_position', { + market_data: marketData, + position_data: positionData + }) + + if (error) throw new Error('Transaction failed') + return data +} + +// SQL function in Supabase +CREATE OR REPLACE FUNCTION create_market_with_position( + market_data jsonb, + position_data jsonb +) +RETURNS jsonb +LANGUAGE plpgsql +AS $$ +BEGIN + -- Start transaction automatically + INSERT INTO markets VALUES (market_data); + INSERT INTO positions VALUES (position_data); + RETURN jsonb_build_object('success', true); +EXCEPTION + WHEN OTHERS THEN + -- Rollback happens automatically + RETURN jsonb_build_object('success', false, 'error', SQLERRM); +END; +$$; +``` + +## 캐싱 전략 + +### Redis 캐싱 레이어 + +```typescript +class CachedMarketRepository implements MarketRepository { + constructor( + private baseRepo: MarketRepository, + private redis: RedisClient + ) {} + + async findById(id: string): Promise { + // Check cache first + const cached = await this.redis.get(`market:${id}`) + + if (cached) { + return JSON.parse(cached) + } + + // Cache miss - fetch from database + const market = await this.baseRepo.findById(id) + + if (market) { + // Cache for 5 minutes + await this.redis.setex(`market:${id}`, 300, JSON.stringify(market)) + } + + return market + } + + async invalidateCache(id: string): Promise { + await this.redis.del(`market:${id}`) + } +} +``` + +### Cache-Aside 패턴 + +```typescript +async function getMarketWithCache(id: string): Promise { + const cacheKey = `market:${id}` + + // Try cache + const cached = await redis.get(cacheKey) + if (cached) return JSON.parse(cached) + + // Cache miss - fetch from DB + const market = await db.markets.findUnique({ where: { id } }) + + if (!market) throw new Error('Market not found') + + // Update cache + await redis.setex(cacheKey, 300, JSON.stringify(market)) + + return market +} +``` + +## 에러 처리 패턴 + +### 중앙화된 에러 핸들러 + +```typescript +class ApiError extends Error { + constructor( + public statusCode: number, + public message: string, + public isOperational = true + ) { + super(message) + Object.setPrototypeOf(this, ApiError.prototype) + } +} + +export function errorHandler(error: unknown, req: Request): Response { + if (error instanceof ApiError) { + return NextResponse.json({ + success: false, + error: error.message + }, { status: error.statusCode }) + } + + if (error instanceof z.ZodError) { + return NextResponse.json({ + success: false, + error: 'Validation failed', + details: error.errors + }, { status: 400 }) + } + + // Log unexpected errors + console.error('Unexpected error:', error) + + return NextResponse.json({ + success: false, + error: 'Internal server error' + }, { status: 500 }) +} + +// Usage +export async function GET(request: Request) { + try { + const data = await fetchData() + return NextResponse.json({ success: true, data }) + } catch (error) { + return errorHandler(error, request) + } +} +``` + +### 지수 백오프를 이용한 재시도 + +```typescript +async function fetchWithRetry( + fn: () => Promise, + maxRetries = 3 +): Promise { + let lastError: Error + + for (let i = 0; i < maxRetries; i++) { + try { + return await fn() + } catch (error) { + lastError = error as Error + + if (i < maxRetries - 1) { + // Exponential backoff: 1s, 2s, 4s + const delay = Math.pow(2, i) * 1000 + await new Promise(resolve => setTimeout(resolve, delay)) + } + } + } + + throw lastError! +} + +// Usage +const data = await fetchWithRetry(() => fetchFromAPI()) +``` + +## 인증 및 인가 + +### JWT 토큰 검증 + +```typescript +import jwt from 'jsonwebtoken' + +interface JWTPayload { + userId: string + email: string + role: 'admin' | 'user' +} + +export function verifyToken(token: string): JWTPayload { + try { + const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload + return payload + } catch (error) { + throw new ApiError(401, 'Invalid token') + } +} + +export async function requireAuth(request: Request) { + const token = request.headers.get('authorization')?.replace('Bearer ', '') + + if (!token) { + throw new ApiError(401, 'Missing authorization token') + } + + return verifyToken(token) +} + +// Usage in API route +export async function GET(request: Request) { + const user = await requireAuth(request) + + const data = await getDataForUser(user.userId) + + return NextResponse.json({ success: true, data }) +} +``` + +### 역할 기반 접근 제어 + +```typescript +type Permission = 'read' | 'write' | 'delete' | 'admin' + +interface User { + id: string + role: 'admin' | 'moderator' | 'user' +} + +const rolePermissions: Record = { + admin: ['read', 'write', 'delete', 'admin'], + moderator: ['read', 'write', 'delete'], + user: ['read', 'write'] +} + +export function hasPermission(user: User, permission: Permission): boolean { + return rolePermissions[user.role].includes(permission) +} + +export function requirePermission(permission: Permission) { + return (handler: (request: Request, user: User) => Promise) => { + return async (request: Request) => { + const user = await requireAuth(request) + + if (!hasPermission(user, permission)) { + throw new ApiError(403, 'Insufficient permissions') + } + + return handler(request, user) + } + } +} + +// Usage - HOF wraps the handler +export const DELETE = requirePermission('delete')( + async (request: Request, user: User) => { + // Handler receives authenticated user with verified permission + return new Response('Deleted', { status: 200 }) + } +) +``` + +## 요청 제한 + +### 간단한 인메모리 요청 제한기 + +```typescript +class RateLimiter { + private requests = new Map() + + async checkLimit( + identifier: string, + maxRequests: number, + windowMs: number + ): Promise { + const now = Date.now() + const requests = this.requests.get(identifier) || [] + + // Remove old requests outside window + const recentRequests = requests.filter(time => now - time < windowMs) + + if (recentRequests.length >= maxRequests) { + return false // Rate limit exceeded + } + + // Add current request + recentRequests.push(now) + this.requests.set(identifier, recentRequests) + + return true + } +} + +const limiter = new RateLimiter() + +export async function GET(request: Request) { + const ip = request.headers.get('x-forwarded-for') || 'unknown' + + const allowed = await limiter.checkLimit(ip, 100, 60000) // 100 req/min + + if (!allowed) { + return NextResponse.json({ + error: 'Rate limit exceeded' + }, { status: 429 }) + } + + // Continue with request +} +``` + +## 백그라운드 작업 및 큐 + +### 간단한 큐 패턴 + +```typescript +class JobQueue { + private queue: T[] = [] + private processing = false + + async add(job: T): Promise { + this.queue.push(job) + + if (!this.processing) { + this.process() + } + } + + private async process(): Promise { + this.processing = true + + while (this.queue.length > 0) { + const job = this.queue.shift()! + + try { + await this.execute(job) + } catch (error) { + console.error('Job failed:', error) + } + } + + this.processing = false + } + + private async execute(job: T): Promise { + // Job execution logic + } +} + +// Usage for indexing markets +interface IndexJob { + marketId: string +} + +const indexQueue = new JobQueue() + +export async function POST(request: Request) { + const { marketId } = await request.json() + + // Add to queue instead of blocking + await indexQueue.add({ marketId }) + + return NextResponse.json({ success: true, message: 'Job queued' }) +} +``` + +## 로깅 및 모니터링 + +### 구조화된 로깅 + +```typescript +interface LogContext { + userId?: string + requestId?: string + method?: string + path?: string + [key: string]: unknown +} + +class Logger { + log(level: 'info' | 'warn' | 'error', message: string, context?: LogContext) { + const entry = { + timestamp: new Date().toISOString(), + level, + message, + ...context + } + + console.log(JSON.stringify(entry)) + } + + info(message: string, context?: LogContext) { + this.log('info', message, context) + } + + warn(message: string, context?: LogContext) { + this.log('warn', message, context) + } + + error(message: string, error: Error, context?: LogContext) { + this.log('error', message, { + ...context, + error: error.message, + stack: error.stack + }) + } +} + +const logger = new Logger() + +// Usage +export async function GET(request: Request) { + const requestId = crypto.randomUUID() + + logger.info('Fetching markets', { + requestId, + method: 'GET', + path: '/api/markets' + }) + + try { + const markets = await fetchMarkets() + return NextResponse.json({ success: true, data: markets }) + } catch (error) { + logger.error('Failed to fetch markets', error as Error, { requestId }) + return NextResponse.json({ error: 'Internal error' }, { status: 500 }) + } +} +``` + +**기억하세요**: 백엔드 패턴은 확장 가능하고 유지보수 가능한 서버 사이드 애플리케이션을 가능하게 합니다. 복잡도 수준에 맞는 패턴을 선택하세요. diff --git a/docs/ko-KR/skills/clickhouse-io/SKILL.md b/docs/ko-KR/skills/clickhouse-io/SKILL.md new file mode 100644 index 00000000..736667b8 --- /dev/null +++ b/docs/ko-KR/skills/clickhouse-io/SKILL.md @@ -0,0 +1,439 @@ +--- +name: clickhouse-io +description: 고성능 분석 워크로드를 위한 ClickHouse 데이터베이스 패턴, 쿼리 최적화, 분석 및 데이터 엔지니어링 모범 사례. +origin: ECC +--- + +# ClickHouse 분석 패턴 + +고성능 분석 및 데이터 엔지니어링을 위한 ClickHouse 전용 패턴. + +## 활성화 시점 + +- ClickHouse 테이블 스키마 설계 시 (MergeTree 엔진 선택) +- 분석 쿼리 작성 시 (집계, 윈도우 함수, 조인) +- 쿼리 성능 최적화 시 (파티션 프루닝, 프로젝션, 구체화된 뷰) +- 대량 데이터 수집 시 (배치 삽입, Kafka 통합) +- PostgreSQL/MySQL에서 ClickHouse로 분석 마이그레이션 시 +- 실시간 대시보드 또는 시계열 분석 구현 시 + +## 개요 + +ClickHouse는 온라인 분석 처리(OLAP)를 위한 컬럼 지향 데이터베이스 관리 시스템(DBMS)입니다. 대규모 데이터셋에 대한 빠른 분석 쿼리에 최적화되어 있습니다. + +**주요 특징:** +- 컬럼 지향 저장소 +- 데이터 압축 +- 병렬 쿼리 실행 +- 분산 쿼리 +- 실시간 분석 + +## 테이블 설계 패턴 + +### MergeTree 엔진 (가장 일반적) + +```sql +CREATE TABLE markets_analytics ( + date Date, + market_id String, + market_name String, + volume UInt64, + trades UInt32, + unique_traders UInt32, + avg_trade_size Float64, + created_at DateTime +) ENGINE = MergeTree() +PARTITION BY toYYYYMM(date) +ORDER BY (date, market_id) +SETTINGS index_granularity = 8192; +``` + +### ReplacingMergeTree (중복 제거) + +```sql +-- For data that may have duplicates (e.g., from multiple sources) +CREATE TABLE user_events ( + event_id String, + user_id String, + event_type String, + timestamp DateTime, + properties String +) ENGINE = ReplacingMergeTree() +PARTITION BY toYYYYMM(timestamp) +ORDER BY (user_id, event_id, timestamp) +PRIMARY KEY (user_id, event_id); +``` + +### AggregatingMergeTree (사전 집계) + +```sql +-- For maintaining aggregated metrics +CREATE TABLE market_stats_hourly ( + hour DateTime, + market_id String, + total_volume AggregateFunction(sum, UInt64), + total_trades AggregateFunction(count, UInt32), + unique_users AggregateFunction(uniq, String) +) ENGINE = AggregatingMergeTree() +PARTITION BY toYYYYMM(hour) +ORDER BY (hour, market_id); + +-- Query aggregated data +SELECT + hour, + market_id, + sumMerge(total_volume) AS volume, + countMerge(total_trades) AS trades, + uniqMerge(unique_users) AS users +FROM market_stats_hourly +WHERE hour >= toStartOfHour(now() - INTERVAL 24 HOUR) +GROUP BY hour, market_id +ORDER BY hour DESC; +``` + +## 쿼리 최적화 패턴 + +### 효율적인 필터링 + +```sql +-- ✅ GOOD: Use indexed columns first +SELECT * +FROM markets_analytics +WHERE date >= '2025-01-01' + AND market_id = 'market-123' + AND volume > 1000 +ORDER BY date DESC +LIMIT 100; + +-- ❌ BAD: Filter on non-indexed columns first +SELECT * +FROM markets_analytics +WHERE volume > 1000 + AND market_name LIKE '%election%' + AND date >= '2025-01-01'; +``` + +### 집계 + +```sql +-- ✅ GOOD: Use ClickHouse-specific aggregation functions +SELECT + toStartOfDay(created_at) AS day, + market_id, + sum(volume) AS total_volume, + count() AS total_trades, + uniq(trader_id) AS unique_traders, + avg(trade_size) AS avg_size +FROM trades +WHERE created_at >= today() - INTERVAL 7 DAY +GROUP BY day, market_id +ORDER BY day DESC, total_volume DESC; + +-- ✅ Use quantile for percentiles (more efficient than percentile) +SELECT + quantile(0.50)(trade_size) AS median, + quantile(0.95)(trade_size) AS p95, + quantile(0.99)(trade_size) AS p99 +FROM trades +WHERE created_at >= now() - INTERVAL 1 HOUR; +``` + +### 윈도우 함수 + +```sql +-- Calculate running totals +SELECT + date, + market_id, + volume, + sum(volume) OVER ( + PARTITION BY market_id + ORDER BY date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS cumulative_volume +FROM markets_analytics +WHERE date >= today() - INTERVAL 30 DAY +ORDER BY market_id, date; +``` + +## 데이터 삽입 패턴 + +### 배치 삽입 (권장) + +```typescript +import { ClickHouse } from 'clickhouse' + +const clickhouse = new ClickHouse({ + url: process.env.CLICKHOUSE_URL, + port: 8123, + basicAuth: { + username: process.env.CLICKHOUSE_USER, + password: process.env.CLICKHOUSE_PASSWORD + } +}) + +// ✅ Batch insert (efficient) +async function bulkInsertTrades(trades: Trade[]) { + const values = trades.map(trade => `( + '${trade.id}', + '${trade.market_id}', + '${trade.user_id}', + ${trade.amount}, + '${trade.timestamp.toISOString()}' + )`).join(',') + + await clickhouse.query(` + INSERT INTO trades (id, market_id, user_id, amount, timestamp) + VALUES ${values} + `).toPromise() +} + +// ❌ Individual inserts (slow) +async function insertTrade(trade: Trade) { + // Don't do this in a loop! + await clickhouse.query(` + INSERT INTO trades VALUES ('${trade.id}', ...) + `).toPromise() +} +``` + +### 스트리밍 삽입 + +```typescript +// For continuous data ingestion +import { createWriteStream } from 'fs' +import { pipeline } from 'stream/promises' + +async function streamInserts() { + const stream = clickhouse.insert('trades').stream() + + for await (const batch of dataSource) { + stream.write(batch) + } + + await stream.end() +} +``` + +## 구체화된 뷰 + +### 실시간 집계 + +```sql +-- Create materialized view for hourly stats +CREATE MATERIALIZED VIEW market_stats_hourly_mv +TO market_stats_hourly +AS SELECT + toStartOfHour(timestamp) AS hour, + market_id, + sumState(amount) AS total_volume, + countState() AS total_trades, + uniqState(user_id) AS unique_users +FROM trades +GROUP BY hour, market_id; + +-- Query the materialized view +SELECT + hour, + market_id, + sumMerge(total_volume) AS volume, + countMerge(total_trades) AS trades, + uniqMerge(unique_users) AS users +FROM market_stats_hourly +WHERE hour >= now() - INTERVAL 24 HOUR +GROUP BY hour, market_id; +``` + +## 성능 모니터링 + +### 쿼리 성능 + +```sql +-- Check slow queries +SELECT + query_id, + user, + query, + query_duration_ms, + read_rows, + read_bytes, + memory_usage +FROM system.query_log +WHERE type = 'QueryFinish' + AND query_duration_ms > 1000 + AND event_time >= now() - INTERVAL 1 HOUR +ORDER BY query_duration_ms DESC +LIMIT 10; +``` + +### 테이블 통계 + +```sql +-- Check table sizes +SELECT + database, + table, + formatReadableSize(sum(bytes)) AS size, + sum(rows) AS rows, + max(modification_time) AS latest_modification +FROM system.parts +WHERE active +GROUP BY database, table +ORDER BY sum(bytes) DESC; +``` + +## 일반적인 분석 쿼리 + +### 시계열 분석 + +```sql +-- Daily active users +SELECT + toDate(timestamp) AS date, + uniq(user_id) AS daily_active_users +FROM events +WHERE timestamp >= today() - INTERVAL 30 DAY +GROUP BY date +ORDER BY date; + +-- Retention analysis +SELECT + signup_date, + countIf(days_since_signup = 0) AS day_0, + countIf(days_since_signup = 1) AS day_1, + countIf(days_since_signup = 7) AS day_7, + countIf(days_since_signup = 30) AS day_30 +FROM ( + SELECT + user_id, + min(toDate(timestamp)) AS signup_date, + toDate(timestamp) AS activity_date, + dateDiff('day', signup_date, activity_date) AS days_since_signup + FROM events + GROUP BY user_id, activity_date +) +GROUP BY signup_date +ORDER BY signup_date DESC; +``` + +### 퍼널 분석 + +```sql +-- Conversion funnel +SELECT + countIf(step = 'viewed_market') AS viewed, + countIf(step = 'clicked_trade') AS clicked, + countIf(step = 'completed_trade') AS completed, + round(clicked / viewed * 100, 2) AS view_to_click_rate, + round(completed / clicked * 100, 2) AS click_to_completion_rate +FROM ( + SELECT + user_id, + session_id, + event_type AS step + FROM events + WHERE event_date = today() +) +GROUP BY session_id; +``` + +### 코호트 분석 + +```sql +-- User cohorts by signup month +SELECT + toStartOfMonth(signup_date) AS cohort, + toStartOfMonth(activity_date) AS month, + dateDiff('month', cohort, month) AS months_since_signup, + count(DISTINCT user_id) AS active_users +FROM ( + SELECT + user_id, + min(toDate(timestamp)) OVER (PARTITION BY user_id) AS signup_date, + toDate(timestamp) AS activity_date + FROM events +) +GROUP BY cohort, month, months_since_signup +ORDER BY cohort, months_since_signup; +``` + +## 데이터 파이프라인 패턴 + +### ETL 패턴 + +```typescript +// Extract, Transform, Load +async function etlPipeline() { + // 1. Extract from source + const rawData = await extractFromPostgres() + + // 2. Transform + const transformed = rawData.map(row => ({ + date: new Date(row.created_at).toISOString().split('T')[0], + market_id: row.market_slug, + volume: parseFloat(row.total_volume), + trades: parseInt(row.trade_count) + })) + + // 3. Load to ClickHouse + await bulkInsertToClickHouse(transformed) +} + +// Run periodically +setInterval(etlPipeline, 60 * 60 * 1000) // Every hour +``` + +### 변경 데이터 캡처 (CDC) + +```typescript +// Listen to PostgreSQL changes and sync to ClickHouse +import { Client } from 'pg' + +const pgClient = new Client({ connectionString: process.env.DATABASE_URL }) + +pgClient.query('LISTEN market_updates') + +pgClient.on('notification', async (msg) => { + const update = JSON.parse(msg.payload) + + await clickhouse.insert('market_updates', [ + { + market_id: update.id, + event_type: update.operation, // INSERT, UPDATE, DELETE + timestamp: new Date(), + data: JSON.stringify(update.new_data) + } + ]) +}) +``` + +## 모범 사례 + +### 1. 파티셔닝 전략 +- 시간별 파티셔닝 (보통 월 또는 일) +- 파티션이 너무 많은 것 방지 (성능 영향) +- 파티션 키에 DATE 타입 사용 + +### 2. 정렬 키 +- 가장 자주 필터링되는 컬럼을 먼저 배치 +- 카디널리티 고려 (높은 카디널리티 먼저) +- 정렬이 압축에 영향을 미침 + +### 3. 데이터 타입 +- 가장 작은 적절한 타입 사용 (UInt32 vs UInt64) +- 반복되는 문자열에 LowCardinality 사용 +- 범주형 데이터에 Enum 사용 + +### 4. 피해야 할 것 +- SELECT * (컬럼을 명시) +- FINAL (쿼리 전에 데이터를 병합) +- 너무 많은 JOIN (분석을 위해 비정규화) +- 작은 빈번한 삽입 (배치 처리) + +### 5. 모니터링 +- 쿼리 성능 추적 +- 디스크 사용량 모니터링 +- 병합 작업 확인 +- 슬로우 쿼리 로그 검토 + +**기억하세요**: ClickHouse는 분석 워크로드에 탁월합니다. 쿼리 패턴에 맞게 테이블을 설계하고, 배치 삽입을 사용하며, 실시간 집계를 위해 구체화된 뷰를 활용하세요. diff --git a/docs/ko-KR/skills/coding-standards/SKILL.md b/docs/ko-KR/skills/coding-standards/SKILL.md new file mode 100644 index 00000000..e3e708f8 --- /dev/null +++ b/docs/ko-KR/skills/coding-standards/SKILL.md @@ -0,0 +1,530 @@ +--- +name: coding-standards +description: TypeScript, JavaScript, React, Node.js 개발을 위한 범용 코딩 표준, 모범 사례 및 패턴. +origin: ECC +--- + +# 코딩 표준 및 모범 사례 + +모든 프로젝트에 적용 가능한 범용 코딩 표준. + +## 활성화 시점 + +- 새 프로젝트 또는 모듈을 시작할 때 +- 코드 품질 및 유지보수성을 검토할 때 +- 기존 코드를 컨벤션에 맞게 리팩터링할 때 +- 네이밍, 포맷팅 또는 구조적 일관성을 적용할 때 +- 린팅, 포맷팅 또는 타입 검사 규칙을 설정할 때 +- 새 기여자에게 코딩 컨벤션을 안내할 때 + +## 코드 품질 원칙 + +### 1. 가독성 우선 +- 코드는 작성보다 읽히는 횟수가 더 많다 +- 명확한 변수 및 함수 이름 사용 +- 주석보다 자기 문서화 코드를 선호 +- 일관된 포맷팅 유지 + +### 2. KISS (Keep It Simple, Stupid) +- 동작하는 가장 단순한 해결책 +- 과도한 엔지니어링 지양 +- 조기 최적화 금지 +- 이해하기 쉬운 코드 > 영리한 코드 + +### 3. DRY (Don't Repeat Yourself) +- 공통 로직을 함수로 추출 +- 재사용 가능한 컴포넌트 생성 +- 모듈 간 유틸리티 공유 +- 복사-붙여넣기 프로그래밍 지양 + +### 4. YAGNI (You Aren't Gonna Need It) +- 필요하기 전에 기능을 만들지 않기 +- 추측에 의한 일반화 지양 +- 필요할 때만 복잡성 추가 +- 단순하게 시작하고 필요할 때 리팩터링 + +## TypeScript/JavaScript 표준 + +### 변수 네이밍 + +```typescript +// ✅ GOOD: Descriptive names +const marketSearchQuery = 'election' +const isUserAuthenticated = true +const totalRevenue = 1000 + +// ❌ BAD: Unclear names +const q = 'election' +const flag = true +const x = 1000 +``` + +### 함수 네이밍 + +```typescript +// ✅ GOOD: Verb-noun pattern +async function fetchMarketData(marketId: string) { } +function calculateSimilarity(a: number[], b: number[]) { } +function isValidEmail(email: string): boolean { } + +// ❌ BAD: Unclear or noun-only +async function market(id: string) { } +function similarity(a, b) { } +function email(e) { } +``` + +### 불변성 패턴 (필수) + +```typescript +// ✅ ALWAYS use spread operator +const updatedUser = { + ...user, + name: 'New Name' +} + +const updatedArray = [...items, newItem] + +// ❌ NEVER mutate directly +user.name = 'New Name' // BAD +items.push(newItem) // BAD +``` + +### 에러 처리 + +```typescript +// ✅ GOOD: Comprehensive error handling +async function fetchData(url: string) { + try { + const response = await fetch(url) + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`) + } + + return await response.json() + } catch (error) { + console.error('Fetch failed:', error) + throw new Error('Failed to fetch data') + } +} + +// ❌ BAD: No error handling +async function fetchData(url) { + const response = await fetch(url) + return response.json() +} +``` + +### Async/Await 모범 사례 + +```typescript +// ✅ GOOD: Parallel execution when possible +const [users, markets, stats] = await Promise.all([ + fetchUsers(), + fetchMarkets(), + fetchStats() +]) + +// ❌ BAD: Sequential when unnecessary +const users = await fetchUsers() +const markets = await fetchMarkets() +const stats = await fetchStats() +``` + +### 타입 안전성 + +```typescript +// ✅ GOOD: Proper types +interface Market { + id: string + name: string + status: 'active' | 'resolved' | 'closed' + created_at: Date +} + +function getMarket(id: string): Promise { + // Implementation +} + +// ❌ BAD: Using 'any' +function getMarket(id: any): Promise { + // Implementation +} +``` + +## React 모범 사례 + +### 컴포넌트 구조 + +```typescript +// ✅ GOOD: Functional component with types +interface ButtonProps { + children: React.ReactNode + onClick: () => void + disabled?: boolean + variant?: 'primary' | 'secondary' +} + +export function Button({ + children, + onClick, + disabled = false, + variant = 'primary' +}: ButtonProps) { + return ( + + ) +} + +// ❌ BAD: No types, unclear structure +export function Button(props) { + return +} +``` + +### 커스텀 Hook + +```typescript +// ✅ GOOD: Reusable custom hook +export function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value) + }, delay) + + return () => clearTimeout(handler) + }, [value, delay]) + + return debouncedValue +} + +// Usage +const debouncedQuery = useDebounce(searchQuery, 500) +``` + +### 상태 관리 + +```typescript +// ✅ GOOD: Proper state updates +const [count, setCount] = useState(0) + +// Functional update for state based on previous state +setCount(prev => prev + 1) + +// ❌ BAD: Direct state reference +setCount(count + 1) // Can be stale in async scenarios +``` + +### 조건부 렌더링 + +```typescript +// ✅ GOOD: Clear conditional rendering +{isLoading && } +{error && } +{data && } + +// ❌ BAD: Ternary hell +{isLoading ? : error ? : data ? : null} +``` + +## API 설계 표준 + +### REST API 컨벤션 + +``` +GET /api/markets # List all markets +GET /api/markets/:id # Get specific market +POST /api/markets # Create new market +PUT /api/markets/:id # Update market (full) +PATCH /api/markets/:id # Update market (partial) +DELETE /api/markets/:id # Delete market + +# Query parameters for filtering +GET /api/markets?status=active&limit=10&offset=0 +``` + +### 응답 형식 + +```typescript +// ✅ GOOD: Consistent response structure +interface ApiResponse { + success: boolean + data?: T + error?: string + meta?: { + total: number + page: number + limit: number + } +} + +// Success response +return NextResponse.json({ + success: true, + data: markets, + meta: { total: 100, page: 1, limit: 10 } +}) + +// Error response +return NextResponse.json({ + success: false, + error: 'Invalid request' +}, { status: 400 }) +``` + +### 입력 유효성 검사 + +```typescript +import { z } from 'zod' + +// ✅ GOOD: Schema validation +const CreateMarketSchema = z.object({ + name: z.string().min(1).max(200), + description: z.string().min(1).max(2000), + endDate: z.string().datetime(), + categories: z.array(z.string()).min(1) +}) + +export async function POST(request: Request) { + const body = await request.json() + + try { + const validated = CreateMarketSchema.parse(body) + // Proceed with validated data + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json({ + success: false, + error: 'Validation failed', + details: error.errors + }, { status: 400 }) + } + } +} +``` + +## 파일 구성 + +### 프로젝트 구조 + +``` +src/ +├── app/ # Next.js App Router +│ ├── api/ # API routes +│ ├── markets/ # Market pages +│ └── (auth)/ # Auth pages (route groups) +├── components/ # React components +│ ├── ui/ # Generic UI components +│ ├── forms/ # Form components +│ └── layouts/ # Layout components +├── hooks/ # Custom React hooks +├── lib/ # Utilities and configs +│ ├── api/ # API clients +│ ├── utils/ # Helper functions +│ └── constants/ # Constants +├── types/ # TypeScript types +└── styles/ # Global styles +``` + +### 파일 네이밍 + +``` +components/Button.tsx # PascalCase for components +hooks/useAuth.ts # camelCase with 'use' prefix +lib/formatDate.ts # camelCase for utilities +types/market.types.ts # camelCase with .types suffix +``` + +## 주석 및 문서화 + +### 주석을 작성해야 하는 경우 + +```typescript +// ✅ GOOD: Explain WHY, not WHAT +// Use exponential backoff to avoid overwhelming the API during outages +const delay = Math.min(1000 * Math.pow(2, retryCount), 30000) + +// Deliberately using mutation here for performance with large arrays +items.push(newItem) + +// ❌ BAD: Stating the obvious +// Increment counter by 1 +count++ + +// Set name to user's name +name = user.name +``` + +### 공개 API를 위한 JSDoc + +```typescript +/** + * Searches markets using semantic similarity. + * + * @param query - Natural language search query + * @param limit - Maximum number of results (default: 10) + * @returns Array of markets sorted by similarity score + * @throws {Error} If OpenAI API fails or Redis unavailable + * + * @example + * ```typescript + * const results = await searchMarkets('election', 5) + * console.log(results[0].name) // "Trump vs Biden" + * ``` + */ +export async function searchMarkets( + query: string, + limit: number = 10 +): Promise { + // Implementation +} +``` + +## 성능 모범 사례 + +### 메모이제이션 + +```typescript +import { useMemo, useCallback } from 'react' + +// ✅ GOOD: Memoize expensive computations +const sortedMarkets = useMemo(() => { + return markets.sort((a, b) => b.volume - a.volume) +}, [markets]) + +// ✅ GOOD: Memoize callbacks +const handleSearch = useCallback((query: string) => { + setSearchQuery(query) +}, []) +``` + +### 지연 로딩 + +```typescript +import { lazy, Suspense } from 'react' + +// ✅ GOOD: Lazy load heavy components +const HeavyChart = lazy(() => import('./HeavyChart')) + +export function Dashboard() { + return ( + }> + + + ) +} +``` + +### 데이터베이스 쿼리 + +```typescript +// ✅ GOOD: Select only needed columns +const { data } = await supabase + .from('markets') + .select('id, name, status') + .limit(10) + +// ❌ BAD: Select everything +const { data } = await supabase + .from('markets') + .select('*') +``` + +## 테스트 표준 + +### 테스트 구조 (AAA 패턴) + +```typescript +test('calculates similarity correctly', () => { + // Arrange + const vector1 = [1, 0, 0] + const vector2 = [0, 1, 0] + + // Act + const similarity = calculateCosineSimilarity(vector1, vector2) + + // Assert + expect(similarity).toBe(0) +}) +``` + +### 테스트 네이밍 + +```typescript +// ✅ GOOD: Descriptive test names +test('returns empty array when no markets match query', () => { }) +test('throws error when OpenAI API key is missing', () => { }) +test('falls back to substring search when Redis unavailable', () => { }) + +// ❌ BAD: Vague test names +test('works', () => { }) +test('test search', () => { }) +``` + +## 코드 스멜 감지 + +다음 안티패턴을 주의하세요: + +### 1. 긴 함수 +```typescript +// ❌ BAD: Function > 50 lines +function processMarketData() { + // 100 lines of code +} + +// ✅ GOOD: Split into smaller functions +function processMarketData() { + const validated = validateData() + const transformed = transformData(validated) + return saveData(transformed) +} +``` + +### 2. 깊은 중첩 +```typescript +// ❌ BAD: 5+ levels of nesting +if (user) { + if (user.isAdmin) { + if (market) { + if (market.isActive) { + if (hasPermission) { + // Do something + } + } + } + } +} + +// ✅ GOOD: Early returns +if (!user) return +if (!user.isAdmin) return +if (!market) return +if (!market.isActive) return +if (!hasPermission) return + +// Do something +``` + +### 3. 매직 넘버 +```typescript +// ❌ BAD: Unexplained numbers +if (retryCount > 3) { } +setTimeout(callback, 500) + +// ✅ GOOD: Named constants +const MAX_RETRIES = 3 +const DEBOUNCE_DELAY_MS = 500 + +if (retryCount > MAX_RETRIES) { } +setTimeout(callback, DEBOUNCE_DELAY_MS) +``` + +**기억하세요**: 코드 품질은 타협할 수 없습니다. 명확하고 유지보수 가능한 코드가 빠른 개발과 자신감 있는 리팩터링을 가능하게 합니다. diff --git a/docs/ko-KR/skills/continuous-learning-v2/SKILL.md b/docs/ko-KR/skills/continuous-learning-v2/SKILL.md new file mode 100644 index 00000000..31cdbd13 --- /dev/null +++ b/docs/ko-KR/skills/continuous-learning-v2/SKILL.md @@ -0,0 +1,363 @@ +--- +name: continuous-learning-v2 +description: 훅을 통해 세션을 관찰하고, 신뢰도 점수가 있는 원자적 본능을 생성하며, 이를 스킬/명령어/에이전트로 진화시키는 본능 기반 학습 시스템. v2.1에서는 프로젝트 간 오염을 방지하기 위한 프로젝트 범위 본능이 추가되었습니다. +origin: ECC +version: 2.1.0 +--- + +# 지속적 학습 v2.1 - 본능 기반 아키텍처 + +Claude Code 세션을 원자적 "본능(instinct)" -- 신뢰도 점수가 있는 작은 학습된 행동 -- 을 통해 재사용 가능한 지식으로 변환하는 고급 학습 시스템입니다. + +**v2.1**에서는 **프로젝트 범위 본능**이 추가되었습니다 -- React 패턴은 React 프로젝트에, Python 규칙은 Python 프로젝트에 유지되며, 범용 패턴(예: "항상 입력 유효성 검사")은 전역으로 공유됩니다. + +## 활성화 시점 + +- Claude Code 세션에서 자동 학습 설정 시 +- 훅을 통한 본능 기반 행동 추출 구성 시 +- 학습된 행동의 신뢰도 임계값 조정 시 +- 본능 라이브러리 검토, 내보내기, 가져오기 시 +- 본능을 완전한 스킬, 명령어 또는 에이전트로 진화 시 +- 프로젝트 범위 vs 전역 본능 관리 시 +- 프로젝트에서 전역 범위로 본능 승격 시 + +## v2.1의 새로운 기능 + +| 기능 | v2.0 | v2.1 | +|---------|------|------| +| 저장소 | 전역 (~/.claude/homunculus/) | 프로젝트 범위 (projects//) | +| 범위 | 모든 본능이 어디서나 적용 | 프로젝트 범위 + 전역 | +| 감지 | 없음 | git remote URL / 저장소 경로 | +| 승격 | 해당 없음 | 2개 이상 프로젝트에서 확인 시 프로젝트 -> 전역 | +| 명령어 | 4개 (status/evolve/export/import) | 6개 (+promote/projects) | +| 프로젝트 간 | 오염 위험 | 기본적으로 격리 | + +## v2의 새로운 기능 (v1 대비) + +| 기능 | v1 | v2 | +|---------|----|----| +| 관찰 | Stop 훅 (세션 종료) | PreToolUse/PostToolUse (100% 신뢰성) | +| 분석 | 메인 컨텍스트 | 백그라운드 에이전트 (Haiku) | +| 세분성 | 전체 스킬 | 원자적 "본능" | +| 신뢰도 | 없음 | 0.3-0.9 가중치 | +| 진화 | 직접 스킬로 | 본능 -> 클러스터 -> 스킬/명령어/에이전트 | +| 공유 | 없음 | 본능 내보내기/가져오기 | + +## 본능 모델 + +본능은 작은 학습된 행동입니다: + +```yaml +--- +id: prefer-functional-style +trigger: "when writing new functions" +confidence: 0.7 +domain: "code-style" +source: "session-observation" +scope: project +project_id: "a1b2c3d4e5f6" +project_name: "my-react-app" +--- + +# Prefer Functional Style + +## Action +Use functional patterns over classes when appropriate. + +## Evidence +- Observed 5 instances of functional pattern preference +- User corrected class-based approach to functional on 2025-01-15 +``` + +**속성:** +- **원자적** -- 하나의 트리거, 하나의 액션 +- **신뢰도 가중치** -- 0.3 = 잠정적, 0.9 = 거의 확실 +- **도메인 태그** -- code-style, testing, git, debugging, workflow 등 +- **증거 기반** -- 어떤 관찰이 이를 생성했는지 추적 +- **범위 인식** -- `project` (기본값) 또는 `global` + +## 작동 방식 + +``` +세션 활동 (git 저장소 내) + | + | 훅이 프롬프트 + 도구 사용을 캡처 (100% 신뢰성) + | + 프로젝트 컨텍스트 감지 (git remote / 저장소 경로) + v ++---------------------------------------------+ +| projects//observations.jsonl | +| (프롬프트, 도구 호출, 결과, 프로젝트) | ++---------------------------------------------+ + | + | 관찰자 에이전트가 읽기 (백그라운드, Haiku) + v ++---------------------------------------------+ +| 패턴 감지 | +| * 사용자 수정 -> 본능 | +| * 에러 해결 -> 본능 | +| * 반복 워크플로우 -> 본능 | +| * 범위 결정: 프로젝트 또는 전역? | ++---------------------------------------------+ + | + | 생성/업데이트 + v ++---------------------------------------------+ +| projects//instincts/personal/ | +| * prefer-functional.yaml (0.7) [project] | +| * use-react-hooks.yaml (0.9) [project] | ++---------------------------------------------+ +| instincts/personal/ (전역) | +| * always-validate-input.yaml (0.85) [global]| +| * grep-before-edit.yaml (0.6) [global] | ++---------------------------------------------+ + | + | /evolve 클러스터링 + /promote + v ++---------------------------------------------+ +| projects//evolved/ (프로젝트 범위) | +| evolved/ (전역) | +| * commands/new-feature.md | +| * skills/testing-workflow.md | +| * agents/refactor-specialist.md | ++---------------------------------------------+ +``` + +## 프로젝트 감지 + +시스템이 현재 프로젝트를 자동으로 감지합니다: + +1. **`CLAUDE_PROJECT_DIR` 환경 변수** (최우선 순위) +2. **`git remote get-url origin`** -- 이식 가능한 프로젝트 ID를 생성하기 위해 해시됨 (서로 다른 머신에서 같은 저장소는 같은 ID를 가짐) +3. **`git rev-parse --show-toplevel`** -- 저장소 경로를 사용한 폴백 (머신별) +4. **전역 폴백** -- 프로젝트가 감지되지 않으면 본능은 전역 범위로 이동 + +각 프로젝트는 12자 해시 ID를 받습니다 (예: `a1b2c3d4e5f6`). `~/.claude/homunculus/projects.json`의 레지스트리 파일이 ID를 사람이 읽을 수 있는 이름에 매핑합니다. + +## 빠른 시작 + +### 1. 관찰 훅 활성화 + +`~/.claude/settings.json`에 추가하세요. + +**플러그인으로 설치한 경우** (권장): + +```json +{ + "hooks": { + "PreToolUse": [{ + "matcher": "*", + "hooks": [{ + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/hooks/observe.sh" + }] + }], + "PostToolUse": [{ + "matcher": "*", + "hooks": [{ + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/hooks/observe.sh" + }] + }] + } +} +``` + +**수동으로 `~/.claude/skills`에 설치한 경우**: + +```json +{ + "hooks": { + "PreToolUse": [{ + "matcher": "*", + "hooks": [{ + "type": "command", + "command": "~/.claude/skills/continuous-learning-v2/hooks/observe.sh" + }] + }], + "PostToolUse": [{ + "matcher": "*", + "hooks": [{ + "type": "command", + "command": "~/.claude/skills/continuous-learning-v2/hooks/observe.sh" + }] + }] + } +} +``` + +### 2. 디렉터리 구조 초기화 + +시스템은 첫 사용 시 자동으로 디렉터리를 생성하지만, 수동으로도 생성할 수 있습니다: + +```bash +# Global directories +mkdir -p ~/.claude/homunculus/{instincts/{personal,inherited},evolved/{agents,skills,commands},projects} + +# Project directories are auto-created when the hook first runs in a git repo +``` + +### 3. 본능 명령어 사용 + +```bash +/instinct-status # 학습된 본능 표시 (프로젝트 + 전역) +/evolve # 관련 본능을 스킬/명령어로 클러스터링 +/instinct-export # 본능을 파일로 내보내기 +/instinct-import # 다른 사람의 본능 가져오기 +/promote # 프로젝트 본능을 전역 범위로 승격 +/projects # 모든 알려진 프로젝트와 본능 개수 목록 +``` + +## 명령어 + +| 명령어 | 설명 | +|---------|-------------| +| `/instinct-status` | 모든 본능 (프로젝트 범위 + 전역) 을 신뢰도와 함께 표시 | +| `/evolve` | 관련 본능을 스킬/명령어로 클러스터링, 승격 제안 | +| `/instinct-export` | 본능 내보내기 (범위/도메인으로 필터링 가능) | +| `/instinct-import ` | 범위 제어와 함께 본능 가져오기 | +| `/promote [id]` | 프로젝트 본능을 전역 범위로 승격 | +| `/projects` | 모든 알려진 프로젝트와 본능 개수 목록 | + +## 구성 + +백그라운드 관찰자를 제어하려면 `config.json`을 편집하세요: + +```json +{ + "version": "2.1", + "observer": { + "enabled": false, + "run_interval_minutes": 5, + "min_observations_to_analyze": 20 + } +} +``` + +| 키 | 기본값 | 설명 | +|-----|---------|-------------| +| `observer.enabled` | `false` | 백그라운드 관찰자 에이전트 활성화 | +| `observer.run_interval_minutes` | `5` | 관찰자가 관찰 결과를 분석하는 빈도 | +| `observer.min_observations_to_analyze` | `20` | 분석 실행 전 최소 관찰 횟수 | + +기타 동작 (관찰 캡처, 본능 임계값, 프로젝트 범위, 승격 기준)은 `instinct-cli.py`와 `observe.sh`의 코드 기본값으로 구성됩니다. + +## 파일 구조 + +``` +~/.claude/homunculus/ ++-- identity.json # 프로필, 기술 수준 ++-- projects.json # 레지스트리: 프로젝트 해시 -> 이름/경로/리모트 ++-- observations.jsonl # 전역 관찰 결과 (폴백) ++-- instincts/ +| +-- personal/ # 전역 자동 학습된 본능 +| +-- inherited/ # 전역 가져온 본능 ++-- evolved/ +| +-- agents/ # 전역 생성된 에이전트 +| +-- skills/ # 전역 생성된 스킬 +| +-- commands/ # 전역 생성된 명령어 ++-- projects/ + +-- a1b2c3d4e5f6/ # 프로젝트 해시 (git remote URL에서) + | +-- observations.jsonl + | +-- observations.archive/ + | +-- instincts/ + | | +-- personal/ # 프로젝트별 자동 학습 + | | +-- inherited/ # 프로젝트별 가져온 것 + | +-- evolved/ + | +-- skills/ + | +-- commands/ + | +-- agents/ + +-- f6e5d4c3b2a1/ # 다른 프로젝트 + +-- ... +``` + +## 범위 결정 가이드 + +| 패턴 유형 | 범위 | 예시 | +|-------------|-------|---------| +| 언어/프레임워크 규칙 | **project** | "React hooks 사용", "Django REST 패턴 따르기" | +| 파일 구조 선호도 | **project** | "`__tests__`/에 테스트", "src/components/에 컴포넌트" | +| 코드 스타일 | **project** | "함수형 스타일 사용", "dataclasses 선호" | +| 에러 처리 전략 | **project** | "에러에 Result 타입 사용" | +| 보안 관행 | **global** | "사용자 입력 유효성 검사", "SQL 새니타이징" | +| 일반 모범 사례 | **global** | "테스트 먼저 작성", "항상 에러 처리" | +| 도구 워크플로우 선호도 | **global** | "편집 전 Grep", "쓰기 전 Read" | +| Git 관행 | **global** | "Conventional commits", "작고 집중된 커밋" | + +## 본능 승격 (프로젝트 -> 전역) + +같은 본능이 높은 신뢰도로 여러 프로젝트에 나타나면, 전역 범위로 승격할 후보가 됩니다. + +**자동 승격 기준:** +- 2개 이상 프로젝트에서 같은 본능 ID +- 평균 신뢰도 >= 0.8 + +**승격 방법:** + +```bash +# Promote a specific instinct +python3 instinct-cli.py promote prefer-explicit-errors + +# Auto-promote all qualifying instincts +python3 instinct-cli.py promote + +# Preview without changes +python3 instinct-cli.py promote --dry-run +``` + +`/evolve` 명령어도 승격 후보를 제안합니다. + +## 신뢰도 점수 + +신뢰도는 시간이 지남에 따라 진화합니다: + +| 점수 | 의미 | 동작 | +|-------|---------|----------| +| 0.3 | 잠정적 | 제안되지만 강제되지 않음 | +| 0.5 | 보통 | 관련 시 적용 | +| 0.7 | 강함 | 적용이 자동 승인됨 | +| 0.9 | 거의 확실 | 핵심 행동 | + +**신뢰도가 증가하는 경우:** +- 패턴이 반복적으로 관찰됨 +- 사용자가 제안된 행동을 수정하지 않음 +- 다른 소스의 유사한 본능이 동의함 + +**신뢰도가 감소하는 경우:** +- 사용자가 행동을 명시적으로 수정함 +- 패턴이 오랜 기간 관찰되지 않음 +- 모순되는 증거가 나타남 + +## 왜 관찰에 스킬이 아닌 훅을 사용하나요? + +> "v1은 관찰에 스킬을 의존했습니다. 스킬은 확률적입니다 -- Claude의 판단에 따라 약 50-80%의 확률로 실행됩니다." + +훅은 **100% 확률로** 결정적으로 실행됩니다. 이는 다음을 의미합니다: +- 모든 도구 호출이 관찰됨 +- 패턴이 누락되지 않음 +- 학습이 포괄적임 + +## 하위 호환성 + +v2.1은 v2.0 및 v1과 완전히 호환됩니다: +- `~/.claude/homunculus/instincts/`의 기존 전역 본능이 전역 본능으로 계속 작동 +- v1의 기존 `~/.claude/skills/learned/` 스킬이 계속 작동 +- Stop 훅이 여전히 실행됨 (하지만 이제 v2에도 데이터를 공급) +- 점진적 마이그레이션: 둘 다 병렬로 실행 가능 + +## 개인정보 보호 + +- 관찰 결과는 사용자의 머신에 **로컬**로 유지 +- 프로젝트 범위 본능은 프로젝트별로 격리됨 +- **본능**(패턴)만 내보낼 수 있음 -- 원시 관찰 결과는 아님 +- 실제 코드나 대화 내용은 공유되지 않음 +- 내보내기와 승격 대상을 사용자가 제어 + +## 관련 자료 + +- [Skill Creator](https://skill-creator.app) - 저장소 히스토리에서 본능 생성 +- Homunculus - v2 본능 기반 아키텍처에 영감을 준 커뮤니티 프로젝트 (원자적 관찰, 신뢰도 점수, 본능 진화 파이프라인) +- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 지속적 학습 섹션 + +--- + +*본능 기반 학습: Claude에게 당신의 패턴을 가르치기, 한 번에 하나의 프로젝트씩.* diff --git a/docs/ko-KR/skills/continuous-learning/SKILL.md b/docs/ko-KR/skills/continuous-learning/SKILL.md new file mode 100644 index 00000000..d279ec32 --- /dev/null +++ b/docs/ko-KR/skills/continuous-learning/SKILL.md @@ -0,0 +1,119 @@ +--- +name: continuous-learning +description: Claude Code 세션에서 재사용 가능한 패턴을 자동으로 추출하여 향후 사용을 위한 학습된 스킬로 저장합니다. +origin: ECC +--- + +# 지속적 학습 스킬 + +Claude Code 세션 종료 시 자동으로 평가하여 학습된 스킬로 저장할 수 있는 재사용 가능한 패턴을 추출합니다. + +## 활성화 시점 + +- Claude Code 세션에서 자동 패턴 추출을 설정할 때 +- 세션 평가를 위한 Stop Hook을 구성할 때 +- `~/.claude/skills/learned/`에서 학습된 스킬을 검토하거나 큐레이션할 때 +- 추출 임계값이나 패턴 카테고리를 조정할 때 +- v1 (이 방식)과 v2 (본능 기반) 접근법을 비교할 때 + +## 작동 방식 + +이 스킬은 각 세션 종료 시 **Stop Hook**으로 실행됩니다: + +1. **세션 평가**: 세션에 충분한 메시지가 있는지 확인 (기본값: 10개 이상) +2. **패턴 감지**: 세션에서 추출 가능한 패턴을 식별 +3. **스킬 추출**: 유용한 패턴을 `~/.claude/skills/learned/`에 저장 + +## 구성 + +`config.json`을 편집하여 사용자 지정합니다: + +```json +{ + "min_session_length": 10, + "extraction_threshold": "medium", + "auto_approve": false, + "learned_skills_path": "~/.claude/skills/learned/", + "patterns_to_detect": [ + "error_resolution", + "user_corrections", + "workarounds", + "debugging_techniques", + "project_specific" + ], + "ignore_patterns": [ + "simple_typos", + "one_time_fixes", + "external_api_issues" + ] +} +``` + +## 패턴 유형 + +| 패턴 | 설명 | +|---------|-------------| +| `error_resolution` | 특정 에러가 어떻게 해결되었는지 | +| `user_corrections` | 사용자 수정으로부터의 패턴 | +| `workarounds` | 프레임워크/라이브러리 특이점에 대한 해결책 | +| `debugging_techniques` | 효과적인 디버깅 접근법 | +| `project_specific` | 프로젝트 고유 컨벤션 | + +## Hook 설정 + +`~/.claude/settings.json`에 추가합니다: + +```json +{ + "hooks": { + "Stop": [{ + "matcher": "*", + "hooks": [{ + "type": "command", + "command": "~/.claude/skills/continuous-learning/evaluate-session.sh" + }] + }] + } +} +``` + +## Stop Hook을 사용하는 이유 + +- **경량**: 세션 종료 시 한 번만 실행 +- **비차단**: 모든 메시지에 지연을 추가하지 않음 +- **완전한 컨텍스트**: 전체 세션 트랜스크립트에 접근 가능 + +## 관련 항목 + +- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 지속적 학습 섹션 +- `/learn` 명령어 - 세션 중 수동 패턴 추출 + +--- + +## 비교 노트 (연구: 2025년 1월) + +### vs Homunculus + +Homunculus v2는 더 정교한 접근법을 취합니다: + +| 기능 | 우리의 접근법 | Homunculus v2 | +|---------|--------------|---------------| +| 관찰 | Stop Hook (세션 종료 시) | PreToolUse/PostToolUse Hook (100% 신뢰) | +| 분석 | 메인 컨텍스트 | 백그라운드 에이전트 (Haiku) | +| 세분성 | 완전한 스킬 | 원자적 "본능" | +| 신뢰도 | 없음 | 0.3-0.9 가중치 | +| 진화 | 스킬로 직접 | 본능 -> 클러스터 -> 스킬/명령어/에이전트 | +| 공유 | 없음 | 본능 내보내기/가져오기 | + +**Homunculus의 핵심 통찰:** +> "v1은 관찰을 스킬에 의존했습니다. 스킬은 확률적이어서 약 50-80%의 확률로 실행됩니다. v2는 관찰에 Hook(100% 신뢰)을 사용하고 본능을 학습된 행동의 원자 단위로 사용합니다." + +### 잠재적 v2 개선 사항 + +1. **본능 기반 학습** - 신뢰도 점수가 있는 더 작고 원자적인 행동 +2. **백그라운드 관찰자** - 병렬로 분석하는 Haiku 에이전트 +3. **신뢰도 감쇠** - 반박 시 본능의 신뢰도 감소 +4. **도메인 태깅** - code-style, testing, git, debugging 등 +5. **진화 경로** - 관련 본능을 스킬/명령어로 클러스터링 + +자세한 사양은 `docs/continuous-learning-v2-spec.md`를 참조하세요. diff --git a/docs/ko-KR/skills/eval-harness/SKILL.md b/docs/ko-KR/skills/eval-harness/SKILL.md new file mode 100644 index 00000000..6422e6de --- /dev/null +++ b/docs/ko-KR/skills/eval-harness/SKILL.md @@ -0,0 +1,270 @@ +--- +name: eval-harness +description: 평가 주도 개발(EDD) 원칙을 구현하는 Claude Code 세션용 공식 평가 프레임워크 +origin: ECC +tools: Read, Write, Edit, Bash, Grep, Glob +--- + +# 평가 하네스 스킬 + +Claude Code 세션을 위한 공식 평가 프레임워크로, 평가 주도 개발(EDD) 원칙을 구현합니다. + +## 활성화 시점 + +- AI 지원 워크플로우에 평가 주도 개발(EDD) 설정 시 +- Claude Code 작업 완료에 대한 합격/불합격 기준 정의 시 +- pass@k 메트릭으로 에이전트 신뢰성 측정 시 +- 프롬프트 또는 에이전트 변경에 대한 회귀 테스트 스위트 생성 시 +- 모델 버전 간 에이전트 성능 벤치마킹 시 + +## 철학 + +평가 주도 개발은 평가를 "AI 개발의 단위 테스트"로 취급합니다: +- 구현 전에 예상 동작 정의 +- 개발 중 지속적으로 평가 실행 +- 각 변경 시 회귀 추적 +- 신뢰성 측정을 위해 pass@k 메트릭 사용 + +## 평가 유형 + +### 기능 평가 +Claude가 이전에 할 수 없었던 것을 할 수 있는지 테스트: +```markdown +[CAPABILITY EVAL: feature-name] +Task: Description of what Claude should accomplish +Success Criteria: + - [ ] Criterion 1 + - [ ] Criterion 2 + - [ ] Criterion 3 +Expected Output: Description of expected result +``` + +### 회귀 평가 +변경 사항이 기존 기능을 손상시키지 않는지 확인: +```markdown +[REGRESSION EVAL: feature-name] +Baseline: SHA or checkpoint name +Tests: + - existing-test-1: PASS/FAIL + - existing-test-2: PASS/FAIL + - existing-test-3: PASS/FAIL +Result: X/Y passed (previously Y/Y) +``` + +## 채점자 유형 + +### 1. 코드 기반 채점자 +코드를 사용한 결정론적 검사: +```bash +# Check if file contains expected pattern +grep -q "export function handleAuth" src/auth.ts && echo "PASS" || echo "FAIL" + +# Check if tests pass +npm test -- --testPathPattern="auth" && echo "PASS" || echo "FAIL" + +# Check if build succeeds +npm run build && echo "PASS" || echo "FAIL" +``` + +### 2. 모델 기반 채점자 +Claude를 사용하여 개방형 출력 평가: +```markdown +[MODEL GRADER PROMPT] +Evaluate the following code change: +1. Does it solve the stated problem? +2. Is it well-structured? +3. Are edge cases handled? +4. Is error handling appropriate? + +Score: 1-5 (1=poor, 5=excellent) +Reasoning: [explanation] +``` + +### 3. 사람 채점자 +수동 검토 플래그: +```markdown +[HUMAN REVIEW REQUIRED] +Change: Description of what changed +Reason: Why human review is needed +Risk Level: LOW/MEDIUM/HIGH +``` + +## 메트릭 + +### pass@k +"k번 시도 중 최소 한 번 성공" +- pass@1: 첫 번째 시도 성공률 +- pass@3: 3번 시도 내 성공 +- 일반적인 목표: pass@3 > 90% + +### pass^k +"k번 시행 모두 성공" +- 신뢰성에 대한 더 높은 기준 +- pass^3: 3회 연속 성공 +- 핵심 경로에 사용 + +## 평가 워크플로우 + +### 1. 정의 (코딩 전) +```markdown +## EVAL DEFINITION: feature-xyz + +### Capability Evals +1. Can create new user account +2. Can validate email format +3. Can hash password securely + +### Regression Evals +1. Existing login still works +2. Session management unchanged +3. Logout flow intact + +### Success Metrics +- pass@3 > 90% for capability evals +- pass^3 = 100% for regression evals +``` + +### 2. 구현 +정의된 평가를 통과하기 위한 코드 작성. + +### 3. 평가 +```bash +# Run capability evals +[Run each capability eval, record PASS/FAIL] + +# Run regression evals +npm test -- --testPathPattern="existing" + +# Generate report +``` + +### 4. 보고서 +```markdown +EVAL REPORT: feature-xyz +======================== + +Capability Evals: + create-user: PASS (pass@1) + validate-email: PASS (pass@2) + hash-password: PASS (pass@1) + Overall: 3/3 passed + +Regression Evals: + login-flow: PASS + session-mgmt: PASS + logout-flow: PASS + Overall: 3/3 passed + +Metrics: + pass@1: 67% (2/3) + pass@3: 100% (3/3) + +Status: READY FOR REVIEW +``` + +## 통합 패턴 + +### 구현 전 +``` +/eval define feature-name +``` +`.claude/evals/feature-name.md`에 평가 정의 파일 생성 + +### 구현 중 +``` +/eval check feature-name +``` +현재 평가를 실행하고 상태 보고 + +### 구현 후 +``` +/eval report feature-name +``` +전체 평가 보고서 생성 + +## 평가 저장소 + +프로젝트에 평가 저장: +``` +.claude/ + evals/ + feature-xyz.md # 평가 정의 + feature-xyz.log # 평가 실행 이력 + baseline.json # 회귀 베이스라인 +``` + +## 모범 사례 + +1. **코딩 전에 평가 정의** - 성공 기준에 대한 명확한 사고를 강제 +2. **자주 평가 실행** - 회귀를 조기에 포착 +3. **시간에 따른 pass@k 추적** - 신뢰성 추세 모니터링 +4. **가능하면 코드 채점자 사용** - 결정론적 > 확률적 +5. **보안에는 사람 검토** - 보안 검사를 완전히 자동화하지 말 것 +6. **평가를 빠르게 유지** - 느린 평가는 실행되지 않음 +7. **코드와 함께 평가 버전 관리** - 평가는 일급 산출물 + +## 예시: 인증 추가 + +```markdown +## EVAL: add-authentication + +### Phase 1: 정의 (10분) +Capability Evals: +- [ ] User can register with email/password +- [ ] User can login with valid credentials +- [ ] Invalid credentials rejected with proper error +- [ ] Sessions persist across page reloads +- [ ] Logout clears session + +Regression Evals: +- [ ] Public routes still accessible +- [ ] API responses unchanged +- [ ] Database schema compatible + +### Phase 2: 구현 (가변) +[Write code] + +### Phase 3: 평가 +Run: /eval check add-authentication + +### Phase 4: 보고서 +EVAL REPORT: add-authentication +============================== +Capability: 5/5 passed (pass@3: 100%) +Regression: 3/3 passed (pass^3: 100%) +Status: SHIP IT +``` + +## 제품 평가 (v1.8) + +행동 품질을 단위 테스트만으로 포착할 수 없을 때 제품 평가를 사용하세요. + +### 채점자 유형 + +1. 코드 채점자 (결정론적 어서션) +2. 규칙 채점자 (정규식/스키마 제약 조건) +3. 모델 채점자 (LLM 심사위원 루브릭) +4. 사람 채점자 (모호한 출력에 대한 수동 판정) + +### pass@k 가이드 + +- `pass@1`: 직접 신뢰성 +- `pass@3`: 제어된 재시도 하에서의 실용적 신뢰성 +- `pass^3`: 안정성 테스트 (3회 모두 통과해야 함) + +권장 임계값: +- 기능 평가: pass@3 >= 0.90 +- 회귀 평가: 릴리스 핵심 경로에 pass^3 = 1.00 + +### 평가 안티패턴 + +- 알려진 평가 예시에 프롬프트 과적합 +- 정상 경로 출력만 측정 +- 합격률을 쫓으면서 비용과 지연 시간 변동 무시 +- 릴리스 게이트에 불안정한 채점자 허용 + +### 최소 평가 산출물 레이아웃 + +- `.claude/evals/.md` 정의 +- `.claude/evals/.log` 실행 이력 +- `docs/releases//eval-summary.md` 릴리스 스냅샷 diff --git a/docs/ko-KR/skills/frontend-patterns/SKILL.md b/docs/ko-KR/skills/frontend-patterns/SKILL.md new file mode 100644 index 00000000..cf3cdd45 --- /dev/null +++ b/docs/ko-KR/skills/frontend-patterns/SKILL.md @@ -0,0 +1,642 @@ +--- +name: frontend-patterns +description: React, Next.js, 상태 관리, 성능 최적화 및 UI 모범 사례를 위한 프론트엔드 개발 패턴. +origin: ECC +--- + +# 프론트엔드 개발 패턴 + +React, Next.js 및 고성능 사용자 인터페이스를 위한 모던 프론트엔드 패턴. + +## 활성화 시점 + +- React 컴포넌트를 구축할 때 (합성, props, 렌더링) +- 상태를 관리할 때 (useState, useReducer, Zustand, Context) +- 데이터 페칭을 구현할 때 (SWR, React Query, server components) +- 성능을 최적화할 때 (메모이제이션, 가상화, 코드 분할) +- 폼을 다룰 때 (유효성 검사, 제어 입력, Zod 스키마) +- 클라이언트 사이드 라우팅과 네비게이션을 처리할 때 +- 접근성 있고 반응형인 UI 패턴을 구축할 때 + +## 컴포넌트 패턴 + +### 상속보다 합성 + +```typescript +// ✅ GOOD: Component composition +interface CardProps { + children: React.ReactNode + variant?: 'default' | 'outlined' +} + +export function Card({ children, variant = 'default' }: CardProps) { + return
{children}
+} + +export function CardHeader({ children }: { children: React.ReactNode }) { + return
{children}
+} + +export function CardBody({ children }: { children: React.ReactNode }) { + return
{children}
+} + +// Usage + + Title + Content + +``` + +### Compound Components + +```typescript +interface TabsContextValue { + activeTab: string + setActiveTab: (tab: string) => void +} + +const TabsContext = createContext(undefined) + +export function Tabs({ children, defaultTab }: { + children: React.ReactNode + defaultTab: string +}) { + const [activeTab, setActiveTab] = useState(defaultTab) + + return ( + + {children} + + ) +} + +export function TabList({ children }: { children: React.ReactNode }) { + return
{children}
+} + +export function Tab({ id, children }: { id: string, children: React.ReactNode }) { + const context = useContext(TabsContext) + if (!context) throw new Error('Tab must be used within Tabs') + + return ( + + ) +} + +// Usage + + + Overview + Details + + +``` + +### Render Props 패턴 + +```typescript +interface DataLoaderProps { + url: string + children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode +} + +export function DataLoader({ url, children }: DataLoaderProps) { + const [data, setData] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch(url) + .then(res => res.json()) + .then(setData) + .catch(setError) + .finally(() => setLoading(false)) + }, [url]) + + return <>{children(data, loading, error)} +} + +// Usage + url="/api/markets"> + {(markets, loading, error) => { + if (loading) return + if (error) return + return + }} + +``` + +## 커스텀 Hook 패턴 + +### 상태 관리 Hook + +```typescript +export function useToggle(initialValue = false): [boolean, () => void] { + const [value, setValue] = useState(initialValue) + + const toggle = useCallback(() => { + setValue(v => !v) + }, []) + + return [value, toggle] +} + +// Usage +const [isOpen, toggleOpen] = useToggle() +``` + +### 비동기 데이터 페칭 Hook + +```typescript +interface UseQueryOptions { + onSuccess?: (data: T) => void + onError?: (error: Error) => void + enabled?: boolean +} + +export function useQuery( + key: string, + fetcher: () => Promise, + options?: UseQueryOptions +) { + const [data, setData] = useState(null) + const [error, setError] = useState(null) + const [loading, setLoading] = useState(false) + + const refetch = useCallback(async () => { + setLoading(true) + setError(null) + + try { + const result = await fetcher() + setData(result) + options?.onSuccess?.(result) + } catch (err) { + const error = err as Error + setError(error) + options?.onError?.(error) + } finally { + setLoading(false) + } + }, [fetcher, options]) + + useEffect(() => { + if (options?.enabled !== false) { + refetch() + } + }, [key, refetch, options?.enabled]) + + return { data, error, loading, refetch } +} + +// Usage +const { data: markets, loading, error, refetch } = useQuery( + 'markets', + () => fetch('/api/markets').then(r => r.json()), + { + onSuccess: data => console.log('Fetched', data.length, 'markets'), + onError: err => console.error('Failed:', err) + } +) +``` + +### Debounce Hook + +```typescript +export function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value) + }, delay) + + return () => clearTimeout(handler) + }, [value, delay]) + + return debouncedValue +} + +// Usage +const [searchQuery, setSearchQuery] = useState('') +const debouncedQuery = useDebounce(searchQuery, 500) + +useEffect(() => { + if (debouncedQuery) { + performSearch(debouncedQuery) + } +}, [debouncedQuery]) +``` + +## 상태 관리 패턴 + +### Context + Reducer 패턴 + +```typescript +interface State { + markets: Market[] + selectedMarket: Market | null + loading: boolean +} + +type Action = + | { type: 'SET_MARKETS'; payload: Market[] } + | { type: 'SELECT_MARKET'; payload: Market } + | { type: 'SET_LOADING'; payload: boolean } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'SET_MARKETS': + return { ...state, markets: action.payload } + case 'SELECT_MARKET': + return { ...state, selectedMarket: action.payload } + case 'SET_LOADING': + return { ...state, loading: action.payload } + default: + return state + } +} + +const MarketContext = createContext<{ + state: State + dispatch: Dispatch +} | undefined>(undefined) + +export function MarketProvider({ children }: { children: React.ReactNode }) { + const [state, dispatch] = useReducer(reducer, { + markets: [], + selectedMarket: null, + loading: false + }) + + return ( + + {children} + + ) +} + +export function useMarkets() { + const context = useContext(MarketContext) + if (!context) throw new Error('useMarkets must be used within MarketProvider') + return context +} +``` + +## 성능 최적화 + +### 메모이제이션 + +```typescript +// ✅ useMemo for expensive computations +const sortedMarkets = useMemo(() => { + return markets.sort((a, b) => b.volume - a.volume) +}, [markets]) + +// ✅ useCallback for functions passed to children +const handleSearch = useCallback((query: string) => { + setSearchQuery(query) +}, []) + +// ✅ React.memo for pure components +export const MarketCard = React.memo(({ market }) => { + return ( +
+

{market.name}

+

{market.description}

+
+ ) +}) +``` + +### 코드 분할 및 지연 로딩 + +```typescript +import { lazy, Suspense } from 'react' + +// ✅ Lazy load heavy components +const HeavyChart = lazy(() => import('./HeavyChart')) +const ThreeJsBackground = lazy(() => import('./ThreeJsBackground')) + +export function Dashboard() { + return ( +
+ }> + + + + + + +
+ ) +} +``` + +### 긴 리스트를 위한 가상화 + +```typescript +import { useVirtualizer } from '@tanstack/react-virtual' + +export function VirtualMarketList({ markets }: { markets: Market[] }) { + const parentRef = useRef(null) + + const virtualizer = useVirtualizer({ + count: markets.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 100, // Estimated row height + overscan: 5 // Extra items to render + }) + + return ( +
+
+ {virtualizer.getVirtualItems().map(virtualRow => ( +
+ +
+ ))} +
+
+ ) +} +``` + +## 폼 처리 패턴 + +### 유효성 검사가 포함된 제어 폼 + +```typescript +interface FormData { + name: string + description: string + endDate: string +} + +interface FormErrors { + name?: string + description?: string + endDate?: string +} + +export function CreateMarketForm() { + const [formData, setFormData] = useState({ + name: '', + description: '', + endDate: '' + }) + + const [errors, setErrors] = useState({}) + + const validate = (): boolean => { + const newErrors: FormErrors = {} + + if (!formData.name.trim()) { + newErrors.name = 'Name is required' + } else if (formData.name.length > 200) { + newErrors.name = 'Name must be under 200 characters' + } + + if (!formData.description.trim()) { + newErrors.description = 'Description is required' + } + + if (!formData.endDate) { + newErrors.endDate = 'End date is required' + } + + setErrors(newErrors) + return Object.keys(newErrors).length === 0 + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + + if (!validate()) return + + try { + await createMarket(formData) + // Success handling + } catch (error) { + // Error handling + } + } + + return ( +
+ setFormData(prev => ({ ...prev, name: e.target.value }))} + placeholder="Market name" + /> + {errors.name && {errors.name}} + + {/* Other fields */} + + +
+ ) +} +``` + +## Error Boundary 패턴 + +```typescript +interface ErrorBoundaryState { + hasError: boolean + error: Error | null +} + +export class ErrorBoundary extends React.Component< + { children: React.ReactNode }, + ErrorBoundaryState +> { + state: ErrorBoundaryState = { + hasError: false, + error: null + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + return { hasError: true, error } + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error('Error boundary caught:', error, errorInfo) + } + + render() { + if (this.state.hasError) { + return ( +
+

Something went wrong

+

{this.state.error?.message}

+ +
+ ) + } + + return this.props.children + } +} + +// Usage + + + +``` + +## 애니메이션 패턴 + +### Framer Motion 애니메이션 + +```typescript +import { motion, AnimatePresence } from 'framer-motion' + +// ✅ List animations +export function AnimatedMarketList({ markets }: { markets: Market[] }) { + return ( + + {markets.map(market => ( + + + + ))} + + ) +} + +// ✅ Modal animations +export function Modal({ isOpen, onClose, children }: ModalProps) { + return ( + + {isOpen && ( + <> + + + {children} + + + )} + + ) +} +``` + +## 접근성 패턴 + +### 키보드 네비게이션 + +```typescript +export function Dropdown({ options, onSelect }: DropdownProps) { + const [isOpen, setIsOpen] = useState(false) + const [activeIndex, setActiveIndex] = useState(0) + + const handleKeyDown = (e: React.KeyboardEvent) => { + switch (e.key) { + case 'ArrowDown': + e.preventDefault() + setActiveIndex(i => Math.min(i + 1, options.length - 1)) + break + case 'ArrowUp': + e.preventDefault() + setActiveIndex(i => Math.max(i - 1, 0)) + break + case 'Enter': + e.preventDefault() + onSelect(options[activeIndex]) + setIsOpen(false) + break + case 'Escape': + setIsOpen(false) + break + } + } + + return ( +
+ {/* Dropdown implementation */} +
+ ) +} +``` + +### 포커스 관리 + +```typescript +export function Modal({ isOpen, onClose, children }: ModalProps) { + const modalRef = useRef(null) + const previousFocusRef = useRef(null) + + useEffect(() => { + if (isOpen) { + // Save currently focused element + previousFocusRef.current = document.activeElement as HTMLElement + + // Focus modal + modalRef.current?.focus() + } else { + // Restore focus when closing + previousFocusRef.current?.focus() + } + }, [isOpen]) + + return isOpen ? ( +
e.key === 'Escape' && onClose()} + > + {children} +
+ ) : null +} +``` + +**기억하세요**: 모던 프론트엔드 패턴은 유지보수 가능하고 고성능인 사용자 인터페이스를 가능하게 합니다. 프로젝트 복잡도에 맞는 패턴을 선택하세요. diff --git a/docs/ko-KR/skills/golang-patterns/SKILL.md b/docs/ko-KR/skills/golang-patterns/SKILL.md new file mode 100644 index 00000000..63548e87 --- /dev/null +++ b/docs/ko-KR/skills/golang-patterns/SKILL.md @@ -0,0 +1,674 @@ +--- +name: golang-patterns +description: 견고하고 효율적이며 유지보수 가능한 Go 애플리케이션 구축을 위한 관용적 Go 패턴, 모범 사례 및 규칙. +origin: ECC +--- + +# Go 개발 패턴 + +견고하고 효율적이며 유지보수 가능한 애플리케이션 구축을 위한 관용적 Go 패턴과 모범 사례. + +## 활성화 시점 + +- 새로운 Go 코드 작성 시 +- Go 코드 리뷰 시 +- 기존 Go 코드 리팩토링 시 +- Go 패키지/모듈 설계 시 + +## 핵심 원칙 + +### 1. 단순성과 명확성 + +Go는 영리함보다 단순성을 선호합니다. 코드는 명확하고 읽기 쉬워야 합니다. + +```go +// Good: Clear and direct +func GetUser(id string) (*User, error) { + user, err := db.FindUser(id) + if err != nil { + return nil, fmt.Errorf("get user %s: %w", id, err) + } + return user, nil +} + +// Bad: Overly clever +func GetUser(id string) (*User, error) { + return func() (*User, error) { + if u, e := db.FindUser(id); e == nil { + return u, nil + } else { + return nil, e + } + }() +} +``` + +### 2. 제로 값을 유용하게 만들기 + +제로 값이 초기화 없이 즉시 사용 가능하도록 타입을 설계하세요. + +```go +// Good: Zero value is useful +type Counter struct { + mu sync.Mutex + count int // zero value is 0, ready to use +} + +func (c *Counter) Inc() { + c.mu.Lock() + c.count++ + c.mu.Unlock() +} + +// Good: bytes.Buffer works with zero value +var buf bytes.Buffer +buf.WriteString("hello") + +// Bad: Requires initialization +type BadCounter struct { + counts map[string]int // nil map will panic +} +``` + +### 3. 인터페이스를 받고 구조체를 반환하기 + +함수는 인터페이스 매개변수를 받고 구체적 타입을 반환해야 합니다. + +```go +// Good: Accepts interface, returns concrete type +func ProcessData(r io.Reader) (*Result, error) { + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + return &Result{Data: data}, nil +} + +// Bad: Returns interface (hides implementation details unnecessarily) +func ProcessData(r io.Reader) (io.Reader, error) { + // ... +} +``` + +## 에러 처리 패턴 + +### 컨텍스트가 있는 에러 래핑 + +```go +// Good: Wrap errors with context +func LoadConfig(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("load config %s: %w", path, err) + } + + var cfg Config + if err := json.Unmarshal(data, &cfg); err != nil { + return nil, fmt.Errorf("parse config %s: %w", path, err) + } + + return &cfg, nil +} +``` + +### 커스텀 에러 타입 + +```go +// Define domain-specific errors +type ValidationError struct { + Field string + Message string +} + +func (e *ValidationError) Error() string { + return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message) +} + +// Sentinel errors for common cases +var ( + ErrNotFound = errors.New("resource not found") + ErrUnauthorized = errors.New("unauthorized") + ErrInvalidInput = errors.New("invalid input") +) +``` + +### errors.Is와 errors.As를 사용한 에러 확인 + +```go +func HandleError(err error) { + // Check for specific error + if errors.Is(err, sql.ErrNoRows) { + log.Println("No records found") + return + } + + // Check for error type + var validationErr *ValidationError + if errors.As(err, &validationErr) { + log.Printf("Validation error on field %s: %s", + validationErr.Field, validationErr.Message) + return + } + + // Unknown error + log.Printf("Unexpected error: %v", err) +} +``` + +### 에러를 절대 무시하지 말 것 + +```go +// Bad: Ignoring error with blank identifier +result, _ := doSomething() + +// Good: Handle or explicitly document why it's safe to ignore +result, err := doSomething() +if err != nil { + return err +} + +// Acceptable: When error truly doesn't matter (rare) +_ = writer.Close() // Best-effort cleanup, error logged elsewhere +``` + +## 동시성 패턴 + +### 워커 풀 + +```go +func WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) { + var wg sync.WaitGroup + + for i := 0; i < numWorkers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for job := range jobs { + results <- process(job) + } + }() + } + + wg.Wait() + close(results) +} +``` + +### 취소 및 타임아웃을 위한 Context + +```go +func FetchWithTimeout(ctx context.Context, url string) ([]byte, error) { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("fetch %s: %w", url, err) + } + defer resp.Body.Close() + + return io.ReadAll(resp.Body) +} +``` + +### 우아한 종료 + +```go +func GracefulShutdown(server *http.Server) { + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + + <-quit + log.Println("Shutting down server...") + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + if err := server.Shutdown(ctx); err != nil { + log.Fatalf("Server forced to shutdown: %v", err) + } + + log.Println("Server exited") +} +``` + +### 조율된 고루틴을 위한 errgroup + +```go +import "golang.org/x/sync/errgroup" + +func FetchAll(ctx context.Context, urls []string) ([][]byte, error) { + g, ctx := errgroup.WithContext(ctx) + results := make([][]byte, len(urls)) + + for i, url := range urls { + i, url := i, url // Capture loop variables + g.Go(func() error { + data, err := FetchWithTimeout(ctx, url) + if err != nil { + return err + } + results[i] = data + return nil + }) + } + + if err := g.Wait(); err != nil { + return nil, err + } + return results, nil +} +``` + +### 고루틴 누수 방지 + +```go +// Bad: Goroutine leak if context is cancelled +func leakyFetch(ctx context.Context, url string) <-chan []byte { + ch := make(chan []byte) + go func() { + data, _ := fetch(url) + ch <- data // Blocks forever if no receiver + }() + return ch +} + +// Good: Properly handles cancellation +func safeFetch(ctx context.Context, url string) <-chan []byte { + ch := make(chan []byte, 1) // Buffered channel + go func() { + data, err := fetch(url) + if err != nil { + return + } + select { + case ch <- data: + case <-ctx.Done(): + } + }() + return ch +} +``` + +## 인터페이스 설계 + +### 작고 집중된 인터페이스 + +```go +// Good: Single-method interfaces +type Reader interface { + Read(p []byte) (n int, err error) +} + +type Writer interface { + Write(p []byte) (n int, err error) +} + +type Closer interface { + Close() error +} + +// Compose interfaces as needed +type ReadWriteCloser interface { + Reader + Writer + Closer +} +``` + +### 사용되는 곳에서 인터페이스 정의 + +```go +// In the consumer package, not the provider +package service + +// UserStore defines what this service needs +type UserStore interface { + GetUser(id string) (*User, error) + SaveUser(user *User) error +} + +type Service struct { + store UserStore +} + +// Concrete implementation can be in another package +// It doesn't need to know about this interface +``` + +### 타입 어서션을 통한 선택적 동작 + +```go +type Flusher interface { + Flush() error +} + +func WriteAndFlush(w io.Writer, data []byte) error { + if _, err := w.Write(data); err != nil { + return err + } + + // Flush if supported + if f, ok := w.(Flusher); ok { + return f.Flush() + } + return nil +} +``` + +## 패키지 구성 + +### 표준 프로젝트 레이아웃 + +```text +myproject/ +├── cmd/ +│ └── myapp/ +│ └── main.go # Entry point +├── internal/ +│ ├── handler/ # HTTP handlers +│ ├── service/ # Business logic +│ ├── repository/ # Data access +│ └── config/ # Configuration +├── pkg/ +│ └── client/ # Public API client +├── api/ +│ └── v1/ # API definitions (proto, OpenAPI) +├── testdata/ # Test fixtures +├── go.mod +├── go.sum +└── Makefile +``` + +### 패키지 명명 + +```go +// Good: Short, lowercase, no underscores +package http +package json +package user + +// Bad: Verbose, mixed case, or redundant +package httpHandler +package json_parser +package userService // Redundant 'Service' suffix +``` + +### 패키지 수준 상태 피하기 + +```go +// Bad: Global mutable state +var db *sql.DB + +func init() { + db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL")) +} + +// Good: Dependency injection +type Server struct { + db *sql.DB +} + +func NewServer(db *sql.DB) *Server { + return &Server{db: db} +} +``` + +## 구조체 설계 + +### 함수형 옵션 패턴 + +```go +type Server struct { + addr string + timeout time.Duration + logger *log.Logger +} + +type Option func(*Server) + +func WithTimeout(d time.Duration) Option { + return func(s *Server) { + s.timeout = d + } +} + +func WithLogger(l *log.Logger) Option { + return func(s *Server) { + s.logger = l + } +} + +func NewServer(addr string, opts ...Option) *Server { + s := &Server{ + addr: addr, + timeout: 30 * time.Second, // default + logger: log.Default(), // default + } + for _, opt := range opts { + opt(s) + } + return s +} + +// Usage +server := NewServer(":8080", + WithTimeout(60*time.Second), + WithLogger(customLogger), +) +``` + +### 합성을 위한 임베딩 + +```go +type Logger struct { + prefix string +} + +func (l *Logger) Log(msg string) { + fmt.Printf("[%s] %s\n", l.prefix, msg) +} + +type Server struct { + *Logger // Embedding - Server gets Log method + addr string +} + +func NewServer(addr string) *Server { + return &Server{ + Logger: &Logger{prefix: "SERVER"}, + addr: addr, + } +} + +// Usage +s := NewServer(":8080") +s.Log("Starting...") // Calls embedded Logger.Log +``` + +## 메모리 및 성능 + +### 크기를 알 때 슬라이스 미리 할당 + +```go +// Bad: Grows slice multiple times +func processItems(items []Item) []Result { + var results []Result + for _, item := range items { + results = append(results, process(item)) + } + return results +} + +// Good: Single allocation +func processItems(items []Item) []Result { + results := make([]Result, 0, len(items)) + for _, item := range items { + results = append(results, process(item)) + } + return results +} +``` + +### 빈번한 할당에 sync.Pool 사용 + +```go +var bufferPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + +func ProcessRequest(data []byte) []byte { + buf := bufferPool.Get().(*bytes.Buffer) + defer func() { + buf.Reset() + bufferPool.Put(buf) + }() + + buf.Write(data) + // Process... + return buf.Bytes() +} +``` + +### 루프에서 문자열 연결 피하기 + +```go +// Bad: Creates many string allocations +func join(parts []string) string { + var result string + for _, p := range parts { + result += p + "," + } + return result +} + +// Good: Single allocation with strings.Builder +func join(parts []string) string { + var sb strings.Builder + for i, p := range parts { + if i > 0 { + sb.WriteString(",") + } + sb.WriteString(p) + } + return sb.String() +} + +// Best: Use standard library +func join(parts []string) string { + return strings.Join(parts, ",") +} +``` + +## Go 도구 통합 + +### 필수 명령어 + +```bash +# Build and run +go build ./... +go run ./cmd/myapp + +# Testing +go test ./... +go test -race ./... +go test -cover ./... + +# Static analysis +go vet ./... +staticcheck ./... +golangci-lint run + +# Module management +go mod tidy +go mod verify + +# Formatting +gofmt -w . +goimports -w . +``` + +### 권장 린터 구성 (.golangci.yml) + +```yaml +linters: + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - unused + - gofmt + - goimports + - misspell + - unconvert + - unparam + +linters-settings: + errcheck: + check-type-assertions: true + govet: + check-shadowing: true + +issues: + exclude-use-default: false +``` + +## 빠른 참조: Go 관용구 + +| 관용구 | 설명 | +|-------|-------------| +| Accept interfaces, return structs | 함수는 인터페이스 매개변수를 받고 구체적 타입을 반환 | +| Errors are values | 에러를 예외가 아닌 일급 값으로 취급 | +| Don't communicate by sharing memory | 고루틴 간 조율에 채널 사용 | +| Make the zero value useful | 타입이 명시적 초기화 없이 작동해야 함 | +| A little copying is better than a little dependency | 불필요한 외부 의존성 피하기 | +| Clear is better than clever | 영리함보다 가독성 우선 | +| gofmt is no one's favorite but everyone's friend | 항상 gofmt/goimports로 포맷팅 | +| Return early | 에러를 먼저 처리하고 정상 경로는 들여쓰기 없이 유지 | + +## 피해야 할 안티패턴 + +```go +// Bad: Naked returns in long functions +func process() (result int, err error) { + // ... 50 lines ... + return // What is being returned? +} + +// Bad: Using panic for control flow +func GetUser(id string) *User { + user, err := db.Find(id) + if err != nil { + panic(err) // Don't do this + } + return user +} + +// Bad: Passing context in struct +type Request struct { + ctx context.Context // Context should be first param + ID string +} + +// Good: Context as first parameter +func ProcessRequest(ctx context.Context, id string) error { + // ... +} + +// Bad: Mixing value and pointer receivers +type Counter struct{ n int } +func (c Counter) Value() int { return c.n } // Value receiver +func (c *Counter) Increment() { c.n++ } // Pointer receiver +// Pick one style and be consistent +``` + +**기억하세요**: Go 코드는 최고의 의미에서 지루해야 합니다 - 예측 가능하고, 일관적이며, 이해하기 쉽게. 의심스러울 때는 단순하게 유지하세요. diff --git a/docs/ko-KR/skills/golang-testing/SKILL.md b/docs/ko-KR/skills/golang-testing/SKILL.md new file mode 100644 index 00000000..05ce0e48 --- /dev/null +++ b/docs/ko-KR/skills/golang-testing/SKILL.md @@ -0,0 +1,720 @@ +--- +name: golang-testing +description: 테이블 주도 테스트, 서브테스트, 벤치마크, 퍼징, 테스트 커버리지를 포함한 Go 테스팅 패턴. 관용적 Go 관행과 함께 TDD 방법론을 따릅니다. +origin: ECC +--- + +# Go 테스팅 패턴 + +TDD 방법론을 따르는 신뢰할 수 있고 유지보수 가능한 테스트 작성을 위한 포괄적인 Go 테스팅 패턴. + +## 활성화 시점 + +- 새로운 Go 함수나 메서드 작성 시 +- 기존 코드에 테스트 커버리지 추가 시 +- 성능이 중요한 코드에 벤치마크 생성 시 +- 입력 유효성 검사를 위한 퍼즈 테스트 구현 시 +- Go 프로젝트에서 TDD 워크플로우 따를 시 + +## Go에서의 TDD 워크플로우 + +### RED-GREEN-REFACTOR 사이클 + +``` +RED → Write a failing test first +GREEN → Write minimal code to pass the test +REFACTOR → Improve code while keeping tests green +REPEAT → Continue with next requirement +``` + +### Go에서의 단계별 TDD + +```go +// Step 1: Define the interface/signature +// calculator.go +package calculator + +func Add(a, b int) int { + panic("not implemented") // Placeholder +} + +// Step 2: Write failing test (RED) +// calculator_test.go +package calculator + +import "testing" + +func TestAdd(t *testing.T) { + got := Add(2, 3) + want := 5 + if got != want { + t.Errorf("Add(2, 3) = %d; want %d", got, want) + } +} + +// Step 3: Run test - verify FAIL +// $ go test +// --- FAIL: TestAdd (0.00s) +// panic: not implemented + +// Step 4: Implement minimal code (GREEN) +func Add(a, b int) int { + return a + b +} + +// Step 5: Run test - verify PASS +// $ go test +// PASS + +// Step 6: Refactor if needed, verify tests still pass +``` + +## 테이블 주도 테스트 + +Go 테스트의 표준 패턴. 최소한의 코드로 포괄적인 커버리지를 가능하게 합니다. + +```go +func TestAdd(t *testing.T) { + tests := []struct { + name string + a, b int + expected int + }{ + {"positive numbers", 2, 3, 5}, + {"negative numbers", -1, -2, -3}, + {"zero values", 0, 0, 0}, + {"mixed signs", -1, 1, 0}, + {"large numbers", 1000000, 2000000, 3000000}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Add(tt.a, tt.b) + if got != tt.expected { + t.Errorf("Add(%d, %d) = %d; want %d", + tt.a, tt.b, got, tt.expected) + } + }) + } +} +``` + +### 에러 케이스가 있는 테이블 주도 테스트 + +```go +func TestParseConfig(t *testing.T) { + tests := []struct { + name string + input string + want *Config + wantErr bool + }{ + { + name: "valid config", + input: `{"host": "localhost", "port": 8080}`, + want: &Config{Host: "localhost", Port: 8080}, + }, + { + name: "invalid JSON", + input: `{invalid}`, + wantErr: true, + }, + { + name: "empty input", + input: "", + wantErr: true, + }, + { + name: "minimal config", + input: `{}`, + want: &Config{}, // Zero value config + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseConfig(tt.input) + + if tt.wantErr { + if err == nil { + t.Error("expected error, got nil") + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("got %+v; want %+v", got, tt.want) + } + }) + } +} +``` + +## 서브테스트 및 서브벤치마크 + +### 관련 테스트 구성 + +```go +func TestUser(t *testing.T) { + // Setup shared by all subtests + db := setupTestDB(t) + + t.Run("Create", func(t *testing.T) { + user := &User{Name: "Alice"} + err := db.CreateUser(user) + if err != nil { + t.Fatalf("CreateUser failed: %v", err) + } + if user.ID == "" { + t.Error("expected user ID to be set") + } + }) + + t.Run("Get", func(t *testing.T) { + user, err := db.GetUser("alice-id") + if err != nil { + t.Fatalf("GetUser failed: %v", err) + } + if user.Name != "Alice" { + t.Errorf("got name %q; want %q", user.Name, "Alice") + } + }) + + t.Run("Update", func(t *testing.T) { + // ... + }) + + t.Run("Delete", func(t *testing.T) { + // ... + }) +} +``` + +### 병렬 서브테스트 + +```go +func TestParallel(t *testing.T) { + tests := []struct { + name string + input string + }{ + {"case1", "input1"}, + {"case2", "input2"}, + {"case3", "input3"}, + } + + for _, tt := range tests { + tt := tt // Capture range variable + t.Run(tt.name, func(t *testing.T) { + t.Parallel() // Run subtests in parallel + result := Process(tt.input) + // assertions... + _ = result + }) + } +} +``` + +## 테스트 헬퍼 + +### 헬퍼 함수 + +```go +func setupTestDB(t *testing.T) *sql.DB { + t.Helper() // Marks this as a helper function + + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("failed to open database: %v", err) + } + + // Cleanup when test finishes + t.Cleanup(func() { + db.Close() + }) + + // Run migrations + if _, err := db.Exec(schema); err != nil { + t.Fatalf("failed to create schema: %v", err) + } + + return db +} + +func assertNoError(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + +func assertEqual[T comparable](t *testing.T, got, want T) { + t.Helper() + if got != want { + t.Errorf("got %v; want %v", got, want) + } +} +``` + +### 임시 파일 및 디렉터리 + +```go +func TestFileProcessing(t *testing.T) { + // Create temp directory - automatically cleaned up + tmpDir := t.TempDir() + + // Create test file + testFile := filepath.Join(tmpDir, "test.txt") + err := os.WriteFile(testFile, []byte("test content"), 0644) + if err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + // Run test + result, err := ProcessFile(testFile) + if err != nil { + t.Fatalf("ProcessFile failed: %v", err) + } + + // Assert... + _ = result +} +``` + +## 골든 파일 + +`testdata/`에 저장된 예상 출력 파일에 대한 테스트. + +```go +var update = flag.Bool("update", false, "update golden files") + +func TestRender(t *testing.T) { + tests := []struct { + name string + input Template + }{ + {"simple", Template{Name: "test"}}, + {"complex", Template{Name: "test", Items: []string{"a", "b"}}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Render(tt.input) + + golden := filepath.Join("testdata", tt.name+".golden") + + if *update { + // Update golden file: go test -update + err := os.WriteFile(golden, got, 0644) + if err != nil { + t.Fatalf("failed to update golden file: %v", err) + } + } + + want, err := os.ReadFile(golden) + if err != nil { + t.Fatalf("failed to read golden file: %v", err) + } + + if !bytes.Equal(got, want) { + t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, want) + } + }) + } +} +``` + +## 인터페이스를 사용한 모킹 + +### 인터페이스 기반 모킹 + +```go +// Define interface for dependencies +type UserRepository interface { + GetUser(id string) (*User, error) + SaveUser(user *User) error +} + +// Production implementation +type PostgresUserRepository struct { + db *sql.DB +} + +func (r *PostgresUserRepository) GetUser(id string) (*User, error) { + // Real database query +} + +// Mock implementation for tests +type MockUserRepository struct { + GetUserFunc func(id string) (*User, error) + SaveUserFunc func(user *User) error +} + +func (m *MockUserRepository) GetUser(id string) (*User, error) { + return m.GetUserFunc(id) +} + +func (m *MockUserRepository) SaveUser(user *User) error { + return m.SaveUserFunc(user) +} + +// Test using mock +func TestUserService(t *testing.T) { + mock := &MockUserRepository{ + GetUserFunc: func(id string) (*User, error) { + if id == "123" { + return &User{ID: "123", Name: "Alice"}, nil + } + return nil, ErrNotFound + }, + } + + service := NewUserService(mock) + + user, err := service.GetUserProfile("123") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if user.Name != "Alice" { + t.Errorf("got name %q; want %q", user.Name, "Alice") + } +} +``` + +## 벤치마크 + +### 기본 벤치마크 + +```go +func BenchmarkProcess(b *testing.B) { + data := generateTestData(1000) + b.ResetTimer() // Don't count setup time + + for i := 0; i < b.N; i++ { + Process(data) + } +} + +// Run: go test -bench=BenchmarkProcess -benchmem +// Output: BenchmarkProcess-8 10000 105234 ns/op 4096 B/op 10 allocs/op +``` + +### 다양한 크기의 벤치마크 + +```go +func BenchmarkSort(b *testing.B) { + sizes := []int{100, 1000, 10000, 100000} + + for _, size := range sizes { + b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) { + data := generateRandomSlice(size) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + // Make a copy to avoid sorting already sorted data + tmp := make([]int, len(data)) + copy(tmp, data) + sort.Ints(tmp) + } + }) + } +} +``` + +### 메모리 할당 벤치마크 + +```go +func BenchmarkStringConcat(b *testing.B) { + parts := []string{"hello", "world", "foo", "bar", "baz"} + + b.Run("plus", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var s string + for _, p := range parts { + s += p + } + _ = s + } + }) + + b.Run("builder", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var sb strings.Builder + for _, p := range parts { + sb.WriteString(p) + } + _ = sb.String() + } + }) + + b.Run("join", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = strings.Join(parts, "") + } + }) +} +``` + +## 퍼징 (Go 1.18+) + +### 기본 퍼즈 테스트 + +```go +func FuzzParseJSON(f *testing.F) { + // Add seed corpus + f.Add(`{"name": "test"}`) + f.Add(`{"count": 123}`) + f.Add(`[]`) + f.Add(`""`) + + f.Fuzz(func(t *testing.T, input string) { + var result map[string]interface{} + err := json.Unmarshal([]byte(input), &result) + + if err != nil { + // Invalid JSON is expected for random input + return + } + + // If parsing succeeded, re-encoding should work + _, err = json.Marshal(result) + if err != nil { + t.Errorf("Marshal failed after successful Unmarshal: %v", err) + } + }) +} + +// Run: go test -fuzz=FuzzParseJSON -fuzztime=30s +``` + +### 다중 입력 퍼즈 테스트 + +```go +func FuzzCompare(f *testing.F) { + f.Add("hello", "world") + f.Add("", "") + f.Add("abc", "abc") + + f.Fuzz(func(t *testing.T, a, b string) { + result := Compare(a, b) + + // Property: Compare(a, a) should always equal 0 + if a == b && result != 0 { + t.Errorf("Compare(%q, %q) = %d; want 0", a, b, result) + } + + // Property: Compare(a, b) and Compare(b, a) should have opposite signs + reverse := Compare(b, a) + if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) { + if result != 0 || reverse != 0 { + t.Errorf("Compare(%q, %q) = %d, Compare(%q, %q) = %d; inconsistent", + a, b, result, b, a, reverse) + } + } + }) +} +``` + +## 테스트 커버리지 + +### 커버리지 실행 + +```bash +# Basic coverage +go test -cover ./... + +# Generate coverage profile +go test -coverprofile=coverage.out ./... + +# View coverage in browser +go tool cover -html=coverage.out + +# View coverage by function +go tool cover -func=coverage.out + +# Coverage with race detection +go test -race -coverprofile=coverage.out ./... +``` + +### 커버리지 목표 + +| 코드 유형 | 목표 | +|-----------|--------| +| 핵심 비즈니스 로직 | 100% | +| 공개 API | 90%+ | +| 일반 코드 | 80%+ | +| 생성된 코드 | 제외 | + +### 생성된 코드를 커버리지에서 제외 + +```go +//go:generate mockgen -source=interface.go -destination=mock_interface.go + +// In coverage profile, exclude with build tags: +// go test -cover -tags=!generate ./... +``` + +## HTTP 핸들러 테스팅 + +```go +func TestHealthHandler(t *testing.T) { + // Create request + req := httptest.NewRequest(http.MethodGet, "/health", nil) + w := httptest.NewRecorder() + + // Call handler + HealthHandler(w, req) + + // Check response + resp := w.Result() + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Errorf("got status %d; want %d", resp.StatusCode, http.StatusOK) + } + + body, _ := io.ReadAll(resp.Body) + if string(body) != "OK" { + t.Errorf("got body %q; want %q", body, "OK") + } +} + +func TestAPIHandler(t *testing.T) { + tests := []struct { + name string + method string + path string + body string + wantStatus int + wantBody string + }{ + { + name: "get user", + method: http.MethodGet, + path: "/users/123", + wantStatus: http.StatusOK, + wantBody: `{"id":"123","name":"Alice"}`, + }, + { + name: "not found", + method: http.MethodGet, + path: "/users/999", + wantStatus: http.StatusNotFound, + }, + { + name: "create user", + method: http.MethodPost, + path: "/users", + body: `{"name":"Bob"}`, + wantStatus: http.StatusCreated, + }, + } + + handler := NewAPIHandler() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var body io.Reader + if tt.body != "" { + body = strings.NewReader(tt.body) + } + + req := httptest.NewRequest(tt.method, tt.path, body) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + handler.ServeHTTP(w, req) + + if w.Code != tt.wantStatus { + t.Errorf("got status %d; want %d", w.Code, tt.wantStatus) + } + + if tt.wantBody != "" && w.Body.String() != tt.wantBody { + t.Errorf("got body %q; want %q", w.Body.String(), tt.wantBody) + } + }) + } +} +``` + +## 테스팅 명령어 + +```bash +# Run all tests +go test ./... + +# Run tests with verbose output +go test -v ./... + +# Run specific test +go test -run TestAdd ./... + +# Run tests matching pattern +go test -run "TestUser/Create" ./... + +# Run tests with race detector +go test -race ./... + +# Run tests with coverage +go test -cover -coverprofile=coverage.out ./... + +# Run short tests only +go test -short ./... + +# Run tests with timeout +go test -timeout 30s ./... + +# Run benchmarks +go test -bench=. -benchmem ./... + +# Run fuzzing +go test -fuzz=FuzzParse -fuzztime=30s ./... + +# Count test runs (for flaky test detection) +go test -count=10 ./... +``` + +## 모범 사례 + +**해야 할 것:** +- 테스트를 먼저 작성 (TDD) +- 포괄적인 커버리지를 위해 테이블 주도 테스트 사용 +- 구현이 아닌 동작을 테스트 +- 헬퍼 함수에서 `t.Helper()` 사용 +- 독립적인 테스트에 `t.Parallel()` 사용 +- `t.Cleanup()`으로 리소스 정리 +- 시나리오를 설명하는 의미 있는 테스트 이름 사용 + +**하지 말아야 할 것:** +- 비공개 함수를 직접 테스트 (공개 API를 통해 테스트) +- 테스트에서 `time.Sleep()` 사용 (채널이나 조건 사용) +- 불안정한 테스트 무시 (수정하거나 제거) +- 모든 것을 모킹 (가능하면 통합 테스트 선호) +- 에러 경로 테스트 생략 + +## CI/CD 통합 + +```yaml +# GitHub Actions example +test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.22' + + - name: Run tests + run: go test -race -coverprofile=coverage.out ./... + + - name: Check coverage + run: | + go tool cover -func=coverage.out | grep total | awk '{print $3}' | \ + awk -F'%' '{if ($1 < 80) exit 1}' +``` + +**기억하세요**: 테스트는 문서입니다. 코드가 어떻게 사용되어야 하는지를 보여줍니다. 명확하게 작성하고 최신 상태로 유지하세요. diff --git a/docs/ko-KR/skills/iterative-retrieval/SKILL.md b/docs/ko-KR/skills/iterative-retrieval/SKILL.md new file mode 100644 index 00000000..2ab52325 --- /dev/null +++ b/docs/ko-KR/skills/iterative-retrieval/SKILL.md @@ -0,0 +1,211 @@ +--- +name: iterative-retrieval +description: 서브에이전트 컨텍스트 문제를 해결하기 위한 점진적 컨텍스트 검색 개선 패턴 +origin: ECC +--- + +# 반복적 검색 패턴 + +서브에이전트가 작업을 시작하기 전까지 필요한 컨텍스트를 알 수 없는 멀티 에이전트 워크플로우의 "컨텍스트 문제"를 해결합니다. + +## 활성화 시점 + +- 사전에 예측할 수 없는 코드베이스 컨텍스트가 필요한 서브에이전트를 생성할 때 +- 컨텍스트가 점진적으로 개선되는 멀티 에이전트 워크플로우를 구축할 때 +- 에이전트 작업에서 "컨텍스트 초과" 또는 "컨텍스트 누락" 실패를 겪을 때 +- 코드 탐색을 위한 RAG 유사 검색 파이프라인을 설계할 때 +- 에이전트 오케스트레이션에서 토큰 사용량을 최적화할 때 + +## 문제 + +서브에이전트는 제한된 컨텍스트로 생성됩니다. 다음을 알 수 없습니다: +- 관련 코드가 포함된 파일 +- 코드베이스에 존재하는 패턴 +- 프로젝트에서 사용하는 용어 + +표준 접근법의 실패: +- **모든 것을 전송**: 컨텍스트 제한 초과 +- **아무것도 전송하지 않음**: 에이전트가 중요한 정보를 갖지 못함 +- **필요한 것을 추측**: 종종 잘못됨 + +## 해결책: 반복적 검색 + +컨텍스트를 점진적으로 개선하는 4단계 루프: + +``` +┌─────────────────────────────────────────────┐ +│ │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ DISPATCH │─────▶│ EVALUATE │ │ +│ └──────────┘ └──────────┘ │ +│ ▲ │ │ +│ │ ▼ │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ LOOP │◀─────│ REFINE │ │ +│ └──────────┘ └──────────┘ │ +│ │ +│ Max 3 cycles, then proceed │ +└─────────────────────────────────────────────┘ +``` + +### 1단계: DISPATCH + +후보 파일을 수집하기 위한 초기 광범위 쿼리: + +```javascript +// Start with high-level intent +const initialQuery = { + patterns: ['src/**/*.ts', 'lib/**/*.ts'], + keywords: ['authentication', 'user', 'session'], + excludes: ['*.test.ts', '*.spec.ts'] +}; + +// Dispatch to retrieval agent +const candidates = await retrieveFiles(initialQuery); +``` + +### 2단계: EVALUATE + +검색된 콘텐츠의 관련성 평가: + +```javascript +function evaluateRelevance(files, task) { + return files.map(file => ({ + path: file.path, + relevance: scoreRelevance(file.content, task), + reason: explainRelevance(file.content, task), + missingContext: identifyGaps(file.content, task) + })); +} +``` + +점수 기준: +- **높음 (0.8-1.0)**: 대상 기능을 직접 구현 +- **중간 (0.5-0.7)**: 관련 패턴이나 타입을 포함 +- **낮음 (0.2-0.4)**: 간접적으로 관련 +- **없음 (0-0.2)**: 관련 없음, 제외 + +### 3단계: REFINE + +평가를 기반으로 검색 기준 업데이트: + +```javascript +function refineQuery(evaluation, previousQuery) { + return { + // Add new patterns discovered in high-relevance files + patterns: [...previousQuery.patterns, ...extractPatterns(evaluation)], + + // Add terminology found in codebase + keywords: [...previousQuery.keywords, ...extractKeywords(evaluation)], + + // Exclude confirmed irrelevant paths + excludes: [...previousQuery.excludes, ...evaluation + .filter(e => e.relevance < 0.2) + .map(e => e.path) + ], + + // Target specific gaps + focusAreas: evaluation + .flatMap(e => e.missingContext) + .filter(unique) + }; +} +``` + +### 4단계: LOOP + +개선된 기준으로 반복 (최대 3회): + +```javascript +async function iterativeRetrieve(task, maxCycles = 3) { + let query = createInitialQuery(task); + let bestContext = []; + + for (let cycle = 0; cycle < maxCycles; cycle++) { + const candidates = await retrieveFiles(query); + const evaluation = evaluateRelevance(candidates, task); + + // Check if we have sufficient context + const highRelevance = evaluation.filter(e => e.relevance >= 0.7); + if (highRelevance.length >= 3 && !hasCriticalGaps(evaluation)) { + return highRelevance; + } + + // Refine and continue + query = refineQuery(evaluation, query); + bestContext = mergeContext(bestContext, highRelevance); + } + + return bestContext; +} +``` + +## 실용적인 예시 + +### 예시 1: 버그 수정 컨텍스트 + +``` +Task: "Fix the authentication token expiry bug" + +Cycle 1: + DISPATCH: Search for "token", "auth", "expiry" in src/** + EVALUATE: Found auth.ts (0.9), tokens.ts (0.8), user.ts (0.3) + REFINE: Add "refresh", "jwt" keywords; exclude user.ts + +Cycle 2: + DISPATCH: Search refined terms + EVALUATE: Found session-manager.ts (0.95), jwt-utils.ts (0.85) + REFINE: Sufficient context (2 high-relevance files) + +Result: auth.ts, tokens.ts, session-manager.ts, jwt-utils.ts +``` + +### 예시 2: 기능 구현 + +``` +Task: "Add rate limiting to API endpoints" + +Cycle 1: + DISPATCH: Search "rate", "limit", "api" in routes/** + EVALUATE: No matches - codebase uses "throttle" terminology + REFINE: Add "throttle", "middleware" keywords + +Cycle 2: + DISPATCH: Search refined terms + EVALUATE: Found throttle.ts (0.9), middleware/index.ts (0.7) + REFINE: Need router patterns + +Cycle 3: + DISPATCH: Search "router", "express" patterns + EVALUATE: Found router-setup.ts (0.8) + REFINE: Sufficient context + +Result: throttle.ts, middleware/index.ts, router-setup.ts +``` + +## 에이전트와의 통합 + +에이전트 프롬프트에서 사용: + +```markdown +When retrieving context for this task: +1. Start with broad keyword search +2. Evaluate each file's relevance (0-1 scale) +3. Identify what context is still missing +4. Refine search criteria and repeat (max 3 cycles) +5. Return files with relevance >= 0.7 +``` + +## 모범 사례 + +1. **광범위하게 시작하여 점진적으로 좁히기** - 초기 쿼리를 과도하게 지정하지 않기 +2. **코드베이스 용어 학습** - 첫 번째 사이클에서 주로 네이밍 컨벤션이 드러남 +3. **누락된 것 추적** - 명시적 격차 식별이 개선을 주도 +4. **"충분히 좋은" 수준에서 중단** - 관련성 높은 파일 3개가 보통 수준의 파일 10개보다 나음 +5. **자신 있게 제외** - 관련성 낮은 파일은 관련성이 높아지지 않음 + +## 관련 항목 + +- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 서브에이전트 오케스트레이션 섹션 +- `continuous-learning` 스킬 - 시간이 지남에 따라 개선되는 패턴 +- `~/.claude/agents/`의 에이전트 정의 diff --git a/docs/ko-KR/skills/postgres-patterns/SKILL.md b/docs/ko-KR/skills/postgres-patterns/SKILL.md new file mode 100644 index 00000000..1007fdbb --- /dev/null +++ b/docs/ko-KR/skills/postgres-patterns/SKILL.md @@ -0,0 +1,147 @@ +--- +name: postgres-patterns +description: 쿼리 최적화, 스키마 설계, 인덱싱, 보안을 위한 PostgreSQL 데이터베이스 패턴. Supabase 모범 사례 기반. +origin: ECC +--- + +# PostgreSQL 패턴 + +PostgreSQL 모범 사례 빠른 참조. 자세한 가이드는 `database-reviewer` 에이전트를 사용하세요. + +## 활성화 시점 + +- SQL 쿼리 또는 마이그레이션을 작성할 때 +- 데이터베이스 스키마를 설계할 때 +- 느린 쿼리를 문제 해결할 때 +- Row Level Security를 구현할 때 +- 커넥션 풀링을 설정할 때 + +## 빠른 참조 + +### 인덱스 치트 시트 + +| 쿼리 패턴 | 인덱스 유형 | 예시 | +|--------------|------------|---------| +| `WHERE col = value` | B-tree (기본값) | `CREATE INDEX idx ON t (col)` | +| `WHERE col > value` | B-tree | `CREATE INDEX idx ON t (col)` | +| `WHERE a = x AND b > y` | Composite | `CREATE INDEX idx ON t (a, b)` | +| `WHERE jsonb @> '{}'` | GIN | `CREATE INDEX idx ON t USING gin (col)` | +| `WHERE tsv @@ query` | GIN | `CREATE INDEX idx ON t USING gin (col)` | +| 시계열 범위 | BRIN | `CREATE INDEX idx ON t USING brin (col)` | + +### 데이터 타입 빠른 참조 + +| 사용 사례 | 올바른 타입 | 지양 | +|----------|-------------|-------| +| ID | `bigint` | `int`, random UUID | +| 문자열 | `text` | `varchar(255)` | +| 타임스탬프 | `timestamptz` | `timestamp` | +| 금액 | `numeric(10,2)` | `float` | +| 플래그 | `boolean` | `varchar`, `int` | + +### 일반 패턴 + +**복합 인덱스 순서:** +```sql +-- Equality columns first, then range columns +CREATE INDEX idx ON orders (status, created_at); +-- Works for: WHERE status = 'pending' AND created_at > '2024-01-01' +``` + +**커버링 인덱스:** +```sql +CREATE INDEX idx ON users (email) INCLUDE (name, created_at); +-- Avoids table lookup for SELECT email, name, created_at +``` + +**부분 인덱스:** +```sql +CREATE INDEX idx ON users (email) WHERE deleted_at IS NULL; +-- Smaller index, only includes active users +``` + +**RLS 정책 (최적화):** +```sql +CREATE POLICY policy ON orders + USING ((SELECT auth.uid()) = user_id); -- Wrap in SELECT! +``` + +**UPSERT:** +```sql +INSERT INTO settings (user_id, key, value) +VALUES (123, 'theme', 'dark') +ON CONFLICT (user_id, key) +DO UPDATE SET value = EXCLUDED.value; +``` + +**커서 페이지네이션:** +```sql +SELECT * FROM products WHERE id > $last_id ORDER BY id LIMIT 20; +-- O(1) vs OFFSET which is O(n) +``` + +**큐 처리:** +```sql +UPDATE jobs SET status = 'processing' +WHERE id = ( + SELECT id FROM jobs WHERE status = 'pending' + ORDER BY created_at LIMIT 1 + FOR UPDATE SKIP LOCKED +) RETURNING *; +``` + +### 안티패턴 감지 + +```sql +-- Find unindexed foreign keys +SELECT conrelid::regclass, a.attname +FROM pg_constraint c +JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey) +WHERE c.contype = 'f' + AND NOT EXISTS ( + SELECT 1 FROM pg_index i + WHERE i.indrelid = c.conrelid AND a.attnum = ANY(i.indkey) + ); + +-- Find slow queries +SELECT query, mean_exec_time, calls +FROM pg_stat_statements +WHERE mean_exec_time > 100 +ORDER BY mean_exec_time DESC; + +-- Check table bloat +SELECT relname, n_dead_tup, last_vacuum +FROM pg_stat_user_tables +WHERE n_dead_tup > 1000 +ORDER BY n_dead_tup DESC; +``` + +### 구성 템플릿 + +```sql +-- Connection limits (adjust for RAM) +ALTER SYSTEM SET max_connections = 100; +ALTER SYSTEM SET work_mem = '8MB'; + +-- Timeouts +ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s'; +ALTER SYSTEM SET statement_timeout = '30s'; + +-- Monitoring +CREATE EXTENSION IF NOT EXISTS pg_stat_statements; + +-- Security defaults +REVOKE ALL ON SCHEMA public FROM public; + +SELECT pg_reload_conf(); +``` + +## 관련 항목 + +- 에이전트: `database-reviewer` - 전체 데이터베이스 리뷰 워크플로우 +- 스킬: `clickhouse-io` - ClickHouse 분석 패턴 +- 스킬: `backend-patterns` - API 및 백엔드 패턴 + +--- + +*Supabase Agent Skills 기반 (크레딧: Supabase 팀) (MIT License)* diff --git a/docs/ko-KR/skills/project-guidelines-example/SKILL.md b/docs/ko-KR/skills/project-guidelines-example/SKILL.md new file mode 100644 index 00000000..a70dcc3a --- /dev/null +++ b/docs/ko-KR/skills/project-guidelines-example/SKILL.md @@ -0,0 +1,349 @@ +--- +name: project-guidelines-example +description: "실제 프로덕션 애플리케이션을 기반으로 한 프로젝트별 스킬 템플릿 예시." +origin: ECC +--- + +# 프로젝트 가이드라인 스킬 (예시) + +이것은 프로젝트별 스킬의 예시입니다. 자신의 프로젝트에 맞는 템플릿으로 사용하세요. + +실제 프로덕션 애플리케이션을 기반으로 합니다: [Zenith](https://zenith.chat) - AI 기반 고객 발견 플랫폼. + +## 사용 시점 + +이 스킬이 설계된 특정 프로젝트에서 작업할 때 참조하세요. 프로젝트 스킬에는 다음이 포함됩니다: +- 아키텍처 개요 +- 파일 구조 +- 코드 패턴 +- 테스팅 요구사항 +- 배포 워크플로우 + +--- + +## 아키텍처 개요 + +**기술 스택:** +- **Frontend**: Next.js 15 (App Router), TypeScript, React +- **Backend**: FastAPI (Python), Pydantic 모델 +- **Database**: Supabase (PostgreSQL) +- **AI**: Claude API (도구 호출 및 구조화된 출력) +- **Deployment**: Google Cloud Run +- **Testing**: Playwright (E2E), pytest (백엔드), React Testing Library + +**서비스:** +``` +┌─────────────────────────────────────────────────────────────┐ +│ Frontend │ +│ Next.js 15 + TypeScript + TailwindCSS │ +│ Deployed: Vercel / Cloud Run │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Backend │ +│ FastAPI + Python 3.11 + Pydantic │ +│ Deployed: Cloud Run │ +└─────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────┐ + │ Supabase │ │ Claude │ │ Redis │ + │ Database │ │ API │ │ Cache │ + └──────────┘ └──────────┘ └──────────┘ +``` + +--- + +## 파일 구조 + +``` +project/ +├── frontend/ +│ └── src/ +│ ├── app/ # Next.js app router 페이지 +│ │ ├── api/ # API 라우트 +│ │ ├── (auth)/ # 인증 보호 라우트 +│ │ └── workspace/ # 메인 앱 워크스페이스 +│ ├── components/ # React 컴포넌트 +│ │ ├── ui/ # 기본 UI 컴포넌트 +│ │ ├── forms/ # 폼 컴포넌트 +│ │ └── layouts/ # 레이아웃 컴포넌트 +│ ├── hooks/ # 커스텀 React hooks +│ ├── lib/ # 유틸리티 +│ ├── types/ # TypeScript 정의 +│ └── config/ # 설정 +│ +├── backend/ +│ ├── routers/ # FastAPI 라우트 핸들러 +│ ├── models.py # Pydantic 모델 +│ ├── main.py # FastAPI 앱 엔트리 +│ ├── auth_system.py # 인증 +│ ├── database.py # 데이터베이스 작업 +│ ├── services/ # 비즈니스 로직 +│ └── tests/ # pytest 테스트 +│ +├── deploy/ # 배포 설정 +├── docs/ # 문서 +└── scripts/ # 유틸리티 스크립트 +``` + +--- + +## 코드 패턴 + +### API 응답 형식 (FastAPI) + +```python +from pydantic import BaseModel +from typing import Generic, TypeVar, Optional + +T = TypeVar('T') + +class ApiResponse(BaseModel, Generic[T]): + success: bool + data: Optional[T] = None + error: Optional[str] = None + + @classmethod + def ok(cls, data: T) -> "ApiResponse[T]": + return cls(success=True, data=data) + + @classmethod + def fail(cls, error: str) -> "ApiResponse[T]": + return cls(success=False, error=error) +``` + +### Frontend API 호출 (TypeScript) + +```typescript +interface ApiResponse { + success: boolean + data?: T + error?: string +} + +async function fetchApi( + endpoint: string, + options?: RequestInit +): Promise> { + try { + const response = await fetch(`/api${endpoint}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers, + }, + }) + + if (!response.ok) { + return { success: false, error: `HTTP ${response.status}` } + } + + return await response.json() + } catch (error) { + return { success: false, error: String(error) } + } +} +``` + +### Claude AI 통합 (구조화된 출력) + +```python +from anthropic import Anthropic +from pydantic import BaseModel + +class AnalysisResult(BaseModel): + summary: str + key_points: list[str] + confidence: float + +async def analyze_with_claude(content: str) -> AnalysisResult: + client = Anthropic() + + response = client.messages.create( + model="claude-sonnet-4-5-20250514", + max_tokens=1024, + messages=[{"role": "user", "content": content}], + tools=[{ + "name": "provide_analysis", + "description": "Provide structured analysis", + "input_schema": AnalysisResult.model_json_schema() + }], + tool_choice={"type": "tool", "name": "provide_analysis"} + ) + + # Extract tool use result + tool_use = next( + block for block in response.content + if block.type == "tool_use" + ) + + return AnalysisResult(**tool_use.input) +``` + +### 커스텀 Hooks (React) + +```typescript +import { useState, useCallback } from 'react' + +interface UseApiState { + data: T | null + loading: boolean + error: string | null +} + +export function useApi( + fetchFn: () => Promise> +) { + const [state, setState] = useState>({ + data: null, + loading: false, + error: null, + }) + + const execute = useCallback(async () => { + setState(prev => ({ ...prev, loading: true, error: null })) + + const result = await fetchFn() + + if (result.success) { + setState({ data: result.data!, loading: false, error: null }) + } else { + setState({ data: null, loading: false, error: result.error! }) + } + }, [fetchFn]) + + return { ...state, execute } +} +``` + +--- + +## 테스팅 요구사항 + +### Backend (pytest) + +```bash +# Run all tests +poetry run pytest tests/ + +# Run with coverage +poetry run pytest tests/ --cov=. --cov-report=html + +# Run specific test file +poetry run pytest tests/test_auth.py -v +``` + +**테스트 구조:** +```python +import pytest +from httpx import AsyncClient +from main import app + +@pytest.fixture +async def client(): + async with AsyncClient(app=app, base_url="http://test") as ac: + yield ac + +@pytest.mark.asyncio +async def test_health_check(client: AsyncClient): + response = await client.get("/health") + assert response.status_code == 200 + assert response.json()["status"] == "healthy" +``` + +### Frontend (React Testing Library) + +```bash +# Run tests +npm run test + +# Run with coverage +npm run test -- --coverage + +# Run E2E tests +npm run test:e2e +``` + +**테스트 구조:** +```typescript +import { render, screen, fireEvent } from '@testing-library/react' +import { WorkspacePanel } from './WorkspacePanel' + +describe('WorkspacePanel', () => { + it('renders workspace correctly', () => { + render() + expect(screen.getByRole('main')).toBeInTheDocument() + }) + + it('handles session creation', async () => { + render() + fireEvent.click(screen.getByText('New Session')) + expect(await screen.findByText('Session created')).toBeInTheDocument() + }) +}) +``` + +--- + +## 배포 워크플로우 + +### 배포 전 체크리스트 + +- [ ] 모든 테스트가 로컬에서 통과 +- [ ] `npm run build` 성공 (frontend) +- [ ] `poetry run pytest` 통과 (backend) +- [ ] 하드코딩된 시크릿 없음 +- [ ] 환경 변수 문서화됨 +- [ ] 데이터베이스 마이그레이션 준비됨 + +### 배포 명령어 + +```bash +# Build and deploy frontend +cd frontend && npm run build +gcloud run deploy frontend --source . + +# Build and deploy backend +cd backend +gcloud run deploy backend --source . +``` + +### 환경 변수 + +```bash +# Frontend (.env.local) +NEXT_PUBLIC_API_URL=https://api.example.com +NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... + +# Backend (.env) +DATABASE_URL=postgresql://... +ANTHROPIC_API_KEY=sk-ant-... +SUPABASE_URL=https://xxx.supabase.co +SUPABASE_KEY=eyJ... +``` + +--- + +## 핵심 규칙 + +1. **코드, 주석, 문서에 이모지 없음** +2. **불변성** - 객체나 배열을 절대 변형하지 않음 +3. **TDD** - 구현 전에 테스트 작성 +4. **80% 커버리지** 최소 +5. **작은 파일 여러 개** - 200-400줄이 일반적, 800줄 최대 +6. **프로덕션 코드에 console.log 없음** +7. **적절한 에러 처리** (try/catch 사용) +8. **입력 유효성 검사** (Pydantic/Zod 사용) + +--- + +## 관련 스킬 + +- `coding-standards.md` - 일반 코딩 모범 사례 +- `backend-patterns.md` - API 및 데이터베이스 패턴 +- `frontend-patterns.md` - React 및 Next.js 패턴 +- `tdd-workflow/` - 테스트 주도 개발 방법론 diff --git a/docs/ko-KR/skills/security-review/SKILL.md b/docs/ko-KR/skills/security-review/SKILL.md new file mode 100644 index 00000000..6b6878cd --- /dev/null +++ b/docs/ko-KR/skills/security-review/SKILL.md @@ -0,0 +1,495 @@ +--- +name: security-review +description: 인증 추가, 사용자 입력 처리, 시크릿 관리, API 엔드포인트 생성, 결제/민감한 기능 구현 시 이 스킬을 사용하세요. 포괄적인 보안 체크리스트와 패턴을 제공합니다. +origin: ECC +--- + +# 보안 리뷰 스킬 + +이 스킬은 모든 코드가 보안 모범 사례를 따르고 잠재적 취약점을 식별하도록 보장합니다. + +## 활성화 시점 + +- 인증 또는 권한 부여 구현 시 +- 사용자 입력 또는 파일 업로드 처리 시 +- 새로운 API 엔드포인트 생성 시 +- 시크릿 또는 자격 증명 작업 시 +- 결제 기능 구현 시 +- 민감한 데이터 저장 또는 전송 시 +- 서드파티 API 통합 시 + +## 보안 체크리스트 + +### 1. 시크릿 관리 + +#### 절대 하지 말아야 할 것 +```typescript +const apiKey = "sk-proj-xxxxx" // Hardcoded secret +const dbPassword = "password123" // In source code +``` + +#### 반드시 해야 할 것 +```typescript +const apiKey = process.env.OPENAI_API_KEY +const dbUrl = process.env.DATABASE_URL + +// Verify secrets exist +if (!apiKey) { + throw new Error('OPENAI_API_KEY not configured') +} +``` + +#### 확인 단계 +- [ ] 하드코딩된 API 키, 토큰, 비밀번호 없음 +- [ ] 모든 시크릿이 환경 변수에 저장됨 +- [ ] `.env.local`이 .gitignore에 포함됨 +- [ ] git 히스토리에 시크릿 없음 +- [ ] 프로덕션 시크릿이 호스팅 플랫폼(Vercel, Railway)에 저장됨 + +### 2. 입력 유효성 검사 + +#### 항상 사용자 입력을 검증할 것 +```typescript +import { z } from 'zod' + +// Define validation schema +const CreateUserSchema = z.object({ + email: z.string().email(), + name: z.string().min(1).max(100), + age: z.number().int().min(0).max(150) +}) + +// Validate before processing +export async function createUser(input: unknown) { + try { + const validated = CreateUserSchema.parse(input) + return await db.users.create(validated) + } catch (error) { + if (error instanceof z.ZodError) { + return { success: false, errors: error.errors } + } + throw error + } +} +``` + +#### 파일 업로드 유효성 검사 +```typescript +function validateFileUpload(file: File) { + // Size check (5MB max) + const maxSize = 5 * 1024 * 1024 + if (file.size > maxSize) { + throw new Error('File too large (max 5MB)') + } + + // Type check + const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'] + if (!allowedTypes.includes(file.type)) { + throw new Error('Invalid file type') + } + + // Extension check + const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif'] + const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0] + if (!extension || !allowedExtensions.includes(extension)) { + throw new Error('Invalid file extension') + } + + return true +} +``` + +#### 확인 단계 +- [ ] 모든 사용자 입력이 스키마로 검증됨 +- [ ] 파일 업로드가 제한됨 (크기, 타입, 확장자) +- [ ] 사용자 입력이 쿼리에 직접 사용되지 않음 +- [ ] 화이트리스트 검증 사용 (블랙리스트가 아닌) +- [ ] 에러 메시지가 민감한 정보를 노출하지 않음 + +### 3. SQL Injection 방지 + +#### 절대 SQL을 연결하지 말 것 +```typescript +// DANGEROUS - SQL Injection vulnerability +const query = `SELECT * FROM users WHERE email = '${userEmail}'` +await db.query(query) +``` + +#### 반드시 파라미터화된 쿼리를 사용할 것 +```typescript +// Safe - parameterized query +const { data } = await supabase + .from('users') + .select('*') + .eq('email', userEmail) + +// Or with raw SQL +await db.query( + 'SELECT * FROM users WHERE email = $1', + [userEmail] +) +``` + +#### 확인 단계 +- [ ] 모든 데이터베이스 쿼리가 파라미터화된 쿼리 사용 +- [ ] SQL에서 문자열 연결 없음 +- [ ] ORM/쿼리 빌더가 올바르게 사용됨 +- [ ] Supabase 쿼리가 적절히 새니타이징됨 + +### 4. 인증 및 권한 부여 + +#### JWT 토큰 처리 +```typescript +// ❌ WRONG: localStorage (vulnerable to XSS) +localStorage.setItem('token', token) + +// ✅ CORRECT: httpOnly cookies +res.setHeader('Set-Cookie', + `token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`) +``` + +#### 권한 부여 확인 +```typescript +export async function deleteUser(userId: string, requesterId: string) { + // ALWAYS verify authorization first + const requester = await db.users.findUnique({ + where: { id: requesterId } + }) + + if (requester.role !== 'admin') { + return NextResponse.json( + { error: 'Unauthorized' }, + { status: 403 } + ) + } + + // Proceed with deletion + await db.users.delete({ where: { id: userId } }) +} +``` + +#### Row Level Security (Supabase) +```sql +-- Enable RLS on all tables +ALTER TABLE users ENABLE ROW LEVEL SECURITY; + +-- Users can only view their own data +CREATE POLICY "Users view own data" + ON users FOR SELECT + USING (auth.uid() = id); + +-- Users can only update their own data +CREATE POLICY "Users update own data" + ON users FOR UPDATE + USING (auth.uid() = id); +``` + +#### 확인 단계 +- [ ] 토큰이 httpOnly 쿠키에 저장됨 (localStorage가 아닌) +- [ ] 민감한 작업 전에 권한 부여 확인 +- [ ] Supabase에서 Row Level Security 활성화됨 +- [ ] 역할 기반 접근 제어 구현됨 +- [ ] 세션 관리가 안전함 + +### 5. XSS 방지 + +#### HTML 새니타이징 +```typescript +import DOMPurify from 'isomorphic-dompurify' + +// ALWAYS sanitize user-provided HTML +function renderUserContent(html: string) { + const clean = DOMPurify.sanitize(html, { + ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'], + ALLOWED_ATTR: [] + }) + return
+} +``` + +#### Content Security Policy +```typescript +// next.config.js +const securityHeaders = [ + { + key: 'Content-Security-Policy', + value: ` + default-src 'self'; + script-src 'self' 'unsafe-eval' 'unsafe-inline'; + style-src 'self' 'unsafe-inline'; + img-src 'self' data: https:; + font-src 'self'; + connect-src 'self' https://api.example.com; + `.replace(/\s{2,}/g, ' ').trim() + } +] +``` + +#### 확인 단계 +- [ ] 사용자 제공 HTML이 새니타이징됨 +- [ ] CSP 헤더가 구성됨 +- [ ] 검증되지 않은 동적 콘텐츠 렌더링 없음 +- [ ] React의 내장 XSS 보호가 사용됨 + +### 6. CSRF 보호 + +#### CSRF 토큰 +```typescript +import { csrf } from '@/lib/csrf' + +export async function POST(request: Request) { + const token = request.headers.get('X-CSRF-Token') + + if (!csrf.verify(token)) { + return NextResponse.json( + { error: 'Invalid CSRF token' }, + { status: 403 } + ) + } + + // Process request +} +``` + +#### SameSite 쿠키 +```typescript +res.setHeader('Set-Cookie', + `session=${sessionId}; HttpOnly; Secure; SameSite=Strict`) +``` + +#### 확인 단계 +- [ ] 상태 변경 작업에 CSRF 토큰 적용 +- [ ] 모든 쿠키에 SameSite=Strict 설정 +- [ ] Double-submit 쿠키 패턴 구현 + +### 7. 속도 제한 + +#### API 속도 제한 +```typescript +import rateLimit from 'express-rate-limit' + +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // 100 requests per window + message: 'Too many requests' +}) + +// Apply to routes +app.use('/api/', limiter) +``` + +#### 비용이 높은 작업 +```typescript +// Aggressive rate limiting for searches +const searchLimiter = rateLimit({ + windowMs: 60 * 1000, // 1 minute + max: 10, // 10 requests per minute + message: 'Too many search requests' +}) + +app.use('/api/search', searchLimiter) +``` + +#### 확인 단계 +- [ ] 모든 API 엔드포인트에 속도 제한 적용 +- [ ] 비용이 높은 작업에 더 엄격한 제한 +- [ ] IP 기반 속도 제한 +- [ ] 사용자 기반 속도 제한 (인증된 사용자) + +### 8. 민감한 데이터 노출 + +#### 로깅 +```typescript +// ❌ WRONG: Logging sensitive data +console.log('User login:', { email, password }) +console.log('Payment:', { cardNumber, cvv }) + +// ✅ CORRECT: Redact sensitive data +console.log('User login:', { email, userId }) +console.log('Payment:', { last4: card.last4, userId }) +``` + +#### 에러 메시지 +```typescript +// ❌ WRONG: Exposing internal details +catch (error) { + return NextResponse.json( + { error: error.message, stack: error.stack }, + { status: 500 } + ) +} + +// ✅ CORRECT: Generic error messages +catch (error) { + console.error('Internal error:', error) + return NextResponse.json( + { error: 'An error occurred. Please try again.' }, + { status: 500 } + ) +} +``` + +#### 확인 단계 +- [ ] 로그에 비밀번호, 토큰, 시크릿 없음 +- [ ] 사용자에게 표시되는 에러 메시지가 일반적임 +- [ ] 상세 에러는 서버 로그에만 기록 +- [ ] 사용자에게 스택 트레이스가 노출되지 않음 + +### 9. 블록체인 보안 (Solana) + +#### 지갑 검증 +```typescript +import { verify } from '@solana/web3.js' + +async function verifyWalletOwnership( + publicKey: string, + signature: string, + message: string +) { + try { + const isValid = verify( + Buffer.from(message), + Buffer.from(signature, 'base64'), + Buffer.from(publicKey, 'base64') + ) + return isValid + } catch (error) { + return false + } +} +``` + +#### 트랜잭션 검증 +```typescript +async function verifyTransaction(transaction: Transaction) { + // Verify recipient + if (transaction.to !== expectedRecipient) { + throw new Error('Invalid recipient') + } + + // Verify amount + if (transaction.amount > maxAmount) { + throw new Error('Amount exceeds limit') + } + + // Verify user has sufficient balance + const balance = await getBalance(transaction.from) + if (balance < transaction.amount) { + throw new Error('Insufficient balance') + } + + return true +} +``` + +#### 확인 단계 +- [ ] 지갑 서명 검증됨 +- [ ] 트랜잭션 세부 정보 유효성 검사됨 +- [ ] 트랜잭션 전 잔액 확인 +- [ ] 블라인드 트랜잭션 서명 없음 + +### 10. 의존성 보안 + +#### 정기 업데이트 +```bash +# Check for vulnerabilities +npm audit + +# Fix automatically fixable issues +npm audit fix + +# Update dependencies +npm update + +# Check for outdated packages +npm outdated +``` + +#### 잠금 파일 +```bash +# ALWAYS commit lock files +git add package-lock.json + +# Use in CI/CD for reproducible builds +npm ci # Instead of npm install +``` + +#### 확인 단계 +- [ ] 의존성이 최신 상태 +- [ ] 알려진 취약점 없음 (npm audit 클린) +- [ ] 잠금 파일 커밋됨 +- [ ] GitHub에서 Dependabot 활성화됨 +- [ ] 정기적인 보안 업데이트 + +## 보안 테스트 + +### 자동화된 보안 테스트 +```typescript +// Test authentication +test('requires authentication', async () => { + const response = await fetch('/api/protected') + expect(response.status).toBe(401) +}) + +// Test authorization +test('requires admin role', async () => { + const response = await fetch('/api/admin', { + headers: { Authorization: `Bearer ${userToken}` } + }) + expect(response.status).toBe(403) +}) + +// Test input validation +test('rejects invalid input', async () => { + const response = await fetch('/api/users', { + method: 'POST', + body: JSON.stringify({ email: 'not-an-email' }) + }) + expect(response.status).toBe(400) +}) + +// Test rate limiting +test('enforces rate limits', async () => { + const requests = Array(101).fill(null).map(() => + fetch('/api/endpoint') + ) + + const responses = await Promise.all(requests) + const tooManyRequests = responses.filter(r => r.status === 429) + + expect(tooManyRequests.length).toBeGreaterThan(0) +}) +``` + +## 배포 전 보안 체크리스트 + +모든 프로덕션 배포 전: + +- [ ] **시크릿**: 하드코딩된 시크릿 없음, 모두 환경 변수에 저장 +- [ ] **입력 유효성 검사**: 모든 사용자 입력 검증됨 +- [ ] **SQL Injection**: 모든 쿼리 파라미터화됨 +- [ ] **XSS**: 사용자 콘텐츠 새니타이징됨 +- [ ] **CSRF**: 보호 활성화됨 +- [ ] **인증**: 적절한 토큰 처리 +- [ ] **권한 부여**: 역할 확인 적용됨 +- [ ] **속도 제한**: 모든 엔드포인트에서 활성화됨 +- [ ] **HTTPS**: 프로덕션에서 강제 적용 +- [ ] **보안 헤더**: CSP, X-Frame-Options 구성됨 +- [ ] **에러 처리**: 에러에 민감한 데이터 없음 +- [ ] **로깅**: 민감한 데이터가 로그에 없음 +- [ ] **의존성**: 최신 상태, 취약점 없음 +- [ ] **Row Level Security**: Supabase에서 활성화됨 +- [ ] **CORS**: 적절히 구성됨 +- [ ] **파일 업로드**: 유효성 검사됨 (크기, 타입) +- [ ] **지갑 서명**: 검증됨 (블록체인인 경우) + +## 참고 자료 + +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) +- [Next.js Security](https://nextjs.org/docs/security) +- [Supabase Security](https://supabase.com/docs/guides/auth) +- [Web Security Academy](https://portswigger.net/web-security) + +--- + +**기억하세요**: 보안은 선택 사항이 아닙니다. 하나의 취약점이 전체 플랫폼을 침해할 수 있습니다. 의심스러울 때는 보수적으로 대응하세요. diff --git a/docs/ko-KR/skills/security-review/cloud-infrastructure-security.md b/docs/ko-KR/skills/security-review/cloud-infrastructure-security.md new file mode 100644 index 00000000..b6051418 --- /dev/null +++ b/docs/ko-KR/skills/security-review/cloud-infrastructure-security.md @@ -0,0 +1,361 @@ +| name | description | +|------|-------------| +| cloud-infrastructure-security | 클라우드 플랫폼 배포, 인프라 구성, IAM 정책 관리, 로깅/모니터링 설정, CI/CD 파이프라인 구현 시 이 스킬을 사용하세요. 모범 사례에 맞춘 클라우드 보안 체크리스트를 제공합니다. | + +# 클라우드 및 인프라 보안 스킬 + +이 스킬은 클라우드 인프라, CI/CD 파이프라인, 배포 구성이 보안 모범 사례를 따르고 업계 표준을 준수하도록 보장합니다. + +## 활성화 시점 + +- 클라우드 플랫폼(AWS, Vercel, Railway, Cloudflare)에 애플리케이션 배포 시 +- IAM 역할 및 권한 구성 시 +- CI/CD 파이프라인 설정 시 +- Infrastructure as Code(Terraform, CloudFormation) 구현 시 +- 로깅 및 모니터링 구성 시 +- 클라우드 환경에서 시크릿 관리 시 +- CDN 및 엣지 보안 설정 시 +- 재해 복구 및 백업 전략 구현 시 + +## 클라우드 보안 체크리스트 + +### 1. IAM 및 접근 제어 + +#### 최소 권한 원칙 + +```yaml +# ✅ CORRECT: Minimal permissions +iam_role: + permissions: + - s3:GetObject # Only read access + - s3:ListBucket + resources: + - arn:aws:s3:::my-bucket/* # Specific bucket only + +# ❌ WRONG: Overly broad permissions +iam_role: + permissions: + - s3:* # All S3 actions + resources: + - "*" # All resources +``` + +#### 다중 인증 (MFA) + +```bash +# ALWAYS enable MFA for root/admin accounts +aws iam enable-mfa-device \ + --user-name admin \ + --serial-number arn:aws:iam::123456789:mfa/admin \ + --authentication-code1 123456 \ + --authentication-code2 789012 +``` + +#### 확인 단계 + +- [ ] 프로덕션에서 루트 계정 사용 없음 +- [ ] 모든 권한 있는 계정에 MFA 활성화됨 +- [ ] 서비스 계정이 장기 자격 증명이 아닌 역할을 사용 +- [ ] IAM 정책이 최소 권한을 따름 +- [ ] 정기적인 접근 검토 수행 +- [ ] 사용하지 않는 자격 증명 교체 또는 제거 + +### 2. 시크릿 관리 + +#### 클라우드 시크릿 매니저 + +```typescript +// ✅ CORRECT: Use cloud secrets manager +import { SecretsManager } from '@aws-sdk/client-secrets-manager'; + +const client = new SecretsManager({ region: 'us-east-1' }); +const secret = await client.getSecretValue({ SecretId: 'prod/api-key' }); +const apiKey = JSON.parse(secret.SecretString).key; + +// ❌ WRONG: Hardcoded or in environment variables only +const apiKey = process.env.API_KEY; // Not rotated, not audited +``` + +#### 시크릿 교체 + +```bash +# Set up automatic rotation for database credentials +aws secretsmanager rotate-secret \ + --secret-id prod/db-password \ + --rotation-lambda-arn arn:aws:lambda:region:account:function:rotate \ + --rotation-rules AutomaticallyAfterDays=30 +``` + +#### 확인 단계 + +- [ ] 모든 시크릿이 클라우드 시크릿 매니저에 저장됨 (AWS Secrets Manager, Vercel Secrets) +- [ ] 데이터베이스 자격 증명에 대한 자동 교체 활성화됨 +- [ ] API 키가 최소 분기별로 교체됨 +- [ ] 코드, 로그, 에러 메시지에 시크릿 없음 +- [ ] 시크릿 접근에 대한 감사 로깅 활성화됨 + +### 3. 네트워크 보안 + +#### VPC 및 방화벽 구성 + +```terraform +# ✅ CORRECT: Restricted security group +resource "aws_security_group" "app" { + name = "app-sg" + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["10.0.0.0/16"] # Internal VPC only + } + + egress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] # Only HTTPS outbound + } +} + +# ❌ WRONG: Open to the internet +resource "aws_security_group" "bad" { + ingress { + from_port = 0 + to_port = 65535 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] # All ports, all IPs! + } +} +``` + +#### 확인 단계 + +- [ ] 데이터베이스가 공개적으로 접근 불가 +- [ ] SSH/RDP 포트가 VPN/배스천에만 제한됨 +- [ ] 보안 그룹이 최소 권한을 따름 +- [ ] 네트워크 ACL이 구성됨 +- [ ] VPC 플로우 로그가 활성화됨 + +### 4. 로깅 및 모니터링 + +#### CloudWatch/로깅 구성 + +```typescript +// ✅ CORRECT: Comprehensive logging +import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs'; + +const logSecurityEvent = async (event: SecurityEvent) => { + await cloudwatch.putLogEvents({ + logGroupName: '/aws/security/events', + logStreamName: 'authentication', + logEvents: [{ + timestamp: Date.now(), + message: JSON.stringify({ + type: event.type, + userId: event.userId, + ip: event.ip, + result: event.result, + // Never log sensitive data + }) + }] + }); +}; +``` + +#### 확인 단계 + +- [ ] 모든 서비스에 CloudWatch/로깅 활성화됨 +- [ ] 실패한 인증 시도가 로깅됨 +- [ ] 관리자 작업이 감사됨 +- [ ] 로그 보존 기간이 구성됨 (규정 준수를 위해 90일 이상) +- [ ] 의심스러운 활동에 대한 알림 구성됨 +- [ ] 로그가 중앙 집중화되고 변조 방지됨 + +### 5. CI/CD 파이프라인 보안 + +#### 보안 파이프라인 구성 + +```yaml +# ✅ CORRECT: Secure GitHub Actions workflow +name: Deploy + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read # Minimal permissions + + steps: + - uses: actions/checkout@v4 + + # Scan for secrets + - name: Secret scanning + uses: trufflesecurity/trufflehog@main + + # Dependency audit + - name: Audit dependencies + run: npm audit --audit-level=high + + # Use OIDC, not long-lived tokens + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole + aws-region: us-east-1 +``` + +#### 공급망 보안 + +```json +// package.json - Use lock files and integrity checks +{ + "scripts": { + "install": "npm ci", // Use ci for reproducible builds + "audit": "npm audit --audit-level=moderate", + "check": "npm outdated" + } +} +``` + +#### 확인 단계 + +- [ ] 장기 자격 증명 대신 OIDC 사용 +- [ ] 파이프라인에서 시크릿 스캐닝 +- [ ] 의존성 취약점 스캐닝 +- [ ] 컨테이너 이미지 스캐닝 (해당하는 경우) +- [ ] 브랜치 보호 규칙 적용됨 +- [ ] 병합 전 코드 리뷰 필수 +- [ ] 서명된 커밋 적용 + +### 6. Cloudflare 및 CDN 보안 + +#### Cloudflare 보안 구성 + +```typescript +// ✅ CORRECT: Cloudflare Workers with security headers +export default { + async fetch(request: Request): Promise { + const response = await fetch(request); + + // Add security headers + const headers = new Headers(response.headers); + headers.set('X-Frame-Options', 'DENY'); + headers.set('X-Content-Type-Options', 'nosniff'); + headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); + headers.set('Permissions-Policy', 'geolocation=(), microphone=()'); + + return new Response(response.body, { + status: response.status, + headers + }); + } +}; +``` + +#### WAF 규칙 + +```bash +# Enable Cloudflare WAF managed rules +# - OWASP Core Ruleset +# - Cloudflare Managed Ruleset +# - Rate limiting rules +# - Bot protection +``` + +#### 확인 단계 + +- [ ] OWASP 규칙으로 WAF 활성화됨 +- [ ] 속도 제한 구성됨 +- [ ] 봇 보호 활성화됨 +- [ ] DDoS 보호 활성화됨 +- [ ] 보안 헤더 구성됨 +- [ ] SSL/TLS 엄격 모드 활성화됨 + +### 7. 백업 및 재해 복구 + +#### 자동 백업 + +```terraform +# ✅ CORRECT: Automated RDS backups +resource "aws_db_instance" "main" { + allocated_storage = 20 + engine = "postgres" + + backup_retention_period = 30 # 30 days retention + backup_window = "03:00-04:00" + maintenance_window = "mon:04:00-mon:05:00" + + enabled_cloudwatch_logs_exports = ["postgresql"] + + deletion_protection = true # Prevent accidental deletion +} +``` + +#### 확인 단계 + +- [ ] 자동 일일 백업 구성됨 +- [ ] 백업 보존 기간이 규정 준수 요구사항을 충족 +- [ ] 특정 시점 복구 활성화됨 +- [ ] 분기별 백업 테스트 수행 +- [ ] 재해 복구 계획 문서화됨 +- [ ] RPO 및 RTO가 정의되고 테스트됨 + +## 배포 전 클라우드 보안 체크리스트 + +모든 프로덕션 클라우드 배포 전: + +- [ ] **IAM**: 루트 계정 미사용, MFA 활성화, 최소 권한 정책 +- [ ] **시크릿**: 모든 시크릿이 클라우드 시크릿 매니저에 교체와 함께 저장됨 +- [ ] **네트워크**: 보안 그룹 제한됨, 공개 데이터베이스 없음 +- [ ] **로깅**: CloudWatch/로깅이 보존 기간과 함께 활성화됨 +- [ ] **모니터링**: 이상 징후에 대한 알림 구성됨 +- [ ] **CI/CD**: OIDC 인증, 시크릿 스캐닝, 의존성 감사 +- [ ] **CDN/WAF**: OWASP 규칙으로 Cloudflare WAF 활성화됨 +- [ ] **암호화**: 저장 및 전송 중 데이터 암호화 +- [ ] **백업**: 테스트된 복구와 함께 자동 백업 +- [ ] **규정 준수**: GDPR/HIPAA 요구사항 충족 (해당하는 경우) +- [ ] **문서화**: 인프라 문서화, 런북 작성됨 +- [ ] **인시던트 대응**: 보안 인시던트 계획 마련 + +## 일반적인 클라우드 보안 잘못된 구성 + +### S3 버킷 노출 + +```bash +# ❌ WRONG: Public bucket +aws s3api put-bucket-acl --bucket my-bucket --acl public-read + +# ✅ CORRECT: Private bucket with specific access +aws s3api put-bucket-acl --bucket my-bucket --acl private +aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json +``` + +### RDS 공개 접근 + +```terraform +# ❌ WRONG +resource "aws_db_instance" "bad" { + publicly_accessible = true # NEVER do this! +} + +# ✅ CORRECT +resource "aws_db_instance" "good" { + publicly_accessible = false + vpc_security_group_ids = [aws_security_group.db.id] +} +``` + +## 참고 자료 + +- [AWS Security Best Practices](https://aws.amazon.com/security/best-practices/) +- [CIS AWS Foundations Benchmark](https://www.cisecurity.org/benchmark/amazon_web_services) +- [Cloudflare Security Documentation](https://developers.cloudflare.com/security/) +- [OWASP Cloud Security](https://owasp.org/www-project-cloud-security/) +- [Terraform Security Best Practices](https://www.terraform.io/docs/cloud/guides/recommended-practices/) + +**기억하세요**: 클라우드 잘못된 구성은 데이터 유출의 주요 원인입니다. 하나의 노출된 S3 버킷이나 과도하게 허용적인 IAM 정책이 전체 인프라를 침해할 수 있습니다. 항상 최소 권한 원칙과 심층 방어를 따르세요. diff --git a/docs/ko-KR/skills/strategic-compact/SKILL.md b/docs/ko-KR/skills/strategic-compact/SKILL.md new file mode 100644 index 00000000..e58f4a66 --- /dev/null +++ b/docs/ko-KR/skills/strategic-compact/SKILL.md @@ -0,0 +1,103 @@ +--- +name: strategic-compact +description: 임의의 자동 컴팩션 대신 논리적 간격에서 수동 컨텍스트 압축을 제안하여 작업 단계를 통해 컨텍스트를 보존합니다. +origin: ECC +--- + +# 전략적 컴팩트 스킬 + +임의의 자동 컴팩션에 의존하지 않고 워크플로우의 전략적 지점에서 수동 `/compact`를 제안합니다. + +## 활성화 시점 + +- 컨텍스트 제한에 근접하는 긴 세션을 실행할 때 (200K+ 토큰) +- 다단계 작업을 수행할 때 (조사 -> 계획 -> 구현 -> 테스트) +- 같은 세션 내에서 관련 없는 작업 간 전환할 때 +- 주요 마일스톤을 완료하고 새 작업을 시작할 때 +- 응답이 느려지거나 일관성이 떨어질 때 (컨텍스트 압박) + +## 전략적 컴팩션이 필요한 이유 + +자동 컴팩션은 임의의 지점에서 실행됩니다: +- 종종 작업 중간에 실행되어 중요한 컨텍스트를 잃음 +- 논리적 작업 경계를 인식하지 못함 +- 복잡한 다단계 작업을 중단할 수 있음 + +논리적 경계에서의 전략적 컴팩션: +- **탐색 후, 실행 전** -- 조사 컨텍스트를 압축하고 구현 계획은 유지 +- **마일스톤 완료 후** -- 다음 단계를 위한 새로운 시작 +- **주요 컨텍스트 전환 전** -- 다른 작업 시작 전에 탐색 컨텍스트 정리 + +## 작동 방식 + +`suggest-compact.js` 스크립트는 PreToolUse (Edit/Write)에서 실행되며 다음을 수행합니다: + +1. **도구 호출 추적** -- 세션 내 도구 호출 횟수를 카운트 +2. **임계값 감지** -- 설정 가능한 임계값에서 제안 (기본값: 50회) +3. **주기적 알림** -- 임계값 이후 25회마다 알림 + +## Hook 설정 + +`~/.claude/settings.json`에 추가합니다: + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Edit", + "hooks": [{ "type": "command", "command": "node ~/.claude/skills/strategic-compact/suggest-compact.js" }] + }, + { + "matcher": "Write", + "hooks": [{ "type": "command", "command": "node ~/.claude/skills/strategic-compact/suggest-compact.js" }] + } + ] + } +} +``` + +## 구성 + +환경 변수: +- `COMPACT_THRESHOLD` -- 첫 번째 제안까지의 도구 호출 횟수 (기본값: 50) + +## 컴팩션 결정 가이드 + +컴팩션 시기를 결정하기 위해 이 표를 사용하세요: + +| 단계 전환 | 컴팩션? | 이유 | +|-----------------|----------|-----| +| 조사 -> 계획 | 예 | 조사 컨텍스트는 부피가 크고, 계획이 증류된 결과물 | +| 계획 -> 구현 | 예 | 계획은 TodoWrite 또는 파일에 있으므로 코드를 위한 컨텍스트 확보 | +| 구현 -> 테스트 | 경우에 따라 | 테스트가 최근 코드를 참조하면 유지; 포커스 전환 시 컴팩션 | +| 디버깅 -> 다음 기능 | 예 | 디버그 추적이 관련 없는 작업의 컨텍스트를 오염시킴 | +| 구현 중간 | 아니오 | 변수명, 파일 경로, 부분 상태를 잃는 비용이 큼 | +| 실패한 접근 후 | 예 | 새 접근을 시도하기 전에 막다른 길의 추론을 정리 | + +## 컴팩션에서 유지되는 것 + +무엇이 유지되는지 이해하면 자신 있게 컴팩션할 수 있습니다: + +| 유지됨 | 손실됨 | +|----------|------| +| CLAUDE.md 지침 | 중간 추론 및 분석 | +| TodoWrite 작업 목록 | 이전에 읽은 파일 내용 | +| 메모리 파일 (`~/.claude/memory/`) | 다단계 대화 컨텍스트 | +| Git 상태 (커밋, 브랜치) | 도구 호출 기록 및 횟수 | +| 디스크의 파일 | 구두로 언급된 세밀한 사용자 선호도 | + +## 모범 사례 + +1. **계획 후 컴팩션** -- TodoWrite에서 계획이 확정되면 새로 시작하기 위해 컴팩션 +2. **디버깅 후 컴팩션** -- 계속하기 전에 에러 해결 컨텍스트 정리 +3. **구현 중간에는 컴팩션하지 않기** -- 관련 변경 사항의 컨텍스트 보존 +4. **제안을 읽기** -- Hook이 *언제*를 알려주고, *할지* 여부는 당신이 결정 +5. **컴팩션 전에 기록** -- 컴팩션 전에 중요한 컨텍스트를 파일이나 메모리에 저장 +6. **요약과 함께 `/compact` 사용** -- 커스텀 메시지 추가: `/compact Focus on implementing auth middleware next` + +## 관련 항목 + +- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) -- 토큰 최적화 섹션 +- 메모리 영속성 Hook -- 컴팩션에서 살아남는 상태를 위해 +- `continuous-learning` 스킬 -- 세션 종료 전 패턴 추출 diff --git a/docs/ko-KR/skills/tdd-workflow/SKILL.md b/docs/ko-KR/skills/tdd-workflow/SKILL.md new file mode 100644 index 00000000..74a29398 --- /dev/null +++ b/docs/ko-KR/skills/tdd-workflow/SKILL.md @@ -0,0 +1,410 @@ +--- +name: tdd-workflow +description: 새 기능 작성, 버그 수정 또는 코드 리팩터링 시 이 스킬을 사용하세요. 단위, 통합, E2E 테스트를 포함한 80% 이상의 커버리지로 테스트 주도 개발을 시행합니다. +origin: ECC +--- + +# 테스트 주도 개발 워크플로우 + +이 스킬은 모든 코드 개발이 포괄적인 테스트 커버리지와 함께 TDD 원칙을 따르도록 보장합니다. + +## 활성화 시점 + +- 새 기능이나 기능성을 작성할 때 +- 버그나 이슈를 수정할 때 +- 기존 코드를 리팩터링할 때 +- API 엔드포인트를 추가할 때 +- 새 컴포넌트를 생성할 때 + +## 핵심 원칙 + +### 1. 코드보다 테스트가 먼저 +항상 테스트를 먼저 작성한 후, 테스트를 통과시키는 코드를 구현합니다. + +### 2. 커버리지 요구 사항 +- 최소 80% 커버리지 (단위 + 통합 + E2E) +- 모든 엣지 케이스 커버 +- 에러 시나리오 테스트 +- 경계 조건 검증 + +### 3. 테스트 유형 + +#### 단위 테스트 +- 개별 함수 및 유틸리티 +- 컴포넌트 로직 +- 순수 함수 +- 헬퍼 및 유틸리티 + +#### 통합 테스트 +- API 엔드포인트 +- 데이터베이스 작업 +- 서비스 상호작용 +- 외부 API 호출 + +#### E2E 테스트 (Playwright) +- 핵심 사용자 플로우 +- 완전한 워크플로우 +- 브라우저 자동화 +- UI 상호작용 + +## TDD 워크플로우 단계 + +### 단계 1: 사용자 여정 작성 +``` +As a [role], I want to [action], so that [benefit] + +Example: +As a user, I want to search for markets semantically, +so that I can find relevant markets even without exact keywords. +``` + +### 단계 2: 테스트 케이스 생성 +각 사용자 여정에 대해 포괄적인 테스트 케이스를 작성합니다: + +```typescript +describe('Semantic Search', () => { + it('returns relevant markets for query', async () => { + // Test implementation + }) + + it('handles empty query gracefully', async () => { + // Test edge case + }) + + it('falls back to substring search when Redis unavailable', async () => { + // Test fallback behavior + }) + + it('sorts results by similarity score', async () => { + // Test sorting logic + }) +}) +``` + +### 단계 3: 테스트 실행 (실패해야 함) +```bash +npm test +# Tests should fail - we haven't implemented yet +``` + +### 단계 4: 코드 구현 +테스트를 통과시키기 위한 최소한의 코드를 작성합니다: + +```typescript +// Implementation guided by tests +export async function searchMarkets(query: string) { + // Implementation here +} +``` + +### 단계 5: 테스트 재실행 +```bash +npm test +# Tests should now pass +``` + +### 단계 6: 리팩터링 +테스트가 통과하는 상태를 유지하면서 코드 품질을 개선합니다: +- 중복 제거 +- 네이밍 개선 +- 성능 최적화 +- 가독성 향상 + +### 단계 7: 커버리지 확인 +```bash +npm run test:coverage +# Verify 80%+ coverage achieved +``` + +## 테스트 패턴 + +### 단위 테스트 패턴 (Jest/Vitest) +```typescript +import { render, screen, fireEvent } from '@testing-library/react' +import { Button } from './Button' + +describe('Button Component', () => { + it('renders with correct text', () => { + render() + expect(screen.getByText('Click me')).toBeInTheDocument() + }) + + it('calls onClick when clicked', () => { + const handleClick = jest.fn() + render() + + fireEvent.click(screen.getByRole('button')) + + expect(handleClick).toHaveBeenCalledTimes(1) + }) + + it('is disabled when disabled prop is true', () => { + render() + expect(screen.getByRole('button')).toBeDisabled() + }) +}) +``` + +### API 통합 테스트 패턴 +```typescript +import { NextRequest } from 'next/server' +import { GET } from './route' + +describe('GET /api/markets', () => { + it('returns markets successfully', async () => { + const request = new NextRequest('http://localhost/api/markets') + const response = await GET(request) + const data = await response.json() + + expect(response.status).toBe(200) + expect(data.success).toBe(true) + expect(Array.isArray(data.data)).toBe(true) + }) + + it('validates query parameters', async () => { + const request = new NextRequest('http://localhost/api/markets?limit=invalid') + const response = await GET(request) + + expect(response.status).toBe(400) + }) + + it('handles database errors gracefully', async () => { + // Mock database failure + const request = new NextRequest('http://localhost/api/markets') + // Test error handling + }) +}) +``` + +### E2E 테스트 패턴 (Playwright) +```typescript +import { test, expect } from '@playwright/test' + +test('user can search and filter markets', async ({ page }) => { + // Navigate to markets page + await page.goto('/') + await page.click('a[href="/markets"]') + + // Verify page loaded + await expect(page.locator('h1')).toContainText('Markets') + + // Search for markets + await page.fill('input[placeholder="Search markets"]', 'election') + + // Wait for debounce and results + await page.waitForTimeout(600) + + // Verify search results displayed + const results = page.locator('[data-testid="market-card"]') + await expect(results).toHaveCount(5, { timeout: 5000 }) + + // Verify results contain search term + const firstResult = results.first() + await expect(firstResult).toContainText('election', { ignoreCase: true }) + + // Filter by status + await page.click('button:has-text("Active")') + + // Verify filtered results + await expect(results).toHaveCount(3) +}) + +test('user can create a new market', async ({ page }) => { + // Login first + await page.goto('/creator-dashboard') + + // Fill market creation form + await page.fill('input[name="name"]', 'Test Market') + await page.fill('textarea[name="description"]', 'Test description') + await page.fill('input[name="endDate"]', '2025-12-31') + + // Submit form + await page.click('button[type="submit"]') + + // Verify success message + await expect(page.locator('text=Market created successfully')).toBeVisible() + + // Verify redirect to market page + await expect(page).toHaveURL(/\/markets\/test-market/) +}) +``` + +## 테스트 파일 구성 + +``` +src/ +├── components/ +│ ├── Button/ +│ │ ├── Button.tsx +│ │ ├── Button.test.tsx # Unit tests +│ │ └── Button.stories.tsx # Storybook +│ └── MarketCard/ +│ ├── MarketCard.tsx +│ └── MarketCard.test.tsx +├── app/ +│ └── api/ +│ └── markets/ +│ ├── route.ts +│ └── route.test.ts # Integration tests +└── e2e/ + ├── markets.spec.ts # E2E tests + ├── trading.spec.ts + └── auth.spec.ts +``` + +## 외부 서비스 모킹 + +### Supabase Mock +```typescript +jest.mock('@/lib/supabase', () => ({ + supabase: { + from: jest.fn(() => ({ + select: jest.fn(() => ({ + eq: jest.fn(() => Promise.resolve({ + data: [{ id: 1, name: 'Test Market' }], + error: null + })) + })) + })) + } +})) +``` + +### Redis Mock +```typescript +jest.mock('@/lib/redis', () => ({ + searchMarketsByVector: jest.fn(() => Promise.resolve([ + { slug: 'test-market', similarity_score: 0.95 } + ])), + checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true })) +})) +``` + +### OpenAI Mock +```typescript +jest.mock('@/lib/openai', () => ({ + generateEmbedding: jest.fn(() => Promise.resolve( + new Array(1536).fill(0.1) // Mock 1536-dim embedding + )) +})) +``` + +## 테스트 커버리지 검증 + +### 커버리지 리포트 실행 +```bash +npm run test:coverage +``` + +### 커버리지 임계값 +```json +{ + "jest": { + "coverageThresholds": { + "global": { + "branches": 80, + "functions": 80, + "lines": 80, + "statements": 80 + } + } + } +} +``` + +## 흔한 테스트 실수 + +### 잘못된 예: 구현 세부사항 테스트 +```typescript +// Don't test internal state +expect(component.state.count).toBe(5) +``` + +### 올바른 예: 사용자에게 보이는 동작 테스트 +```typescript +// Test what users see +expect(screen.getByText('Count: 5')).toBeInTheDocument() +``` + +### 잘못된 예: 취약한 셀렉터 +```typescript +// Breaks easily +await page.click('.css-class-xyz') +``` + +### 올바른 예: 시맨틱 셀렉터 +```typescript +// Resilient to changes +await page.click('button:has-text("Submit")') +await page.click('[data-testid="submit-button"]') +``` + +### 잘못된 예: 테스트 격리 없음 +```typescript +// Tests depend on each other +test('creates user', () => { /* ... */ }) +test('updates same user', () => { /* depends on previous test */ }) +``` + +### 올바른 예: 독립적인 테스트 +```typescript +// Each test sets up its own data +test('creates user', () => { + const user = createTestUser() + // Test logic +}) + +test('updates user', () => { + const user = createTestUser() + // Update logic +}) +``` + +## 지속적 테스트 + +### 개발 중 Watch 모드 +```bash +npm test -- --watch +# Tests run automatically on file changes +``` + +### Pre-Commit Hook +```bash +# Runs before every commit +npm test && npm run lint +``` + +### CI/CD 통합 +```yaml +# GitHub Actions +- name: Run Tests + run: npm test -- --coverage +- name: Upload Coverage + uses: codecov/codecov-action@v3 +``` + +## 모범 사례 + +1. **테스트 먼저 작성** - 항상 TDD +2. **테스트당 하나의 Assert** - 단일 동작에 집중 +3. **설명적인 테스트 이름** - 무엇을 테스트하는지 설명 +4. **Arrange-Act-Assert** - 명확한 테스트 구조 +5. **외부 의존성 모킹** - 단위 테스트 격리 +6. **엣지 케이스 테스트** - null, undefined, 빈 값, 큰 값 +7. **에러 경로 테스트** - 정상 경로만이 아닌 +8. **테스트 속도 유지** - 단위 테스트 각 50ms 미만 +9. **테스트 후 정리** - 부작용 없음 +10. **커버리지 리포트 검토** - 누락 부분 식별 + +## 성공 지표 + +- 80% 이상의 코드 커버리지 달성 +- 모든 테스트 통과 (그린) +- 건너뛴 테스트나 비활성화된 테스트 없음 +- 빠른 테스트 실행 (단위 테스트 30초 미만) +- E2E 테스트가 핵심 사용자 플로우를 커버 +- 테스트가 프로덕션 이전에 버그를 포착 + +--- + +**기억하세요**: 테스트는 선택 사항이 아닙니다. 테스트는 자신감 있는 리팩터링, 빠른 개발, 그리고 프로덕션 안정성을 가능하게 하는 안전망입니다. diff --git a/docs/ko-KR/skills/verification-loop/SKILL.md b/docs/ko-KR/skills/verification-loop/SKILL.md new file mode 100644 index 00000000..0571b6a0 --- /dev/null +++ b/docs/ko-KR/skills/verification-loop/SKILL.md @@ -0,0 +1,126 @@ +--- +name: verification-loop +description: "Claude Code 세션을 위한 포괄적인 검증 시스템." +origin: ECC +--- + +# 검증 루프 스킬 + +Claude Code 세션을 위한 포괄적인 검증 시스템. + +## 사용 시점 + +다음 상황에서 이 스킬을 호출하세요: +- 기능 또는 주요 코드 변경을 완료한 후 +- PR을 생성하기 전 +- 품질 게이트가 통과하는지 확인하고 싶을 때 +- 리팩터링 후 + +## 검증 단계 + +### 단계 1: 빌드 검증 +```bash +# Check if project builds +npm run build 2>&1 | tail -20 +# OR +pnpm build 2>&1 | tail -20 +``` + +빌드가 실패하면 계속하기 전에 중단하고 수정합니다. + +### 단계 2: 타입 검사 +```bash +# TypeScript projects +npx tsc --noEmit 2>&1 | head -30 + +# Python projects +pyright . 2>&1 | head -30 +``` + +모든 타입 에러를 보고합니다. 중요한 것은 계속하기 전에 수정합니다. + +### 단계 3: 린트 검사 +```bash +# JavaScript/TypeScript +npm run lint 2>&1 | head -30 + +# Python +ruff check . 2>&1 | head -30 +``` + +### 단계 4: 테스트 스위트 +```bash +# Run tests with coverage +npm run test -- --coverage 2>&1 | tail -50 + +# Check coverage threshold +# Target: 80% minimum +``` + +보고 항목: +- 전체 테스트: X +- 통과: X +- 실패: X +- 커버리지: X% + +### 단계 5: 보안 스캔 +```bash +# Check for secrets +grep -rn "sk-" --include="*.ts" --include="*.js" . 2>/dev/null | head -10 +grep -rn "api_key" --include="*.ts" --include="*.js" . 2>/dev/null | head -10 + +# Check for console.log +grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | head -10 +``` + +### 단계 6: Diff 리뷰 +```bash +# Show what changed +git diff --stat +git diff HEAD~1 --name-only +``` + +각 변경된 파일에서 다음을 검토합니다: +- 의도하지 않은 변경 +- 누락된 에러 처리 +- 잠재적 엣지 케이스 + +## 출력 형식 + +모든 단계를 실행한 후 검증 보고서를 생성합니다: + +``` +VERIFICATION REPORT +================== + +Build: [PASS/FAIL] +Types: [PASS/FAIL] (X errors) +Lint: [PASS/FAIL] (X warnings) +Tests: [PASS/FAIL] (X/Y passed, Z% coverage) +Security: [PASS/FAIL] (X issues) +Diff: [X files changed] + +Overall: [READY/NOT READY] for PR + +Issues to Fix: +1. ... +2. ... +``` + +## 연속 모드 + +긴 세션에서는 15분마다 또는 주요 변경 후에 검증을 실행합니다: + +```markdown +Set a mental checkpoint: +- After completing each function +- After finishing a component +- Before moving to next task + +Run: /verify +``` + +## Hook과의 통합 + +이 스킬은 PostToolUse Hook을 보완하지만 더 깊은 검증을 제공합니다. +Hook은 즉시 문제를 포착하고, 이 스킬은 포괄적인 검토를 제공합니다. From cbe2e68c26bc2b8c1b376efe88525e62026945b7 Mon Sep 17 00:00:00 2001 From: hahmee Date: Tue, 10 Mar 2026 13:39:16 +0900 Subject: [PATCH 5/9] docs(ko-KR): complete missing sections in code-reviewer and planner translations - code-reviewer: add code examples (deep nesting, useEffect deps, key props, N+1 queries), Project-Specific Guidelines section, cost-awareness check - planner: add Worked Example (Stripe Subscriptions) and Red Flags sections --- docs/ko-KR/agents/code-reviewer.md | 97 +++++++++++++++++++++++++++++- docs/ko-KR/agents/planner.md | 92 +++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 2 deletions(-) diff --git a/docs/ko-KR/agents/code-reviewer.md b/docs/ko-KR/agents/code-reviewer.md index c1af0aad..c715a47c 100644 --- a/docs/ko-KR/agents/code-reviewer.md +++ b/docs/ko-KR/agents/code-reviewer.md @@ -42,6 +42,23 @@ model: sonnet - **취약한 의존성** — 알려진 취약점이 있는 패키지 - **로그에 비밀 노출** — 민감한 데이터 로깅 (토큰, 비밀번호, PII) +```typescript +// BAD: 문자열 연결을 통한 SQL 인젝션 +const query = `SELECT * FROM users WHERE id = ${userId}`; + +// GOOD: 매개변수화된 쿼리 +const query = `SELECT * FROM users WHERE id = $1`; +const result = await db.query(query, [userId]); +``` + +```typescript +// BAD: 소독 없이 사용자 HTML 렌더링 +// 항상 DOMPurify.sanitize() 또는 동등한 것으로 사용자 콘텐츠 소독 + +// GOOD: 텍스트 콘텐츠 사용 또는 소독 +
{userComment}
+``` + ### 코드 품질 (HIGH) - **큰 함수** (50줄 초과) — 작고 집중된 함수로 분리 @@ -53,6 +70,31 @@ model: sonnet - **테스트 누락** — 테스트 커버리지 없는 새 코드 경로 - **죽은 코드** — 주석 처리된 코드, 사용되지 않는 import, 도달 불가능한 분기 +```typescript +// BAD: 깊은 중첩 + 변이 +function processUsers(users) { + if (users) { + for (const user of users) { + if (user.active) { + if (user.email) { + user.verified = true; // 변이! + results.push(user); + } + } + } + } + return results; +} + +// GOOD: 조기 반환 + 불변성 + 플랫 +function processUsers(users) { + if (!users) return []; + return users + .filter(user => user.active && user.email) + .map(user => ({ ...user, verified: true })); +} +``` + ### React/Next.js 패턴 (HIGH) React/Next.js 코드 리뷰 시 추가 확인: @@ -66,6 +108,26 @@ React/Next.js 코드 리뷰 시 추가 확인: - **로딩/에러 상태 누락** — 폴백 UI 없는 데이터 페칭 - **오래된 클로저** — 오래된 상태 값을 캡처하는 이벤트 핸들러 +```tsx +// BAD: 의존성 누락, 오래된 클로저 +useEffect(() => { + fetchData(userId); +}, []); // userId가 deps에서 누락 + +// GOOD: 완전한 의존성 +useEffect(() => { + fetchData(userId); +}, [userId]); +``` + +```tsx +// BAD: 재정렬 가능한 목록에서 인덱스를 key로 사용 +{items.map((item, i) => )} + +// GOOD: 안정적인 고유 key +{items.map(item => )} +``` + ### Node.js/Backend 패턴 (HIGH) 백엔드 코드 리뷰 시: @@ -78,6 +140,22 @@ React/Next.js 코드 리뷰 시 추가 확인: - **에러 메시지 누출** — 클라이언트에 내부 에러 세부사항 전송 - **CORS 설정 누락** — 의도하지 않은 오리진에서 접근 가능한 API +```typescript +// BAD: N+1 쿼리 패턴 +const users = await db.query('SELECT * FROM users'); +for (const user of users) { + user.posts = await db.query('SELECT * FROM posts WHERE user_id = $1', [user.id]); +} + +// GOOD: JOIN 또는 배치를 사용한 단일 쿼리 +const usersWithPosts = await db.query(` + SELECT u.*, json_agg(p.*) as posts + FROM users u + LEFT JOIN posts p ON p.user_id = u.id + GROUP BY u.id +`); +``` + ### 성능 (MEDIUM) - **비효율적 알고리즘** — O(n log n) 또는 O(n)이 가능한데 O(n²) @@ -132,7 +210,20 @@ Fix: 환경 변수로 이동하고 .gitignore/.env.example에 추가 - **경고**: HIGH 이슈만 (주의하여 merge 가능) - **차단**: CRITICAL 이슈 발견 — merge 전에 반드시 수정 -## AI 생성 코드 리뷰 부록 +## 프로젝트별 가이드라인 + +가능한 경우, `CLAUDE.md` 또는 프로젝트 규칙의 프로젝트별 컨벤션도 확인: + +- 파일 크기 제한 (예: 일반적으로 200-400줄, 최대 800줄) +- 이모지 정책 (많은 프로젝트가 코드에서 이모지 사용 금지) +- 불변성 요구사항 (변이 대신 spread 연산자) +- 데이터베이스 정책 (RLS, 마이그레이션 패턴) +- 에러 처리 패턴 (커스텀 에러 클래스, 에러 바운더리) +- 상태 관리 컨벤션 (Zustand, Redux, Context) + +프로젝트의 확립된 패턴에 맞게 리뷰를 조정하세요. 확신이 없을 때는 코드베이스의 나머지 부분이 하는 방식에 맞추세요. + +## v1.8 AI 생성 코드 리뷰 부록 AI 생성 변경사항 리뷰 시 우선순위: @@ -140,3 +231,7 @@ AI 생성 변경사항 리뷰 시 우선순위: 2. 보안 가정 및 신뢰 경계 3. 숨겨진 결합 또는 의도치 않은 아키텍처 드리프트 4. 불필요한 모델 비용 유발 복잡성 + +비용 인식 체크: +- 명확한 추론 필요 없이 더 비싼 모델로 에스컬레이션하는 워크플로우를 플래그하세요. +- 결정론적 리팩토링에는 저비용 티어를 기본으로 사용하도록 권장하세요. diff --git a/docs/ko-KR/agents/planner.md b/docs/ko-KR/agents/planner.md index 4acad03c..cf885bbd 100644 --- a/docs/ko-KR/agents/planner.md +++ b/docs/ko-KR/agents/planner.md @@ -95,6 +95,85 @@ model: opus 6. **점진적으로** — 각 단계가 검증 가능해야 함 7. **결정 문서화** — 무엇만이 아닌 왜를 설명 +## 실전 예제: Stripe 구독 추가 + +기대되는 상세 수준을 보여주는 완전한 계획입니다: + +```markdown +# 구현 계획: Stripe 구독 결제 + +## 개요 +무료/프로/엔터프라이즈 티어의 구독 결제를 추가합니다. 사용자는 Stripe Checkout을 +통해 업그레이드하고, 웹훅 이벤트가 구독 상태를 동기화합니다. + +## 요구사항 +- 세 가지 티어: Free (기본), Pro ($29/월), Enterprise ($99/월) +- 결제 흐름을 위한 Stripe Checkout +- 구독 라이프사이클 이벤트를 위한 웹훅 핸들러 +- 구독 티어 기반 기능 게이팅 + +## 아키텍처 변경사항 +- 새 테이블: `subscriptions` (user_id, stripe_customer_id, stripe_subscription_id, status, tier) +- 새 API 라우트: `app/api/checkout/route.ts` — Stripe Checkout 세션 생성 +- 새 API 라우트: `app/api/webhooks/stripe/route.ts` — Stripe 이벤트 처리 +- 새 미들웨어: 게이트된 기능에 대한 구독 티어 확인 +- 새 컴포넌트: `PricingTable` — 업그레이드 버튼이 있는 티어 표시 + +## 구현 단계 + +### Phase 1: 데이터베이스 & 백엔드 (2개 파일) +1. **구독 마이그레이션 생성** (File: supabase/migrations/004_subscriptions.sql) + - Action: RLS 정책과 함께 CREATE TABLE subscriptions + - Why: 결제 상태를 서버 측에 저장, 클라이언트를 절대 신뢰하지 않음 + - Dependencies: 없음 + - Risk: Low + +2. **Stripe 웹훅 핸들러 생성** (File: src/app/api/webhooks/stripe/route.ts) + - Action: checkout.session.completed, customer.subscription.updated, + customer.subscription.deleted 이벤트 처리 + - Why: 구독 상태를 Stripe와 동기화 유지 + - Dependencies: 단계 1 (subscriptions 테이블 필요) + - Risk: High — 웹훅 서명 검증이 중요 + +### Phase 2: 체크아웃 흐름 (2개 파일) +3. **체크아웃 API 라우트 생성** (File: src/app/api/checkout/route.ts) + - Action: price_id와 success/cancel URL로 Stripe Checkout 세션 생성 + - Why: 서버 측 세션 생성으로 가격 변조 방지 + - Dependencies: 단계 1 + - Risk: Medium — 사용자 인증 여부를 반드시 검증해야 함 + +4. **가격 페이지 구축** (File: src/components/PricingTable.tsx) + - Action: 기능 비교와 업그레이드 버튼이 있는 세 가지 티어 표시 + - Why: 사용자 대면 업그레이드 흐름 + - Dependencies: 단계 3 + - Risk: Low + +### Phase 3: 기능 게이팅 (1개 파일) +5. **티어 기반 미들웨어 추가** (File: src/middleware.ts) + - Action: 보호된 라우트에서 구독 티어 확인, 무료 사용자 리다이렉트 + - Why: 서버 측에서 티어 제한 강제 + - Dependencies: 단계 1-2 (구독 데이터 필요) + - Risk: Medium — 엣지 케이스 처리 필요 (expired, past_due) + +## 테스트 전략 +- 단위 테스트: 웹훅 이벤트 파싱, 티어 확인 로직 +- 통합 테스트: 체크아웃 세션 생성, 웹훅 처리 +- E2E 테스트: 전체 업그레이드 흐름 (Stripe 테스트 모드) + +## 위험 및 완화 +- **위험**: 웹훅 이벤트가 순서 없이 도착 + - 완화: 이벤트 타임스탬프 사용, 멱등 업데이트 +- **위험**: 사용자가 업그레이드했지만 웹훅 실패 + - 완화: 폴백으로 Stripe 폴링, "처리 중" 상태 표시 + +## 성공 기준 +- [ ] 사용자가 Stripe Checkout을 통해 Free에서 Pro로 업그레이드 가능 +- [ ] 웹훅이 구독 상태를 정확히 동기화 +- [ ] 무료 사용자가 Pro 기능에 접근 불가 +- [ ] 다운그레이드/취소가 정상 작동 +- [ ] 모든 테스트가 80% 이상 커버리지로 통과 +``` + ## 리팩토링 계획 시 1. 코드 스멜과 기술 부채 식별 @@ -114,6 +193,17 @@ model: opus 각 Phase는 독립적으로 merge 가능해야 합니다. 모든 Phase가 완료되어야 작동하는 계획은 피하세요. ---- +## 확인해야 할 위험 신호 + +- 큰 함수 (50줄 초과) +- 깊은 중첩 (4단계 초과) +- 중복 코드 +- 에러 처리 누락 +- 하드코딩된 값 +- 테스트 누락 +- 성능 병목 +- 테스트 전략 없는 계획 +- 명확한 파일 경로 없는 단계 +- 독립적으로 전달할 수 없는 Phase **기억하세요**: 좋은 계획은 구체적이고, 실행 가능하며, 해피 패스와 엣지 케이스 모두를 고려합니다. 최고의 계획은 자신감 있고 점진적인 구현을 가능하게 합니다. From 3e9c207c2553d3021a0b5ad7e8f4013c70251142 Mon Sep 17 00:00:00 2001 From: hahmee Date: Tue, 10 Mar 2026 13:59:43 +0900 Subject: [PATCH 6/9] docs(ko-KR): complete all command translations with full examples Add missing example sessions, code blocks, and detailed sections to 14 command files that were previously summarized versions. --- docs/ko-KR/commands/e2e.md | 278 +++++++++++++++++++++++++ docs/ko-KR/commands/eval.md | 90 +++++++- docs/ko-KR/commands/go-build.md | 145 +++++++++++++ docs/ko-KR/commands/go-review.md | 107 +++++++++- docs/ko-KR/commands/go-test.md | 220 +++++++++++++++++++ docs/ko-KR/commands/learn.md | 25 ++- docs/ko-KR/commands/orchestrate.md | 123 ++++++++++- docs/ko-KR/commands/plan.md | 99 +++++++-- docs/ko-KR/commands/refactor-clean.md | 64 ++++-- docs/ko-KR/commands/setup-pm.md | 47 ++++- docs/ko-KR/commands/tdd.md | 276 +++++++++++++++++++++++- docs/ko-KR/commands/test-coverage.md | 33 ++- docs/ko-KR/commands/update-codemaps.md | 45 +++- docs/ko-KR/commands/update-docs.md | 57 ++++- 14 files changed, 1548 insertions(+), 61 deletions(-) diff --git a/docs/ko-KR/commands/e2e.md b/docs/ko-KR/commands/e2e.md index 74a36bd9..f82c70d0 100644 --- a/docs/ko-KR/commands/e2e.md +++ b/docs/ko-KR/commands/e2e.md @@ -34,6 +34,253 @@ e2e-runner 에이전트가 수행하는 작업: 5. 결과와 아티팩트로 **보고서 생성** 6. **불안정한 테스트 식별** 및 수정 권장 +## 사용 예시 + +``` +User: /e2e 마켓 검색 및 조회 흐름 테스트 + +Agent (e2e-runner): +# E2E 테스트 생성: 마켓 검색 및 조회 흐름 + +## 식별된 테스트 시나리오 + +**사용자 여정:** 마켓 검색 → 결과 확인 → 마켓 클릭 → 상세 정보 확인 + +**테스트 범위:** +1. 마켓 페이지로 이동 +2. 시맨틱 검색 수행 +3. 검색 결과 확인 +4. 첫 번째 결과 클릭 +5. 마켓 상세 페이지 로드 확인 +6. 차트 렌더링 확인 + +## 생성된 테스트 코드 + +```typescript +// tests/e2e/markets/search-and-view.spec.ts +import { test, expect } from '@playwright/test' +import { MarketsPage } from '../../pages/MarketsPage' +import { MarketDetailsPage } from '../../pages/MarketDetailsPage' + +test.describe('Market Search and View Flow', () => { + test('user can search markets and view details', async ({ page }) => { + // 1. 마켓 페이지로 이동 + const marketsPage = new MarketsPage(page) + await marketsPage.goto() + + // 페이지 로드 확인 + await expect(page).toHaveTitle(/Markets/) + await expect(page.locator('h1')).toContainText('Markets') + + // 2. 시맨틱 검색 수행 + await marketsPage.searchMarkets('election') + + // API 응답 대기 + await page.waitForResponse(resp => + resp.url().includes('/api/markets/search') && resp.status() === 200 + ) + + // 3. 검색 결과 확인 + const marketCards = marketsPage.marketCards + await expect(marketCards.first()).toBeVisible() + const resultCount = await marketCards.count() + expect(resultCount).toBeGreaterThan(0) + + // 검색 결과 스크린샷 촬영 + await page.screenshot({ path: 'artifacts/search-results.png' }) + + // 4. 첫 번째 결과 클릭 + const firstMarketTitle = await marketCards.first().textContent() + await marketCards.first().click() + + // 5. 마켓 상세 페이지 로드 확인 + await expect(page).toHaveURL(/\/markets\/[a-z0-9-]+/) + + const detailsPage = new MarketDetailsPage(page) + await expect(detailsPage.marketName).toBeVisible() + await expect(detailsPage.marketDescription).toBeVisible() + + // 6. 차트 렌더링 확인 + await expect(detailsPage.priceChart).toBeVisible() + + // 마켓 이름 일치 확인 + const detailsTitle = await detailsPage.marketName.textContent() + expect(detailsTitle?.toLowerCase()).toContain( + firstMarketTitle?.toLowerCase().substring(0, 20) || '' + ) + + // 마켓 상세 스크린샷 촬영 + await page.screenshot({ path: 'artifacts/market-details.png' }) + }) + + test('search with no results shows empty state', async ({ page }) => { + const marketsPage = new MarketsPage(page) + await marketsPage.goto() + + // 존재하지 않는 마켓 검색 + await marketsPage.searchMarkets('xyznonexistentmarket123456') + + // 빈 상태 확인 + await expect(page.locator('[data-testid="no-results"]')).toBeVisible() + await expect(page.locator('[data-testid="no-results"]')).toContainText( + /no.*results|no.*markets/i + ) + + const marketCount = await marketsPage.marketCards.count() + expect(marketCount).toBe(0) + }) + + test('can clear search and see all markets again', async ({ page }) => { + const marketsPage = new MarketsPage(page) + await marketsPage.goto() + + // 초기 마켓 수 + const initialCount = await marketsPage.marketCards.count() + + // 검색 수행 + await marketsPage.searchMarkets('trump') + await page.waitForLoadState('networkidle') + + // 필터링된 결과 확인 + const filteredCount = await marketsPage.marketCards.count() + expect(filteredCount).toBeLessThan(initialCount) + + // 검색 초기화 + await marketsPage.searchInput.clear() + await page.waitForLoadState('networkidle') + + // 모든 마켓이 다시 표시되는지 확인 + const finalCount = await marketsPage.marketCards.count() + expect(finalCount).toBe(initialCount) + }) +}) +``` + +## 테스트 실행 + +```bash +# 생성된 테스트 실행 +npx playwright test tests/e2e/markets/search-and-view.spec.ts + +Running 3 tests using 3 workers + + ✓ [chromium] › search-and-view.spec.ts:5:3 › user can search markets and view details (4.2s) + ✓ [chromium] › search-and-view.spec.ts:52:3 › search with no results shows empty state (1.8s) + ✓ [chromium] › search-and-view.spec.ts:67:3 › can clear search and see all markets again (2.9s) + + 3 passed (9.1s) + +생성된 아티팩트: +- artifacts/search-results.png +- artifacts/market-details.png +- playwright-report/index.html +``` + +## 테스트 보고서 + +``` +╔══════════════════════════════════════════════════════════════╗ +║ E2E 테스트 결과 ║ +╠══════════════════════════════════════════════════════════════╣ +║ 상태: ✅ 모든 테스트 통과 ║ +║ 전체: 3개 테스트 ║ +║ 통과: 3 (100%) ║ +║ 실패: 0 ║ +║ 불안정: 0 ║ +║ 소요시간: 9.1s ║ +╚══════════════════════════════════════════════════════════════╝ + +아티팩트: +📸 스크린샷: 2개 파일 +📹 비디오: 0개 파일 (실패 시에만) +🔍 트레이스: 0개 파일 (실패 시에만) +📊 HTML 보고서: playwright-report/index.html + +보고서 확인: npx playwright show-report +``` + +✅ CI/CD 통합 준비가 완료된 E2E 테스트 모음! +``` + +## 테스트 아티팩트 + +테스트 실행 시 다음 아티팩트가 캡처됩니다: + +**모든 테스트:** +- 타임라인과 결과가 포함된 HTML 보고서 +- CI 통합을 위한 JUnit XML + +**실패 시에만:** +- 실패 상태의 스크린샷 +- 테스트의 비디오 녹화 +- 디버깅을 위한 트레이스 파일 (단계별 재생) +- 네트워크 로그 +- 콘솔 로그 + +## 아티팩트 확인 + +```bash +# 브라우저에서 HTML 보고서 확인 +npx playwright show-report + +# 특정 트레이스 파일 확인 +npx playwright show-trace artifacts/trace-abc123.zip + +# 스크린샷은 artifacts/ 디렉토리에 저장됨 +open artifacts/search-results.png +``` + +## 불안정한 테스트 감지 + +테스트가 간헐적으로 실패하는 경우: + +``` +⚠️ 불안정한 테스트 감지됨: tests/e2e/markets/trade.spec.ts + +테스트가 10회 중 7회 통과 (70% 통과율) + +일반적인 실패 원인: +"요소 '[data-testid="confirm-btn"]'을 대기하는 중 타임아웃" + +권장 수정 사항: +1. 명시적 대기 추가: await page.waitForSelector('[data-testid="confirm-btn"]') +2. 타임아웃 증가: { timeout: 10000 } +3. 컴포넌트의 레이스 컨디션 확인 +4. 애니메이션에 의해 요소가 숨겨져 있지 않은지 확인 + +격리 권장: 수정될 때까지 test.fixme()로 표시 +``` + +## 브라우저 구성 + +기본적으로 여러 브라우저에서 테스트가 실행됩니다: +- Chromium (데스크톱 Chrome) +- Firefox (데스크톱) +- WebKit (데스크톱 Safari) +- Mobile Chrome (선택 사항) + +`playwright.config.ts`에서 브라우저를 조정할 수 있습니다. + +## CI/CD 통합 + +CI 파이프라인에 추가: + +```yaml +# .github/workflows/e2e.yml +- name: Install Playwright + run: npx playwright install --with-deps + +- name: Run E2E tests + run: npx playwright test + +- name: Upload artifacts + if: always() + uses: actions/upload-artifact@v3 + with: + name: playwright-report + path: playwright-report/ +``` + ## 모범 사례 **해야 할 것:** @@ -42,15 +289,46 @@ e2e-runner 에이전트가 수행하는 작업: - 임의의 타임아웃 대신 API 응답을 대기 - 핵심 사용자 여정을 E2E로 테스트 - main에 merge하기 전에 테스트 실행 +- 테스트 실패 시 아티팩트 검토 **하지 말아야 할 것:** - 취약한 셀렉터 사용 (CSS 클래스는 변경될 수 있음) - 구현 세부사항 테스트 - 프로덕션에 대해 테스트 실행 - 불안정한 테스트 무시 +- 실패 시 아티팩트 검토 생략 - E2E로 모든 엣지 케이스 테스트 (단위 테스트 사용) +## 다른 커맨드와의 연동 + +- `/plan`을 사용하여 테스트할 핵심 여정 식별 +- `/tdd`를 사용하여 단위 테스트 (더 빠르고 세밀함) +- `/e2e`를 사용하여 통합 및 사용자 여정 테스트 +- `/code-review`를 사용하여 테스트 품질 검증 + ## 관련 에이전트 이 커맨드는 `e2e-runner` 에이전트를 호출합니다: `~/.claude/agents/e2e-runner.md` + +## 빠른 커맨드 + +```bash +# 모든 E2E 테스트 실행 +npx playwright test + +# 특정 테스트 파일 실행 +npx playwright test tests/e2e/markets/search.spec.ts + +# headed 모드로 실행 (브라우저 표시) +npx playwright test --headed + +# 테스트 디버그 +npx playwright test --debug + +# 테스트 코드 생성 +npx playwright codegen http://localhost:3000 + +# 보고서 확인 +npx playwright show-report +``` diff --git a/docs/ko-KR/commands/eval.md b/docs/ko-KR/commands/eval.md index 49f15df2..e07ea256 100644 --- a/docs/ko-KR/commands/eval.md +++ b/docs/ko-KR/commands/eval.md @@ -12,8 +12,26 @@ 새로운 평가 정의를 생성합니다: -1. `.claude/evals/feature-name.md`에 템플릿 생성 -2. 사용자에게 구체적인 기준을 입력하도록 안내 +1. `.claude/evals/feature-name.md`에 템플릿을 생성합니다: + +```markdown +## EVAL: feature-name +Created: $(date) + +### Capability Evals +- [ ] [기능 1에 대한 설명] +- [ ] [기능 2에 대한 설명] + +### Regression Evals +- [ ] [기존 동작 1이 여전히 작동함] +- [ ] [기존 동작 2이 여전히 작동함] + +### Success Criteria +- capability eval에 대해 pass@3 > 90% +- regression eval에 대해 pass^3 = 100% +``` + +2. 사용자에게 구체적인 기준을 입력하도록 안내합니다 ## 평가 확인 @@ -21,22 +39,76 @@ 기능에 대한 평가를 실행합니다: -1. `.claude/evals/feature-name.md`에서 평가 정의 읽기 -2. 각 기능 평가: 기준 검증 시도, PASS/FAIL 기록 -3. 각 회귀 평가: 관련 테스트 실행, 기준선과 비교 -4. 현재 상태 보고 +1. `.claude/evals/feature-name.md`에서 평가 정의를 읽습니다 +2. 각 capability eval에 대해: + - 기준 검증을 시도합니다 + - PASS/FAIL을 기록합니다 + - `.claude/evals/feature-name.log`에 시도를 기록합니다 +3. 각 regression eval에 대해: + - 관련 테스트를 실행합니다 + - 기준선과 비교합니다 + - PASS/FAIL을 기록합니다 +4. 현재 상태를 보고합니다: + +``` +EVAL CHECK: feature-name +======================== +Capability: X/Y passing +Regression: X/Y passing +Status: IN PROGRESS / READY +``` ## 평가 보고 `/eval report feature-name` -포괄적인 평가 보고서를 생성합니다. +포괄적인 평가 보고서를 생성합니다: + +``` +EVAL REPORT: feature-name +========================= +Generated: $(date) + +CAPABILITY EVALS +---------------- +[eval-1]: PASS (pass@1) +[eval-2]: PASS (pass@2) - 재시도 필요했음 +[eval-3]: FAIL - 비고 참조 + +REGRESSION EVALS +---------------- +[test-1]: PASS +[test-2]: PASS +[test-3]: PASS + +METRICS +------- +Capability pass@1: 67% +Capability pass@3: 100% +Regression pass^3: 100% + +NOTES +----- +[이슈, 엣지 케이스 또는 관찰 사항] + +RECOMMENDATION +-------------- +[SHIP / NEEDS WORK / BLOCKED] +``` ## 평가 목록 `/eval list` -모든 평가 정의를 표시합니다. +모든 평가 정의를 표시합니다: + +``` +EVAL DEFINITIONS +================ +feature-auth [3/5 passing] IN PROGRESS +feature-search [5/5 passing] READY +feature-export [0/4 passing] NOT STARTED +``` ## 인자 @@ -45,4 +117,4 @@ $ARGUMENTS: - `check ` - 평가 실행 및 확인 - `report ` - 전체 보고서 생성 - `list` - 모든 평가 표시 -- `clean` - 오래된 평가 로그 제거 (최근 10회 유지) +- `clean` - 오래된 평가 로그 제거 (최근 10회 실행 유지) diff --git a/docs/ko-KR/commands/go-build.md b/docs/ko-KR/commands/go-build.md index 824240f8..3933af10 100644 --- a/docs/ko-KR/commands/go-build.md +++ b/docs/ko-KR/commands/go-build.md @@ -23,6 +23,138 @@ description: Go build 에러, go vet 경고, 린터 이슈를 점진적으로 - 모듈 의존성이 깨졌을 때 - 변경사항을 pull한 후 build가 깨졌을 때 +## 실행되는 진단 커맨드 + +```bash +# 기본 build 확인 +go build ./... + +# 정적 분석 +go vet ./... + +# 확장 린팅 (사용 가능한 경우) +staticcheck ./... +golangci-lint run + +# 모듈 이슈 +go mod verify +go mod tidy -v +``` + +## 예시 세션 + +```text +User: /go-build + +Agent: +# Go Build 해결 + +## 초기 진단 + +```bash +$ go build ./... +internal/service/user.go:25:15: undefined: UserRepository +internal/handler/api.go:42:9: cannot use x (type string) as type int +internal/handler/api.go:58:2: missing return at end of function +``` + +발견된 에러: 3개 + +## 수정 1: 정의되지 않은 식별자 + +파일: internal/service/user.go:25 +에러: undefined: UserRepository +원인: import 누락 + +```go +// import 추가 +import "project/internal/repository" + +// 변경 전 +var repo UserRepository +// 변경 후 +var repo repository.UserRepository +``` + +```bash +$ go build ./... +# 2개 에러 남음 +``` + +## 수정 2: 타입 불일치 + +파일: internal/handler/api.go:42 +에러: cannot use x (type string) as type int + +```go +// 변경 전 +count := params.Get("count") +// 변경 후 +countStr := params.Get("count") +count, _ := strconv.Atoi(countStr) +``` + +```bash +$ go build ./... +# 1개 에러 남음 +``` + +## 수정 3: 누락된 return + +파일: internal/handler/api.go:58 +에러: missing return at end of function + +```go +func GetUser(id string) (*User, error) { + if id == "" { + return nil, ErrInvalidID + } + user := findUser(id) + // 누락된 return 추가 + return user, nil +} +``` + +```bash +$ go build ./... +# Build 성공! +``` + +## 최종 검증 + +```bash +$ go vet ./... +# 이슈 없음 + +$ go test ./... +ok project/internal/service 0.015s +ok project/internal/handler 0.023s +``` + +## 요약 + +| 항목 | 수량 | +|------|------| +| 수정된 Build 에러 | 3 | +| 수정된 Vet 경고 | 0 | +| 수정된 파일 | 2 | +| 남은 이슈 | 0 | + +Build 상태: ✅ 성공 +``` + +## 자주 발생하는 에러 + +| 에러 | 일반적인 수정 방법 | +|------|-------------------| +| `undefined: X` | import 추가 또는 오타 수정 | +| `cannot use X as Y` | 타입 변환 또는 할당 수정 | +| `missing return` | return 문 추가 | +| `X does not implement Y` | 누락된 메서드 추가 | +| `import cycle` | 패키지 구조 재구성 | +| `declared but not used` | 변수 제거 또는 사용 | +| `cannot find package` | `go get` 또는 `go mod tidy` | + ## 수정 전략 1. **Build 에러 먼저** - 코드가 컴파일되어야 함 @@ -31,8 +163,21 @@ description: Go build 에러, go vet 경고, 린터 이슈를 점진적으로 4. **한 번에 하나씩** - 각 변경 검증 5. **최소한의 변경** - 리팩토링이 아닌 수정만 +## 중단 조건 + +에이전트가 중단하고 보고하는 경우: +- 3번 시도 후에도 같은 에러가 지속 +- 수정이 더 많은 에러를 발생시킴 +- 아키텍처 변경이 필요한 경우 +- 외부 의존성이 누락된 경우 + ## 관련 커맨드 - `/go-test` - build 성공 후 테스트 실행 - `/go-review` - 코드 품질 리뷰 - `/verify` - 전체 검증 루프 + +## 관련 항목 + +- 에이전트: `agents/go-build-resolver.md` +- 스킬: `skills/golang-patterns/` diff --git a/docs/ko-KR/commands/go-review.md b/docs/ko-KR/commands/go-review.md index 395f9f56..61b272e7 100644 --- a/docs/ko-KR/commands/go-review.md +++ b/docs/ko-KR/commands/go-review.md @@ -22,6 +22,7 @@ description: 관용적 패턴, 동시성 안전성, 에러 처리, 보안에 대 - Go 변경사항을 커밋하기 전 - Go 코드가 포함된 PR 리뷰 시 - 새 Go 코드베이스에 온보딩할 때 +- 관용적 Go 패턴 학습 시 ## 리뷰 카테고리 @@ -30,20 +31,118 @@ description: 관용적 패턴, 동시성 안전성, 에러 처리, 보안에 대 - 동기화 없는 레이스 컨디션 - 고루틴 누수 - 하드코딩된 인증 정보 +- unsafe 포인터 사용 +- 핵심 경로에서 에러 무시 ### HIGH (수정 권장) - 컨텍스트 없는 에러 래핑 누락 - 에러 반환 대신 panic 사용 - 컨텍스트 전파 누락 - 데드락을 유발하는 버퍼 없는 채널 +- 인터페이스 미충족 에러 +- 뮤텍스 보호 누락 ### MEDIUM (고려) - 비관용적 코드 패턴 - 공개 항목에 godoc 주석 누락 - 비효율적인 문자열 연결 +- 슬라이스 사전 할당 누락 +- 테이블 기반 테스트 미사용 -## 관련 커맨드 +## 실행되는 자동 검사 -- `/go-test` - 먼저 테스트 통과 확인 -- `/go-build` - build 에러 발생 시 -- `/code-review` - Go 외 일반적인 관심사항 +```bash +# 정적 분석 +go vet ./... + +# 고급 검사 (설치된 경우) +staticcheck ./... +golangci-lint run + +# 레이스 감지 +go build -race ./... + +# 보안 취약점 +govulncheck ./... +``` + +## 사용 예시 + +```text +User: /go-review + +Agent: +# Go 코드 리뷰 보고서 + +## 리뷰된 파일 +- internal/handler/user.go (수정됨) +- internal/service/auth.go (수정됨) + +## 정적 분석 결과 +✓ go vet: 이슈 없음 +✓ staticcheck: 이슈 없음 + +## 발견된 이슈 + +[CRITICAL] 레이스 컨디션 +파일: internal/service/auth.go:45 +이슈: 동기화 없이 공유 맵에 접근 +```go +var cache = map[string]*Session{} // 동시 접근! + +func GetSession(id string) *Session { + return cache[id] // 레이스 컨디션 +} +``` +수정: sync.RWMutex 또는 sync.Map 사용 +```go +var ( + cache = map[string]*Session{} + cacheMu sync.RWMutex +) + +func GetSession(id string) *Session { + cacheMu.RLock() + defer cacheMu.RUnlock() + return cache[id] +} +``` + +[HIGH] 에러 컨텍스트 누락 +파일: internal/handler/user.go:28 +이슈: 컨텍스트 없이 에러 반환 +```go +return err // 컨텍스트 없음 +``` +수정: 컨텍스트와 함께 래핑 +```go +return fmt.Errorf("get user %s: %w", userID, err) +``` + +## 요약 +- CRITICAL: 1 +- HIGH: 1 +- MEDIUM: 0 + +권장: ❌ CRITICAL 이슈가 수정될 때까지 merge 차단 +``` + +## 승인 기준 + +| 상태 | 조건 | +|------|------| +| ✅ 승인 | CRITICAL 또는 HIGH 이슈 없음 | +| ⚠️ 경고 | MEDIUM 이슈만 있음 (주의하여 merge) | +| ❌ 차단 | CRITICAL 또는 HIGH 이슈 발견 | + +## 다른 커맨드와의 연동 + +- `/go-test`를 먼저 사용하여 테스트 통과 확인 +- `/go-build`를 사용하여 build 에러 발생 시 수정 +- `/go-review`를 커밋 전에 사용 +- `/code-review`를 사용하여 Go 외 일반적인 관심사항 리뷰 + +## 관련 항목 + +- 에이전트: `agents/go-reviewer.md` +- 스킬: `skills/golang-patterns/`, `skills/golang-testing/` diff --git a/docs/ko-KR/commands/go-test.md b/docs/ko-KR/commands/go-test.md index a30a1648..3e9e83c6 100644 --- a/docs/ko-KR/commands/go-test.md +++ b/docs/ko-KR/commands/go-test.md @@ -22,6 +22,7 @@ description: Go용 TDD 워크플로우 강제. 테이블 기반 테스트를 먼 - 기존 코드에 테스트 커버리지 추가 - 버그 수정 (실패하는 테스트를 먼저 작성) - 핵심 비즈니스 로직 구현 +- Go에서 TDD 워크플로우 학습 ## TDD 사이클 @@ -32,6 +33,204 @@ REFACTOR → 코드 개선, 테스트는 통과 유지 REPEAT → 다음 테스트 케이스 ``` +## 예시 세션 + +```` +User: /go-test 이메일 주소를 검증하는 함수가 필요합니다 + +Agent: +# TDD 세션: 이메일 검증기 + +## 1단계: 인터페이스 정의 + +```go +// validator/email.go +package validator + +// ValidateEmail은 주어진 문자열이 유효한 이메일 주소인지 확인합니다. +// 유효하면 nil을 반환하고, 그렇지 않으면 이슈를 설명하는 error를 반환합니다. +func ValidateEmail(email string) error { + panic("not implemented") +} +``` + +## 2단계: 테이블 기반 테스트 작성 (RED) + +```go +// validator/email_test.go +package validator + +import ( + "testing" +) + +func TestValidateEmail(t *testing.T) { + tests := []struct { + name string + email string + wantErr bool + }{ + // 유효한 이메일 + {"단순 이메일", "user@example.com", false}, + {"서브도메인 포함", "user@mail.example.com", false}, + {"플러스 포함", "user+tag@example.com", false}, + {"점 포함", "first.last@example.com", false}, + + // 유효하지 않은 이메일 + {"빈 문자열", "", true}, + {"@ 기호 없음", "userexample.com", true}, + {"도메인 없음", "user@", true}, + {"로컬 파트 없음", "@example.com", true}, + {"이중 @", "user@@example.com", true}, + {"공백 포함", "user @example.com", true}, + {"TLD 없음", "user@example", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateEmail(tt.email) + if tt.wantErr && err == nil { + t.Errorf("ValidateEmail(%q) = nil; want error", tt.email) + } + if !tt.wantErr && err != nil { + t.Errorf("ValidateEmail(%q) = %v; want nil", tt.email, err) + } + }) + } +} +``` + +## 3단계: 테스트 실행 - 실패 확인 + +```bash +$ go test ./validator/... + +--- FAIL: TestValidateEmail (0.00s) + --- FAIL: TestValidateEmail/단순_이메일 (0.00s) + panic: not implemented + +FAIL +``` + +✓ 예상대로 테스트 실패 (panic). + +## 4단계: 최소한의 코드 구현 (GREEN) + +```go +// validator/email.go +package validator + +import ( + "errors" + "regexp" +) + +var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) + +var ( + ErrEmailEmpty = errors.New("email cannot be empty") + ErrEmailInvalid = errors.New("email format is invalid") +) + +func ValidateEmail(email string) error { + if email == "" { + return ErrEmailEmpty + } + if !emailRegex.MatchString(email) { + return ErrEmailInvalid + } + return nil +} +``` + +## 5단계: 테스트 실행 - 통과 확인 + +```bash +$ go test ./validator/... + +PASS +ok project/validator 0.003s +``` + +✓ 모든 테스트 통과! + +## 6단계: 커버리지 확인 + +```bash +$ go test -cover ./validator/... + +PASS +coverage: 100.0% of statements +ok project/validator 0.003s +``` + +✓ 커버리지: 100% + +## TDD 완료! +```` + +## 테스트 패턴 + +### 테이블 기반 테스트 +```go +tests := []struct { + name string + input InputType + want OutputType + wantErr bool +}{ + {"케이스 1", input1, want1, false}, + {"케이스 2", input2, want2, true}, +} + +for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Function(tt.input) + // 단언문 + }) +} +``` + +### 병렬 테스트 +```go +for _, tt := range tests { + tt := tt // 캡처 + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + // 테스트 본문 + }) +} +``` + +### 테스트 헬퍼 +```go +func setupTestDB(t *testing.T) *sql.DB { + t.Helper() + db := createDB() + t.Cleanup(func() { db.Close() }) + return db +} +``` + +## 커버리지 커맨드 + +```bash +# 기본 커버리지 +go test -cover ./... + +# 커버리지 프로파일 +go test -coverprofile=coverage.out ./... + +# 브라우저에서 확인 +go tool cover -html=coverage.out + +# 함수별 커버리지 +go tool cover -func=coverage.out + +# 레이스 감지와 함께 +go test -race -cover ./... +``` + ## 커버리지 목표 | 코드 유형 | 목표 | @@ -41,8 +240,29 @@ REPEAT → 다음 테스트 케이스 | 일반 코드 | 80%+ | | 생성된 코드 | 제외 | +## TDD 모범 사례 + +**해야 할 것:** +- 구현 전에 테스트를 먼저 작성 +- 각 변경 후 테스트 실행 +- 포괄적인 커버리지를 위해 테이블 기반 테스트 사용 +- 구현 세부사항이 아닌 동작 테스트 +- 엣지 케이스 포함 (빈 값, nil, 최대값) + +**하지 말아야 할 것:** +- 테스트 전에 구현 작성 +- RED 단계 건너뛰기 +- private 함수를 직접 테스트 +- 테스트에서 `time.Sleep` 사용 +- 불안정한 테스트 무시 + ## 관련 커맨드 - `/go-build` - build 에러 수정 - `/go-review` - 구현 후 코드 리뷰 - `/verify` - 전체 검증 루프 + +## 관련 항목 + +- 스킬: `skills/golang-testing/` +- 스킬: `skills/tdd-workflow/` diff --git a/docs/ko-KR/commands/learn.md b/docs/ko-KR/commands/learn.md index 3ded15a0..d4e81a40 100644 --- a/docs/ko-KR/commands/learn.md +++ b/docs/ko-KR/commands/learn.md @@ -21,7 +21,7 @@ - 효과적인 도구 조합 - 진단 패턴 -3. **해결 방법** +3. **우회 방법** - 라이브러리 특이 사항 - API 제한 사항 - 버전별 수정 사항 @@ -31,6 +31,29 @@ - 내려진 아키텍처 결정 - 통합 패턴 +## 출력 형식 + +`~/.claude/skills/learned/[pattern-name].md`에 스킬 파일을 생성합니다: + +```markdown +# [설명적인 패턴 이름] + +**추출일:** [날짜] +**컨텍스트:** [이 패턴이 적용되는 상황에 대한 간략한 설명] + +## 문제 +[이 패턴이 해결하는 문제 - 구체적으로 작성] + +## 해결 방법 +[패턴/기법/우회 방법] + +## 예시 +[해당하는 경우 코드 예시] + +## 사용 시점 +[트리거 조건 - 이 스킬이 활성화되어야 하는 상황] +``` + ## 프로세스 1. 세션에서 추출 가능한 패턴 검토 diff --git a/docs/ko-KR/commands/orchestrate.md b/docs/ko-KR/commands/orchestrate.md index 37c73151..a9845ea3 100644 --- a/docs/ko-KR/commands/orchestrate.md +++ b/docs/ko-KR/commands/orchestrate.md @@ -41,6 +41,113 @@ security-reviewer -> code-reviewer -> architect 3. 체인의 **다음 에이전트에 전달** 4. **결과를 종합**하여 최종 보고서 작성 +## 핸드오프 문서 형식 + +에이전트 간에 핸드오프 문서를 생성합니다: + +```markdown +## HANDOFF: [이전-에이전트] -> [다음-에이전트] + +### Context +[수행된 작업 요약] + +### Findings +[주요 발견 사항 또는 결정 사항] + +### Files Modified +[수정된 파일 목록] + +### Open Questions +[다음 에이전트를 위한 미해결 항목] + +### Recommendations +[제안하는 다음 단계] +``` + +## 예시: Feature 워크플로우 + +``` +/orchestrate feature "Add user authentication" +``` + +실행 순서: + +1. **Planner 에이전트** + - 요구사항 분석 + - 구현 계획 작성 + - 의존성 식별 + - 출력: `HANDOFF: planner -> tdd-guide` + +2. **TDD Guide 에이전트** + - planner 핸드오프 읽기 + - 테스트 먼저 작성 + - 테스트를 통과하도록 구현 + - 출력: `HANDOFF: tdd-guide -> code-reviewer` + +3. **Code Reviewer 에이전트** + - 구현 리뷰 + - 이슈 확인 + - 개선사항 제안 + - 출력: `HANDOFF: code-reviewer -> security-reviewer` + +4. **Security Reviewer 에이전트** + - 보안 감사 + - 취약점 점검 + - 최종 승인 + - 출력: 최종 보고서 + +## 최종 보고서 형식 + +``` +ORCHESTRATION REPORT +==================== +Workflow: feature +Task: Add user authentication +Agents: planner -> tdd-guide -> code-reviewer -> security-reviewer + +SUMMARY +------- +[한 단락 요약] + +AGENT OUTPUTS +------------- +Planner: [요약] +TDD Guide: [요약] +Code Reviewer: [요약] +Security Reviewer: [요약] + +FILES CHANGED +------------- +[수정된 모든 파일 목록] + +TEST RESULTS +------------ +[테스트 통과/실패 요약] + +SECURITY STATUS +--------------- +[보안 발견 사항] + +RECOMMENDATION +-------------- +[SHIP / NEEDS WORK / BLOCKED] +``` + +## 병렬 실행 + +독립적인 검사에 대해서는 에이전트를 병렬로 실행합니다: + +```markdown +### Parallel Phase +동시에 실행: +- code-reviewer (품질) +- security-reviewer (보안) +- architect (설계) + +### Merge Results +출력을 단일 보고서로 통합 +``` + ## 인자 $ARGUMENTS: @@ -50,10 +157,16 @@ $ARGUMENTS: - `security ` - 보안 리뷰 워크플로우 - `custom ` - 사용자 정의 에이전트 순서 +## 사용자 정의 워크플로우 예시 + +``` +/orchestrate custom "architect,tdd-guide,code-reviewer" "Redesign caching layer" +``` + ## 팁 -1. 복잡한 기능에는 **planner부터 시작** -2. merge 전에는 **항상 code-reviewer 포함** -3. 인증/결제/개인정보에는 **security-reviewer 사용** -4. **핸드오프는 간결하게** - 다음 에이전트에 필요한 것에 집중 -5. 필요하면 에이전트 사이에 **검증 실행** +1. 복잡한 기능에는 **planner부터 시작**하세요 +2. merge 전에는 **항상 code-reviewer를 포함**하세요 +3. 인증/결제/개인정보 처리에는 **security-reviewer를 사용**하세요 +4. **핸드오프는 간결하게** 유지하세요 - 다음 에이전트에 필요한 것에 집중 +5. 필요한 경우 에이전트 사이에 **검증을 실행**하세요 diff --git a/docs/ko-KR/commands/plan.md b/docs/ko-KR/commands/plan.md index 512e0d93..3e0985f5 100644 --- a/docs/ko-KR/commands/plan.md +++ b/docs/ko-KR/commands/plan.md @@ -6,19 +6,19 @@ description: 요구사항을 재확인하고, 위험을 평가하며, 단계별 이 커맨드는 **planner** 에이전트를 호출하여 코드를 작성하기 전에 포괄적인 구현 계획을 만듭니다. -## 이 커맨드가 하는 것 +## 이 커맨드가 하는 일 -1. **요구사항 재확인** - 무엇을 만들어야 하는지 명확히 함 -2. **위험 식별** - 잠재적 이슈와 차단 요소 도출 -3. **단계별 계획 작성** - 구현을 단계로 분해 -4. **확인 대기** - 진행 전 반드시 사용자 승인을 받음 +1. **요구사항 재확인** - 무엇을 만들어야 하는지 명확히 합니다 +2. **위험 식별** - 잠재적 이슈와 차단 요소를 도출합니다 +3. **단계별 계획 작성** - 구현을 단계별로 분해합니다 +4. **확인 대기** - 진행하기 전에 반드시 사용자 승인을 받아야 합니다 ## 사용 시점 `/plan`을 사용해야 할 때: -- 새 기능 시작 -- 중요한 아키텍처 변경 -- 복잡한 리팩토링 작업 +- 새 기능을 시작할 때 +- 중요한 아키텍처 변경을 할 때 +- 복잡한 리팩토링 작업을 할 때 - 여러 파일/컴포넌트에 영향을 미칠 때 - 요구사항이 불명확하거나 모호할 때 @@ -26,25 +26,88 @@ description: 요구사항을 재확인하고, 위험을 평가하며, 단계별 planner 에이전트가 수행하는 작업: -1. 요청을 **분석**하고 요구사항을 명확하게 재확인 -2. 구체적이고 실행 가능한 단계로 **분해** -3. 컴포넌트 간 **의존성 식별** -4. **위험 평가** 및 잠재적 차단 요소 파악 -5. **복잡도 추정** (High/Medium/Low) -6. 계획을 **제시**하고 명시적 확인을 **대기** +1. 요청을 **분석**하고 요구사항을 명확한 용어로 재확인합니다 +2. 구체적이고 실행 가능한 단계로 **분해**합니다 +3. 컴포넌트 간 **의존성을 식별**합니다 +4. **위험을 평가**하고 잠재적 차단 요소를 파악합니다 +5. **복잡도를 추정**합니다 (High/Medium/Low) +6. 계획을 **제시**하고 명시적 확인을 **대기**합니다 + +## 사용 예시 + +``` +User: /plan 마켓이 해결될 때 실시간 알림을 추가해야 합니다 + +Agent (planner): +# 구현 계획: 실시간 마켓 해결 알림 + +## 요구사항 재확인 +- 사용자가 주시 중인 마켓이 해결될 때 알림 전송 +- 다중 알림 채널 지원 (인앱, 이메일, webhook) +- 알림이 안정적으로 전달되도록 보장 +- 마켓 결과와 사용자의 포지션 결과 포함 + +## 구현 단계 + +### 1단계: 데이터베이스 스키마 +- id, user_id, market_id, type, status, created_at 컬럼을 가진 notifications 테이블 추가 +- 채널 설정을 위한 user_notification_preferences 테이블 추가 +- 성능을 위해 user_id와 market_id에 인덱스 생성 + +### 2단계: 알림 서비스 +- lib/notifications.ts에 알림 서비스 생성 +- BullMQ/Redis를 사용한 알림 큐 구현 +- 전송 실패 시 재시도 로직 추가 +- 알림 템플릿 생성 + +### 3단계: 연동 포인트 +- 마켓 해결 로직에 연결 (status가 "resolved"로 변경될 때) +- 해당 마켓에 포지션을 가진 모든 사용자 조회 +- 각 사용자에 대한 알림 큐에 추가 + +### 4단계: 프론트엔드 컴포넌트 +- 헤더에 NotificationBell 컴포넌트 생성 +- NotificationList 모달 추가 +- Supabase subscriptions를 통한 실시간 업데이트 구현 +- 알림 설정 페이지 추가 + +## 의존성 +- Redis (큐용) +- 이메일 서비스 (SendGrid/Resend) +- Supabase 실시간 subscriptions + +## 위험 +- HIGH: 이메일 전달률 (SPF/DKIM 필요) +- MEDIUM: 마켓당 1000명 이상의 사용자 시 성능 +- MEDIUM: 마켓이 자주 해결될 경우 알림 스팸 +- LOW: 실시간 subscription 오버헤드 + +## 예상 복잡도: MEDIUM +- 백엔드: 4-6시간 +- 프론트엔드: 3-4시간 +- 테스트: 2-3시간 +- 합계: 9-13시간 + +**확인 대기 중**: 이 계획으로 진행할까요? (yes/no/modify) +``` ## 중요 참고 사항 -**핵심**: planner 에이전트는 "yes"나 "proceed" 같은 긍정적 응답으로 명시적으로 확인하기 전까지 코드를 **작성하지 않습니다.** +**핵심**: planner 에이전트는 "yes"나 "proceed" 같은 긍정적 응답으로 명시적으로 계획을 확인하기 전까지 코드를 **절대 작성하지 않습니다.** + +변경을 원하면 다음과 같이 응답하세요: +- "modify: [변경 사항]" +- "different approach: [대안]" +- "skip phase 2 and do phase 3 first" ## 다른 커맨드와의 연계 계획 수립 후: - `/tdd`를 사용하여 테스트 주도 개발로 구현 -- `/build-fix`로 build 에러 발생 시 수정 -- `/code-review`로 완성된 구현 리뷰 +- 빌드 에러 발생 시 `/build-fix` 사용 +- 완성된 구현을 `/code-review`로 리뷰 ## 관련 에이전트 -이 커맨드는 `planner` 에이전트를 호출합니다: +이 커맨드는 다음 위치의 `planner` 에이전트를 호출합니다: `~/.claude/agents/planner.md` diff --git a/docs/ko-KR/commands/refactor-clean.md b/docs/ko-KR/commands/refactor-clean.md index 6923c8f7..0ab81fe7 100644 --- a/docs/ko-KR/commands/refactor-clean.md +++ b/docs/ko-KR/commands/refactor-clean.md @@ -1,10 +1,10 @@ # Refactor Clean -사용하지 않는 코드를 안전하게 식별하고 매 단계마다 테스트 검증을 수행합니다. +사용하지 않는 코드를 안전하게 식별하고 매 단계마다 테스트 검증을 수행하여 제거합니다. ## 1단계: 사용하지 않는 코드 감지 -프로젝트 유형에 따라 분석 도구 실행: +프로젝트 유형에 따라 분석 도구를 실행합니다: | 도구 | 감지 대상 | 커맨드 | |------|----------|--------| @@ -13,30 +13,68 @@ | ts-prune | 미사용 TypeScript exports | `npx ts-prune` | | vulture | 미사용 Python 코드 | `vulture src/` | | deadcode | 미사용 Go 코드 | `deadcode ./...` | +| cargo-udeps | 미사용 Rust 의존성 | `cargo +nightly udeps` | + +사용 가능한 도구가 없는 경우, Grep을 사용하여 import가 없는 export를 찾습니다: +``` +# export를 찾은 후, 다른 곳에서 import되는지 확인 +``` ## 2단계: 결과 분류 -안전 등급별 분류: +안전 등급별로 결과를 분류합니다: | 등급 | 예시 | 조치 | |------|------|------| | **안전** | 미사용 유틸리티, 테스트 헬퍼, 내부 함수 | 확신을 가지고 삭제 | -| **주의** | 컴포넌트, API 라우트, 미들웨어 | 동적 임포트나 외부 소비자가 없는지 확인 | -| **위험** | 설정 파일, 엔트리 포인트, 타입 정의 | 건드리기 전에 조사 | +| **주의** | 컴포넌트, API 라우트, 미들웨어 | 동적 import나 외부 소비자가 없는지 확인 | +| **위험** | 설정 파일, 엔트리 포인트, 타입 정의 | 건드리기 전에 조사 필요 | ## 3단계: 안전한 삭제 루프 각 안전 항목에 대해: -1. **전체 테스트 실행** — 기준선 확립 (모두 통과) -2. **사용하지 않는 코드 삭제** — Edit 도구로 정밀 삭제 -3. **테스트 재실행** — 깨진 것이 없는지 확인 -4. **테스트 실패 시** — 즉시 `git checkout -- `로 되돌리고 건너뜀 -5. **테스트 통과 시** — 다음 항목으로 이동 +1. **전체 테스트 스위트 실행** --- 기준선 확립 (모두 통과) +2. **사용하지 않는 코드 삭제** --- Edit 도구로 정밀하게 제거 +3. **테스트 스위트 재실행** --- 깨진 것이 없는지 확인 +4. **테스트 실패 시** --- 즉시 `git checkout -- `로 되돌리고 해당 항목을 건너뜀 +5. **테스트 통과 시** --- 다음 항목으로 이동 + +## 4단계: 주의 항목 처리 + +주의 항목을 삭제하기 전에: +- 동적 import 검색: `import()`, `require()`, `__import__` +- 문자열 참조 검색: 라우트 이름, 설정 파일의 컴포넌트 이름 +- 공개 패키지 API에서 export되는지 확인 +- 외부 소비자가 없는지 확인 (게시된 경우 의존 패키지 확인) + +## 5단계: 중복 통합 + +사용하지 않는 코드를 제거한 후 다음을 찾습니다: +- 거의 중복된 함수 (80% 이상 유사) --- 하나로 병합 +- 중복된 타입 정의 --- 통합 +- 가치를 추가하지 않는 래퍼 함수 --- 인라인 처리 +- 목적이 없는 re-export --- 간접 참조 제거 + +## 6단계: 요약 + +결과를 보고합니다: + +``` +Dead Code Cleanup +────────────────────────────── +삭제: 미사용 함수 12개 + 미사용 파일 3개 + 미사용 의존성 5개 +건너뜀: 항목 2개 (테스트 실패) +절감: 약 450줄 제거 +────────────────────────────── +모든 테스트 통과 ✅ +``` ## 규칙 - **테스트를 먼저 실행하지 않고 절대 삭제하지 않기** -- **한 번에 하나씩 삭제** — 원자적 변경으로 롤백 용이 -- **확실하지 않으면 건너뛰기** — 프로덕션을 깨뜨리는 것보다 사용하지 않는 코드를 유지하는 것이 나음 -- **정리하면서 리팩토링하지 않기** — 관심사 분리 (먼저 정리, 나중에 리팩토링) +- **한 번에 하나씩 삭제** --- 원자적 변경으로 롤백이 쉬움 +- **확실하지 않으면 건너뛰기** --- 프로덕션을 깨뜨리는 것보다 사용하지 않는 코드를 유지하는 것이 나음 +- **정리하면서 리팩토링하지 않기** --- 관심사 분리 (먼저 정리, 나중에 리팩토링) diff --git a/docs/ko-KR/commands/setup-pm.md b/docs/ko-KR/commands/setup-pm.md index 5e9d54b1..43b01ace 100644 --- a/docs/ko-KR/commands/setup-pm.md +++ b/docs/ko-KR/commands/setup-pm.md @@ -30,6 +30,51 @@ node scripts/setup-package-manager.js --list 1. **환경 변수**: `CLAUDE_PACKAGE_MANAGER` 2. **프로젝트 설정**: `.claude/package-manager.json` 3. **package.json**: `packageManager` 필드 -4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb +4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb의 존재 여부 5. **전역 설정**: `~/.claude/package-manager.json` 6. **폴백**: 사용 가능한 첫 번째 패키지 매니저 (pnpm > bun > yarn > npm) + +## 설정 파일 + +### 전역 설정 +```json +// ~/.claude/package-manager.json +{ + "packageManager": "pnpm" +} +``` + +### 프로젝트 설정 +```json +// .claude/package-manager.json +{ + "packageManager": "bun" +} +``` + +### package.json +```json +{ + "packageManager": "pnpm@8.6.0" +} +``` + +## 환경 변수 + +`CLAUDE_PACKAGE_MANAGER`를 설정하면 다른 모든 감지 방법을 무시합니다: + +```bash +# Windows (PowerShell) +$env:CLAUDE_PACKAGE_MANAGER = "pnpm" + +# macOS/Linux +export CLAUDE_PACKAGE_MANAGER=pnpm +``` + +## 감지 실행 + +현재 패키지 매니저 감지 결과를 확인하려면 다음을 실행하세요: + +```bash +node scripts/setup-package-manager.js --detect +``` diff --git a/docs/ko-KR/commands/tdd.md b/docs/ko-KR/commands/tdd.md index 197bd8bf..eba7a754 100644 --- a/docs/ko-KR/commands/tdd.md +++ b/docs/ko-KR/commands/tdd.md @@ -23,6 +23,18 @@ description: 테스트 주도 개발 워크플로우 강제. 인터페이스를 - 기존 코드 리팩토링 - 핵심 비즈니스 로직 구현 +## 작동 방식 + +tdd-guide 에이전트가 수행하는 작업: + +1. 입출력에 대한 **인터페이스 정의** +2. (코드가 아직 존재하지 않으므로) **실패하는 테스트 작성** +3. 올바른 이유로 실패하는지 **테스트 실행** 및 확인 +4. 테스트를 통과하도록 **최소한의 구현 작성** +5. 통과하는지 **테스트 실행** 및 확인 +6. 테스트를 통과시키면서 코드 **리팩토링** +7. **커버리지 확인** 및 80% 미만이면 테스트 추가 + ## TDD 사이클 ``` @@ -34,23 +46,281 @@ REFACTOR: 코드 개선, 테스트 계속 통과 유지 REPEAT: 다음 기능/시나리오 ``` -## 모범 사례 +## 사용 예시 + +``` +User: /tdd 마켓 유동성 점수를 계산하는 함수가 필요합니다 + +Agent (tdd-guide): +# TDD 세션: 마켓 유동성 점수 계산기 + +## 1단계: 인터페이스 정의 (SCAFFOLD) + +```typescript +// lib/liquidity.ts +export interface MarketData { + totalVolume: number + bidAskSpread: number + activeTraders: number + lastTradeTime: Date +} + +export function calculateLiquidityScore(market: MarketData): number { + // TODO: 구현 + throw new Error('Not implemented') +} +``` + +## 2단계: 실패하는 테스트 작성 (RED) + +```typescript +// lib/liquidity.test.ts +import { calculateLiquidityScore } from './liquidity' + +describe('calculateLiquidityScore', () => { + it('should return high score for liquid market', () => { + const market = { + totalVolume: 100000, + bidAskSpread: 0.01, + activeTraders: 500, + lastTradeTime: new Date() + } + + const score = calculateLiquidityScore(market) + + expect(score).toBeGreaterThan(80) + expect(score).toBeLessThanOrEqual(100) + }) + + it('should return low score for illiquid market', () => { + const market = { + totalVolume: 100, + bidAskSpread: 0.5, + activeTraders: 2, + lastTradeTime: new Date(Date.now() - 86400000) // 1일 전 + } + + const score = calculateLiquidityScore(market) + + expect(score).toBeLessThan(30) + expect(score).toBeGreaterThanOrEqual(0) + }) + + it('should handle edge case: zero volume', () => { + const market = { + totalVolume: 0, + bidAskSpread: 0, + activeTraders: 0, + lastTradeTime: new Date() + } + + const score = calculateLiquidityScore(market) + + expect(score).toBe(0) + }) +}) +``` + +## 3단계: 테스트 실행 - 실패 확인 + +```bash +npm test lib/liquidity.test.ts + +FAIL lib/liquidity.test.ts + ✕ should return high score for liquid market (2 ms) + Error: Not implemented + +1 test failed, 0 passed +``` + +✅ 예상대로 테스트 실패. 구현 준비 완료. + +## 4단계: 최소한의 코드 구현 (GREEN) + +```typescript +// lib/liquidity.ts +export function calculateLiquidityScore(market: MarketData): number { + // 거래량 0인 엣지 케이스 처리 + if (market.totalVolume === 0) { + return 0 + } + + // 구성 요소별 점수 계산 (0-100 스케일) + const volumeScore = Math.min(market.totalVolume / 1000, 100) + const spreadScore = Math.max(100 - (market.bidAskSpread * 1000), 0) + const traderScore = Math.min(market.activeTraders / 10, 100) + + // 최근 활동 보너스 + const hoursSinceLastTrade = (Date.now() - market.lastTradeTime.getTime()) / (1000 * 60 * 60) + const recencyScore = Math.max(100 - (hoursSinceLastTrade * 10), 0) + + // 가중 평균 + const score = ( + volumeScore * 0.4 + + spreadScore * 0.3 + + traderScore * 0.2 + + recencyScore * 0.1 + ) + + return Math.min(Math.max(score, 0), 100) // 0-100으로 클램핑 +} +``` + +## 5단계: 테스트 실행 - 통과 확인 + +```bash +npm test lib/liquidity.test.ts + +PASS lib/liquidity.test.ts + ✓ should return high score for liquid market (3 ms) + ✓ should return low score for illiquid market (2 ms) + ✓ should handle edge case: zero volume (1 ms) + +3 tests passed +``` + +✅ 모든 테스트 통과! + +## 6단계: 리팩토링 (IMPROVE) + +```typescript +// lib/liquidity.ts - 상수와 가독성 향상을 위한 리팩토링 +const WEIGHTS = { + VOLUME: 0.4, + SPREAD: 0.3, + TRADERS: 0.2, + RECENCY: 0.1, +} as const + +const SCALE_FACTORS = { + VOLUME: 1000, + SPREAD: 1000, + TRADERS: 10, + RECENCY_PENALTY: 10, +} as const + +function clamp(value: number, min: number, max: number): number { + return Math.min(Math.max(value, min), max) +} + +export function calculateLiquidityScore(market: MarketData): number { + if (market.totalVolume === 0) return 0 + + const volumeScore = Math.min(market.totalVolume / SCALE_FACTORS.VOLUME, 100) + const spreadScore = clamp(100 - (market.bidAskSpread * SCALE_FACTORS.SPREAD), 0, 100) + const traderScore = Math.min(market.activeTraders / SCALE_FACTORS.TRADERS, 100) + + const hoursSinceLastTrade = (Date.now() - market.lastTradeTime.getTime()) / (1000 * 60 * 60) + const recencyScore = clamp(100 - (hoursSinceLastTrade * SCALE_FACTORS.RECENCY_PENALTY), 0, 100) + + const weightedScore = + volumeScore * WEIGHTS.VOLUME + + spreadScore * WEIGHTS.SPREAD + + traderScore * WEIGHTS.TRADERS + + recencyScore * WEIGHTS.RECENCY + + return clamp(weightedScore, 0, 100) +} +``` + +## 7단계: 테스트가 여전히 통과하는지 확인 + +```bash +npm test lib/liquidity.test.ts + +PASS lib/liquidity.test.ts + ✓ should return high score for liquid market (3 ms) + ✓ should return low score for illiquid market (2 ms) + ✓ should handle edge case: zero volume (1 ms) + +3 tests passed +``` + +✅ 리팩토링 완료, 테스트 여전히 통과! + +## 8단계: 커버리지 확인 + +```bash +npm test -- --coverage lib/liquidity.test.ts + +File | % Stmts | % Branch | % Funcs | % Lines +---------------|---------|----------|---------|-------- +liquidity.ts | 100 | 100 | 100 | 100 + +Coverage: 100% ✅ (목표: 80%) +``` + +✅ TDD 세션 완료! +``` + +## TDD 모범 사례 **해야 할 것:** - 구현 전에 테스트를 먼저 작성 -- 각 변경 후 테스트 실행 및 실패 확인 +- 구현 전에 테스트를 실행하여 실패하는지 확인 - 테스트를 통과하기 위한 최소한의 코드 작성 - 테스트가 통과한 후에만 리팩토링 +- 엣지 케이스와 에러 시나리오 추가 - 80% 이상 커버리지 목표 (핵심 코드는 100%) **하지 말아야 할 것:** - 테스트 전에 구현 작성 -- RED 단계 건너뛰기 +- 각 변경 후 테스트 실행 건너뛰기 - 한 번에 너무 많은 코드 작성 - 실패하는 테스트 무시 - 구현 세부사항 테스트 (동작을 테스트) +- 모든 것을 mock (통합 테스트 선호) + +## 포함할 테스트 유형 + +**단위 테스트** (함수 수준): +- 정상 경로 시나리오 +- 엣지 케이스 (빈 값, null, 최대값) +- 에러 조건 +- 경계값 + +**통합 테스트** (컴포넌트 수준): +- API 엔드포인트 +- 데이터베이스 작업 +- 외부 서비스 호출 +- hooks가 포함된 React 컴포넌트 + +**E2E 테스트** (`/e2e` 커맨드 사용): +- 핵심 사용자 흐름 +- 다단계 프로세스 +- 풀 스택 통합 + +## 커버리지 요구사항 + +- **80% 최소** - 모든 코드에 대해 +- **100% 필수** - 다음 항목에 대해: + - 금융 계산 + - 인증 로직 + - 보안에 중요한 코드 + - 핵심 비즈니스 로직 + +## 중요 사항 + +**필수**: 테스트는 반드시 구현 전에 작성해야 합니다. TDD 사이클은 다음과 같습니다: + +1. **RED** - 실패하는 테스트 작성 +2. **GREEN** - 통과하도록 구현 +3. **REFACTOR** - 코드 개선 + +절대 RED 단계를 건너뛰지 마세요. 절대 테스트 전에 코드를 작성하지 마세요. + +## 다른 커맨드와의 연동 + +- `/plan`을 먼저 사용하여 무엇을 만들지 이해 +- `/tdd`를 사용하여 테스트와 함께 구현 +- `/build-fix`를 사용하여 빌드 에러 발생 시 수정 +- `/code-review`를 사용하여 구현 리뷰 +- `/test-coverage`를 사용하여 커버리지 검증 ## 관련 에이전트 이 커맨드는 `tdd-guide` 에이전트를 호출합니다: `~/.claude/agents/tdd-guide.md` + +그리고 `tdd-workflow` 스킬을 참조할 수 있습니다: +`~/.claude/skills/tdd-workflow/` diff --git a/docs/ko-KR/commands/test-coverage.md b/docs/ko-KR/commands/test-coverage.md index ec507e46..ecde8a18 100644 --- a/docs/ko-KR/commands/test-coverage.md +++ b/docs/ko-KR/commands/test-coverage.md @@ -10,6 +10,7 @@ | `vitest.config.*` | `npx vitest run --coverage` | | `pytest.ini` / `pyproject.toml` pytest | `pytest --cov=src --cov-report=json` | | `Cargo.toml` | `cargo llvm-cov --json` | +| `pom.xml` with JaCoCo | `mvn test jacoco:report` | | `go.mod` | `go test -coverprofile=coverage.out ./...` | ## 2단계: 커버리지 보고서 분석 @@ -17,26 +18,52 @@ 1. 커버리지 커맨드 실행 2. 출력 파싱 (JSON 요약 또는 터미널 출력) 3. **80% 미만인 파일**을 최저순으로 정렬하여 목록화 -4. 각 미달 파일에서 미테스트 함수, 누락된 분기 커버리지, 데드 코드 식별 +4. 각 커버리지 미달 파일에 대해 다음을 식별: + - 테스트되지 않은 함수 또는 메서드 + - 누락된 분기 커버리지 (if/else, switch, 에러 경로) + - 분모를 부풀리는 데드 코드 ## 3단계: 누락된 테스트 생성 -우선순위에 따라 테스트 생성: +각 커버리지 미달 파일에 대해 다음 우선순위에 따라 테스트를 생성합니다: 1. **Happy path** — 유효한 입력의 핵심 기능 2. **에러 처리** — 잘못된 입력, 누락된 데이터, 네트워크 실패 3. **엣지 케이스** — 빈 배열, null/undefined, 경계값 (0, -1, MAX_INT) 4. **분기 커버리지** — 각 if/else, switch case, 삼항 연산자 +### 테스트 생성 규칙 + +- 소스 파일 옆에 테스트 배치: `foo.ts` → `foo.test.ts` (또는 프로젝트 컨벤션에 따름) +- 프로젝트의 기존 테스트 패턴 사용 (import 스타일, assertion 라이브러리, mocking 방식) +- 외부 의존성 mock 처리 (데이터베이스, API, 파일 시스템) +- 각 테스트는 독립적이어야 함 — 테스트 간 공유 가변 상태 없음 +- 테스트 이름은 설명적으로: `test_create_user_with_duplicate_email_returns_409` + ## 4단계: 검증 -1. 전체 테스트 실행 — 모든 테스트 통과 확인 +1. 전체 테스트 스위트 실행 — 모든 테스트가 통과해야 함 2. 커버리지 재실행 — 개선 확인 3. 여전히 80% 미만이면 나머지 갭에 대해 3단계 반복 +## 5단계: 보고서 + +이전/이후 비교를 표시합니다: + +``` +커버리지 보고서 +────────────────────────────── +파일 이전 이후 +src/services/auth.ts 45% 88% +src/utils/validation.ts 32% 82% +────────────────────────────── +전체: 67% 84% ✅ +``` + ## 집중 영역 - 복잡한 분기가 있는 함수 (높은 순환 복잡도) - 에러 핸들러와 catch 블록 - 코드베이스 전반에서 사용되는 유틸리티 함수 - API 엔드포인트 핸들러 (요청 → 응답 흐름) +- 엣지 케이스: null, undefined, 빈 문자열, 빈 배열, 0, 음수 diff --git a/docs/ko-KR/commands/update-codemaps.md b/docs/ko-KR/commands/update-codemaps.md index 4f71bd8f..a2bf3138 100644 --- a/docs/ko-KR/commands/update-codemaps.md +++ b/docs/ko-KR/commands/update-codemaps.md @@ -10,7 +10,7 @@ ## 2단계: 코드맵 생성 -`docs/CODEMAPS/`에 코드맵 생성 또는 업데이트: +`docs/CODEMAPS/` (또는 `.reports/codemaps/`)에 코드맵 생성 또는 업데이트: | 파일 | 내용 | |------|------| @@ -20,6 +20,49 @@ | `data.md` | 데이터베이스 테이블, 관계, 마이그레이션 히스토리 | | `dependencies.md` | 외부 서비스, 서드파티 통합, 공유 라이브러리 | +### 코드맵 형식 + +각 코드맵은 토큰 효율적이어야 합니다 — AI 컨텍스트 소비에 최적화: + +```markdown +# Backend 아키텍처 + +## 라우트 +POST /api/users → UserController.create → UserService.create → UserRepo.insert +GET /api/users/:id → UserController.get → UserService.findById → UserRepo.findById + +## 주요 파일 +src/services/user.ts (비즈니스 로직, 120줄) +src/repos/user.ts (데이터베이스 접근, 80줄) + +## 의존성 +- PostgreSQL (주 데이터 저장소) +- Redis (세션 캐시, 속도 제한) +- Stripe (결제 처리) +``` + +## 3단계: 변경 감지 + +1. 이전 코드맵이 있는 경우 변경 비율 계산 +2. 변경이 30%를 초과하면 diff를 표시하고 덮어쓰기 전에 사용자 승인 요청 +3. 변경이 30% 이하이면 기존 파일에 바로 업데이트 + +## 4단계: 메타데이터 추가 + +각 코드맵에 최신 정보 헤더를 추가합니다: + +```markdown + +``` + +## 5단계: 분석 보고서 저장 + +`.reports/codemap-diff.txt`에 요약을 작성합니다: +- 마지막 스캔 이후 추가/제거/수정된 파일 +- 새로 감지된 의존성 +- 아키텍처 변경 사항 (새 라우트, 새 서비스 등) +- 90일 이상 업데이트되지 않은 문서에 대한 오래된 항목 경고 + ## 팁 - **구현 세부사항이 아닌 상위 구조**에 집중 diff --git a/docs/ko-KR/commands/update-docs.md b/docs/ko-KR/commands/update-docs.md index 49b3062f..3d353ce2 100644 --- a/docs/ko-KR/commands/update-docs.md +++ b/docs/ko-KR/commands/update-docs.md @@ -16,18 +16,69 @@ 1. `package.json` (또는 `Makefile`, `Cargo.toml`, `pyproject.toml`) 읽기 2. 모든 스크립트/커맨드와 설명 추출 -3. 참조 테이블 생성 +3. 참조 테이블 생성: + +```markdown +| 커맨드 | 설명 | +|--------|------| +| `npm run dev` | hot reload로 개발 서버 시작 | +| `npm run build` | 타입 체크 포함 프로덕션 빌드 | +| `npm test` | 커버리지 포함 테스트 스위트 실행 | +``` ## 3단계: 환경 변수 문서 생성 -1. `.env.example` 읽기 +1. `.env.example` (또는 `.env.template`, `.env.sample`) 읽기 2. 모든 변수와 용도 추출 3. 필수 vs 선택으로 분류 4. 예상 형식과 유효 값 문서화 +```markdown +| 변수 | 필수 | 설명 | 예시 | +|------|------|------|------| +| `DATABASE_URL` | 예 | PostgreSQL 연결 문자열 | `postgres://user:pass@host:5432/db` | +| `LOG_LEVEL` | 아니오 | 로깅 상세도 (기본값: info) | `debug`, `info`, `warn`, `error` | +``` + +## 4단계: 기여 가이드 업데이트 + +`docs/CONTRIBUTING.md`를 생성 또는 업데이트합니다: +- 개발 환경 설정 (사전 요구 사항, 설치 단계) +- 사용 가능한 스크립트와 용도 +- 테스트 절차 (실행 방법, 새 테스트 작성 방법) +- 코드 스타일 적용 (linter, formatter, pre-commit hook) +- PR 제출 체크리스트 + +## 5단계: 운영 매뉴얼 업데이트 + +`docs/RUNBOOK.md`를 생성 또는 업데이트합니다: +- 배포 절차 (단계별) +- 헬스 체크 엔드포인트 및 모니터링 +- 일반적인 이슈와 해결 방법 +- 롤백 절차 +- 알림 및 에스컬레이션 경로 + +## 6단계: 오래된 항목 점검 + +1. 90일 이상 수정되지 않은 문서 파일 찾기 +2. 최근 소스 코드 변경 사항과 교차 참조 +3. 잠재적으로 오래된 문서를 수동 검토 대상으로 표시 + +## 7단계: 요약 표시 + +``` +문서 업데이트 +────────────────────────────── +업데이트: docs/CONTRIBUTING.md (스크립트 테이블) +업데이트: docs/ENV.md (새 변수 3개) +플래그: docs/DEPLOY.md (142일 경과) +건너뜀: docs/API.md (변경 사항 없음) +────────────────────────────── +``` + ## 규칙 -- **단일 원본**: 항상 코드에서 생성, 생성된 섹션을 수동으로 편집하지 않기 +- **단일 원본**: 항상 코드에서 생성하고, 생성된 섹션을 수동으로 편집하지 않기 - **수동 섹션 보존**: 생성된 섹션만 업데이트; 수기 작성 내용은 그대로 유지 - **생성된 콘텐츠 표시**: 생성된 섹션 주변에 `` 마커 사용 - **요청 없이 문서 생성하지 않기**: 커맨드가 명시적으로 요청한 경우에만 새 문서 파일 생성 From 3144b96faa6a94e44d9d974dec0f1f9c01d96111 Mon Sep 17 00:00:00 2001 From: hahmee Date: Tue, 10 Mar 2026 14:28:14 +0900 Subject: [PATCH 7/9] docs(ko-KR): add Korean terminology glossary Add TERMINOLOGY.md with translation conventions and term mappings to ensure consistency across all 58 translated files. --- docs/ko-KR/TERMINOLOGY.md | 104 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/ko-KR/TERMINOLOGY.md diff --git a/docs/ko-KR/TERMINOLOGY.md b/docs/ko-KR/TERMINOLOGY.md new file mode 100644 index 00000000..47751941 --- /dev/null +++ b/docs/ko-KR/TERMINOLOGY.md @@ -0,0 +1,104 @@ +# 용어 대조표 (Terminology Glossary) + +본 문서는 한국어 번역의 용어 대조를 기록하여 번역 일관성을 보장합니다. + +## 상태 설명 + +- **확정 (Confirmed)**: 확정된 번역 +- **미확정 (Pending)**: 검토 대기 중인 번역 + +--- + +## 용어표 + +| English | ko-KR | 상태 | 비고 | +|---------|-------|------|------| +| Agent | Agent | 확정 | 영문 유지 | +| Hook | Hook | 확정 | 영문 유지 | +| Plugin | 플러그인 | 확정 | | +| Token | Token | 확정 | 영문 유지 | +| Skill | 스킬 | 확정 | | +| Command | 커맨드 | 확정 | | +| Rule | 규칙 | 확정 | | +| TDD (Test-Driven Development) | TDD(테스트 주도 개발) | 확정 | 최초 사용 시 전개 | +| E2E (End-to-End) | E2E(엔드 투 엔드) | 확정 | 최초 사용 시 전개 | +| API | API | 확정 | 영문 유지 | +| CLI | CLI | 확정 | 영문 유지 | +| IDE | IDE | 확정 | 영문 유지 | +| MCP (Model Context Protocol) | MCP | 확정 | 영문 유지 | +| Workflow | 워크플로우 | 확정 | | +| Codebase | 코드베이스 | 확정 | | +| Coverage | 커버리지 | 확정 | | +| Build | 빌드 | 확정 | | +| Debug | 디버그 | 확정 | | +| Deploy | 배포 | 확정 | | +| Commit | 커밋 | 확정 | | +| PR (Pull Request) | PR | 확정 | 영문 유지 | +| Branch | 브랜치 | 확정 | | +| Merge | merge | 확정 | 영문 유지 | +| Repository | 저장소 | 확정 | | +| Fork | Fork | 확정 | 영문 유지 | +| Supabase | Supabase | - | 제품명 유지 | +| Redis | Redis | - | 제품명 유지 | +| Playwright | Playwright | - | 제품명 유지 | +| TypeScript | TypeScript | - | 언어명 유지 | +| JavaScript | JavaScript | - | 언어명 유지 | +| Go/Golang | Go | - | 언어명 유지 | +| React | React | - | 프레임워크명 유지 | +| Next.js | Next.js | - | 프레임워크명 유지 | +| PostgreSQL | PostgreSQL | - | 제품명 유지 | +| RLS (Row Level Security) | RLS(행 수준 보안) | 확정 | 최초 사용 시 전개 | +| OWASP | OWASP | - | 영문 유지 | +| XSS | XSS | - | 영문 유지 | +| SQL Injection | SQL 인젝션 | 확정 | | +| CSRF | CSRF | - | 영문 유지 | +| Refactor | 리팩토링 | 확정 | | +| Dead Code | 데드 코드 | 확정 | | +| Lint/Linter | Lint | 확정 | 영문 유지 | +| Code Review | 코드 리뷰 | 확정 | | +| Security Review | 보안 리뷰 | 확정 | | +| Best Practices | 모범 사례 | 확정 | | +| Edge Case | 엣지 케이스 | 확정 | | +| Happy Path | 해피 패스 | 확정 | | +| Fallback | 폴백 | 확정 | | +| Cache | 캐시 | 확정 | | +| Queue | 큐 | 확정 | | +| Pagination | 페이지네이션 | 확정 | | +| Cursor | 커서 | 확정 | | +| Index | 인덱스 | 확정 | | +| Schema | 스키마 | 확정 | | +| Migration | 마이그레이션 | 확정 | | +| Transaction | 트랜잭션 | 확정 | | +| Concurrency | 동시성 | 확정 | | +| Goroutine | Goroutine | - | Go 용어 유지 | +| Channel | Channel | 확정 | Go 컨텍스트에서 유지 | +| Mutex | Mutex | - | 영문 유지 | +| Interface | 인터페이스 | 확정 | | +| Struct | Struct | - | Go 용어 유지 | +| Mock | Mock | 확정 | 테스트 용어 유지 | +| Stub | Stub | 확정 | 테스트 용어 유지 | +| Fixture | Fixture | 확정 | 테스트 용어 유지 | +| Assertion | 어설션 | 확정 | | +| Snapshot | 스냅샷 | 확정 | | +| Trace | 트레이스 | 확정 | | +| Artifact | 아티팩트 | 확정 | | +| CI/CD | CI/CD | - | 영문 유지 | +| Pipeline | 파이프라인 | 확정 | | + +--- + +## 번역 원칙 + +1. **제품명**: 영문 유지 (Supabase, Redis, Playwright) +2. **프로그래밍 언어**: 영문 유지 (TypeScript, Go, JavaScript) +3. **프레임워크명**: 영문 유지 (React, Next.js, Vue) +4. **기술 약어**: 영문 유지 (API, CLI, IDE, MCP, TDD, E2E) +5. **Git 용어**: 대부분 영문 유지 (commit, PR, fork) +6. **코드 내용**: 번역하지 않음 (변수명, 함수명은 원문 유지, 설명 주석은 번역) +7. **최초 등장**: 약어 최초 등장 시 전개 설명 + +--- + +## 업데이트 기록 + +- 2026-03-10: 초판 작성, 전체 번역 파일에서 사용된 용어 정리 From 526a9070e64903b49bbf3e6c6da021cef7e2b63a Mon Sep 17 00:00:00 2001 From: hahmee Date: Tue, 10 Mar 2026 17:09:23 +0900 Subject: [PATCH 8/9] docs(ko-KR): add Korean translation for examples Translate 6 CLAUDE.md examples (project, user, SaaS Next.js, Django API, Go microservice, Rust API) and copy statusline.json config. --- docs/ko-KR/examples/CLAUDE.md | 100 ++++++ docs/ko-KR/examples/django-api-CLAUDE.md | 308 ++++++++++++++++++ docs/ko-KR/examples/go-microservice-CLAUDE.md | 267 +++++++++++++++ docs/ko-KR/examples/rust-api-CLAUDE.md | 285 ++++++++++++++++ docs/ko-KR/examples/saas-nextjs-CLAUDE.md | 166 ++++++++++ docs/ko-KR/examples/statusline.json | 19 ++ docs/ko-KR/examples/user-CLAUDE.md | 109 +++++++ 7 files changed, 1254 insertions(+) create mode 100644 docs/ko-KR/examples/CLAUDE.md create mode 100644 docs/ko-KR/examples/django-api-CLAUDE.md create mode 100644 docs/ko-KR/examples/go-microservice-CLAUDE.md create mode 100644 docs/ko-KR/examples/rust-api-CLAUDE.md create mode 100644 docs/ko-KR/examples/saas-nextjs-CLAUDE.md create mode 100644 docs/ko-KR/examples/statusline.json create mode 100644 docs/ko-KR/examples/user-CLAUDE.md diff --git a/docs/ko-KR/examples/CLAUDE.md b/docs/ko-KR/examples/CLAUDE.md new file mode 100644 index 00000000..5f89b82d --- /dev/null +++ b/docs/ko-KR/examples/CLAUDE.md @@ -0,0 +1,100 @@ +# 프로젝트 CLAUDE.md 예제 + +프로젝트 수준의 CLAUDE.md 파일 예제입니다. 프로젝트 루트에 배치하세요. + +## 프로젝트 개요 + +[프로젝트에 대한 간단한 설명 - 기능, 기술 스택] + +## 핵심 규칙 + +### 1. 코드 구성 + +- 큰 파일 소수보다 작은 파일 다수를 선호 +- 높은 응집도, 낮은 결합도 +- 일반적으로 200-400줄, 파일당 최대 800줄 +- 타입별이 아닌 기능/도메인별로 구성 + +### 2. 코드 스타일 + +- 코드, 주석, 문서에 이모지 사용 금지 +- 항상 불변성 유지 - 객체나 배열을 직접 변경하지 않음 +- 프로덕션 코드에 console.log 사용 금지 +- try/catch를 사용한 적절한 에러 처리 +- Zod 또는 유사 라이브러리를 사용한 입력 유효성 검사 + +### 3. 테스트 + +- TDD: 테스트를 먼저 작성 +- 최소 80% 커버리지 +- 유틸리티에 대한 단위 테스트 +- API에 대한 통합 테스트 +- 핵심 흐름에 대한 E2E 테스트 + +### 4. 보안 + +- 하드코딩된 시크릿 금지 +- 민감한 데이터는 환경 변수 사용 +- 모든 사용자 입력 유효성 검사 +- 매개변수화된 쿼리만 사용 +- CSRF 보호 활성화 + +## 파일 구조 + +``` +src/ +|-- app/ # Next.js app router +|-- components/ # 재사용 가능한 UI 컴포넌트 +|-- hooks/ # 커스텀 React hooks +|-- lib/ # 유틸리티 라이브러리 +|-- types/ # TypeScript 타입 정의 +``` + +## 주요 패턴 + +### API 응답 형식 + +```typescript +interface ApiResponse { + success: boolean + data?: T + error?: string +} +``` + +### 에러 처리 + +```typescript +try { + const result = await operation() + return { success: true, data: result } +} catch (error) { + console.error('Operation failed:', error) + return { success: false, error: 'User-friendly message' } +} +``` + +## 환경 변수 + +```bash +# 필수 +DATABASE_URL= +API_KEY= + +# 선택 +DEBUG=false +``` + +## 사용 가능한 명령어 + +- `/tdd` - 테스트 주도 개발 워크플로우 +- `/plan` - 구현 계획 생성 +- `/code-review` - 코드 품질 리뷰 +- `/build-fix` - 빌드 에러 수정 + +## Git 워크플로우 + +- Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `test:` +- main 브랜치에 직접 커밋 금지 +- PR은 리뷰 필수 +- 병합 전 모든 테스트 통과 필수 diff --git a/docs/ko-KR/examples/django-api-CLAUDE.md b/docs/ko-KR/examples/django-api-CLAUDE.md new file mode 100644 index 00000000..18cd261c --- /dev/null +++ b/docs/ko-KR/examples/django-api-CLAUDE.md @@ -0,0 +1,308 @@ +# Django REST API — 프로젝트 CLAUDE.md + +> PostgreSQL과 Celery를 사용하는 Django REST Framework API의 실전 예시입니다. +> 프로젝트 루트에 복사하여 서비스에 맞게 커스터마이즈하세요. + +## 프로젝트 개요 + +**기술 스택:** Python 3.12+, Django 5.x, Django REST Framework, PostgreSQL, Celery + Redis, pytest, Docker Compose + +**아키텍처:** 비즈니스 도메인별 앱으로 구성된 도메인 주도 설계. API 레이어에 DRF, 비동기 작업에 Celery, 테스트에 pytest 사용. 모든 엔드포인트는 JSON을 반환하며 템플릿 렌더링은 없음. + +## 필수 규칙 + +### Python 규칙 + +- 모든 함수 시그니처에 type hints 사용 — `from __future__ import annotations` 사용 +- `print()` 문 사용 금지 — `logging.getLogger(__name__)` 사용 +- 문자열 포매팅은 f-strings 사용, `%`나 `.format()`은 사용 금지 +- 파일 작업에 `os.path` 대신 `pathlib.Path` 사용 +- isort로 import 정렬: stdlib, third-party, local 순서 (ruff에 의해 강제) + +### 데이터베이스 + +- 모든 쿼리는 Django ORM 사용 — raw SQL은 `.raw()`와 parameterized 쿼리로만 사용 +- 마이그레이션은 git에 커밋 — 프로덕션에서 `--fake` 사용 금지 +- N+1 쿼리 방지를 위해 `select_related()`와 `prefetch_related()` 사용 +- 모든 모델에 `created_at`과 `updated_at` 자동 필드 필수 +- `filter()`, `order_by()`, 또는 `WHERE` 절에 사용되는 모든 필드에 인덱스 추가 + +```python +# 나쁜 예: N+1 쿼리 +orders = Order.objects.all() +for order in orders: + print(order.customer.name) # 각 주문마다 DB를 조회함 + +# 좋은 예: join을 사용한 단일 쿼리 +orders = Order.objects.select_related("customer").all() +``` + +### 인증 + +- `djangorestframework-simplejwt`를 통한 JWT — access token (15분) + refresh token (7일) +- 모든 뷰에 permission 클래스 지정 — 기본값에 의존하지 않기 +- `IsAuthenticated`를 기본으로, 객체 수준 접근에는 커스텀 permission 추가 +- 로그아웃을 위한 token blacklisting 활성화 + +### Serializers + +- 간단한 CRUD에는 `ModelSerializer`, 복잡한 유효성 검증에는 `Serializer` 사용 +- 입력/출력 형태가 다를 때는 읽기와 쓰기 serializer를 분리 +- 유효성 검증은 serializer 레벨에서 — 뷰는 얇게 유지 + +```python +class CreateOrderSerializer(serializers.Serializer): + product_id = serializers.UUIDField() + quantity = serializers.IntegerField(min_value=1, max_value=100) + + def validate_product_id(self, value): + if not Product.objects.filter(id=value, active=True).exists(): + raise serializers.ValidationError("Product not found or inactive") + return value + +class OrderDetailSerializer(serializers.ModelSerializer): + customer = CustomerSerializer(read_only=True) + product = ProductSerializer(read_only=True) + + class Meta: + model = Order + fields = ["id", "customer", "product", "quantity", "total", "status", "created_at"] +``` + +### 오류 처리 + +- 일관된 오류 응답을 위해 DRF exception handler 사용 +- 비즈니스 로직용 커스텀 예외는 `core/exceptions.py`에 정의 +- 클라이언트에 내부 오류 세부 정보를 노출하지 않기 + +```python +# core/exceptions.py +from rest_framework.exceptions import APIException + +class InsufficientStockError(APIException): + status_code = 409 + default_detail = "Insufficient stock for this order" + default_code = "insufficient_stock" +``` + +### 코드 스타일 + +- 코드나 주석에 이모지 사용 금지 +- 최대 줄 길이: 120자 (ruff에 의해 강제) +- 클래스: PascalCase, 함수/변수: snake_case, 상수: UPPER_SNAKE_CASE +- 뷰는 얇게 유지 — 비즈니스 로직은 서비스 함수나 모델 메서드에 배치 + +## 파일 구조 + +``` +config/ + settings/ + base.py # 공유 설정 + local.py # 개발 환경 오버라이드 (DEBUG=True) + production.py # 프로덕션 설정 + urls.py # 루트 URL 설정 + celery.py # Celery 앱 설정 +apps/ + accounts/ # 사용자 인증, 회원가입, 프로필 + models.py + serializers.py + views.py + services.py # 비즈니스 로직 + tests/ + test_views.py + test_services.py + factories.py # Factory Boy 팩토리 + orders/ # 주문 관리 + models.py + serializers.py + views.py + services.py + tasks.py # Celery 작업 + tests/ + products/ # 상품 카탈로그 + models.py + serializers.py + views.py + tests/ +core/ + exceptions.py # 커스텀 API 예외 + permissions.py # 공유 permission 클래스 + pagination.py # 커스텀 페이지네이션 + middleware.py # 요청 로깅, 타이밍 + tests/ +``` + +## 주요 패턴 + +### Service 레이어 + +```python +# apps/orders/services.py +from django.db import transaction + +def create_order(*, customer, product_id: uuid.UUID, quantity: int) -> Order: + """재고 검증과 결제 보류를 포함한 주문 생성.""" + product = Product.objects.select_for_update().get(id=product_id) + + if product.stock < quantity: + raise InsufficientStockError() + + with transaction.atomic(): + order = Order.objects.create( + customer=customer, + product=product, + quantity=quantity, + total=product.price * quantity, + ) + product.stock -= quantity + product.save(update_fields=["stock", "updated_at"]) + + # 비동기: 주문 확인 이메일 발송 + send_order_confirmation.delay(order.id) + return order +``` + +### View 패턴 + +```python +# apps/orders/views.py +class OrderViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated] + pagination_class = StandardPagination + + def get_serializer_class(self): + if self.action == "create": + return CreateOrderSerializer + return OrderDetailSerializer + + def get_queryset(self): + return ( + Order.objects + .filter(customer=self.request.user) + .select_related("product", "customer") + .order_by("-created_at") + ) + + def perform_create(self, serializer): + order = create_order( + customer=self.request.user, + product_id=serializer.validated_data["product_id"], + quantity=serializer.validated_data["quantity"], + ) + serializer.instance = order +``` + +### 테스트 패턴 (pytest + Factory Boy) + +```python +# apps/orders/tests/factories.py +import factory +from apps.accounts.tests.factories import UserFactory +from apps.products.tests.factories import ProductFactory + +class OrderFactory(factory.django.DjangoModelFactory): + class Meta: + model = "orders.Order" + + customer = factory.SubFactory(UserFactory) + product = factory.SubFactory(ProductFactory, stock=100) + quantity = 1 + total = factory.LazyAttribute(lambda o: o.product.price * o.quantity) + +# apps/orders/tests/test_views.py +import pytest +from rest_framework.test import APIClient + +@pytest.mark.django_db +class TestCreateOrder: + def setup_method(self): + self.client = APIClient() + self.user = UserFactory() + self.client.force_authenticate(self.user) + + def test_create_order_success(self): + product = ProductFactory(price=29_99, stock=10) + response = self.client.post("/api/orders/", { + "product_id": str(product.id), + "quantity": 2, + }) + assert response.status_code == 201 + assert response.data["total"] == 59_98 + + def test_create_order_insufficient_stock(self): + product = ProductFactory(stock=0) + response = self.client.post("/api/orders/", { + "product_id": str(product.id), + "quantity": 1, + }) + assert response.status_code == 409 + + def test_create_order_unauthenticated(self): + self.client.force_authenticate(None) + response = self.client.post("/api/orders/", {}) + assert response.status_code == 401 +``` + +## 환경 변수 + +```bash +# Django +SECRET_KEY= +DEBUG=False +ALLOWED_HOSTS=api.example.com + +# 데이터베이스 +DATABASE_URL=postgres://user:pass@localhost:5432/myapp + +# Redis (Celery broker + 캐시) +REDIS_URL=redis://localhost:6379/0 + +# JWT +JWT_ACCESS_TOKEN_LIFETIME=15 # 분 +JWT_REFRESH_TOKEN_LIFETIME=10080 # 분 (7일) + +# 이메일 +EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend +EMAIL_HOST=smtp.example.com +``` + +## 테스트 전략 + +```bash +# 전체 테스트 실행 +pytest --cov=apps --cov-report=term-missing + +# 특정 앱 테스트 실행 +pytest apps/orders/tests/ -v + +# 병렬 실행 +pytest -n auto + +# 마지막 실행에서 실패한 테스트만 실행 +pytest --lf +``` + +## ECC 워크플로우 + +```bash +# 계획 수립 +/plan "Add order refund system with Stripe integration" + +# TDD로 개발 +/tdd # pytest 기반 TDD 워크플로우 + +# 리뷰 +/python-review # Python 전용 코드 리뷰 +/security-scan # Django 보안 감사 +/code-review # 일반 품질 검사 + +# 검증 +/verify # 빌드, 린트, 테스트, 보안 스캔 +``` + +## Git 워크플로우 + +- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경 +- `main`에서 feature 브랜치 생성, PR 필수 +- CI: ruff (린트 + 포맷), mypy (타입), pytest (테스트), safety (의존성 검사) +- 배포: Docker 이미지, Kubernetes 또는 Railway로 관리 diff --git a/docs/ko-KR/examples/go-microservice-CLAUDE.md b/docs/ko-KR/examples/go-microservice-CLAUDE.md new file mode 100644 index 00000000..1966008f --- /dev/null +++ b/docs/ko-KR/examples/go-microservice-CLAUDE.md @@ -0,0 +1,267 @@ +# Go Microservice — 프로젝트 CLAUDE.md + +> PostgreSQL, gRPC, Docker를 사용하는 Go 마이크로서비스의 실전 예시입니다. +> 프로젝트 루트에 복사하여 서비스에 맞게 커스터마이즈하세요. + +## 프로젝트 개요 + +**기술 스택:** Go 1.22+, PostgreSQL, gRPC + REST (grpc-gateway), Docker, sqlc (타입 안전 SQL), Wire (의존성 주입) + +**아키텍처:** domain, repository, service, handler 레이어로 구성된 클린 아키텍처. gRPC를 기본 전송 프로토콜로 사용하고, 외부 클라이언트를 위한 REST gateway 제공. + +## 필수 규칙 + +### Go 규칙 + +- Effective Go와 Go Code Review Comments 가이드를 따를 것 +- 오류 래핑에 `errors.New` / `fmt.Errorf`와 `%w` 사용 — 오류를 문자열 매칭하지 않기 +- `init()` 함수 사용 금지 — `main()`이나 생성자에서 명시적으로 초기화 +- 전역 가변 상태 금지 — 생성자를 통해 의존성 전달 +- Context는 반드시 첫 번째 매개변수이며 모든 레이어를 통해 전파 + +### 데이터베이스 + +- 모든 쿼리는 `queries/`에 순수 SQL로 작성 — sqlc가 타입 안전한 Go 코드를 생성 +- 마이그레이션은 `migrations/`에 golang-migrate 사용 — 데이터베이스를 직접 변경하지 않기 +- 다중 단계 작업에는 `pgx.Tx`를 통한 트랜잭션 사용 +- 모든 쿼리에 parameterized placeholder (`$1`, `$2`) 사용 — 문자열 포매팅 사용 금지 + +### 오류 처리 + +- 오류를 반환하고, panic하지 않기 — panic은 진정으로 복구 불가능한 상황에만 사용 +- 컨텍스트와 함께 오류 래핑: `fmt.Errorf("creating user: %w", err)` +- 비즈니스 로직을 위한 sentinel 오류는 `domain/errors.go`에 정의 +- handler 레이어에서 도메인 오류를 gRPC status 코드로 매핑 + +```go +// 도메인 레이어 — sentinel 오류 +var ( + ErrUserNotFound = errors.New("user not found") + ErrEmailTaken = errors.New("email already registered") +) + +// Handler 레이어 — gRPC status로 매핑 +func toGRPCError(err error) error { + switch { + case errors.Is(err, domain.ErrUserNotFound): + return status.Error(codes.NotFound, err.Error()) + case errors.Is(err, domain.ErrEmailTaken): + return status.Error(codes.AlreadyExists, err.Error()) + default: + return status.Error(codes.Internal, "internal error") + } +} +``` + +### 코드 스타일 + +- 코드나 주석에 이모지 사용 금지 +- 외부로 공개되는 타입과 함수에는 반드시 doc 주석 작성 +- 함수는 50줄 이하로 유지 — 헬퍼 함수로 분리 +- 여러 케이스가 있는 모든 로직에 table-driven 테스트 사용 +- signal 채널에는 `bool`이 아닌 `struct{}` 사용 + +## 파일 구조 + +``` +cmd/ + server/ + main.go # 진입점, Wire 주입, 우아한 종료 +internal/ + domain/ # 비즈니스 타입과 인터페이스 + user.go # User 엔티티와 repository 인터페이스 + errors.go # Sentinel 오류 + service/ # 비즈니스 로직 + user_service.go + user_service_test.go + repository/ # 데이터 접근 (sqlc 생성 + 커스텀) + postgres/ + user_repo.go + user_repo_test.go # testcontainers를 사용한 통합 테스트 + handler/ # gRPC + REST 핸들러 + grpc/ + user_handler.go + rest/ + user_handler.go + config/ # 설정 로딩 + config.go +proto/ # Protobuf 정의 + user/v1/ + user.proto +queries/ # sqlc용 SQL 쿼리 + user.sql +migrations/ # 데이터베이스 마이그레이션 + 001_create_users.up.sql + 001_create_users.down.sql +``` + +## 주요 패턴 + +### Repository 인터페이스 + +```go +type UserRepository interface { + Create(ctx context.Context, user *User) error + FindByID(ctx context.Context, id uuid.UUID) (*User, error) + FindByEmail(ctx context.Context, email string) (*User, error) + Update(ctx context.Context, user *User) error + Delete(ctx context.Context, id uuid.UUID) error +} +``` + +### 의존성 주입을 사용한 Service + +```go +type UserService struct { + repo domain.UserRepository + hasher PasswordHasher + logger *slog.Logger +} + +func NewUserService(repo domain.UserRepository, hasher PasswordHasher, logger *slog.Logger) *UserService { + return &UserService{repo: repo, hasher: hasher, logger: logger} +} + +func (s *UserService) Create(ctx context.Context, req CreateUserRequest) (*domain.User, error) { + existing, err := s.repo.FindByEmail(ctx, req.Email) + if err != nil && !errors.Is(err, domain.ErrUserNotFound) { + return nil, fmt.Errorf("checking email: %w", err) + } + if existing != nil { + return nil, domain.ErrEmailTaken + } + + hashed, err := s.hasher.Hash(req.Password) + if err != nil { + return nil, fmt.Errorf("hashing password: %w", err) + } + + user := &domain.User{ + ID: uuid.New(), + Name: req.Name, + Email: req.Email, + Password: hashed, + } + if err := s.repo.Create(ctx, user); err != nil { + return nil, fmt.Errorf("creating user: %w", err) + } + return user, nil +} +``` + +### Table-Driven 테스트 + +```go +func TestUserService_Create(t *testing.T) { + tests := []struct { + name string + req CreateUserRequest + setup func(*MockUserRepo) + wantErr error + }{ + { + name: "valid user", + req: CreateUserRequest{Name: "Alice", Email: "alice@example.com", Password: "secure123"}, + setup: func(m *MockUserRepo) { + m.On("FindByEmail", mock.Anything, "alice@example.com").Return(nil, domain.ErrUserNotFound) + m.On("Create", mock.Anything, mock.Anything).Return(nil) + }, + wantErr: nil, + }, + { + name: "duplicate email", + req: CreateUserRequest{Name: "Alice", Email: "taken@example.com", Password: "secure123"}, + setup: func(m *MockUserRepo) { + m.On("FindByEmail", mock.Anything, "taken@example.com").Return(&domain.User{}, nil) + }, + wantErr: domain.ErrEmailTaken, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + repo := new(MockUserRepo) + tt.setup(repo) + svc := NewUserService(repo, &bcryptHasher{}, slog.Default()) + + _, err := svc.Create(context.Background(), tt.req) + + if tt.wantErr != nil { + assert.ErrorIs(t, err, tt.wantErr) + } else { + assert.NoError(t, err) + } + }) + } +} +``` + +## 환경 변수 + +```bash +# 데이터베이스 +DATABASE_URL=postgres://user:pass@localhost:5432/myservice?sslmode=disable + +# gRPC +GRPC_PORT=50051 +REST_PORT=8080 + +# 인증 +JWT_SECRET= # 프로덕션에서는 vault에서 로드 +TOKEN_EXPIRY=24h + +# 관측 가능성 +LOG_LEVEL=info # debug, info, warn, error +OTEL_ENDPOINT= # OpenTelemetry 콜렉터 +``` + +## 테스트 전략 + +```bash +/go-test # Go용 TDD 워크플로우 +/go-review # Go 전용 코드 리뷰 +/go-build # 빌드 오류 수정 +``` + +### 테스트 명령어 + +```bash +# 단위 테스트 (빠름, 외부 의존성 없음) +go test ./internal/... -short -count=1 + +# 통합 테스트 (testcontainers를 위해 Docker 필요) +go test ./internal/repository/... -count=1 -timeout 120s + +# 전체 테스트와 커버리지 +go test ./... -coverprofile=coverage.out -count=1 +go tool cover -func=coverage.out # 요약 +go tool cover -html=coverage.out # 브라우저 + +# Race detector +go test ./... -race -count=1 +``` + +## ECC 워크플로우 + +```bash +# 계획 수립 +/plan "Add rate limiting to user endpoints" + +# 개발 +/go-test # Go 전용 패턴으로 TDD + +# 리뷰 +/go-review # Go 관용구, 오류 처리, 동시성 +/security-scan # 시크릿 및 취약점 점검 + +# 머지 전 확인 +go vet ./... +staticcheck ./... +``` + +## Git 워크플로우 + +- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경 +- `main`에서 feature 브랜치 생성, PR 필수 +- CI: `go vet`, `staticcheck`, `go test -race`, `golangci-lint` +- 배포: CI에서 Docker 이미지 빌드, Kubernetes에 배포 diff --git a/docs/ko-KR/examples/rust-api-CLAUDE.md b/docs/ko-KR/examples/rust-api-CLAUDE.md new file mode 100644 index 00000000..0f05c8b2 --- /dev/null +++ b/docs/ko-KR/examples/rust-api-CLAUDE.md @@ -0,0 +1,285 @@ +# Rust API Service — 프로젝트 CLAUDE.md + +> Axum, PostgreSQL, Docker를 사용하는 Rust API 서비스의 실전 예시입니다. +> 프로젝트 루트에 복사하여 서비스에 맞게 커스터마이즈하세요. + +## 프로젝트 개요 + +**기술 스택:** Rust 1.78+, Axum (웹 프레임워크), SQLx (비동기 데이터베이스), PostgreSQL, Tokio (비동기 런타임), Docker + +**아키텍처:** handler -> service -> repository로 분리된 레이어드 아키텍처. HTTP에 Axum, 컴파일 타임에 타입이 검증되는 SQL에 SQLx, 횡단 관심사에 Tower 미들웨어 사용. + +## 필수 규칙 + +### Rust 규칙 + +- 라이브러리 오류에 `thiserror`, 바이너리 크레이트나 테스트에서만 `anyhow` 사용 +- 프로덕션 코드에서 `.unwrap()`이나 `.expect()` 사용 금지 — `?`로 오류 전파 +- 함수 매개변수에 `String`보다 `&str` 선호; 소유권 이전 시 `String` 반환 +- `#![deny(clippy::all, clippy::pedantic)]`과 함께 `clippy` 사용 — 모든 경고 수정 +- 모든 공개 타입에 `Debug` derive; `Clone`, `PartialEq`는 필요할 때만 derive +- `// SAFETY:` 주석으로 정당화하지 않는 한 `unsafe` 블록 사용 금지 + +### 데이터베이스 + +- 모든 쿼리에 SQLx `query!` 또는 `query_as!` 매크로 사용 — 스키마에 대해 컴파일 타임에 검증 +- 마이그레이션은 `migrations/`에 `sqlx migrate` 사용 — 데이터베이스를 직접 변경하지 않기 +- 공유 상태로 `sqlx::Pool` 사용 — 요청마다 커넥션을 생성하지 않기 +- 모든 쿼리에 parameterized placeholder (`$1`, `$2`) 사용 — 문자열 포매팅 사용 금지 + +```rust +// 나쁜 예: 문자열 보간 (SQL injection 위험) +let q = format!("SELECT * FROM users WHERE id = '{}'", id); + +// 좋은 예: parameterized 쿼리, 컴파일 타임에 검증 +let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id) + .fetch_optional(&pool) + .await?; +``` + +### 오류 처리 + +- 모듈별로 `thiserror`를 사용한 도메인 오류 enum 정의 +- `IntoResponse`를 통해 오류를 HTTP 응답으로 매핑 — 내부 세부 정보를 노출하지 않기 +- 구조화된 로깅에 `tracing` 사용 — `println!`이나 `eprintln!` 사용 금지 + +```rust +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum AppError { + #[error("Resource not found")] + NotFound, + #[error("Validation failed: {0}")] + Validation(String), + #[error("Unauthorized")] + Unauthorized, + #[error(transparent)] + Internal(#[from] anyhow::Error), +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + let (status, message) = match &self { + Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()), + Self::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()), + Self::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()), + Self::Internal(err) => { + tracing::error!(?err, "internal error"); + (StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into()) + } + }; + (status, Json(json!({ "error": message }))).into_response() + } +} +``` + +### 테스트 + +- 각 소스 파일 내의 `#[cfg(test)]` 모듈에서 단위 테스트 +- `tests/` 디렉토리에서 실제 PostgreSQL을 사용한 통합 테스트 (Testcontainers 또는 Docker) +- 자동 마이그레이션과 롤백이 포함된 데이터베이스 테스트에 `#[sqlx::test]` 사용 +- 외부 서비스 모킹에 `mockall` 또는 `wiremock` 사용 + +### 코드 스타일 + +- 최대 줄 길이: 100자 (rustfmt에 의해 강제) +- import 그룹화: `std`, 외부 크레이트, `crate`/`super` — 빈 줄로 구분 +- 모듈: 모듈당 파일 하나, `mod.rs`는 re-export용으로만 사용 +- 타입: PascalCase, 함수/변수: snake_case, 상수: UPPER_SNAKE_CASE + +## 파일 구조 + +``` +src/ + main.rs # 진입점, 서버 설정, 우아한 종료 + lib.rs # 통합 테스트를 위한 re-export + config.rs # envy 또는 figment을 사용한 환경 설정 + router.rs # 모든 라우트가 포함된 Axum 라우터 + middleware/ + auth.rs # JWT 추출 및 검증 + logging.rs # 요청/응답 트레이싱 + handlers/ + mod.rs # 라우트 핸들러 (얇게 — 서비스에 위임) + users.rs + orders.rs + services/ + mod.rs # 비즈니스 로직 + users.rs + orders.rs + repositories/ + mod.rs # 데이터베이스 접근 (SQLx 쿼리) + users.rs + orders.rs + domain/ + mod.rs # 도메인 타입, 오류 enum + user.rs + order.rs +migrations/ + 001_create_users.sql + 002_create_orders.sql +tests/ + common/mod.rs # 공유 테스트 헬퍼, 테스트 서버 설정 + api_users.rs # 사용자 엔드포인트 통합 테스트 + api_orders.rs # 주문 엔드포인트 통합 테스트 +``` + +## 주요 패턴 + +### Handler (얇은 레이어) + +```rust +async fn create_user( + State(ctx): State, + Json(payload): Json, +) -> Result<(StatusCode, Json), AppError> { + let user = ctx.user_service.create(payload).await?; + Ok((StatusCode::CREATED, Json(UserResponse::from(user)))) +} +``` + +### Service (비즈니스 로직) + +```rust +impl UserService { + pub async fn create(&self, req: CreateUserRequest) -> Result { + if self.repo.find_by_email(&req.email).await?.is_some() { + return Err(AppError::Validation("Email already registered".into())); + } + + let password_hash = hash_password(&req.password)?; + let user = self.repo.insert(&req.email, &req.name, &password_hash).await?; + + Ok(user) + } +} +``` + +### Repository (데이터 접근) + +```rust +impl UserRepository { + pub async fn find_by_email(&self, email: &str) -> Result, sqlx::Error> { + sqlx::query_as!(User, "SELECT * FROM users WHERE email = $1", email) + .fetch_optional(&self.pool) + .await + } + + pub async fn insert( + &self, + email: &str, + name: &str, + password_hash: &str, + ) -> Result { + sqlx::query_as!( + User, + r#"INSERT INTO users (email, name, password_hash) + VALUES ($1, $2, $3) RETURNING *"#, + email, name, password_hash, + ) + .fetch_one(&self.pool) + .await + } +} +``` + +### 통합 테스트 + +```rust +#[tokio::test] +async fn test_create_user() { + let app = spawn_test_app().await; + + let response = app + .client + .post(&format!("{}/api/v1/users", app.address)) + .json(&json!({ + "email": "alice@example.com", + "name": "Alice", + "password": "securepassword123" + })) + .send() + .await + .expect("Failed to send request"); + + assert_eq!(response.status(), StatusCode::CREATED); + let body: serde_json::Value = response.json().await.unwrap(); + assert_eq!(body["email"], "alice@example.com"); +} + +#[tokio::test] +async fn test_create_user_duplicate_email() { + let app = spawn_test_app().await; + // 첫 번째 사용자 생성 + create_test_user(&app, "alice@example.com").await; + // 중복 시도 + let response = create_user_request(&app, "alice@example.com").await; + assert_eq!(response.status(), StatusCode::BAD_REQUEST); +} +``` + +## 환경 변수 + +```bash +# 서버 +HOST=0.0.0.0 +PORT=8080 +RUST_LOG=info,tower_http=debug + +# 데이터베이스 +DATABASE_URL=postgres://user:pass@localhost:5432/myapp + +# 인증 +JWT_SECRET=your-secret-key-min-32-chars +JWT_EXPIRY_HOURS=24 + +# 선택 사항 +CORS_ALLOWED_ORIGINS=http://localhost:3000 +``` + +## 테스트 전략 + +```bash +# 전체 테스트 실행 +cargo test + +# 출력과 함께 실행 +cargo test -- --nocapture + +# 특정 테스트 모듈 실행 +cargo test api_users + +# 커버리지 확인 (cargo-llvm-cov 필요) +cargo llvm-cov --html +open target/llvm-cov/html/index.html + +# 린트 +cargo clippy -- -D warnings + +# 포맷 검사 +cargo fmt -- --check +``` + +## ECC 워크플로우 + +```bash +# 계획 수립 +/plan "Add order fulfillment with Stripe payment" + +# TDD로 개발 +/tdd # cargo test 기반 TDD 워크플로우 + +# 리뷰 +/code-review # Rust 전용 코드 리뷰 +/security-scan # 의존성 감사 + unsafe 스캔 + +# 검증 +/verify # 빌드, clippy, 테스트, 보안 스캔 +``` + +## Git 워크플로우 + +- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경 +- `main`에서 feature 브랜치 생성, PR 필수 +- CI: `cargo fmt --check`, `cargo clippy`, `cargo test`, `cargo audit` +- 배포: `scratch` 또는 `distroless` 베이스를 사용한 Docker 멀티스테이지 빌드 diff --git a/docs/ko-KR/examples/saas-nextjs-CLAUDE.md b/docs/ko-KR/examples/saas-nextjs-CLAUDE.md new file mode 100644 index 00000000..c9d501e3 --- /dev/null +++ b/docs/ko-KR/examples/saas-nextjs-CLAUDE.md @@ -0,0 +1,166 @@ +# SaaS 애플리케이션 — 프로젝트 CLAUDE.md + +> Next.js + Supabase + Stripe SaaS 애플리케이션을 위한 실제 사용 예제입니다. +> 프로젝트 루트에 복사한 후 기술 스택에 맞게 커스터마이즈하세요. + +## 프로젝트 개요 + +**기술 스택:** Next.js 15 (App Router), TypeScript, Supabase (인증 + DB), Stripe (결제), Tailwind CSS, Playwright (E2E) + +**아키텍처:** 기본적으로 Server Components 사용. Client Components는 상호작용이 필요한 경우에만 사용. API route는 webhook용, Server Action은 mutation용. + +## 핵심 규칙 + +### 데이터베이스 + +- 모든 쿼리는 RLS가 활성화된 Supabase client 사용 — RLS를 절대 우회하지 않음 +- 마이그레이션은 `supabase/migrations/`에 저장 — 데이터베이스를 직접 수정하지 않음 +- `select('*')` 대신 명시적 컬럼 목록이 포함된 `select()` 사용 +- 모든 사용자 대상 쿼리에는 무제한 결과를 방지하기 위해 `.limit()` 포함 필수 + +### 인증 + +- Server Components에서는 `@supabase/ssr`의 `createServerClient()` 사용 +- Client Components에서는 `@supabase/ssr`의 `createBrowserClient()` 사용 +- 보호된 라우트는 `getUser()`로 확인 — 인증에 `getSession()`만 단독으로 신뢰하지 않음 +- `middleware.ts`의 Middleware가 매 요청마다 인증 토큰 갱신 + +### 결제 + +- Stripe webhook 핸들러는 `app/api/webhooks/stripe/route.ts`에 위치 +- 클라이언트 측 가격 데이터를 절대 신뢰하지 않음 — 항상 서버 측에서 Stripe로부터 조회 +- 구독 상태는 webhook에 의해 동기화되는 `subscription_status` 컬럼으로 확인 +- 무료 플랜 사용자: 프로젝트 3개, 일일 API 호출 100회 + +### 코드 스타일 + +- 코드나 주석에 이모지 사용 금지 +- 불변 패턴만 사용 — spread 연산자 사용, 직접 변경 금지 +- Server Components: `'use client'` 디렉티브 없음, `useState`/`useEffect` 없음 +- Client Components: 파일 상단에 `'use client'` 작성, 최소한으로 유지 — 로직은 hooks로 분리 +- 모든 입력 유효성 검사에 Zod 스키마 사용 선호 (API route, 폼, 환경 변수) + +## 파일 구조 + +``` +src/ + app/ + (auth)/ # 인증 페이지 (로그인, 회원가입, 비밀번호 찾기) + (dashboard)/ # 보호된 대시보드 페이지 + api/ + webhooks/ # Stripe, Supabase webhooks + layout.tsx # Provider가 포함된 루트 레이아웃 + components/ + ui/ # Shadcn/ui 컴포넌트 + forms/ # 유효성 검사가 포함된 폼 컴포넌트 + dashboard/ # 대시보드 전용 컴포넌트 + hooks/ # 커스텀 React hooks + lib/ + supabase/ # Supabase client 팩토리 + stripe/ # Stripe client 및 헬퍼 + utils.ts # 범용 유틸리티 + types/ # 공유 TypeScript 타입 +supabase/ + migrations/ # 데이터베이스 마이그레이션 + seed.sql # 개발용 시드 데이터 +``` + +## 주요 패턴 + +### API 응답 형식 + +```typescript +type ApiResponse = + | { success: true; data: T } + | { success: false; error: string; code?: string } +``` + +### Server Action 패턴 + +```typescript +'use server' + +import { z } from 'zod' +import { createServerClient } from '@/lib/supabase/server' + +const schema = z.object({ + name: z.string().min(1).max(100), +}) + +export async function createProject(formData: FormData) { + const parsed = schema.safeParse({ name: formData.get('name') }) + if (!parsed.success) { + return { success: false, error: parsed.error.flatten() } + } + + const supabase = await createServerClient() + const { data: { user } } = await supabase.auth.getUser() + if (!user) return { success: false, error: 'Unauthorized' } + + const { data, error } = await supabase + .from('projects') + .insert({ name: parsed.data.name, user_id: user.id }) + .select('id, name, created_at') + .single() + + if (error) return { success: false, error: 'Failed to create project' } + return { success: true, data } +} +``` + +## 환경 변수 + +```bash +# Supabase +NEXT_PUBLIC_SUPABASE_URL= +NEXT_PUBLIC_SUPABASE_ANON_KEY= +SUPABASE_SERVICE_ROLE_KEY= # 서버 전용, 클라이언트에 절대 노출 금지 + +# Stripe +STRIPE_SECRET_KEY= +STRIPE_WEBHOOK_SECRET= +NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= + +# 앱 +NEXT_PUBLIC_APP_URL=http://localhost:3000 +``` + +## 테스트 전략 + +```bash +/tdd # 새 기능에 대한 단위 + 통합 테스트 +/e2e # 인증 흐름, 결제, 대시보드에 대한 Playwright 테스트 +/test-coverage # 80% 이상 커버리지 확인 +``` + +### 핵심 E2E 흐름 + +1. 회원가입 → 이메일 인증 → 첫 프로젝트 생성 +2. 로그인 → 대시보드 → CRUD 작업 +3. 플랜 업그레이드 → Stripe checkout → 구독 활성화 +4. Webhook: 구독 취소 → 무료 플랜으로 다운그레이드 + +## ECC 워크플로우 + +```bash +# 기능 계획 수립 +/plan "Add team invitations with email notifications" + +# TDD로 개발 +/tdd + +# 커밋 전 +/code-review +/security-scan + +# 릴리스 전 +/e2e +/test-coverage +``` + +## Git 워크플로우 + +- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경 +- `main`에서 기능 브랜치 생성, PR 필수 +- CI 실행 항목: lint, 타입 체크, 단위 테스트, E2E 테스트 +- 배포: PR 시 Vercel 미리보기, `main` 병합 시 프로덕션 배포 diff --git a/docs/ko-KR/examples/statusline.json b/docs/ko-KR/examples/statusline.json new file mode 100644 index 00000000..4fa84e81 --- /dev/null +++ b/docs/ko-KR/examples/statusline.json @@ -0,0 +1,19 @@ +{ + "statusLine": { + "type": "command", + "command": "input=$(cat); user=$(whoami); cwd=$(echo \"$input\" | jq -r '.workspace.current_dir' | sed \"s|$HOME|~|g\"); model=$(echo \"$input\" | jq -r '.model.display_name'); time=$(date +%H:%M); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); transcript=$(echo \"$input\" | jq -r '.transcript_path'); todo_count=$([ -f \"$transcript\" ] && grep -c '\"type\":\"todo\"' \"$transcript\" 2>/dev/null || echo 0); cd \"$(echo \"$input\" | jq -r '.workspace.current_dir')\" 2>/dev/null; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''); status=''; [ -n \"$branch\" ] && { [ -n \"$(git status --porcelain 2>/dev/null)\" ] && status='*'; }; B='\\033[38;2;30;102;245m'; G='\\033[38;2;64;160;43m'; Y='\\033[38;2;223;142;29m'; M='\\033[38;2;136;57;239m'; C='\\033[38;2;23;146;153m'; R='\\033[0m'; T='\\033[38;2;76;79;105m'; printf \"${C}${user}${R}:${B}${cwd}${R}\"; [ -n \"$branch\" ] && printf \" ${G}${branch}${Y}${status}${R}\"; [ -n \"$remaining\" ] && printf \" ${M}ctx:${remaining}%%${R}\"; printf \" ${T}${model}${R} ${Y}${time}${R}\"; [ \"$todo_count\" -gt 0 ] && printf \" ${C}todos:${todo_count}${R}\"; echo", + "description": "Custom status line showing: user:path branch* ctx:% model time todos:N" + }, + "_comments": { + "colors": { + "B": "Blue - directory path", + "G": "Green - git branch", + "Y": "Yellow - dirty status, time", + "M": "Magenta - context remaining", + "C": "Cyan - username, todos", + "T": "Gray - model name" + }, + "output_example": "affoon:~/projects/myapp main* ctx:73% sonnet-4.6 14:30 todos:3", + "usage": "Copy the statusLine object to your ~/.claude/settings.json" + } +} diff --git a/docs/ko-KR/examples/user-CLAUDE.md b/docs/ko-KR/examples/user-CLAUDE.md new file mode 100644 index 00000000..976ee967 --- /dev/null +++ b/docs/ko-KR/examples/user-CLAUDE.md @@ -0,0 +1,109 @@ +# 사용자 수준 CLAUDE.md 예제 + +사용자 수준 CLAUDE.md 파일 예제입니다. `~/.claude/CLAUDE.md`에 배치하세요. + +사용자 수준 설정은 모든 프로젝트에 전역으로 적용됩니다. 다음 용도로 사용하세요: +- 개인 코딩 선호 설정 +- 항상 적용하고 싶은 범용 규칙 +- 모듈식 규칙 파일 링크 + +--- + +## 핵심 철학 + +당신은 Claude Code입니다. 저는 복잡한 작업에 특화된 agent와 skill을 사용합니다. + +**핵심 원칙:** +1. **Agent 우선**: 복잡한 작업은 특화된 agent에 위임 +2. **병렬 실행**: 가능할 때 Task tool을 사용하여 여러 agent를 동시에 실행 +3. **실행 전 계획**: 복잡한 작업에는 Plan Mode 사용 +4. **테스트 주도**: 구현 전에 테스트 작성 +5. **보안 우선**: 보안에 대해 절대 타협하지 않음 + +--- + +## 모듈식 규칙 + +상세 가이드라인은 `~/.claude/rules/`에 있습니다: + +| 규칙 파일 | 내용 | +|-----------|------| +| security.md | 보안 점검, 시크릿 관리 | +| coding-style.md | 불변성, 파일 구성, 에러 처리 | +| testing.md | TDD 워크플로우, 80% 커버리지 요구사항 | +| git-workflow.md | 커밋 형식, PR 워크플로우 | +| agents.md | Agent 오케스트레이션, 상황별 agent 선택 | +| patterns.md | API 응답, repository 패턴 | +| performance.md | 모델 선택, 컨텍스트 관리 | +| hooks.md | Hooks 시스템 | + +--- + +## 사용 가능한 Agent + +`~/.claude/agents/`에 위치합니다: + +| Agent | 용도 | +|-------|------| +| planner | 기능 구현 계획 수립 | +| architect | 시스템 설계 및 아키텍처 | +| tdd-guide | 테스트 주도 개발 | +| code-reviewer | 품질/보안 코드 리뷰 | +| security-reviewer | 보안 취약점 분석 | +| build-error-resolver | 빌드 에러 해결 | +| e2e-runner | Playwright E2E 테스트 | +| refactor-cleaner | 불필요한 코드 정리 | +| doc-updater | 문서 업데이트 | + +--- + +## 개인 선호 설정 + +### 개인정보 보호 +- 항상 로그를 삭제하고, 시크릿(API 키/토큰/비밀번호/JWT)을 절대 붙여넣지 않음 +- 공유 전 출력 내용을 검토하여 민감한 데이터 제거 + +### 코드 스타일 +- 코드, 주석, 문서에 이모지 사용 금지 +- 불변성 선호 - 객체나 배열을 직접 변경하지 않음 +- 큰 파일 소수보다 작은 파일 다수를 선호 +- 일반적으로 200-400줄, 파일당 최대 800줄 + +### Git +- Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `test:` +- 커밋 전 항상 로컬에서 테스트 +- 작고 집중된 커밋 + +### 테스트 +- TDD: 테스트를 먼저 작성 +- 최소 80% 커버리지 +- 핵심 흐름에 대해 단위 + 통합 + E2E 테스트 + +### 지식 축적 +- 개인 디버깅 메모, 선호 설정, 임시 컨텍스트 → auto memory +- 팀/프로젝트 지식(아키텍처 결정, API 변경, 구현 런북) → 프로젝트의 기존 문서 구조를 따름 +- 현재 작업에서 이미 관련 문서, 주석, 예제를 생성하는 경우 동일한 지식을 다른 곳에 중복하지 않음 +- 적절한 프로젝트 문서 위치가 없는 경우 새로운 최상위 문서를 만들기 전에 먼저 질문 + +--- + +## 에디터 연동 + +저는 Zed을 기본 에디터로 사용합니다: +- 파일 추적을 위한 Agent Panel +- CMD+Shift+R로 명령 팔레트 사용 +- Vim 모드 활성화 + +--- + +## 성공 기준 + +다음 조건을 충족하면 성공입니다: +- 모든 테스트 통과 (80% 이상 커버리지) +- 보안 취약점 없음 +- 코드가 읽기 쉽고 유지보수 가능 +- 사용자 요구사항 충족 + +--- + +**철학**: Agent 우선 설계, 병렬 실행, 실행 전 계획, 코드 전 테스트, 항상 보안 우선. From fb7b73a96292bf6a283a78ce801a7cc839798581 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Fri, 13 Mar 2026 00:17:54 -0700 Subject: [PATCH 9/9] docs: address Korean translation review feedback --- README.md | 2 +- docs/ko-KR/CONTRIBUTING.md | 129 ++++++++++++++++++ docs/ko-KR/README.md | 6 +- docs/ko-KR/TERMINOLOGY.md | 32 ++--- docs/ko-KR/agents/doc-updater.md | 9 ++ docs/ko-KR/agents/e2e-runner.md | 2 +- docs/ko-KR/agents/tdd-guide.md | 12 ++ docs/ko-KR/commands/build-fix.md | 7 +- docs/ko-KR/commands/checkpoint.md | 7 +- docs/ko-KR/commands/code-review.md | 4 +- docs/ko-KR/commands/e2e.md | 4 +- docs/ko-KR/commands/eval.md | 2 +- docs/ko-KR/commands/go-build.md | 4 +- docs/ko-KR/commands/go-review.md | 4 +- docs/ko-KR/commands/setup-pm.md | 2 +- docs/ko-KR/commands/tdd.md | 4 +- docs/ko-KR/commands/test-coverage.md | 5 + docs/ko-KR/commands/update-codemaps.md | 39 +++--- docs/ko-KR/commands/update-docs.md | 5 + docs/ko-KR/commands/verify.md | 8 +- docs/ko-KR/examples/django-api-CLAUDE.md | 10 +- docs/ko-KR/examples/rust-api-CLAUDE.md | 10 +- docs/ko-KR/examples/statusline.json | 2 +- docs/ko-KR/rules/agents.md | 3 + docs/ko-KR/rules/git-workflow.md | 2 +- docs/ko-KR/skills/backend-patterns/SKILL.md | 5 +- docs/ko-KR/skills/clickhouse-io/SKILL.md | 84 ++++++------ docs/ko-KR/skills/coding-standards/SKILL.md | 2 +- .../ko-KR/skills/continuous-learning/SKILL.md | 31 ++++- docs/ko-KR/skills/frontend-patterns/SKILL.md | 22 ++- docs/ko-KR/skills/golang-patterns/SKILL.md | 3 +- docs/ko-KR/skills/security-review/SKILL.md | 25 ++-- .../cloud-infrastructure-security.md | 4 +- docs/ko-KR/skills/strategic-compact/SKILL.md | 14 +- docs/ko-KR/skills/tdd-workflow/SKILL.md | 8 +- docs/ko-KR/skills/verification-loop/SKILL.md | 3 +- 36 files changed, 383 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index 8c03737d..47a0139a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**Language:** English | [繁體中文](docs/zh-TW/README.md) | [한국어](docs/ko-KR/README.md) +**Language:** English | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) # Everything Claude Code diff --git a/docs/ko-KR/CONTRIBUTING.md b/docs/ko-KR/CONTRIBUTING.md index 8f2f5614..7597d7e4 100644 --- a/docs/ko-KR/CONTRIBUTING.md +++ b/docs/ko-KR/CONTRIBUTING.md @@ -195,6 +195,15 @@ model: sonnet | `tools` | 필요한 것만 포함 | `Read, Write, Edit, Bash, Grep, Glob, WebFetch, Task` | | `model` | 복잡도 수준 | `haiku` (단순), `sonnet` (코딩), `opus` (복잡) | +### 예시 에이전트 + +| 에이전트 | 용도 | +|----------|------| +| `tdd-guide.md` | 테스트 주도 개발 | +| `code-reviewer.md` | 코드 리뷰 | +| `security-reviewer.md` | 보안 점검 | +| `build-error-resolver.md` | 빌드 오류 수정 | + --- ## 훅 기여하기 @@ -216,6 +225,68 @@ hooks/hooks.json | `SessionStart` | 세션 시작 시 | 컨텍스트 로딩 | | `Stop` | 세션 종료 시 | 정리, 감사 | +### 훅 형식 + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "tool == \"Bash\" && tool_input.command matches \"rm -rf /\"", + "hooks": [ + { + "type": "command", + "command": "echo '[Hook] BLOCKED: Dangerous command' && exit 1" + } + ], + "description": "위험한 rm 명령 차단" + } + ] + } +} +``` + +### Matcher 문법 + +```javascript +// 특정 도구 매칭 +tool == "Bash" +tool == "Edit" +tool == "Write" + +// 입력 패턴 매칭 +tool_input.command matches "npm install" +tool_input.file_path matches "\\.tsx?$" + +// 조건 결합 +tool == "Bash" && tool_input.command matches "git push" +``` + +### 훅 예시 + +```json +// tmux 밖 dev 서버 차단 +{ + "matcher": "tool == \"Bash\" && tool_input.command matches \"npm run dev\"", + "hooks": [{"type": "command", "command": "echo '개발 서버는 tmux에서 실행하세요' && exit 1"}], + "description": "dev 서버를 tmux에서 실행하도록 강제" +} + +// TypeScript 편집 후 자동 포맷 +{ + "matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.tsx?$\"", + "hooks": [{"type": "command", "command": "npx prettier --write \"$file_path\""}], + "description": "TypeScript 파일 편집 후 포맷" +} + +// git push 전 경고 +{ + "matcher": "tool == \"Bash\" && tool_input.command matches \"git push\"", + "hooks": [{"type": "command", "command": "echo '[Hook] push 전에 변경사항을 다시 검토하세요'"}], + "description": "push 전 검토 리마인더" +} +``` + ### 훅 체크리스트 - [ ] Matcher가 구체적 (너무 광범위하지 않게) @@ -236,6 +307,36 @@ hooks/hooks.json commands/your-command.md ``` +### 커맨드 템플릿 + +```markdown +--- +description: /help에 표시되는 간단한 설명 +--- + +# 커맨드 이름 + +## 목적 + +이 커맨드가 수행하는 작업. + +## 사용법 + +\`\`\` +/your-command [args] +\`\`\` + +## 워크플로우 + +1. 첫 번째 단계 +2. 두 번째 단계 +3. 마지막 단계 + +## 출력 + +사용자가 받는 결과. +``` + ### 커맨드 예시 | 커맨드 | 용도 | @@ -247,6 +348,34 @@ commands/your-command.md --- +## 크로스-하네스 및 번역 + +### 스킬 서브셋 (Codex 및 Cursor) + +ECC는 다른 하네스를 위한 스킬 서브셋도 제공합니다: + +- **Codex:** `.agents/skills/` — `agents/openai.yaml`에 나열된 스킬이 Codex에서 로드됩니다. +- **Cursor:** `.cursor/skills/` — Cursor용 스킬 서브셋이 별도로 포함됩니다. + +Codex 또는 Cursor에서도 제공해야 하는 **새 스킬**을 추가한다면: + +1. 먼저 `skills/your-skill-name/` 아래에 일반적인 ECC 스킬로 추가합니다. +2. **Codex**에서도 제공해야 하면 `.agents/skills/`에 반영하고, 필요하면 `agents/openai.yaml`에도 참조를 추가합니다. +3. **Cursor**에서도 제공해야 하면 Cursor 레이아웃에 맞게 `.cursor/skills/` 아래에 추가합니다. + +기존 디렉터리의 구조를 확인한 뒤 같은 패턴을 따르세요. 이 서브셋 동기화는 수동이므로 PR 설명에 반영 여부를 적어 두는 것이 좋습니다. + +### 번역 + +번역 문서는 `docs/` 아래에 있습니다. 예: `docs/zh-CN`, `docs/zh-TW`, `docs/ja-JP`. + +번역된 에이전트, 커맨드, 스킬을 변경한다면: + +- 대응하는 번역 파일도 함께 업데이트하거나 +- 유지보수자/번역자가 후속 작업을 할 수 있도록 이슈를 열어 주세요. + +--- + ## Pull Request 프로세스 ### 1. PR 제목 형식 diff --git a/docs/ko-KR/README.md b/docs/ko-KR/README.md index 70a1098a..6dc2d25d 100644 --- a/docs/ko-KR/README.md +++ b/docs/ko-KR/README.md @@ -167,7 +167,7 @@ cd everything-claude-code 3. **package.json**: `packageManager` 필드 4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb에서 감지 5. **글로벌 설정**: `~/.claude/package-manager.json` -6. **폴백**: 사용 가능한 첫 번째 패키지 매니저 +6. **폴백**: `npm` 패키지 매니저 설정 방법: @@ -416,7 +416,7 @@ cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 사용하 cp everything-claude-code/commands/*.md ~/.claude/commands/ # 스킬 복사 -cp -r everything-claude-code/.agents/skills/* ~/.claude/skills/ +cp -r everything-claude-code/skills/* ~/.claude/skills/ cp -r everything-claude-code/skills/search-first ~/.claude/skills/ ``` @@ -498,7 +498,9 @@ rules/ | 보안 취약점 찾기 | `/security-scan` | security-reviewer | | 사용하지 않는 코드 제거 | `/refactor-clean` | refactor-cleaner | | 문서 업데이트 | `/update-docs` | doc-updater | +| Go 빌드 실패 수정 | `/go-build` | go-build-resolver | | Go 코드 리뷰 | `/go-review` | go-reviewer | +| 데이터베이스 스키마/쿼리 리뷰 | `/code-review` + database-reviewer 에이전트 | database-reviewer | | Python 코드 리뷰 | `/python-review` | python-reviewer | ### 일반적인 워크플로우 diff --git a/docs/ko-KR/TERMINOLOGY.md b/docs/ko-KR/TERMINOLOGY.md index 47751941..e7bb9cb3 100644 --- a/docs/ko-KR/TERMINOLOGY.md +++ b/docs/ko-KR/TERMINOLOGY.md @@ -38,20 +38,20 @@ | Merge | merge | 확정 | 영문 유지 | | Repository | 저장소 | 확정 | | | Fork | Fork | 확정 | 영문 유지 | -| Supabase | Supabase | - | 제품명 유지 | -| Redis | Redis | - | 제품명 유지 | -| Playwright | Playwright | - | 제품명 유지 | -| TypeScript | TypeScript | - | 언어명 유지 | -| JavaScript | JavaScript | - | 언어명 유지 | -| Go/Golang | Go | - | 언어명 유지 | -| React | React | - | 프레임워크명 유지 | -| Next.js | Next.js | - | 프레임워크명 유지 | -| PostgreSQL | PostgreSQL | - | 제품명 유지 | +| Supabase | Supabase | 확정 | 제품명 유지 | +| Redis | Redis | 확정 | 제품명 유지 | +| Playwright | Playwright | 확정 | 제품명 유지 | +| TypeScript | TypeScript | 확정 | 언어명 유지 | +| JavaScript | JavaScript | 확정 | 언어명 유지 | +| Go/Golang | Go | 확정 | 언어명 유지 | +| React | React | 확정 | 프레임워크명 유지 | +| Next.js | Next.js | 확정 | 프레임워크명 유지 | +| PostgreSQL | PostgreSQL | 확정 | 제품명 유지 | | RLS (Row Level Security) | RLS(행 수준 보안) | 확정 | 최초 사용 시 전개 | -| OWASP | OWASP | - | 영문 유지 | -| XSS | XSS | - | 영문 유지 | +| OWASP | OWASP | 확정 | 영문 유지 | +| XSS | XSS | 확정 | 영문 유지 | | SQL Injection | SQL 인젝션 | 확정 | | -| CSRF | CSRF | - | 영문 유지 | +| CSRF | CSRF | 확정 | 영문 유지 | | Refactor | 리팩토링 | 확정 | | | Dead Code | 데드 코드 | 확정 | | | Lint/Linter | Lint | 확정 | 영문 유지 | @@ -70,11 +70,11 @@ | Migration | 마이그레이션 | 확정 | | | Transaction | 트랜잭션 | 확정 | | | Concurrency | 동시성 | 확정 | | -| Goroutine | Goroutine | - | Go 용어 유지 | +| Goroutine | Goroutine | 확정 | Go 용어 유지 | | Channel | Channel | 확정 | Go 컨텍스트에서 유지 | -| Mutex | Mutex | - | 영문 유지 | +| Mutex | Mutex | 확정 | 영문 유지 | | Interface | 인터페이스 | 확정 | | -| Struct | Struct | - | Go 용어 유지 | +| Struct | Struct | 확정 | Go 용어 유지 | | Mock | Mock | 확정 | 테스트 용어 유지 | | Stub | Stub | 확정 | 테스트 용어 유지 | | Fixture | Fixture | 확정 | 테스트 용어 유지 | @@ -82,7 +82,7 @@ | Snapshot | 스냅샷 | 확정 | | | Trace | 트레이스 | 확정 | | | Artifact | 아티팩트 | 확정 | | -| CI/CD | CI/CD | - | 영문 유지 | +| CI/CD | CI/CD | 확정 | 영문 유지 | | Pipeline | 파이프라인 | 확정 | | --- diff --git a/docs/ko-KR/agents/doc-updater.md b/docs/ko-KR/agents/doc-updater.md index 7da9bcac..ac889fd9 100644 --- a/docs/ko-KR/agents/doc-updater.md +++ b/docs/ko-KR/agents/doc-updater.md @@ -87,6 +87,15 @@ docs/CODEMAPS/ 4. **실행 가능** — 실제로 작동하는 설정 커맨드 포함 5. **상호 참조** — 관련 문서 링크 +## 품질 체크리스트 + +- [ ] 실제 코드에서 코드맵 생성 +- [ ] 모든 파일 경로 존재 확인 +- [ ] 코드 예제가 컴파일 또는 실행됨 +- [ ] 링크 검증 완료 +- [ ] 최신 타임스탬프 업데이트 +- [ ] 오래된 참조 없음 + ## 업데이트 시점 **항상:** 새 주요 기능, API 라우트 변경, 의존성 추가/제거, 아키텍처 변경, 설정 프로세스 수정. diff --git a/docs/ko-KR/agents/e2e-runner.md b/docs/ko-KR/agents/e2e-runner.md index bc199ccb..5d82518f 100644 --- a/docs/ko-KR/agents/e2e-runner.md +++ b/docs/ko-KR/agents/e2e-runner.md @@ -71,7 +71,7 @@ npx playwright show-report # HTML 보고서 보기 - **시맨틱 로케이터 사용**: `[data-testid="..."]` > CSS 셀렉터 > XPath - **시간이 아닌 조건 대기**: `waitForResponse()` > `waitForTimeout()` -- **자동 대기 내장**: `page.locator().click()`은 자동 대기; `page.click()`은 아님 +- **자동 대기 내장**: `locator.click()`과 `page.click()` 모두 자동 대기를 제공하지만, 더 안정적인 `locator` 기반 API를 선호 - **테스트 격리**: 각 테스트는 독립적; 공유 상태 없음 - **빠른 실패**: 모든 핵심 단계에서 `expect()` 어설션 사용 - **재시도 시 트레이스**: 실패 디버깅을 위해 `trace: 'on-first-retry'` 설정 diff --git a/docs/ko-KR/agents/tdd-guide.md b/docs/ko-KR/agents/tdd-guide.md index 2ea68c4b..060241ff 100644 --- a/docs/ko-KR/agents/tdd-guide.md +++ b/docs/ko-KR/agents/tdd-guide.md @@ -21,10 +21,16 @@ model: sonnet 기대 동작을 설명하는 실패하는 테스트 작성. ### 2. 테스트 실행 -- 실패 확인 +Node.js (npm): ```bash npm test ``` +언어 중립: +- 프로젝트의 기본 테스트 명령을 실행하세요. +- Python: `pytest` +- Go: `go test ./...` + ### 3. 최소한의 구현 작성 (GREEN) 테스트를 통과하기에 충분한 코드만. @@ -34,11 +40,17 @@ npm test 중복 제거, 이름 개선, 최적화 -- 테스트는 그린 유지. ### 6. 커버리지 확인 +Node.js (npm): ```bash npm run test:coverage # 필수: branches, functions, lines, statements 80% 이상 ``` +언어 중립: +- 프로젝트의 기본 커버리지 명령을 실행하세요. +- Python: `pytest --cov` +- Go: `go test ./... -cover` + ## 필수 테스트 유형 | 유형 | 테스트 대상 | 시점 | diff --git a/docs/ko-KR/commands/build-fix.md b/docs/ko-KR/commands/build-fix.md index c113cc80..4edb038d 100644 --- a/docs/ko-KR/commands/build-fix.md +++ b/docs/ko-KR/commands/build-fix.md @@ -1,3 +1,8 @@ +--- +name: build-fix +description: 최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다. +--- + # Build 오류 수정 최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다. @@ -14,7 +19,7 @@ | `pom.xml` | `mvn compile` | | `build.gradle` | `./gradlew compileJava` | | `go.mod` | `go build ./...` | -| `pyproject.toml` | `python -m py_compile` 또는 `mypy .` | +| `pyproject.toml` | `python -m compileall .` 또는 `mypy .` | ## 2단계: 오류 파싱 및 그룹화 diff --git a/docs/ko-KR/commands/checkpoint.md b/docs/ko-KR/commands/checkpoint.md index e8d5e578..74a65c16 100644 --- a/docs/ko-KR/commands/checkpoint.md +++ b/docs/ko-KR/commands/checkpoint.md @@ -1,10 +1,15 @@ +--- +name: checkpoint +description: 워크플로우에서 checkpoint를 생성, 검증, 조회 또는 정리합니다. +--- + # Checkpoint 명령어 워크플로우에서 checkpoint를 생성하거나 검증합니다. ## 사용법 -`/checkpoint [create|verify|list] [name]` +`/checkpoint [create|verify|list|clear] [name]` ## Checkpoint 생성 diff --git a/docs/ko-KR/commands/code-review.md b/docs/ko-KR/commands/code-review.md index 3f4c115e..18a86517 100644 --- a/docs/ko-KR/commands/code-review.md +++ b/docs/ko-KR/commands/code-review.md @@ -19,9 +19,9 @@ - 800줄 초과 파일 - 4단계 초과 중첩 깊이 - 누락된 에러 처리 -- console.log 문 +- 디버그 로깅 문구(예: 개발용 로그/print 등) - TODO/FIXME 주석 -- 공개 API에 대한 JSDoc 누락 +- 활성 언어에 대한 공개 API 문서 누락(예: JSDoc/Go doc/Docstring 등) **모범 사례 (MEDIUM):** - 변이(Mutation) 패턴 (불변 패턴을 사용하세요) diff --git a/docs/ko-KR/commands/e2e.md b/docs/ko-KR/commands/e2e.md index f82c70d0..442fcfd7 100644 --- a/docs/ko-KR/commands/e2e.md +++ b/docs/ko-KR/commands/e2e.md @@ -36,7 +36,7 @@ e2e-runner 에이전트가 수행하는 작업: ## 사용 예시 -``` +```` User: /e2e 마켓 검색 및 조회 흐름 테스트 Agent (e2e-runner): @@ -200,7 +200,7 @@ Running 3 tests using 3 workers ``` ✅ CI/CD 통합 준비가 완료된 E2E 테스트 모음! -``` +```` ## 테스트 아티팩트 diff --git a/docs/ko-KR/commands/eval.md b/docs/ko-KR/commands/eval.md index e07ea256..ddf3869d 100644 --- a/docs/ko-KR/commands/eval.md +++ b/docs/ko-KR/commands/eval.md @@ -4,7 +4,7 @@ ## 사용법 -`/eval [define|check|report|list] [feature-name]` +`/eval [define|check|report|list|clean] [feature-name]` ## 평가 정의 diff --git a/docs/ko-KR/commands/go-build.md b/docs/ko-KR/commands/go-build.md index 3933af10..64ea0e06 100644 --- a/docs/ko-KR/commands/go-build.md +++ b/docs/ko-KR/commands/go-build.md @@ -43,7 +43,7 @@ go mod tidy -v ## 예시 세션 -```text +````text User: /go-build Agent: @@ -141,7 +141,7 @@ ok project/internal/handler 0.023s | 남은 이슈 | 0 | Build 상태: ✅ 성공 -``` +```` ## 자주 발생하는 에러 diff --git a/docs/ko-KR/commands/go-review.md b/docs/ko-KR/commands/go-review.md index 61b272e7..f12a3989 100644 --- a/docs/ko-KR/commands/go-review.md +++ b/docs/ko-KR/commands/go-review.md @@ -68,7 +68,7 @@ govulncheck ./... ## 사용 예시 -```text +````text User: /go-review Agent: @@ -125,7 +125,7 @@ return fmt.Errorf("get user %s: %w", userID, err) - MEDIUM: 0 권장: ❌ CRITICAL 이슈가 수정될 때까지 merge 차단 -``` +```` ## 승인 기준 diff --git a/docs/ko-KR/commands/setup-pm.md b/docs/ko-KR/commands/setup-pm.md index 43b01ace..3115e7d4 100644 --- a/docs/ko-KR/commands/setup-pm.md +++ b/docs/ko-KR/commands/setup-pm.md @@ -32,7 +32,7 @@ node scripts/setup-package-manager.js --list 3. **package.json**: `packageManager` 필드 4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb의 존재 여부 5. **전역 설정**: `~/.claude/package-manager.json` -6. **폴백**: 사용 가능한 첫 번째 패키지 매니저 (pnpm > bun > yarn > npm) +6. **폴백**: `npm` ## 설정 파일 diff --git a/docs/ko-KR/commands/tdd.md b/docs/ko-KR/commands/tdd.md index eba7a754..cf21ee23 100644 --- a/docs/ko-KR/commands/tdd.md +++ b/docs/ko-KR/commands/tdd.md @@ -48,7 +48,7 @@ REPEAT: 다음 기능/시나리오 ## 사용 예시 -``` +```` User: /tdd 마켓 유동성 점수를 계산하는 함수가 필요합니다 Agent (tdd-guide): @@ -251,7 +251,7 @@ Coverage: 100% ✅ (목표: 80%) ``` ✅ TDD 세션 완료! -``` +```` ## TDD 모범 사례 diff --git a/docs/ko-KR/commands/test-coverage.md b/docs/ko-KR/commands/test-coverage.md index ecde8a18..7eb8b8b3 100644 --- a/docs/ko-KR/commands/test-coverage.md +++ b/docs/ko-KR/commands/test-coverage.md @@ -1,3 +1,8 @@ +--- +name: test-coverage +description: 테스트 커버리지를 분석하고, 80% 이상을 목표로 누락된 테스트를 식별하고 생성합니다. +--- + # 테스트 커버리지 테스트 커버리지를 분석하고, 갭을 식별하며, 80% 이상 커버리지 달성을 위해 누락된 테스트를 생성합니다. diff --git a/docs/ko-KR/commands/update-codemaps.md b/docs/ko-KR/commands/update-codemaps.md index a2bf3138..091d2d2e 100644 --- a/docs/ko-KR/commands/update-codemaps.md +++ b/docs/ko-KR/commands/update-codemaps.md @@ -10,15 +10,16 @@ ## 2단계: 코드맵 생성 -`docs/CODEMAPS/` (또는 `.reports/codemaps/`)에 코드맵 생성 또는 업데이트: +`docs/CODEMAPS/`에 코드맵 생성 또는 업데이트: | 파일 | 내용 | |------|------| -| `architecture.md` | 상위 시스템 다이어그램, 서비스 경계, 데이터 흐름 | +| `INDEX.md` | 전체 코드베이스 개요와 영역별 링크 | | `backend.md` | API 라우트, 미들웨어 체인, 서비스 → 리포지토리 매핑 | | `frontend.md` | 페이지 트리, 컴포넌트 계층, 상태 관리 흐름 | -| `data.md` | 데이터베이스 테이블, 관계, 마이그레이션 히스토리 | -| `dependencies.md` | 외부 서비스, 서드파티 통합, 공유 라이브러리 | +| `database.md` | 데이터베이스 스키마, 마이그레이션, 저장소 계층 | +| `integrations.md` | 외부 서비스, 서드파티 통합, 어댑터 | +| `workers.md` | 백그라운드 작업, 큐, 스케줄러 | ### 코드맵 형식 @@ -41,27 +42,33 @@ src/repos/user.ts (데이터베이스 접근, 80줄) - Stripe (결제 처리) ``` -## 3단계: 변경 감지 +## 3단계: 영역 분류 -1. 이전 코드맵이 있는 경우 변경 비율 계산 -2. 변경이 30%를 초과하면 diff를 표시하고 덮어쓰기 전에 사용자 승인 요청 -3. 변경이 30% 이하이면 기존 파일에 바로 업데이트 +생성기는 파일 경로 패턴을 기반으로 영역을 자동 분류합니다: + +1. 프론트엔드: `app/`, `pages/`, `components/`, `hooks/`, `.tsx`, `.jsx` +2. 백엔드: `api/`, `routes/`, `controllers/`, `services/`, `.route.ts` +3. 데이터베이스: `db/`, `migrations/`, `prisma/`, `repositories/` +4. 통합: `integrations/`, `adapters/`, `connectors/`, `plugins/` +5. 워커: `workers/`, `jobs/`, `queues/`, `tasks/`, `cron/` ## 4단계: 메타데이터 추가 각 코드맵에 최신 정보 헤더를 추가합니다: ```markdown - +**Last Updated:** 2026-03-12 +**Total Files:** 42 +**Total Lines:** 1875 ``` -## 5단계: 분석 보고서 저장 +## 5단계: 인덱스와 영역 문서 동기화 -`.reports/codemap-diff.txt`에 요약을 작성합니다: -- 마지막 스캔 이후 추가/제거/수정된 파일 -- 새로 감지된 의존성 -- 아키텍처 변경 사항 (새 라우트, 새 서비스 등) -- 90일 이상 업데이트되지 않은 문서에 대한 오래된 항목 경고 +`INDEX.md`는 생성된 영역 문서를 링크하고 요약해야 합니다: +- 각 영역의 파일 수와 총 라인 수 +- 감지된 엔트리 포인트 +- 저장소 트리의 간단한 ASCII 개요 +- 영역별 세부 문서 링크 ## 팁 @@ -69,4 +76,4 @@ src/repos/user.ts (데이터베이스 접근, 80줄) - 전체 코드 블록 대신 **파일 경로와 함수 시그니처** 사용 - 효율적인 컨텍스트 로딩을 위해 각 코드맵을 **1000 토큰 미만**으로 유지 - 장황한 설명 대신 데이터 흐름에 ASCII 다이어그램 사용 -- 주요 기능 추가 또는 리팩토링 세션 후 실행 +- 주요 기능 추가 또는 리팩토링 세션 후 `npx tsx scripts/codemaps/generate.ts` 실행 diff --git a/docs/ko-KR/commands/update-docs.md b/docs/ko-KR/commands/update-docs.md index 3d353ce2..c4500c9e 100644 --- a/docs/ko-KR/commands/update-docs.md +++ b/docs/ko-KR/commands/update-docs.md @@ -1,3 +1,8 @@ +--- +name: update-docs +description: 코드베이스를 기준으로 문서를 동기화하고 생성된 섹션을 갱신합니다. +--- + # 문서 업데이트 문서를 코드베이스와 동기화하고, 원본 소스 파일에서 생성합니다. diff --git a/docs/ko-KR/commands/verify.md b/docs/ko-KR/commands/verify.md index 41ca5cbe..3c973a3e 100644 --- a/docs/ko-KR/commands/verify.md +++ b/docs/ko-KR/commands/verify.md @@ -23,11 +23,15 @@ - 통과/실패 수 보고 - 커버리지 비율 보고 -5. **Console.log 감사** +5. **시크릿 스캔** + - 소스 파일에서 API 키, 토큰, 비밀값 패턴 검색 + - 발견 위치 보고 + +6. **Console.log 감사** - 소스 파일에서 console.log 검색 - 위치 보고 -6. **Git 상태** +7. **Git 상태** - 커밋되지 않은 변경사항 표시 - 마지막 커밋 이후 수정된 파일 표시 diff --git a/docs/ko-KR/examples/django-api-CLAUDE.md b/docs/ko-KR/examples/django-api-CLAUDE.md index 18cd261c..6b56ccf6 100644 --- a/docs/ko-KR/examples/django-api-CLAUDE.md +++ b/docs/ko-KR/examples/django-api-CLAUDE.md @@ -142,12 +142,12 @@ from django.db import transaction def create_order(*, customer, product_id: uuid.UUID, quantity: int) -> Order: """재고 검증과 결제 보류를 포함한 주문 생성.""" - product = Product.objects.select_for_update().get(id=product_id) - - if product.stock < quantity: - raise InsufficientStockError() - with transaction.atomic(): + product = Product.objects.select_for_update().get(id=product_id) + + if product.stock < quantity: + raise InsufficientStockError() + order = Order.objects.create( customer=customer, product=product, diff --git a/docs/ko-KR/examples/rust-api-CLAUDE.md b/docs/ko-KR/examples/rust-api-CLAUDE.md index 0f05c8b2..1e0ef375 100644 --- a/docs/ko-KR/examples/rust-api-CLAUDE.md +++ b/docs/ko-KR/examples/rust-api-CLAUDE.md @@ -55,7 +55,9 @@ pub enum AppError { #[error("Unauthorized")] Unauthorized, #[error(transparent)] - Internal(#[from] anyhow::Error), + Database(#[from] sqlx::Error), + #[error(transparent)] + Io(#[from] std::io::Error), } impl IntoResponse for AppError { @@ -64,7 +66,11 @@ impl IntoResponse for AppError { Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()), Self::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()), Self::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()), - Self::Internal(err) => { + Self::Database(err) => { + tracing::error!(?err, "database error"); + (StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into()) + } + Self::Io(err) => { tracing::error!(?err, "internal error"); (StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into()) } diff --git a/docs/ko-KR/examples/statusline.json b/docs/ko-KR/examples/statusline.json index 4fa84e81..8c020fff 100644 --- a/docs/ko-KR/examples/statusline.json +++ b/docs/ko-KR/examples/statusline.json @@ -1,7 +1,7 @@ { "statusLine": { "type": "command", - "command": "input=$(cat); user=$(whoami); cwd=$(echo \"$input\" | jq -r '.workspace.current_dir' | sed \"s|$HOME|~|g\"); model=$(echo \"$input\" | jq -r '.model.display_name'); time=$(date +%H:%M); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); transcript=$(echo \"$input\" | jq -r '.transcript_path'); todo_count=$([ -f \"$transcript\" ] && grep -c '\"type\":\"todo\"' \"$transcript\" 2>/dev/null || echo 0); cd \"$(echo \"$input\" | jq -r '.workspace.current_dir')\" 2>/dev/null; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''); status=''; [ -n \"$branch\" ] && { [ -n \"$(git status --porcelain 2>/dev/null)\" ] && status='*'; }; B='\\033[38;2;30;102;245m'; G='\\033[38;2;64;160;43m'; Y='\\033[38;2;223;142;29m'; M='\\033[38;2;136;57;239m'; C='\\033[38;2;23;146;153m'; R='\\033[0m'; T='\\033[38;2;76;79;105m'; printf \"${C}${user}${R}:${B}${cwd}${R}\"; [ -n \"$branch\" ] && printf \" ${G}${branch}${Y}${status}${R}\"; [ -n \"$remaining\" ] && printf \" ${M}ctx:${remaining}%%${R}\"; printf \" ${T}${model}${R} ${Y}${time}${R}\"; [ \"$todo_count\" -gt 0 ] && printf \" ${C}todos:${todo_count}${R}\"; echo", + "command": "input=$(cat); user=$(whoami); cwd=$(echo \"$input\" | jq -r '.workspace.current_dir' | sed \"s|$HOME|~|g\"); model=$(echo \"$input\" | jq -r '.model.display_name'); time=$(date +%H:%M); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); transcript=$(echo \"$input\" | jq -r '.transcript_path'); todo_count=$([ -f \"$transcript\" ] && { grep -c '\"type\":\"todo\"' \"$transcript\" 2>/dev/null || true; } || echo 0); cd \"$(echo \"$input\" | jq -r '.workspace.current_dir')\" 2>/dev/null; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''); status=''; [ -n \"$branch\" ] && { [ -n \"$(git status --porcelain 2>/dev/null)\" ] && status='*'; }; B='\\033[38;2;30;102;245m'; G='\\033[38;2;64;160;43m'; Y='\\033[38;2;223;142;29m'; M='\\033[38;2;136;57;239m'; C='\\033[38;2;23;146;153m'; R='\\033[0m'; T='\\033[38;2;76;79;105m'; printf \"${C}${user}${R}:${B}${cwd}${R}\"; [ -n \"$branch\" ] && printf \" ${G}${branch}${Y}${status}${R}\"; [ -n \"$remaining\" ] && printf \" ${M}ctx:${remaining}%%${R}\"; printf \" ${T}${model}${R} ${Y}${time}${R}\"; [ \"$todo_count\" -gt 0 ] && printf \" ${C}todos:${todo_count}${R}\"; echo", "description": "Custom status line showing: user:path branch* ctx:% model time todos:N" }, "_comments": { diff --git a/docs/ko-KR/rules/agents.md b/docs/ko-KR/rules/agents.md index c26b8c53..0f2b8fd1 100644 --- a/docs/ko-KR/rules/agents.md +++ b/docs/ko-KR/rules/agents.md @@ -13,6 +13,9 @@ | security-reviewer | 보안 분석 | 커밋 전 | | build-error-resolver | 빌드 에러 수정 | 빌드 실패 시 | | e2e-runner | E2E 테스팅 | 핵심 사용자 흐름 | +| database-reviewer | 데이터베이스 스키마/쿼리 리뷰 | 스키마 설계, 쿼리 최적화 | +| go-reviewer | Go 코드 리뷰 | Go 코드 작성 또는 수정 후 | +| go-build-resolver | Go 빌드 에러 수정 | `go build` 또는 `go vet` 실패 시 | | refactor-cleaner | 사용하지 않는 코드 정리 | 코드 유지보수 | | doc-updater | 문서 관리 | 문서 업데이트 | diff --git a/docs/ko-KR/rules/git-workflow.md b/docs/ko-KR/rules/git-workflow.md index f3efb92b..56cd5865 100644 --- a/docs/ko-KR/rules/git-workflow.md +++ b/docs/ko-KR/rules/git-workflow.md @@ -9,7 +9,7 @@ 타입: feat, fix, refactor, docs, test, chore, perf, ci -참고: 어트리뷰션은 ~/.claude/settings.json에서 전역적으로 비활성화되어 있습니다. +참고: 어트리뷰션 비활성화 여부는 각자의 `~/.claude/settings.json` 로컬 설정에 따라 달라질 수 있습니다. ## Pull Request 워크플로우 diff --git a/docs/ko-KR/skills/backend-patterns/SKILL.md b/docs/ko-KR/skills/backend-patterns/SKILL.md index 5118b328..b0f45a10 100644 --- a/docs/ko-KR/skills/backend-patterns/SKILL.md +++ b/docs/ko-KR/skills/backend-patterns/SKILL.md @@ -42,6 +42,7 @@ GET /api/markets?status=active&sort=volume&limit=20&offset=0 interface MarketRepository { findAll(filters?: MarketFilters): Promise findById(id: string): Promise + findByIds(ids: string[]): Promise create(data: CreateMarketDto): Promise update(id: string, data: UpdateMarketDto): Promise delete(id: string): Promise @@ -85,7 +86,7 @@ class MarketService { const markets = await this.marketRepo.findByIds(results.map(r => r.id)) // Sort by similarity - return markets.sort((a, b) => { + return [...markets].sort((a, b) => { const scoreA = results.find(r => r.id === a.id)?.score || 0 const scoreB = results.find(r => r.id === b.id)?.score || 0 return scoreA - scoreB @@ -320,7 +321,7 @@ async function fetchWithRetry( fn: () => Promise, maxRetries = 3 ): Promise { - let lastError: Error + let lastError: Error = new Error('Retry attempts exhausted') for (let i = 0; i < maxRetries; i++) { try { diff --git a/docs/ko-KR/skills/clickhouse-io/SKILL.md b/docs/ko-KR/skills/clickhouse-io/SKILL.md index 736667b8..02427247 100644 --- a/docs/ko-KR/skills/clickhouse-io/SKILL.md +++ b/docs/ko-KR/skills/clickhouse-io/SKILL.md @@ -51,7 +51,7 @@ SETTINGS index_granularity = 8192; ### ReplacingMergeTree (중복 제거) ```sql --- For data that may have duplicates (e.g., from multiple sources) +-- 중복이 있을 수 있는 데이터용 (예: 여러 소스에서 수집된 경우) CREATE TABLE user_events ( event_id String, user_id String, @@ -67,7 +67,7 @@ PRIMARY KEY (user_id, event_id); ### AggregatingMergeTree (사전 집계) ```sql --- For maintaining aggregated metrics +-- 집계 메트릭을 유지하기 위한 용도 CREATE TABLE market_stats_hourly ( hour DateTime, market_id String, @@ -78,7 +78,7 @@ CREATE TABLE market_stats_hourly ( PARTITION BY toYYYYMM(hour) ORDER BY (hour, market_id); --- Query aggregated data +-- 집계된 데이터 조회 SELECT hour, market_id, @@ -96,7 +96,7 @@ ORDER BY hour DESC; ### 효율적인 필터링 ```sql --- ✅ GOOD: Use indexed columns first +-- ✅ 좋음: 인덱스된 컬럼을 먼저 사용 SELECT * FROM markets_analytics WHERE date >= '2025-01-01' @@ -105,7 +105,7 @@ WHERE date >= '2025-01-01' ORDER BY date DESC LIMIT 100; --- ❌ BAD: Filter on non-indexed columns first +-- ❌ 나쁨: 비인덱스 컬럼을 먼저 필터링 SELECT * FROM markets_analytics WHERE volume > 1000 @@ -116,7 +116,7 @@ WHERE volume > 1000 ### 집계 ```sql --- ✅ GOOD: Use ClickHouse-specific aggregation functions +-- ✅ 좋음: ClickHouse 전용 집계 함수를 사용 SELECT toStartOfDay(created_at) AS day, market_id, @@ -129,7 +129,7 @@ WHERE created_at >= today() - INTERVAL 7 DAY GROUP BY day, market_id ORDER BY day DESC, total_volume DESC; --- ✅ Use quantile for percentiles (more efficient than percentile) +-- ✅ 백분위수에는 quantile 사용 (percentile보다 효율적) SELECT quantile(0.50)(trade_size) AS median, quantile(0.95)(trade_size) AS p95, @@ -141,7 +141,7 @@ WHERE created_at >= now() - INTERVAL 1 HOUR; ### 윈도우 함수 ```sql --- Calculate running totals +-- 누적 합계 계산 SELECT date, market_id, @@ -172,25 +172,22 @@ const clickhouse = new ClickHouse({ } }) -// ✅ Batch insert (efficient) +// ✅ 배치 삽입 (효율적) async function bulkInsertTrades(trades: Trade[]) { - const values = trades.map(trade => `( - '${trade.id}', - '${trade.market_id}', - '${trade.user_id}', - ${trade.amount}, - '${trade.timestamp.toISOString()}' - )`).join(',') + const rows = trades.map(trade => ({ + id: trade.id, + market_id: trade.market_id, + user_id: trade.user_id, + amount: trade.amount, + timestamp: trade.timestamp.toISOString() + })) - await clickhouse.query(` - INSERT INTO trades (id, market_id, user_id, amount, timestamp) - VALUES ${values} - `).toPromise() + await clickhouse.insert('trades', rows) } -// ❌ Individual inserts (slow) +// ❌ 개별 삽입 (느림) async function insertTrade(trade: Trade) { - // Don't do this in a loop! + // 루프 안에서 이렇게 하지 마세요! await clickhouse.query(` INSERT INTO trades VALUES ('${trade.id}', ...) `).toPromise() @@ -200,7 +197,7 @@ async function insertTrade(trade: Trade) { ### 스트리밍 삽입 ```typescript -// For continuous data ingestion +// 연속적인 데이터 수집용 import { createWriteStream } from 'fs' import { pipeline } from 'stream/promises' @@ -220,7 +217,7 @@ async function streamInserts() { ### 실시간 집계 ```sql --- Create materialized view for hourly stats +-- 시간별 통계를 위한 materialized view 생성 CREATE MATERIALIZED VIEW market_stats_hourly_mv TO market_stats_hourly AS SELECT @@ -232,7 +229,7 @@ AS SELECT FROM trades GROUP BY hour, market_id; --- Query the materialized view +-- materialized view 조회 SELECT hour, market_id, @@ -249,7 +246,7 @@ GROUP BY hour, market_id; ### 쿼리 성능 ```sql --- Check slow queries +-- 느린 쿼리 확인 SELECT query_id, user, @@ -269,7 +266,7 @@ LIMIT 10; ### 테이블 통계 ```sql --- Check table sizes +-- 테이블 크기 확인 SELECT database, table, @@ -287,7 +284,7 @@ ORDER BY sum(bytes) DESC; ### 시계열 분석 ```sql --- Daily active users +-- 일간 활성 사용자 SELECT toDate(timestamp) AS date, uniq(user_id) AS daily_active_users @@ -296,7 +293,7 @@ WHERE timestamp >= today() - INTERVAL 30 DAY GROUP BY date ORDER BY date; --- Retention analysis +-- 리텐션 분석 SELECT signup_date, countIf(days_since_signup = 0) AS day_0, @@ -319,7 +316,7 @@ ORDER BY signup_date DESC; ### 퍼널 분석 ```sql --- Conversion funnel +-- 전환 퍼널 SELECT countIf(step = 'viewed_market') AS viewed, countIf(step = 'clicked_trade') AS clicked, @@ -340,7 +337,7 @@ GROUP BY session_id; ### 코호트 분석 ```sql --- User cohorts by signup month +-- 가입 월별 사용자 코호트 SELECT toStartOfMonth(signup_date) AS cohort, toStartOfMonth(activity_date) AS month, @@ -362,12 +359,12 @@ ORDER BY cohort, months_since_signup; ### ETL 패턴 ```typescript -// Extract, Transform, Load +// 추출, 변환, 적재(ETL) async function etlPipeline() { - // 1. Extract from source + // 1. 소스에서 추출 const rawData = await extractFromPostgres() - // 2. Transform + // 2. 변환 const transformed = rawData.map(row => ({ date: new Date(row.created_at).toISOString().split('T')[0], market_id: row.market_slug, @@ -375,18 +372,29 @@ async function etlPipeline() { trades: parseInt(row.trade_count) })) - // 3. Load to ClickHouse + // 3. ClickHouse에 적재 await bulkInsertToClickHouse(transformed) } -// Run periodically -setInterval(etlPipeline, 60 * 60 * 1000) // Every hour +// 주기적으로 실행 +let etlRunning = false + +setInterval(async () => { + if (etlRunning) return + + etlRunning = true + try { + await etlPipeline() + } finally { + etlRunning = false + } +}, 60 * 60 * 1000) // Every hour ``` ### 변경 데이터 캡처 (CDC) ```typescript -// Listen to PostgreSQL changes and sync to ClickHouse +// PostgreSQL 변경을 수신하고 ClickHouse와 동기화 import { Client } from 'pg' const pgClient = new Client({ connectionString: process.env.DATABASE_URL }) diff --git a/docs/ko-KR/skills/coding-standards/SKILL.md b/docs/ko-KR/skills/coding-standards/SKILL.md index e3e708f8..1fbabe7d 100644 --- a/docs/ko-KR/skills/coding-standards/SKILL.md +++ b/docs/ko-KR/skills/coding-standards/SKILL.md @@ -397,7 +397,7 @@ import { useMemo, useCallback } from 'react' // ✅ GOOD: Memoize expensive computations const sortedMarkets = useMemo(() => { - return markets.sort((a, b) => b.volume - a.volume) + return [...markets].sort((a, b) => b.volume - a.volume) }, [markets]) // ✅ GOOD: Memoize callbacks diff --git a/docs/ko-KR/skills/continuous-learning/SKILL.md b/docs/ko-KR/skills/continuous-learning/SKILL.md index d279ec32..e5de6c3d 100644 --- a/docs/ko-KR/skills/continuous-learning/SKILL.md +++ b/docs/ko-KR/skills/continuous-learning/SKILL.md @@ -77,6 +77,35 @@ Claude Code 세션 종료 시 자동으로 평가하여 학습된 스킬로 저 } ``` +## 예시 + +### 자동 패턴 추출 설정 예시 + +```json +{ + "min_session_length": 10, + "extraction_threshold": "medium", + "auto_approve": false, + "learned_skills_path": "~/.claude/skills/learned/" +} +``` + +### Stop Hook 연결 예시 + +```json +{ + "hooks": { + "Stop": [{ + "matcher": "*", + "hooks": [{ + "type": "command", + "command": "~/.claude/skills/continuous-learning/evaluate-session.sh" + }] + }] + } +} +``` + ## Stop Hook을 사용하는 이유 - **경량**: 세션 종료 시 한 번만 실행 @@ -116,4 +145,4 @@ Homunculus v2는 더 정교한 접근법을 취합니다: 4. **도메인 태깅** - code-style, testing, git, debugging 등 5. **진화 경로** - 관련 본능을 스킬/명령어로 클러스터링 -자세한 사양은 `docs/continuous-learning-v2-spec.md`를 참조하세요. +자세한 사양은 [`continuous-learning-v2-spec.md`](../../../continuous-learning-v2-spec.md)를 참조하세요. diff --git a/docs/ko-KR/skills/frontend-patterns/SKILL.md b/docs/ko-KR/skills/frontend-patterns/SKILL.md index cf3cdd45..f384bc80 100644 --- a/docs/ko-KR/skills/frontend-patterns/SKILL.md +++ b/docs/ko-KR/skills/frontend-patterns/SKILL.md @@ -154,6 +154,8 @@ const [isOpen, toggleOpen] = useToggle() ### 비동기 데이터 페칭 Hook ```typescript +import { useCallback, useEffect, useRef, useState } from 'react' + interface UseQueryOptions { onSuccess?: (data: T) => void onError?: (error: Error) => void @@ -168,6 +170,14 @@ export function useQuery( const [data, setData] = useState(null) const [error, setError] = useState(null) const [loading, setLoading] = useState(false) + const successRef = useRef(options?.onSuccess) + const errorRef = useRef(options?.onError) + const enabled = options?.enabled !== false + + useEffect(() => { + successRef.current = options?.onSuccess + errorRef.current = options?.onError + }, [options?.onSuccess, options?.onError]) const refetch = useCallback(async () => { setLoading(true) @@ -176,21 +186,21 @@ export function useQuery( try { const result = await fetcher() setData(result) - options?.onSuccess?.(result) + successRef.current?.(result) } catch (err) { const error = err as Error setError(error) - options?.onError?.(error) + errorRef.current?.(error) } finally { setLoading(false) } - }, [fetcher, options]) + }, [fetcher]) useEffect(() => { - if (options?.enabled !== false) { + if (enabled) { refetch() } - }, [key, refetch, options?.enabled]) + }, [key, enabled, refetch]) return { data, error, loading, refetch } } @@ -296,7 +306,7 @@ export function useMarkets() { ```typescript // ✅ useMemo for expensive computations const sortedMarkets = useMemo(() => { - return markets.sort((a, b) => b.volume - a.volume) + return [...markets].sort((a, b) => b.volume - a.volume) }, [markets]) // ✅ useCallback for functions passed to children diff --git a/docs/ko-KR/skills/golang-patterns/SKILL.md b/docs/ko-KR/skills/golang-patterns/SKILL.md index 63548e87..ab830154 100644 --- a/docs/ko-KR/skills/golang-patterns/SKILL.md +++ b/docs/ko-KR/skills/golang-patterns/SKILL.md @@ -533,7 +533,8 @@ func ProcessRequest(data []byte) []byte { buf.Write(data) // Process... - return buf.Bytes() + out := append([]byte(nil), buf.Bytes()...) + return out } ``` diff --git a/docs/ko-KR/skills/security-review/SKILL.md b/docs/ko-KR/skills/security-review/SKILL.md index 6b6878cd..8dba9dbf 100644 --- a/docs/ko-KR/skills/security-review/SKILL.md +++ b/docs/ko-KR/skills/security-review/SKILL.md @@ -215,8 +215,8 @@ const securityHeaders = [ key: 'Content-Security-Policy', value: ` default-src 'self'; - script-src 'self' 'unsafe-eval' 'unsafe-inline'; - style-src 'self' 'unsafe-inline'; + script-src 'self' 'nonce-{nonce}'; + style-src 'self' 'nonce-{nonce}'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.example.com; @@ -225,6 +225,8 @@ const securityHeaders = [ ] ``` +`{nonce}`는 요청마다 새로 생성하고, 헤더와 인라인 `