Claude로 PR 코드 리뷰를 자동화하면 어떻게 되나요?
GitHub Actions에 Claude API를 연결하면 PR이 열릴 때마다 자동으로 한국어 리뷰 코멘트를 작성할 수 있습니다. Haiku 4.5 모델을 사용하면 PR 1개당 평균 ₩60–100 수준이고, 월 1,000 PR 기준 약 ₩60,000–100,000 정도가 듭니다. system prompt에 "반드시 한국어로 답변" 한 줄만 명시하면 한국어 리뷰가 안정적으로 출력되며, 보안·성능·가독성 같은 1차 필터링은 90% 이상 자동화할 수 있어요. 다만 마이그레이션, 인증/결제, 권한 변경처럼 위험도가 높은 PR은 사람 시니어의 마지막 검토가 여전히 필수입니다. 자동화의 목표는 시니어 대체가 아니라 시니어가 봐야 할 부분만 남기는 것입니다.
왜 자동 코드 리뷰인가? (한국 스타트업 맥락)
한국 스타트업의 흔한 풍경입니다. 개발자 8명, 시니어는 2명. PR이 하루에 15–20개 쌓이는데 시니어는 본인 기능 개발도 해야 합니다. 결과적으로 PR 리뷰가 반나절–이틀씩 멈추는 병목이 발생합니다. 주니어는 "리뷰 기다리는 중" 상태로 다른 일을 하다 컨텍스트가 깨지고, 시니어는 저녁에 몰아서 리뷰하다 피로가 쌓이죠.
Claude 자동 리뷰는 이 문제의 80%를 해결합니다.
- 단순 실수 (변수명, null 체크 누락, console.log 잔존) → Haiku가 즉시 잡음
- 보안 패턴 위반 (하드코딩된 키, SQL injection 가능성) → 자동 경고
- 한국어 친화 코멘트 → 주니어가 부담 없이 수정
- 시니어는 아키텍처·비즈니스 로직 같은 진짜 리뷰가 필요한 부분에 집중
특히 한국 회사 문화에서 "시니어한테 자꾸 물어보기 미안해서" 막히는 경우가 많은데, AI 1차 리뷰는 이 심리적 장벽도 줄여줍니다.
GitHub Actions workflow YAML
.github/workflows/claude-review.yml 파일을 PR 트리거에 연결합니다.
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize]
paths-ignore:
- 'package-lock.json'
- 'yarn.lock'
- 'pnpm-lock.yaml'
- '**/*.md'
- 'docs/**'
permissions:
contents: read
pull-requests: write
issues: write
jobs:
review:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install @anthropic-ai/sdk@^0.40.0 @octokit/rest
- name: Run Claude review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
run: node .github/scripts/claude-review.mjs
paths-ignore로 lock 파일과 문서 변경을 제외했습니다. 이게 함정 #3을 막아주는 첫 번째 방어선이에요.
PR diff를 Claude에게 보내기 (octokit + tokenize)
.github/scripts/claude-review.mjs의 핵심 부분입니다.
import Anthropic from "@anthropic-ai/sdk";
import { Octokit } from "@octokit/rest";
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const owner = process.env.REPO_OWNER!;
const repo = process.env.REPO_NAME!;
const pull_number = Number(process.env.PR_NUMBER);
// 1. PR diff 가져오기
const { data: diff } = await octokit.rest.pulls.get({
owner, repo, pull_number,
mediaType: { format: "diff" },
});
const diffText = diff as unknown as string;
// 2. 비밀키 패턴 사전 차단 (함정 #2)
const SECRET_PATTERNS = [
/sk-ant-[a-zA-Z0-9_-]{20,}/, // Anthropic
/AKIA[0-9A-Z]{16}/, // AWS access key
/AIza[0-9A-Za-z_-]{35}/, // Google API
/-----BEGIN (RSA |EC )?PRIVATE KEY/, // 개인키
];
if (SECRET_PATTERNS.some((re) => re.test(diffText))) {
await octokit.rest.issues.createComment({
owner, repo, issue_number: pull_number,
body: "비밀키로 보이는 문자열이 감지되어 자동 리뷰를 중단했습니다. 즉시 키를 폐기하고 git 히스토리에서 제거해주세요.",
});
process.exit(0);
}
// 3. 너무 큰 diff는 청크 분할 (함정 #1)
const MAX_CHARS = 60_000; // 약 15K 토큰 input
const chunks = diffText.length > MAX_CHARS
? splitByFile(diffText, MAX_CHARS)
: [diffText];
// 4. Claude 호출 (Haiku 1차)
const reviews: string[] = [];
for (const chunk of chunks) {
const msg = await anthropic.messages.create({
model: "claude-haiku-4-5",
max_tokens: 1500,
system: KOREAN_REVIEW_SYSTEM,
messages: [{ role: "user", content: `다음 PR diff를 리뷰해주세요:\n\n${chunk}` }],
});
reviews.push(msg.content[0].type === "text" ? msg.content[0].text : "");
}
// 5. 결과를 PR 코멘트로 게시
await octokit.rest.issues.createComment({
owner, repo, issue_number: pull_number,
body: `## Claude 자동 코드 리뷰\n\n${reviews.join("\n\n---\n\n")}\n\n_Haiku 4.5 / 1차 자동 리뷰. 머지 전 시니어 확인 필요._`,
});
function splitByFile(diff: string, maxChars: number): string[] {
const files = diff.split(/^diff --git /m).filter(Boolean);
const out: string[] = [];
let buf = "";
for (const f of files) {
const piece = "diff --git " + f;
if ((buf + piece).length > maxChars && buf) { out.push(buf); buf = piece; }
else buf += piece;
}
if (buf) out.push(buf);
return out;
}
한국어 리뷰 system prompt
이 프롬프트가 사실상 봇의 인격을 결정합니다. 한국어 강제, 출력 포맷, 회사 문화까지 모두 여기서 정의합니다.
const KOREAN_REVIEW_SYSTEM = `
당신은 한국 스타트업의 시니어 백엔드 개발자입니다. PR diff를 검토하고 한국어로 코드 리뷰를 작성합니다.
## 출력 규칙 (절대 준수)
1. **반드시 한국어로 답변하세요.** 영어 혼용 금지. 단, 코드·라이브러리·함수명 같은 기술 용어는 그대로 영문 사용.
2. 친근한 존댓말 사용 ("~하면 좋을 것 같아요", "~을 권장합니다").
3. 비난하지 말고 제안하세요. "왜 이렇게 짰어요?"가 아니라 "이 부분은 ~하면 더 안전할 것 같아요".
4. 칭찬할 부분이 있으면 먼저 칭찬하세요 (한국 회사 문화 고려).
## 리뷰 우선순위
1. 보안: 하드코딩된 시크릿, SQL/XSS injection, 인증 우회
2. 버그: null/undefined, race condition, 트랜잭션 누락
3. 성능: N+1 쿼리, 불필요한 await, 큰 페이로드
4. 가독성: 변수명, 함수 분리, 중복 제거
## 출력 포맷
\`\`\`
**요약**: 한 줄
**칭찬할 부분** (있다면)
- ...
**수정 권장 (Critical)**
- 파일:줄번호 — 문제 + 제안 코드
**수정 권장 (Minor)**
- 파일:줄번호 — 문제 + 제안
**질문 / 확인 필요**
- ...
\`\`\`
문제가 없으면 "큰 이슈 발견하지 못했습니다. LGTM" 으로 답하세요.
`;
더 깊은 비용 절감 패턴은 Cost Optimization Masterclass ($59)에서 다룹니다. 캐싱·배치·청크 분할로 PR당 ₩60을 ₩20까지 떨어뜨리는 실전 사례 포함입니다.
모델 선택: Haiku 4.5 vs Sonnet 4.6
이건 정답이 없는 트레이드오프입니다.
- Haiku 4.5: PR당 평균 5초, 단순 실수와 패턴 위반은 거의 다 잡습니다. 정확도 체감 80% 수준. 비용 ₩60/PR.
- Sonnet 4.6: PR당 평균 15초, 비즈니스 로직과 아키텍처 영향까지 추론합니다. 정확도 92%+. 비용 ₩300/PR.
권장 패턴: Hybrid
const HIGH_RISK_PATHS = [
/\/auth\//, /\/payment/, /\/billing/,
/migrations?\//, /schema\.(prisma|sql)/,
/\.env/, /security/,
];
const isHighRisk = HIGH_RISK_PATHS.some((re) =>
diffText.split("\n").some((l) => l.startsWith("+++") && re.test(l))
);
const model = isHighRisk ? "claude-sonnet-4-6" : "claude-haiku-4-5";
전체 PR의 10% 정도만 Sonnet으로 가도 평균 비용이 크게 안 오릅니다.
비용 분석 (월 1,000 PR 기준)
| 모델 | PR당 평균 토큰 | PR당 비용 | 월 비용 |
|---|---|---|---|
| Haiku 4.5 | 입력 4K + 출력 600 | ₩60 | ₩60,000 |
| Sonnet 4.6 | 입력 4K + 출력 600 | ₩300 | ₩300,000 |
| Hybrid (90% Haiku) | 평균 | ₩84 | ₩84,000 |
월 ₩84,000은 시니어 1명의 점심값 수준입니다. 시니어가 PR 리뷰에 쓰던 시간을 1주일에 2시간만 줄여도 ROI가 나옵니다.
추가로 prompt caching을 system prompt에 적용하면 입력 비용을 추가로 50–80% 줄일 수 있습니다 — 자세한 패턴은 Claude prompt caching guide 참고하세요.
흔한 함정 5가지
- PR diff가 너무 큰 경우 (>20K 라인) — 자동 마이그레이션이나 대규모 리팩터에서 발생. 위 코드의
splitByFile로 파일 단위 청크 분할 필수. 200K context 모델이라도 한 번에 다 넣으면 출력이 산만해집니다. - 비밀번호/API 키 노출된 PR — Claude API에 키가 그대로 전송되면 로그에 남을 수 있습니다.
SECRET_PATTERNS사전 필터로 차단하고 사람에게 알림. - 대량 PR (예: package-lock.json 변경 5만 줄) — workflow의
paths-ignore로 lock 파일·자동 생성 파일 제외. 변경 라인 수가 5,000 넘어가면 자동 skip 규칙도 추가하세요. - 한국어 출력이 영어로 섞여 나옴 — system prompt 첫 줄에 "반드시 한국어로 답변하세요. 영어 혼용 금지." 명시. 그래도 가끔 섞이면 출력을 받은 뒤 "If the response contains English sentences, regenerate in Korean" 식 후처리 단계 추가.
- Rate limit (분당 50 RPM Tier 1) — 큰 organization에서 PR이 동시에 10개 열리면 큐에 대기. Tier 2+ 업그레이드(월 $40 결제 후 자동) 또는 큐 워커로 직렬 처리. 주니어 onboarding 주간엔 특히 주의하세요.
비교: 사람 vs AI 리뷰
자동 리뷰의 한계도 솔직히 알아야 합니다. AI 코드 리뷰 vs 사람 비교에서 자세히 다루지만, 핵심만 요약하면:
- AI는 패턴 위반을 거의 다 잡지만, 이 코드가 비즈니스 요구사항과 맞는지는 모릅니다.
- AI는 한 PR 안의 일관성은 보지만, 3개월 전 디자인 결정과의 정합성은 못 봅니다.
- AI는 부드럽게 말하는 능력은 사람보다 낫습니다. 주니어가 받는 심리적 부담이 적어요.
따라서 자동 리뷰의 올바른 위치는 "머지 전 통과해야 하는 1차 필터", 사람 시니어는 **"아키텍처/요구사항 정합성 최종 승인"**입니다.
영어로 더 체계적인 가이드는 automated code review with Claude를 참고하세요.
자주 묻는 질문
코드 리뷰가 정확한가요?
Haiku 4.5 기준으로 단순 버그·보안 패턴 위반은 80% 이상 잡습니다. 단, 거짓 양성(false positive)이 10–15% 정도 나옵니다. 예를 들어 의도적으로 비워둔 catch 블록을 "에러를 무시하고 있어요"라고 지적하는 식이죠. 그래서 "리뷰가 막는다"가 아니라 "리뷰가 코멘트한다"는 위치가 맞습니다. PR을 자동 reject하지 마세요.
사람 리뷰어를 대체할 수 있나요?
대체가 아니라 재배치입니다. 시니어가 변수명 지적에 쓰던 시간을 아키텍처·요구사항 정합성에 쓰게 됩니다. 한국 스타트업 기준으로 시니어 1명당 주 5–10시간을 회수할 수 있어요. 다만 마이그레이션·결제·인증 변경은 사람 리뷰가 여전히 필수입니다.
보안 코드도 안전한가요? (회사 코드를 Claude에게 보내도 되는가)
Anthropic은 API 입력을 학습에 사용하지 않습니다 (consumer 제품과 다름). 다만 30일 정책 위반 모니터링 보관은 있습니다. 민감도가 높다면 (1) 비밀키 패턴 사전 차단, (2) 보안 관련 디렉터리(/auth, /payment)는 자동 리뷰 제외, (3) 별도 self-hosted LLM 검토를 권장합니다. 일반 비즈니스 로직은 대부분 안전합니다.
비용을 더 줄이는 방법은?
세 가지가 효과적입니다. (1) prompt caching — system prompt를 캐시 처리하면 입력 비용 50–80% 절감. (2) 파일 필터 — 테스트·문서·자동 생성 파일 제외. (3) 변경 라인 수 기반 sampling — 5줄 미만 PR은 자동 skip. 자세한 한국어 패턴은 Claude API 비용 절감 가이드 참고하세요.
한국어 출력 품질은 어떤가요?
Claude는 한국어 출력 품질이 매우 안정적입니다 (GPT보다 자연스럽다는 평이 많아요). 존댓말·반말 톤 조절도 system prompt로 정확히 제어됩니다. 다만 "이 부분은 N+1 쿼리가 발생합니다" 같은 기술 용어는 영문 그대로 두는 게 코드 검색·이해에 더 유리합니다. 100% 순한국어를 강제하지 마세요.
자동 코드 리뷰는 한국 스타트업의 시니어 부족 문제를 정면으로 푸는 가장 실전적인 Claude API 활용처입니다. 월 ₩84,000으로 시니어 5–10시간을 회수할 수 있다면, 이건 거의 모든 팀에 ROI가 나옵니다. 오늘 .github/workflows/claude-review.yml 한 파일부터 시작해보세요.
1872단어 작성