From fb7b73a96292bf6a283a78ce801a7cc839798581 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Fri, 13 Mar 2026 00:17:54 -0700 Subject: [PATCH] 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}`는 요청마다 새로 생성하고, 헤더와 인라인 `