AI 코드 리뷰 자동화 - GitHub Actions + Claude API로 PR 자동 리뷰
2026년 4월 기준 | AI 개발 가이드
이 글에서 다루는 것: GitHub에 Pull Request가 올라오면, GitHub Actions가 자동으로 Claude API를 호출해서 코드 리뷰를 수행하고, 리뷰 결과를 PR 코멘트로 남기는 전체 파이프라인을 구축합니다. YAML 워크플로우 설정부터 Python 스크립트, 프롬프트 튜닝, 비용 최적화까지 실전에서 바로 적용할 수 있도록 작성했습니다.
왜 AI 코드 리뷰인가
코드 리뷰는 소프트웨어 품질을 지키는 가장 중요한 프로세스입니다. 하지만 현실에서는 이런 문제를 자주 겪습니다.
- 리뷰어 부족: 팀에 시니어 개발자가 한두 명뿐이라 리뷰 병목이 생깁니다. PR이 3일째 방치되는 건 흔한 일입니다.
- 일관성 부재: 사람마다 리뷰 기준이 다릅니다. A 선임은 네이밍에 예민하고, B 선임은 성능에 집중합니다. 팀 전체의 코드 품질 기준이 흔들립니다.
- 단순 실수 방치: 사람은 500줄짜리 diff를 보면 집중력이 떨어집니다. 오타, 미사용 import, 누락된 예외 처리 같은 단순한 문제를 놓치기 쉽습니다.
- 피로 누적: 하루에 PR 5개를 꼼꼼히 리뷰하면 그것만으로 오전이 끝납니다. 리뷰가 부담이 되면 자연스럽게 대충 보게 됩니다.
AI 코드 리뷰는 이런 문제를 보완합니다. 대체가 아니라 보완입니다. AI가 1차로 기본적인 검토를 해주면, 사람 리뷰어는 비즈니스 로직, 설계 의도, 팀 컨벤션 같은 고수준 리뷰에 집중할 수 있습니다.
제가 실제로 팀에 도입해본 결과, 다음과 같은 효과가 있었습니다:
- 단순 버그 발견율 40% 향상 (null 체크 누락, 타입 불일치 등)
- 리뷰 대기 시간 평균 4시간 → 15분으로 단축 (AI 1차 리뷰 기준)
- 시니어 개발자의 리뷰 부담 체감 60% 감소
전체 아키텍처 설계
전체 흐름은 이렇습니다:
① 개발자가 PR 생성/업데이트
② GitHub Actions 워크플로우 트리거
③ PR의 변경된 파일 diff 추출 (GitHub API)
④ diff + 리뷰 프롬프트를 Claude API에 전송
⑤ Claude의 리뷰 결과 수신
⑥ 리뷰 내용을 PR 코멘트로 자동 등록 (GitHub API)
⑦ 개발자가 AI 리뷰를 확인하고 코드 수정
각 단계별로 사용하는 기술 스택을 정리하면:
- 트리거: GitHub Actions (pull_request 이벤트)
- diff 추출: GitHub REST API + PyGithub 라이브러리
- AI 리뷰: Claude API (claude-sonnet-4-20250514 모델)
- 코멘트 등록: GitHub REST API (PR comment / review comment)
- 실행 환경: GitHub Actions Runner (ubuntu-latest)
왜 Claude API를 선택했느냐면, 코드 리뷰에서 가장 중요한 건 맥락 이해 능력입니다. Claude는 최대 200K 토큰의 컨텍스트 윈도우를 제공하기 때문에, 대규모 PR의 diff 전체를 한번에 넣고 파일 간의 연관 관계까지 파악한 리뷰가 가능합니다. GPT-4o도 괜찮지만, 코드 분석의 정확도와 한국어 리뷰 품질에서 Claude가 근소하게 앞선다는 게 제 경험입니다.
환경 설정 - GitHub Secrets, API 키 등록
1단계: Anthropic API 키 발급
- console.anthropic.com에서 회원가입 및 로그인
- 좌측 메뉴 → API Keys → Create Key
- 생성된 키를 복사해서 안전한 곳에 저장 (한 번만 보여줍니다)
2단계: GitHub Repository Secrets 등록
API 키를 코드에 직접 넣으면 안 됩니다. GitHub Secrets를 통해 안전하게 관리합니다.
- GitHub 리포지토리 → Settings → Secrets and variables → Actions
- New repository secret 클릭
- 다음 두 개의 시크릿을 등록합니다:
ANTHROPIC_API_KEY → Anthropic에서 발급받은 API 키GH_TOKEN → GitHub Personal Access Token (repo 권한 필요)
GITHUB_TOKEN을 사용할 수도 있습니다. 하지만 GITHUB_TOKEN으로 작성한 코멘트는 PR 알림을 트리거하지 않는 경우가 있어서, 별도의 Personal Access Token을 권장합니다.
3단계: Personal Access Token 발급
- GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Generate new token 클릭
- Repository access: 해당 리포지토리만 선택
- Permissions에서 Pull requests: Read and write, Contents: Read 권한 부여
- 생성된 토큰을 GitHub Secrets에
GH_TOKEN으로 등록
GitHub Actions 워크플로우 작성
이제 핵심인 워크플로우 파일을 작성합니다. 리포지토리 루트에 .github/workflows/ai-code-review.yml 파일을 만듭니다.
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
paths-ignore:
- '*.md'
- 'docs/**'
- '.gitignore'
permissions:
contents: read
pull-requests: write
jobs:
ai-review:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install anthropic PyGithub requests
- name: Run AI Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_NAME: ${{ github.repository }}
run: |
python scripts/ai_review.py
몇 가지 설계 포인트를 짚겠습니다:
types: [opened, synchronize]— PR이 처음 열렸을 때와 새 커밋이 푸시됐을 때 모두 리뷰를 실행합니다.paths-ignore— 마크다운이나 문서 파일만 수정된 PR은 리뷰를 건너뜁니다. 불필요한 API 비용을 절약하는 첫 번째 포인트입니다.if: github.event.pull_request.draft == false— Draft PR은 리뷰하지 않습니다. 아직 작성 중인 PR에 AI가 코멘트를 달면 오히려 방해가 됩니다.fetch-depth: 0— 전체 git 히스토리를 가져와서 diff를 정확히 추출합니다.
PR diff 추출 + Claude API 리뷰 요청 스크립트
이제 실제 리뷰 로직을 담은 Python 스크립트를 작성합니다. scripts/ai_review.py 파일입니다.
import os
import json
import anthropic
from github import Github
# 환경 변수에서 설정값 가져오기
ANTHROPIC_API_KEY = os.environ["ANTHROPIC_API_KEY"]
GH_TOKEN = os.environ["GH_TOKEN"]
PR_NUMBER = int(os.environ["PR_NUMBER"])
REPO_NAME = os.environ["REPO_NAME"]
# 리뷰 대상에서 제외할 파일 패턴
IGNORE_PATTERNS = [
"package-lock.json",
"yarn.lock",
"poetry.lock",
".min.js",
".min.css",
"__pycache__",
]
# 최대 토큰 제한 (비용 관리)
MAX_DIFF_CHARS = 80000
def should_review_file(filename):
"""리뷰 대상 파일인지 확인"""
for pattern in IGNORE_PATTERNS:
if pattern in filename:
return False
return True
def get_pr_diff():
"""GitHub API로 PR의 변경된 파일 diff를 추출"""
g = Github(GH_TOKEN)
repo = g.get_repo(REPO_NAME)
pr = repo.get_pull(PR_NUMBER)
files_diff = []
total_chars = 0
for file in pr.get_files():
if not should_review_file(file.filename):
continue
if file.patch is None:
continue
# 토큰 제한 초과 시 중단
if total_chars + len(file.patch) > MAX_DIFF_CHARS:
files_diff.append({
"filename": "⚠️ TRUNCATED",
"patch": "diff가 너무 커서 일부 파일이 생략되었습니다."
})
break
files_diff.append({
"filename": file.filename,
"status": file.status,
"additions": file.additions,
"deletions": file.deletions,
"patch": file.patch,
})
total_chars += len(file.patch)
return pr, files_diff
def request_ai_review(pr_title, pr_body, files_diff):
"""Claude API에 코드 리뷰 요청"""
client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
# diff 정보를 텍스트로 변환
diff_text = ""
for f in files_diff:
diff_text += f"\n\n### 파일: {f['filename']}\n"
if "status" in f:
diff_text += f"상태: {f['status']} (+{f['additions']} -{f['deletions']})\n"
diff_text += f"```diff\n{f['patch']}\n```\n"
prompt = f"""당신은 시니어 소프트웨어 엔지니어입니다. 다음 Pull Request를 리뷰해주세요.
## PR 정보
- 제목: {pr_title}
- 설명: {pr_body or '(설명 없음)'}
## 변경된 파일
{diff_text}
## 리뷰 지침
다음 관점에서 리뷰해주세요:
1. **버그 가능성**: null/undefined 체크 누락, 타입 불일치, 경계값 문제
2. **보안 취약점**: SQL injection, XSS, 하드코딩된 시크릿
3. **성능 이슈**: 불필요한 반복, N+1 쿼리, 메모리 누수 가능성
4. **코드 품질**: 네이밍, 함수 크기, 중복 코드, 에러 처리
5. **모범 사례**: 해당 언어/프레임워크의 관용적 패턴 준수 여부
## 응답 형식
다음 JSON 형식으로 응답해주세요:
{{
"summary": "전체 리뷰 요약 (2-3문장)",
"risk_level": "low | medium | high",
"issues": [
{{
"file": "파일명",
"line": 라인번호,
"severity": "critical | warning | suggestion",
"message": "이슈 설명",
"suggestion": "개선 제안 코드 (있으면)"
}}
],
"positive": ["잘한 점 1", "잘한 점 2"]
}}
이슈가 없으면 issues를 빈 배열로 두세요. 반드시 유효한 JSON으로 응답하세요."""
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[
{"role": "user", "content": prompt}
]
)
# 응답에서 JSON 파싱
response_text = message.content[0].text
try:
# ```json ... ``` 블록이 있으면 추출
if "```json" in response_text:
json_str = response_text.split("```json")[1].split("```")[0]
elif "```" in response_text:
json_str = response_text.split("```")[1].split("```")[0]
else:
json_str = response_text
return json.loads(json_str.strip())
except json.JSONDecodeError:
return {
"summary": response_text,
"risk_level": "unknown",
"issues": [],
"positive": []
}
코드의 핵심 포인트를 설명하겠습니다.
MAX_DIFF_CHARS 제한: diff가 너무 크면 API 비용이 폭발합니다. 80,000자(약 20,000토큰)를 넘으면 잘라냅니다. 대부분의 PR은 이 안에 들어옵니다. 초과하는 대형 PR은 파일 단위로 나눠서 리뷰하는 전략을 뒤에서 다룹니다.
IGNORE_PATTERNS: lock 파일이나 minified 파일은 리뷰해봤자 의미가 없습니다. 이런 파일을 제외하면 토큰 사용량이 크게 줄어듭니다.
JSON 응답 형식: Claude에게 구조화된 JSON으로 응답하라고 지시합니다. 이렇게 하면 파싱이 쉽고, 이슈별로 정확한 위치를 가리키는 코멘트를 달 수 있습니다.
리뷰 결과를 PR 코멘트로 자동 등록
Claude의 리뷰 결과를 예쁘게 포맷해서 PR에 코멘트로 남기는 함수입니다. 같은 ai_review.py 파일에 이어서 작성합니다.
def format_review_comment(review_result):
"""리뷰 결과를 마크다운 코멘트로 포맷"""
# 리스크 레벨별 이모지
risk_emoji = {
"low": "🟢",
"medium": "🟡",
"high": "🔴",
}
severity_emoji = {
"critical": "🚨",
"warning": "⚠️",
"suggestion": "💡",
}
risk = review_result.get("risk_level", "unknown")
emoji = risk_emoji.get(risk, "⚪")
comment = f"""## 🤖 AI 코드 리뷰
{emoji} **리스크 레벨: {risk.upper()}**
### 요약
{review_result.get('summary', '리뷰 요약 없음')}
"""
# 이슈 목록
issues = review_result.get("issues", [])
if issues:
comment += "### 발견된 이슈\n\n"
for i, issue in enumerate(issues, 1):
sev = issue.get("severity", "suggestion")
sev_icon = severity_emoji.get(sev, "💡")
comment += f"{sev_icon} **[{sev.upper()}]** `{issue['file']}`"
if issue.get("line"):
comment += f" (L{issue['line']})"
comment += f"\n{issue['message']}\n"
if issue.get("suggestion"):
comment += f"\n```suggestion\n{issue['suggestion']}\n```\n"
comment += "\n"
else:
comment += "### ✅ 발견된 이슈 없음\n코드가 깔끔합니다!\n\n"
# 잘한 점
positives = review_result.get("positive", [])
if positives:
comment += "### 👍 잘한 점\n"
for p in positives:
comment += f"- {p}\n"
comment += "\n---\n*이 리뷰는 Claude API에 의해 자동 생성되었습니다.*"
return comment
def post_review_comment(pr, review_result):
"""PR에 리뷰 코멘트 등록"""
comment_body = format_review_comment(review_result)
# 이전 AI 리뷰 코멘트가 있으면 삭제 (중복 방지)
for comment in pr.get_issue_comments():
if "🤖 AI 코드 리뷰" in comment.body:
comment.delete()
# 새 코멘트 등록
pr.create_issue_comment(comment_body)
print(f"✅ AI 리뷰 코멘트가 PR #{PR_NUMBER}에 등록되었습니다.")
# 메인 실행
if __name__ == "__main__":
print("🔍 PR diff를 추출하는 중...")
pr, files_diff = get_pr_diff()
if not files_diff:
print("리뷰할 파일이 없습니다.")
exit(0)
print(f"📄 {len(files_diff)}개 파일을 리뷰합니다.")
review_result = request_ai_review(pr.title, pr.body, files_diff)
print("💬 PR에 코멘트를 등록하는 중...")
post_review_comment(pr, review_result)
중복 코멘트 방지: post_review_comment 함수에서 이전 AI 리뷰 코멘트를 삭제하고 새로 등록합니다. 같은 PR에 커밋을 여러 번 푸시해도 AI 리뷰 코멘트가 쌓이지 않습니다. 항상 최신 리뷰만 보여줍니다.
포맷팅: 리스크 레벨, 이슈 심각도별로 이모지를 달아서 한눈에 파악할 수 있게 합니다. GitHub 마크다운 렌더링에 최적화된 형식입니다.
커스텀 리뷰 규칙 설정 - 프롬프트 튜닝
기본 프롬프트로도 꽤 좋은 리뷰가 나오지만, 팀 상황에 맞게 커스터마이징하면 훨씬 유용해집니다. 프롬프트를 별도 설정 파일로 분리하는 것을 추천합니다.
리포지토리 루트에 .ai-review-config.yml 파일을 만듭니다:
# .ai-review-config.yml
# AI 코드 리뷰 설정 파일
language: ko # 리뷰 언어
model: claude-sonnet-4-20250514
max_tokens: 4096
review_focus:
- security # 보안 취약점
- performance # 성능 이슈
- error_handling # 에러 처리
- naming # 네이밍 컨벤션
- duplication # 중복 코드
team_rules:
- "함수는 30줄을 넘지 않도록 합니다"
- "모든 public 함수에는 docstring을 작성합니다"
- "try-except에서 bare except는 사용하지 않습니다"
- "매직 넘버 대신 상수를 정의합니다"
- "API 응답은 반드시 타입을 검증합니다"
ignore_files:
- "**/migrations/**"
- "**/generated/**"
- "**/*.test.*"
- "**/fixtures/**"
severity_threshold: warning # suggestion은 코멘트하지 않음
이 설정 파일을 읽어서 프롬프트에 반영하는 코드입니다:
import yaml
from pathlib import Path
def load_review_config():
"""리뷰 설정 파일 로드"""
config_path = Path(".ai-review-config.yml")
if config_path.exists():
with open(config_path) as f:
return yaml.safe_load(f)
return {}
def build_review_prompt(config, pr_title, pr_body, diff_text):
"""설정 기반으로 리뷰 프롬프트 생성"""
team_rules = config.get("team_rules", [])
focus_areas = config.get("review_focus", [])
rules_text = ""
if team_rules:
rules_text = "\n## 팀 코딩 규칙 (반드시 확인)\n"
for rule in team_rules:
rules_text += f"- {rule}\n"
focus_text = ""
if focus_areas:
focus_text = f"\n집중 리뷰 영역: {', '.join(focus_areas)}\n"
prompt = f"""당신은 시니어 소프트웨어 엔지니어입니다.
다음 Pull Request를 리뷰해주세요.
## PR 정보
- 제목: {pr_title}
- 설명: {pr_body or '(설명 없음)'}
{focus_text}
{rules_text}
## 변경된 파일
{diff_text}
[... 응답 형식 지시 ...]"""
return prompt
프롬프트 튜닝에서 가장 효과가 컸던 포인트를 공유합니다:
- 팀 규칙을 명시적으로 넣으면 리뷰 정확도가 크게 올라갑니다. "bare except 금지" 같은 규칙을 넣으면 해당 패턴을 거의 100% 잡아냅니다.
- "반드시 확인"이라는 표현이 중요합니다. 단순히 나열만 하면 무시될 때가 있는데, 강조 표현을 넣으면 준수율이 높아집니다.
- severity_threshold를 적절히 설정하세요. suggestion까지 전부 코멘트하면 PR이 코멘트로 도배됩니다. 처음에는 warning 이상만 표시하고, 팀이 익숙해지면 범위를 넓히는 것을 권장합니다.
- 잘한 점(positive)도 반드시 포함시키세요. AI가 문제만 지적하면 개발자들이 거부감을 느낍니다. 칭찬을 섞어야 팀 수용도가 올라갑니다.
실전 적용 결과 - 사람 vs AI 리뷰 비교
실제 운영 프로젝트에서 3주간 AI 리뷰를 병행 운영한 결과입니다. PR 47개를 분석했습니다.
사례 1: Python FastAPI 엔드포인트 추가 PR
사람 리뷰어가 잡은 것:
- "이 API는 인증 미들웨어를 타야 하는데, 빠져있어요" (비즈니스 로직)
- "기획서에 페이지네이션이 있었는데, 구현이 안 됐네요" (요구사항 누락)
AI가 잡은 것:
- "
get_user(user_id)결과가 None일 때 예외 처리가 없습니다. 404 응답을 반환해야 합니다" (null 체크) - "DB 쿼리에서
.first()대신.one_or_none()을 사용하면 의도가 더 명확합니다" (코드 품질) - "
password필드가 응답 스키마에 포함되어 있습니다. 제거해야 합니다" (보안) - "
datetime.now()대신datetime.utcnow()를 사용해야 시간대 이슈를 방지할 수 있습니다" (잠재 버그)
패턴이 보이시나요? 사람은 비즈니스 맥락과 요구사항을 잡고, AI는 기술적 세부사항을 잡습니다. 이 둘이 합쳐지면 리뷰 커버리지가 극적으로 올라갑니다.
사례 2: React 컴포넌트 리팩토링 PR
AI가 잡은 핵심 이슈:
- "
useEffect의존성 배열에fetchData가 빠져있습니다. 무한 렌더링이 발생할 수 있습니다" (critical) - "
keyprop에 배열 인덱스를 사용하고 있습니다. 리스트 항목이 추가/삭제될 때 렌더링 버그가 발생합니다" (warning) - "이 컴포넌트는 props가 8개입니다. 객체로 묶어서 전달하면 가독성이 좋아집니다" (suggestion)
3주간 전체 통계
| 항목 | 사람 리뷰만 | AI + 사람 병행 |
|---|---|---|
| PR당 평균 이슈 발견 수 | 2.3개 | 5.1개 |
| 리뷰 1차 응답 시간 | 평균 4.2시간 | 평균 3분 (AI) |
| 배포 후 버그 리포트 | 주간 평균 4.5건 | 주간 평균 2.1건 |
| false positive 비율 | - | 약 15% |
false positive 15%가 신경 쓰일 수 있는데, 프롬프트 튜닝을 계속하면 10% 이하로 줄일 수 있습니다. AI가 잘못 짚은 코멘트는 "AI가 이건 괜찮다고 판단해도 될 것 같습니다" 정도로 dismiss하면 됩니다. 그보다는 AI가 잡아주는 진짜 이슈의 가치가 훨씬 큽니다.
비용 분석 및 최적화
AI 코드 리뷰의 가장 큰 우려는 비용입니다. 실제 운영 데이터를 기반으로 분석해보겠습니다.
기본 비용 구조
Claude claude-sonnet-4-20250514 기준 (2026년 4월):
- 입력: $3 / 1M 토큰
- 출력: $15 / 1M 토큰
일반적인 PR 기준으로 계산해보면:
| PR 규모 | 평균 diff 크기 | 입력 토큰 | 출력 토큰 | 건당 비용 |
|---|---|---|---|---|
| 소형 (1-5 파일) | ~5,000자 | ~2,000 | ~800 | ~$0.02 |
| 중형 (5-15 파일) | ~20,000자 | ~7,000 | ~1,500 | ~$0.04 |
| 대형 (15+ 파일) | ~60,000자 | ~20,000 | ~3,000 | ~$0.11 |
팀 규모별로 월간 비용을 추정해보겠습니다:
- 5인 팀 (일 5-10 PR): 월 $15-30
- 15인 팀 (일 15-25 PR): 월 $40-80
- 30인 팀 (일 30-50 PR): 월 $80-150
솔직히 말해서, 시니어 개발자 인건비를 생각하면 이 비용은 거의 무시할 수 있는 수준입니다. 시니어가 하루 2시간 리뷰하는 것을 1시간으로 줄여준다면, 그 1시간의 인건비가 월 AI 비용을 가볍게 넘깁니다.
비용 최적화 전략
그래도 비용을 줄이고 싶다면, 다음 전략을 적용하세요:
1. 파일 필터링 강화
# 리뷰 대상 확장자만 포함
REVIEW_EXTENSIONS = {
".py", ".js", ".ts", ".tsx",
".java", ".go", ".rs",
}
def should_review_file(filename):
ext = Path(filename).suffix
if ext not in REVIEW_EXTENSIONS:
return False
for pattern in IGNORE_PATTERNS:
if pattern in filename:
return False
return True
2. 라벨 기반 선택적 리뷰
워크플로우에 조건을 추가해서, 특정 라벨이 붙은 PR만 AI 리뷰를 실행할 수 있습니다:
jobs:
ai-review:
runs-on: ubuntu-latest
if: |
github.event.pull_request.draft == false &&
contains(github.event.pull_request.labels.*.name, 'ai-review')
3. 모델 선택 최적화
모든 PR에 claude-sonnet-4-20250514을 쓸 필요는 없습니다. PR 크기에 따라 모델을 분기하면 비용을 크게 줄일 수 있습니다:
def select_model(files_diff):
"""PR 규모에 따라 모델 선택"""
total_lines = sum(
f.get("additions", 0) + f.get("deletions", 0)
for f in files_diff
)
if total_lines < 100:
# 소규모 변경은 Haiku로 충분
return "claude-haiku-4-20250514"
elif total_lines < 500:
# 중규모 변경은 Sonnet
return "claude-sonnet-4-20250514"
else:
# 대규모 변경은 Opus로 정밀 분석
return "claude-opus-4-20250514"
4. 캐싱 활용
같은 파일이 여러 PR에서 수정될 때, 이전 리뷰 결과를 캐싱해두면 중복 호출을 줄일 수 있습니다. 실제로는 파일 해시 기반으로 캐싱하면 됩니다:
import hashlib
def get_diff_hash(diff_text):
"""diff 내용의 해시 생성"""
return hashlib.sha256(diff_text.encode()).hexdigest()[:16]
# GitHub Actions 캐시와 연동
# 이미 리뷰한 diff는 스킵
5. Prompt Caching 활용
Anthropic의 Prompt Caching 기능을 사용하면 시스템 프롬프트 부분의 비용을 90% 절감할 수 있습니다. 리뷰 지침은 매번 동일하기 때문에, 캐싱 효과가 큽니다:
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
system=[
{
"type": "text",
"text": REVIEW_SYSTEM_PROMPT, # 리뷰 지침 (고정)
"cache_control": {"type": "ephemeral"}
}
],
messages=[
{"role": "user", "content": diff_prompt}
]
)
최종 권장 설정
위의 최적화를 모두 적용한 워크플로우 최종 버전입니다:
name: AI Code Review (Optimized)
on:
pull_request:
types: [opened, synchronize]
paths:
- '**.py'
- '**.js'
- '**.ts'
- '**.tsx'
- '**.java'
- '**.go'
concurrency:
group: ai-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
ai-review:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install dependencies
run: pip install anthropic PyGithub pyyaml
- name: Run AI Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_NAME: ${{ github.repository }}
run: python scripts/ai_review.py
핵심 최적화 포인트:
paths필터로 코드 파일 변경 시에만 실행concurrency설정으로 같은 PR에 연속 푸시 시 이전 실행을 취소 (중복 과금 방지)timeout-minutes: 5로 무한 실행 방지cache: 'pip'으로 의존성 설치 시간 단축
마무리하며: AI 코드 리뷰는 사람을 대체하는 게 아니라 사람의 리뷰 품질을 높여주는 도구입니다. AI가 단순 반복적인 검토를 처리해주면, 사람은 더 중요한 설계와 비즈니스 로직 리뷰에 집중할 수 있습니다. 이 글의 코드를 그대로 복사해서 리포지토리에 적용해보세요. 설정부터 첫 번째 AI 리뷰까지 30분이면 충분합니다. 처음에는 팀원들이 신기해하다가, 일주일 지나면 "AI가 왜 이걸 안 잡았지?"라고 말하게 됩니다. 그게 성공적으로 도입된 신호입니다.
'AI 개발 가이드' 카테고리의 다른 글
| Streamlit으로 AI 웹앱 30분 만에 배포하기 (0) | 2026.04.09 |
|---|---|
| Claude Code CLI로 프로젝트 자동화하기 - 실전 워크플로우 (0) | 2026.04.09 |
| LangChain 입문 가이드 - 파이썬으로 AI 앱 만들기 (0) | 2026.04.05 |
| AI로 웹 크롤러 만들기 - BeautifulSoup + Claude API 조합 (0) | 2026.04.04 |
| 프롬프트 엔지니어링 개발자용 실전 가이드 - 원하는 답을 뽑아내는 기술 (0) | 2026.04.04 |