AI 코드 리뷰
AI를 활용한 코드 리뷰 프로세스를 배웁니다. 자동화된 코드 리뷰로 품질을 높이고 시간을 절약하세요.
개요
AI를 코드 리뷰에 활용하면 더 빠르고 일관된 리뷰를 수행할 수 있습니다. 이 가이드에서는 AI 코드 리뷰 프로세스를 설정하고 운영하는 방법을 배웁니다.
📋 AI 코드 리뷰의 장점
- 속도: 수초 내에 리뷰 결과 제공
- 일관성: 모든 코드에 동일한 기준 적용
- 가용성: 24시간 언제든지 리뷰 가능
- 초기 검증: 사람이 직접 리뷰하기 전에 문제 탐지
설정
AI 코드 리뷰 환경을 설정합니다.
도구 선택
| 도구 | 특징 | 설정 난이도 |
|---|---|---|
| Claude CLI | 로컬에서 빠른 리뷰 | 쉬움 |
| GitHub Actions | PR 시 자동 리뷰 | 보통 |
| CodeRabbit | 전용 AI 리뷰 도구 | 쉬움 |
| Amazon CodeGuru | AWS 통합 | 보통 |
코드 리뷰 프롬프트
효과적인 코드 리뷰를 위한 프롬프트입니다.
기본 코드 리뷰 프롬프트
AI에게 요청
다음 코드를 리뷰해주세요:
```
{code}
```
리뷰 관점:
1. 버그 및 취약점
2. 코드 품질 및 가독성
3. 성능 최적화 기회
4. 보안 문제
5. 모범 사례 준수
출력 형식:
- 문제점: [설명]
- 심각도: [높음/중간/낮음]
- 권장 해결책: [구체적인 제안]
언어별 특화 리뷰
Python 리뷰 프롬프트
다음 Python 코드를 PEP 8 스타일 가이드에
따라 리뷰해주세요:
중점 점검:
- 들여쓰기 및 포맷팅
- 네이밍 컨벤션
- 타입 힌트 사용
- docstring 문서화
- 예외 처리
자동화
GitHub Actions와 LLM API로 PR마다 자동 코드 리뷰를 설정합니다.
GitHub Actions 완전 동작 설정
YAML (.github/workflows/ai-review.yml)
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
branches: [main, develop]
permissions:
pull-requests: write # PR 코멘트 작성 권한
contents: read
jobs:
ai-review:
runs-on: ubuntu-latest
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'
- name: Install dependencies
# 사용할 LLM 프로바이더에 맞게 선택
run: pip install anthropic openai PyGithub
- name: Run AI Code Review
env:
# 사용할 프로바이더의 API 키를 설정 (택 1)
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_NAME: ${{ github.repository }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: python .github/scripts/ai_review.py
LLM API 인라인 PR 코멘트 스크립트
Python (.github/scripts/ai_review.py)
#!/usr/bin/env python3
"""LLM API를 이용한 GitHub PR 인라인 코드 리뷰
Anthropic(Claude) 또는 OpenAI(GPT) 중 선택 가능"""
import os, json, subprocess
from github import Github
# 환경 변수
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
REPO_NAME = os.environ["REPO_NAME"]
PR_NUMBER = int(os.environ["PR_NUMBER"])
BASE_SHA = os.environ["BASE_SHA"]
HEAD_SHA = os.environ["HEAD_SHA"]
# 변경된 파일 diff 수집 (최대 50KB)
def get_diff() -> str:
result = subprocess.run(
["git", "diff", "--unified=5", BASE_SHA, HEAD_SHA,
"--", "*.py", "*.js", "*.ts", "*.java", "*.go"],
capture_output=True, text=True
)
diff = result.stdout
return diff[:50000] if len(diff) > 50000 else diff
# 리뷰 프롬프트 (프로바이더 공통)
REVIEW_PROMPT = """다음은 GitHub PR의 diff입니다. 코드 리뷰를 수행하고
JSON 배열로만 응답하세요 (마크다운 없이 순수 JSON):
```diff
{diff}
```
응답 형식 (JSON 배열):
[
{{
"file": "파일 경로",
"line": 변경된 줄 번호 (정수),
"severity": "high|medium|low",
"category": "bug|security|performance|style|readability",
"comment": "구체적인 리뷰 코멘트 (한국어)",
"suggestion": "개선된 코드 예시 (선택)"
}}
]
심각도 기준:
- high: 버그, 보안 취약점, 데이터 손실 위험
- medium: 성능 문제, 잠재적 버그
- low: 스타일, 가독성 개선"""
# --- 프로바이더별 LLM 호출 ---
def review_with_anthropic(diff: str) -> list:
import anthropic
client = anthropic.Anthropic(
api_key=os.environ["ANTHROPIC_API_KEY"]
)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
messages=[{"role": "user",
"content": REVIEW_PROMPT.format(diff=diff)}]
)
return json.loads(response.content[0].text)
def review_with_openai(diff: str) -> list:
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"]
)
response = client.chat.completions.create(
model="gpt-4o",
max_tokens=4096,
messages=[{"role": "user",
"content": REVIEW_PROMPT.format(diff=diff)}]
)
return json.loads(response.choices[0].message.content)
def ai_review(diff: str) -> list:
"""환경 변수에 따라 프로바이더 자동 선택"""
try:
if os.environ.get("ANTHROPIC_API_KEY"):
return review_with_anthropic(diff)
elif os.environ.get("OPENAI_API_KEY"):
return review_with_openai(diff)
else:
print("오류: ANTHROPIC_API_KEY 또는 OPENAI_API_KEY가 필요합니다")
return []
except json.JSONDecodeError:
return []
# 우선순위 정렬 + 거짓 양성 필터링
def filter_and_sort(issues: list) -> list:
SEVERITY_ORDER = {"high": 0, "medium": 1, "low": 2}
# 거짓 양성 제외: 너무 짧은 코멘트, 중복 제거
seen = set()
filtered = []
for issue in issues:
key = (issue.get("file"), issue.get("line"))
comment = issue.get("comment", "")
if key not in seen and len(comment) > 20:
seen.add(key)
filtered.append(issue)
return sorted(filtered, key=lambda x: SEVERITY_ORDER.get(x.get("severity"), 2))
# PR 요약 코멘트 작성 + 인라인 코멘트
def post_review(issues: list):
gh = Github(GITHUB_TOKEN)
repo = gh.get_repo(REPO_NAME)
pr = repo.get_pull(PR_NUMBER)
# 요약 코멘트
high = [i for i in issues if i["severity"] == "high"]
medium = [i for i in issues if i["severity"] == "medium"]
low = [i for i in issues if i["severity"] == "low"]
summary = f"""## 🤖 AI 코드 리뷰 결과
| 심각도 | 건수 |
|--------|------|
| 🔴 높음 (즉시 수정) | {len(high)} |
| 🟡 중간 (권장 수정) | {len(medium)} |
| 🔵 낮음 (선택 수정) | {len(low)} |
> 총 **{len(issues)}건** 발견. 높음 항목을 우선 확인하세요.
> ⚠️ AI 리뷰는 참고용이며 최종 판단은 담당자가 합니다."""
pr.create_issue_comment(summary)
# 인라인 코멘트 (high/medium만 게시)
commit = repo.get_commit(HEAD_SHA)
for issue in issues:
if issue["severity"] == "low":
continue
severity_icon = "🔴" if issue["severity"] == "high" else "🟡"
body = f"""{severity_icon} **[{issue['category'].upper()}]** {issue['comment']}"""
if issue.get("suggestion"):
body += f"\n\n```suggestion\n{issue['suggestion']}\n```"
try:
pr.create_review_comment(
body=body,
commit=commit,
path=issue["file"],
line=issue["line"]
)
except Exception as e:
print(f"인라인 코멘트 실패 ({issue['file']}:{issue['line']}): {e}")
if __name__ == "__main__":
diff = get_diff()
issues = ai_review(diff)
issues = filter_and_sort(issues)
post_review(issues)
print(f"리뷰 완료: {len(issues)}건 게시")
거짓 양성(False Positive) 처리 전략
AI 리뷰는 맥락 없이 경고를 낼 수 있습니다. 다음 전략으로 거짓 양성을 줄이세요.
인라인 무시 주석
Python — AI 리뷰 무시 주석 패턴
# noqa: AI-REVIEW — 의도적인 패턴, 무시
result = eval(user_expr) # noqa: AI-REVIEW
# 스크립트에서 noqa 주석 있는 줄 필터링
def is_ignored_line(file_path: str, line_no: int) -> bool:
with open(file_path) as f:
lines = f.readlines()
if line_no - 1 < len(lines):
return "noqa: AI-REVIEW" in lines[line_no - 1]
return False
허용 목록(Allow-list) 설정
JSON (.github/ai-review-config.json)
{
"skip_files": ["migrations/**", "vendor/**", "*.min.js"],
"skip_categories": ["style"],
"min_comment_length": 30,
"max_issues_per_pr": 20,
"severity_threshold": "medium"
}
⚠️ 주의: AI 리뷰를 PR 블로커(required check)로 설정하지 마세요.
참고용 코멘트로만 활용하고 최종 승인은 사람이 담당해야 합니다.
리뷰 결과 우선순위 전략
| 심각도 | 카테고리 | 대응 방식 | 예시 |
|---|---|---|---|
| 🔴 높음 | bug, security | PR 병합 전 반드시 수정 | SQL 인젝션, 널 역참조 |
| 🟡 중간 | performance | 수정 권장, 담당자 판단 | N+1 쿼리, 불필요한 루프 |
| 🔵 낮음 | style, readability | 선택적 수정, 요약만 게시 | 변수명, 들여쓰기 |
비용 최적화 팁
Python — 대형 PR diff 청크 분할
def chunk_diff(diff: str, max_chars: int = 30000) -> list[str]:
"""diff를 청크로 분할해 토큰 한도 초과 방지"""
chunks, current = [], ""
for line in diff.splitlines(keepends=True):
if len(current) + len(line) > max_chars:
chunks.append(current)
current = line
else:
current += line
if current:
chunks.append(current)
return chunks
# 경량 모델로 스크리닝 → 고성능 모델로 상세 리뷰 (비용 절감)
# Anthropic: Haiku → Sonnet, OpenAI: GPT-4o-mini → GPT-4o
def two_pass_review(diff: str) -> list:
import anthropic
client = anthropic.Anthropic()
# 1단계: Haiku로 빠른 심각도 분류
triage = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=512,
messages=[{"role": "user",
"content": f"다음 diff에 high/security 이슈가 있으면 'YES', 없으면 'NO':\n{diff[:5000]}"}]
)
if "YES" not in triage.content[0].text:
return [] # 이슈 없음 → 고성능 모델 호출 생략
# 2단계: Sonnet으로 상세 리뷰
return review_with_anthropic(diff) # 위에서 정의한 함수
리뷰 프로세스
팀의 코드 리뷰 프로세스를 설계합니다.
AI 코드 리뷰 프로세스
모범 사례
효과적인 AI 코드 리뷰를 위한 모범 사례입니다.
💡 AI 코드 리뷰 팁
- 자동화 + 수동: AI는 초기 스크리닝, 사람은 최종 결정
- 피드백 루프: AI가 놓친 문제를 다시 학습
- 민감한 정보 제외: 보안 관련 코드는 수동 리뷰
- 설정 가능한 민감도: 프로젝트에 맞게 조정
리뷰 카테고리
| 카테고리 | AI 리뷰 적합도 | 설명 |
|---|---|---|
| 코드 포맷 | 높음 | 자동 검사 가능 |
| 버그 탐지 | 중간 | 명확한 버그만 탐지 |
| 보안 | 중간 | 기본적인 취약점 탐지 |
| 아키텍처 | 낮음 | 사람의 판단 필요 |
다음 단계
팀 협업에 대해 더 자세히 배워보세요!
핵심 정리
- 도구 선택: LLM API(Anthropic/OpenAI) + GitHub Actions로 PR 자동 리뷰 구성
- 프롬프트: JSON 구조화 응답으로 파일·줄번호·심각도 추출
- 인라인 코멘트:
create_review_comment()로 diff 위치에 직접 게시 - 우선순위: high→medium 순으로 게시, low는 요약만
- 거짓 양성:
noqa: AI-REVIEW주석 + 허용 목록으로 노이즈 제거 - 비용 절감: Haiku 사전 분류 → Sonnet 상세 리뷰 2-pass 전략