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)  # 위에서 정의한 함수

리뷰 프로세스

팀의 코드 리뷰 프로세스를 설계합니다.

개발자가 PR 생성 AI 자동 리뷰 심각한 문제? 사람 리뷰 수정 요청

AI 코드 리뷰 프로세스

모범 사례

효과적인 AI 코드 리뷰를 위한 모범 사례입니다.

💡 AI 코드 리뷰 팁
  • 자동화 + 수동: AI는 초기 스크리닝, 사람은 최종 결정
  • 피드백 루프: AI가 놓친 문제를 다시 학습
  • 민감한 정보 제외: 보안 관련 코드는 수동 리뷰
  • 설정 가능한 민감도: 프로젝트에 맞게 조정

리뷰 카테고리

카테고리 AI 리뷰 적합도 설명
코드 포맷 높음 자동 검사 가능
버그 탐지 중간 명확한 버그만 탐지
보안 중간 기본적인 취약점 탐지
아키텍처 낮음 사람의 판단 필요

다음 단계

팀 협업에 대해 더 자세히 배워보세요!

팀 프롬프트 라이브러리

팀에서 프롬프트를 공유하세요

팀 프롬프트 라이브러리 →

CI/CD 파이프라인

CI/CD에 코드 리뷰를 통합하세요

CI/CD 파이프라인 →

보안 모범 사례

코드 보안도 챙기세요

보안 모범 사례 →

핵심 정리

  • 도구 선택: LLM API(Anthropic/OpenAI) + GitHub Actions로 PR 자동 리뷰 구성
  • 프롬프트: JSON 구조화 응답으로 파일·줄번호·심각도 추출
  • 인라인 코멘트: create_review_comment()로 diff 위치에 직접 게시
  • 우선순위: high→medium 순으로 게시, low는 요약만
  • 거짓 양성: noqa: AI-REVIEW 주석 + 허용 목록으로 노이즈 제거
  • 비용 절감: Haiku 사전 분류 → Sonnet 상세 리뷰 2-pass 전략