PR ํ์คํ ๋ฆฌ์์ ํ๊ท ํจํด์ ๊ฐ์งํ๊ณ , AI ์ฝ๋ฉ ์ด์์คํดํธ(Claude Code ๋ฑ)์ ๊ท์น์ ์๋์ผ๋ก ์ ์ํฉ๋๋ค.
AI-assisted development์์ ๊ฐ์ ์ค์๊ฐ ๋ฐ๋ณต๋๋ ์ด์ ๋ ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๊ฐ ํผ๋๋ฐฑ ๋ฃจํ์ ์ฐ๊ฒฐ๋์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค.
์ด ๋๊ตฌ๋ PR ํ์คํ ๋ฆฌ๋ฅผ ๋ถ์ํด ์ํํ ๋๋ฉ์ธ๊ณผ ํ๊ท ํด๋ฌ์คํฐ๋ฅผ ๊ฐ์งํ๊ณ , CLAUDE.md ๋๋ .github/copilot-instructions.md์ ์ถ๊ฐํ ๊ท์น์ ์๋ ์์ฑํฉ๋๋ค.
merged PR ํ์คํ ๋ฆฌ (์ ๋ชฉ๋ง, ๋น ๋ฆ)
โ
fix PR ํด๋ฌ์คํฐ ๊ฐ์ง (์ฐ์์ผ๋ก ๊ฐ์ ๋๋ฉ์ธ์์ ๋ฒ๊ทธ ๋ฐ์)
โ
ํด๋ฌ์คํฐ PR๋ง ์๋ fetch โ ํ์ผ ๊ฒฝ๋ก + diff + ๋ฆฌ๋ทฐ ์ฝ๋ฉํธ (smart-fetch)
โ
๊ณ ์ํ ๋๋ฉ์ธยทํ์ผ ์์ ์ฐ์ถ + ๋๋ฉ์ธ ์ฌ๋ถ๋ฅ
โ
ChromaDB์์ ์ ์ฌ diff ํจํด ๊ฒ์ (RAG)
โ
Claude๋ก ๋ฐ์ดํฐ ๊ธฐ๋ฐ CLAUDE.md ๊ท์น ์์ฑ
โ
PR ์ฝ๋ฉํธ or ์ฃผ๊ฐ ์ด์๋ก ์๋ ๊ฒ์ + rule_tracker๋ก ํจ๊ณผ ์ธก์
๊ธฐ์กด์๋ --fetch-files ํ๋๊ทธ๋ฅผ ์จ์ผ๋ง ํ์ผ ๊ฒฝ๋ก์ diff๋ฅผ ์์งํ์ต๋๋ค. ์ด์ ๋ ๊ธฐ๋ณธ ๋์์
๋๋ค.
| ๋ฐฉ์ | API ํธ์ถ (fix PR 50๊ฐ ๊ธฐ์ค) | diff ํฌํจ |
|---|---|---|
| ๊ธฐ์กด ๊ธฐ๋ณธ | 0ํ (์ ๋ชฉ๋ง) | โ |
--fetch-files |
~100ํ ์์ฐจ | โ |
| smart-fetch (ํ์ฌ ๊ธฐ๋ณธ) | ํด๋ฌ์คํฐ PR ร 1ํ ๋ณ๋ ฌ | โ |
ํด๋ฌ์คํฐ์ ์ํ PR๋ง fetchํ๋ฏ๋ก ์ ์ฒด fix PR์ ์ํํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋น ๋ฆ ๋๋ค.
์ด๊ธฐ ๋ฒ์ ์ "fix PR ๋น์จ ๋์ โ ci/cd ๊ท์น ์ถ๊ฐ" ๊ฐ์ ํ ํ๋ฆฟ ๊ธฐ๋ฐ ๊ท์น์ ์์ฑํ์ต๋๋ค. ์ด ๋ฐฉ์์ ํ๊ณ๋ ์ปจํ ์คํธ ๋ถ์ฌ์ ๋๋ค.
ํ์ฌ ์์คํ ์ ๋ ๊ฐ์ง ์ปจํ ์คํธ ์์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
1. ์ค์ diff ํจํด (์๋ก์ด ์ฃผ์ ์์ค)
[gwangcheon-shop] fix PR #58 (ci/cd): Dockerfile deps ์คํ
์ด์ง ์์
๋ณ๊ฒฝ ํ์ผ: Dockerfile
diff:
-COPY package.json pnpm-lock.yaml ./
+COPY package.json pnpm-lock.yaml .npmrc ./
RUN pnpm install --frozen-lockfile
โ ".npmrc๋ฅผ deps ์คํ
์ด์ง์ COPYํ์ง ์์ผ๋ฉด ์ฌ์ค ํจํค์ง ์ค์น ์คํจ"๋ผ๋ ๊ท์น์ด ๋์ต๋๋ค.
2. ์ํ๊ณ ํต๊ณ
8๊ฐ ์ธ์ด ร 30๊ฐ ์์ ๋ ํฌ = 240๊ฐ ๋ ํฌ ํฌ๋กค๋ง (๋งค์ฃผ ์๋)
โ ์ธ์ด/๋๋ฉ์ธ๋ณ ํ๊ท ํจํด โ ChromaDB ๋ฒกํฐ ์๋ฒ ๋ฉ
๊ท์น ์์ฑ ์ diff ํจํด์ด ๋จผ์ ๊ฒ์๋๊ณ , ์์ผ๋ฉด ํต๊ณ๋ก ๋ณด์ํฉ๋๋ค. ๋ฐ์ดํฐ๊ฐ ์์ผ์๋ก ๊ท์น ํ์ง์ด ์๋์ผ๋ก ํฅ์๋ฉ๋๋ค.
์ธ ๊ฐ์ GitHub Actions ์ํฌํ๋ก์ฐ๊ฐ ์๋์ผ๋ก ์คํ๋ฉ๋๋ค.
| ์ํฌํ๋ก์ฐ | ์ค์ผ์ค | ์ญํ |
|---|---|---|
evolve-rules.yml |
๋งค์ผ KST 10:00 | gwangcheon-shop PR ๋ถ์ โ ์ผ์ผ ์ด์ ๋ฆฌํฌํธ โ CLAUDE.md ํจ์น PR โ diff ํจํด ๋์ ์ ์ฅ โ ๊ท์น ํจ๊ณผ ์ธก์ |
learn-patterns.yml |
๋งค์ฃผ ์ผ์์ผ KST 11:00 | 8๊ฐ ์ธ์ด ร 30๊ฐ ๋ ํฌ ํฌ๋กค๋ง โ data/language-patterns.json ๋์ |
lint.yml |
push / PR | src/**/*.py ๋ฌธ๋ฒ ๊ฒ์ฌ + .github/workflows/*.yml YAML ๊ฒ์ฆ |
165๊ฐ PR ๋ถ์ ๊ฒฐ๊ณผ (Next.js + Prisma + Docker ์ปค๋จธ์ค ํ๋ก์ ํธ):
์ด PR: 165๊ฐ | fix PR: 26๊ฐ (15.8%)
๐ ํ๊ท ํด๋ฌ์คํฐ
[ci/cd ] #52 โ #53 โ #54 โ #56 โ #58 โ #59 โ #60 โ #61
[ci/cd ] #62 โ #66 โ #68 โ #78
[payment ] #140 โ #142 โ #150
[external-api] #308 โ #310
๐ก CLAUDE.md ์ถ๊ฐ ๊ท์น ์ ์
- Dockerfile deps ์คํ
์ด์ง์ .npmrc COPY ๋๋ฝ ์ ์ฌ์ค ํจํค์ง ์ค์น ์คํจ โ 4ํ ํ๊ท
- CI/CDยทDocker ๋ณ๊ฒฝ ์ verify-build.sh ์คํ ํ์ โ 11ํ ํ๊ท ๊ฐ์ง
- ๊ฒฐ์ ํ๋ก์ฐ ๋ณ๊ฒฝ ์ Opus ๋ชจ๋ธ๋ก ์ค๊ณ ๊ฒํ ํ ๊ตฌํ โ 4ํ ํ๊ท
- ์ธ๋ถ API(KakaoยทPortOne) ์ฐ๋์ ์คํ
์ด์ง ๋ฐฐํฌ ํ ๊ฒ์ฆ โ 3ํ ํ๊ท
๐ ai-dev-loop-analyzer.rladmsgh34.org
GitHub ๋ ํฌ URL์ ์ ๋ ฅํ๋ฉด ์ฆ์ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
- ๋จ์ผ ๋ถ์:
https://ai-dev-loop-analyzer.rladmsgh34.org/r/vercel/next.js - ๋ ํฌ ๋น๊ต:
https://ai-dev-loop-analyzer.rladmsgh34.org/compare - REST API:
https://ai-dev-loop-analyzer.rladmsgh34.org/api/analyze?owner=vercel&repo=next.js
[](https://ai-dev-loop-analyzer.rladmsgh34.org/r/owner/repo)fix์จ์ ๋ฐ๋ผ ์์์ด ์๋์ผ๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค (๋ น์ โค10% / ๋ ธ๋์ โค20% / ๋นจ๊ฐ์ >20%).
PR์ ํ๊ท ๋ค๋ฐ ๋๋ฉ์ธ ํ์ผ์ด ํฌํจ๋๋ฉด ์๋์ผ๋ก ๊ฒฝ๊ณ ์ฝ๋ฉํธ๋ฅผ ๋ฌ์์ฃผ๋ GitHub Action์ ๋๋ค.
# .github/workflows/risk-check.yml
name: PR Risk Check
on:
pull_request:
types: [opened, synchronize]
jobs:
risk-check:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: rladmsgh34/ai-dev-loop-analyzer/.github/actions/check-risk-zones@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
threshold: '10'Claude Code์ ์ฐ๊ฒฐํ๋ฉด ์ฝ๋ ์์ฑ ์์ ์ ์ค์๊ฐ์ผ๋ก ์ํ ๋๋ฉ์ธ์ ๊ฒฝ๊ณ ํฉ๋๋ค.
pip install mcp
# ๊ธฐ๋ณธ (๋ฒ์ฉ ํจํด)
claude mcp add ai-dev-loop-analyzer -- python3 /path/to/src/mcp_server.py
# ํ๋กํ์ผ ์ ์ฉ (ํ๋ก์ ํธ๋ณ ํค์๋ ์ฌ์ฉ)
claude mcp add ai-dev-loop-analyzer \
-e AI_DEV_LOOP_PROFILE=/path/to/my-project.json \
-- python3 /path/to/src/mcp_server.py๐ด Dockerfile
ํ๊ท ์ด๋ ฅ: 4ํ | ๋๋ฉ์ธ: ci/cd
โ verify-build.sh ์คํ ๊ถ์ฅ
๐ ๋ง์ง๋ง ์์ (fix: Dockerfile deps ์คํ
์ด์ง ์์ ):
```diff
-COPY package.json pnpm-lock.yaml ./
+COPY package.json pnpm-lock.yaml .npmrc ./
RUN pnpm install --frozen-lockfile
"4๋ฒ ํ๊ท๋์"์์ "์ด๋ฐ ํจํด์ด ๋ฌธ์ ์์"์ผ๋ก โ ๊ฐ์ ์ค์๋ฅผ ๋ฐ๋ณตํ์ง ์๋๋ก ๊ตฌ์ฒด์ ์ปจํ
์คํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
| ํด | ์ค๋ช
|
|----|------|
| `check_risk_zones` | ํธ์งํ๋ ค๋ ํ์ผ์ ํ๊ท ์ด๋ ฅ + ๋ง์ง๋ง fix diff ํ์ |
| `analyze_pr_history` | ๋ ํฌ ์ ์ฒด ๋ถ์ (smart-fetch ํฌํจ) |
| `get_active_rules` | ํ์ฌ ์ ์ฉ ์ค์ธ ๊ท์น + ํจ๊ณผ ์ธก์ ๊ฒฐ๊ณผ ์กฐํ |
---
## ๊ท์น ํจ๊ณผ ์ธก์
๊ท์น์ด CLAUDE.md์ ์ถ๊ฐ๋ ์์ ์ ๋๋ฉ์ธ๋ณ fix-rate๋ฅผ ๊ธฐ์ค์ ์ผ๋ก ์ ์ฅํ๊ณ , ์ดํ ๋งค์ผ ๋ณํ๋ฅผ ์ธก์ ํฉ๋๋ค.
- 2026-04-01 ์ถ๊ฐ | fix์จ 30.0% โ 15.0% โผ 15.0%p (๊ฐ์ , ๋๋ฉ์ธ(ci/cd) ๊ธฐ์ค) ๊ท์น: Dockerfile deps ์คํ ์ด์ง์ .npmrc COPY ํ์ โ 4ํ ํ๊ท
๊ท์น ์ถ๊ฐ 7์ผ ํ๋ถํฐ ์ธก์ ์ด ์์๋๋ฉฐ, ๋๋ฉ์ธ๋ณ fix-rate ๋ณํ๋ฅผ ์ฐ์ ์ฌ์ฉํฉ๋๋ค.
---
## ์ฌ์ฉ๋ฒ
### CLI
```bash
pip install -r requirements.txt # ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง, ์ถ๊ฐ ํจํค์ง ๋ถํ์
# ํ์ฌ ๋ ํฌ ๋ถ์ (smart-fetch ๊ธฐ๋ณธ ํฌํจ)
python3 src/analyze.py
# ์ ์ฒด fix PR ๋์ ์ ๋ฐ ๋ถ์ (๋๋ฆผ)
python3 src/analyze.py --fetch-files
# smart-fetch ๋นํ์ฑํ (์ ๋ชฉ๋ง ๋ถ์)
python3 src/analyze.py --no-smart-fetch
# JSON ์ถ๋ ฅ
python3 src/analyze.py --format json > report.json
# .github/workflows/pr-quality.yml
name: PR Quality Check
on:
pull_request:
types: [closed]
jobs:
analyze:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: rladmsgh34/ai-dev-loop-analyzer@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
limit: "200"
post-comment: "true"๋๋ฉ์ธ ํค์๋ยท๊ท์น ํ
ํ๋ฆฟยทfix PR ํจํด์ JSON ํ๋กํ์ผ๋ก ๊ต์ฒดํ ์ ์์ต๋๋ค.
์์ง ์ฝ๋๋ ์คํ ๋ฌด๊ด โ ํ๋ก์ ํธ๋ณ ์ค์ ์ ํ๋ผ์ด๋น ๋ ํฌ์๋ง ๋ก๋๋ค.
profiles/
โโโ default.json # ๋ฒ์ฉ ํ๋ฆฌ์
(๋นํธ์ธ)
โโโ examples/
โโโ nextjs-prisma.json # Next.js + Prisma ์คํ ํ๋ฆฌ์
ํ๋กํ์ผ ๊ตฌ์กฐ:
{
"name": "my-project",
"fix_pr_regex": "^(fix|hotfix|revert)",
"domain_patterns": [
["payment", "payment|stripe|checkout|order"],
["external-api", "stripe|sendgrid|s3"]
],
"rule_templates": {
"payment": "๊ฒฐ์ ํ๋ก์ฐ ๋ณ๊ฒฝ ์ ์ ์คํ ๊ฒํ ํ์ โ {count}ํ ํ๊ท"
},
"rule_hints": {
"payment": "โ ๊ฒฐ์ ๋๋ฉ์ธ: Stripe Webhook ๊ฒ์ฆ ๋๋ฝ ์ฌ๋ถ ํ์ธ"
},
"prompt_hints": {
"language": "en",
"ai_assistant": "Cursor",
"config_file": ".cursorrules"
}
}nextjs-prisma.json์ ์์์ ์ผ๋ก ๋ณต์ฌํด์ ํ๋ก์ ํธ๋ณ ์ธ๋ถ ์๋น์ค ํค์๋๋ฅผ ์ถ๊ฐํ์ธ์.
๊ธฐ๋ณธ ํ๋กํ์ผ(default.json) ๊ธฐ์ค. ํ๋กํ์ผ๋ก ์ ๋ถ ๊ต์ฒด ๊ฐ๋ฅํฉ๋๋ค.
| ๋๋ฉ์ธ | ๊ฐ์ง ํค์๋ (๊ธฐ๋ณธ) |
|---|---|
| ci/cd | deploy, docker, dockerfile, workflow, compose |
| auth | auth, login, session, jwt, credential |
| payment | payment, checkout, order, cart |
| database | migration, schema, migrate |
| security | csp, xss, csrf, rate-limit, content-security |
| external-api | stripe, twilio, sendgrid, s3, cloudfront, firebase |
| test/e2e | e2e, playwright, vitest, jest, coverage, flaky |
| config | .config., env.ts, tsconfig, biome, tailwind |
ํ๋ก์ ํธ๋ณ ํค์๋ (kakao, portone, gcs ๋ฑ)๋ ํ๋กํ์ผ ํ์ผ์๋ง ์ถ๊ฐํ์ธ์.
ํผ๋ธ๋ฆญ ๋ ํฌ ์ฝ๋์ ์๋น์ค๋ช ์ด ํ๋์ฝ๋ฉ๋์ง ์์ต๋๋ค.
๋๋ฉ์ธ ๋ถ๋ฅ๋ PR ์ ๋ชฉ๊ณผ ๋ณ๊ฒฝ ํ์ผ ๊ฒฝ๋ก๋ฅผ ํจ๊ป ๋ด ๋๋ค. smart-fetch๋ก ์์ง๋ ํ์ผ ๊ฒฝ๋ก๊ฐ ์์ผ๋ฉด ์ ๋ชฉ๋ง ๋ณผ ๋๋ณด๋ค ์ ํ๋๊ฐ ๋์์ง๋๋ค.
PR ์ ๋ชฉ์ ์๋ ํจํด์ด ํฌํจ๋๋ฉด fix PR๋ก ๋ถ๋ฅ๋ฉ๋๋ค.
"fix_pr_regex": "^(fix|hotfix|bugfix|revert)"fix / hotfix / revert / regression / bug ์์ / ๋ฒ๊ทธ (ํ๊ตญ์ด ์ง์)
---
## AI ๊ท์น ์์ฑ
๋ณ๋ API ํค ์์ด **GitHub Token๋ง์ผ๋ก** AI ๊ท์น ์์ฑ์ด ๊ฐ๋ฅํฉ๋๋ค.
| ๋ฐฉ์ | ํ์ํ ๊ฒ | ๋ชจ๋ธ |
|------|-----------|------|
| GitHub Models (๊ธฐ๋ณธ) | `GITHUB_TOKEN` โ Actions์์ ์๋ ์ ๊ณต | gpt-4o-mini |
| Anthropic API (์ ํ) | `ANTHROPIC_API_KEY` | claude-haiku |
| ํ
ํ๋ฆฟ ํด๋ฐฑ | ์์ | โ |
```bash
# GitHub Models (gh CLI ํ ํฐ ์๋ ์ฌ์ฉ)
python3 src/analyze.py
# Anthropic API
ANTHROPIC_API_KEY=sk-ant-... python3 src/analyze.py
# AI ์์ด ํ
ํ๋ฆฟ๋ง
python3 src/analyze.py --no-ai
- Python 3.10+
ghCLI (์ธ์ฆ๋ ์ํ)- ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง ์ฌ์ฉ (์ถ๊ฐ ํจํค์ง ๋ถํ์)
RAG ๊ธฐ๋ฅ ์ฌ์ฉ ์ ์ถ๊ฐ ์ค์น:
pip install chromadb sentence-transformersMIT