자동화된 PR 검토 댓글
API로 PR 변경 내용을 읽고 인라인 검토 댓글을 게시합니다.
자동화된 PR 검토 댓글은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
자동화된 PR 검토란 무엇인가요
자동화된 PR 검토 에이전트는 변경된 파일을 읽고 차이점을 분석한 다음, 특정 부분을 대상으로 검토 댓글을 게시합니다. 사람 검토자와 같은 작업을 더 빠르고 일관되게 수행합니다. 사용 사례로는 보안 검사, 스타일 적용, 일반적인 버그 탐지, 테스트 범위 확인, PR 변경 내용 요약 등이 있습니다.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
# Get a specific pull request by number
pr = repo.get_pull(42)
print(f'PR #{pr.number}: {pr.title}')
print(f'Author: {pr.user.login}')
print(f'Base branch: {pr.base.ref} <- Head: {pr.head.ref}')
print(f'State: {pr.state}')
print(f'Changed files: {pr.changed_files}')
print(f'Additions: +{pr.additions}')
print(f'Deletions: -{pr.deletions}')열린 PR 나열하기
repo.get_pulls()를 사용하여 PR을 나열하십시오. 상태(open, closed), 대상 브랜치 또는 정렬 순서로 필터링할 수 있습니다. 검토 봇은 일반적으로 아직 검토되지 않은 open PR을 처리합니다.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
# All open PRs targeting main
open_prs = repo.get_pulls(
state='open',
base='main',
sort='created',
direction='desc'
)
for pr in open_prs:
# Check if agent has already reviewed it
reviews = list(pr.get_reviews())
agent_reviewed = any(
r.user.login == 'my-agent-bot[bot]'
for r in reviews
)
if not agent_reviewed:
print(f'Needs review: PR #{pr.number} - {pr.title}')변경된 파일 가져오기
pr.get_files()는 File 객체 목록을 반환합니다. 각 객체는 PR에서 변경된 하나의 파일을 나타냅니다. 주요 속성은 filename, status(추가됨/수정됨/삭제됨), additions, deletions, patch(통합 차이 텍스트)입니다.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
changed_files = list(pr.get_files())
print(f'Changed files: {len(changed_files)}')
for file in changed_files:
print(f'\n{file.status.upper()}: {file.filename}')
print(f' +{file.additions} -{file.deletions}')
# The patch is the unified diff
if file.patch:
print(f' Patch preview: {file.patch[:200]}')
# Filter to only Python files
python_files = [
f for f in changed_files
if f.filename.endswith('.py') and f.status != 'removed'
]파일 패치(차이) 읽기
file.patch는 통합 차이 문자열입니다. +로 시작하는 줄은 추가된 줄이고, -로 시작하는 줄은 삭제된 줄입니다. @@ 헤더는 변경 묶음이 어느 줄 번호에서 시작하는지 보여 줍니다. 이를 분석하여 새 코드에서 문제가 정확히 어디에 있는지 식별하십시오.
def parse_patch_hunks(patch):
if not patch:
return []
hunks = []
current_hunk = None
new_line_num = 0
for line in patch.split('\n'):
if line.startswith('@@'):
# Parse: @@ -old_start,old_count +new_start,new_count @@
import re
match = re.search(r'\+([0-9]+)', line)
if match:
new_line_num = int(match.group(1))
current_hunk = {'header': line, 'lines': [], 'start_line': new_line_num}
hunks.append(current_hunk)
elif current_hunk is not None:
if line.startswith('+'):
current_hunk['lines'].append({'type': 'add', 'content': line[1:], 'line': new_line_num})
new_line_num += 1
elif line.startswith('-'):
current_hunk['lines'].append({'type': 'remove', 'content': line[1:], 'line': None})
else:
new_line_num += 1
return hunks
# --- demo ---
patch = (
'@@ -10,3 +10,4 @@\n'
' def add(a, b):\n'
'- return a - b\n'
'+ return a + b\n'
'+ # fixed subtraction bug\n'
)
for hunk in parse_patch_hunks(patch):
print(hunk['header'])
for line in hunk['lines']:
print(' ', line)
PR 커밋 가져오기
pr.get_commits()는 PR에 포함된 커밋을 반환합니다. 각 커밋에는 메시지, 작성자, 변경된 파일 목록이 있습니다. 커밋 메시지를 분석하면 검토 봇이 PR의 변경 내용을 요약하고 커밋 관리 상태가 양호한지 확인하는 데 도움이 됩니다.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
commits = list(pr.get_commits())
print(f'Commits in PR: {len(commits)}')
for commit in commits:
print(f'\nCommit: {commit.sha[:8]}')
print(f'Author: {commit.commit.author.name}')
print(f'Message: {commit.commit.message[:80]}')
print(f'Changes: +{commit.stats.additions} -{commit.stats.deletions}')
# Check commit message quality
poor_messages = [
c for c in commits
if len(c.commit.message.split()[0] if c.commit.message else '') < 3
or c.commit.message.lower().startswith(('wip', 'fix', 'test'))
]
if poor_messages:
print(f'{len(poor_messages)} commits have poor message quality')PR 검토 게시하기
pr.create_review()를 사용하여 공식 검토를 제출하십시오. event 매개 변수는 검토 유형을 결정합니다. 'APPROVE', 'REQUEST_CHANGES', 'COMMENT' 중 하나를 사용할 수 있습니다. 검토에는 전체 본문과 특정 줄에 대한 인라인 댓글 목록을 포함할 수 있습니다.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
# Submit a review with a general comment (no inline comments)
pr.create_review(
body=(
'**Automated Code Review**\n\n'
'I analyzed this PR and found the following:\n'
'- No security issues detected\n'
'- Style conforms to project guidelines\n'
'- Test coverage looks adequate\n\n'
'_This review was generated automatically._'
),
event='COMMENT' # 'APPROVE', 'REQUEST_CHANGES', or 'COMMENT'
)인라인 검토 댓글 게시하기
인라인 댓글은 차이 화면의 특정 줄에 직접 표시됩니다. 각 댓글은 path(파일 경로), line(새 파일의 줄 번호), body를 지정합니다. 댓글 목록을 pr.create_review(comments=[...])에 전달하십시오.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
# Build inline comments from analysis results
inline_comments = [
{
'path': 'src/auth.py',
'line': 47,
'body': (
':warning: **Security Issue:** Using `md5` for password hashing.\n'
'Use `bcrypt` or `argon2` instead.\n'
'Example: `import bcrypt; bcrypt.hashpw(password.encode(), bcrypt.gensalt())`'
)
},
{
'path': 'src/utils.py',
'line': 23,
'body': (
':bulb: Consider using `pathlib.Path` instead of `os.path.join` '
'for better cross-platform compatibility.'
)
}
]
pr.create_review(
body='Security and style review complete. See inline comments.',
event='REQUEST_CHANGES',
comments=inline_comments
)
print(f'Review submitted with {len(inline_comments)} inline comments')LLM 기반 코드 분석
보안 및 품질 검토 프롬프트와 함께 파일 차이를 LLM에 전달하십시오. 파일 경로, 줄 번호, 심각도, 설명이 포함된 문제 목록을 구조화된 JSON 응답으로 반환하도록 요청하십시오. 이 결과를 사용하여 인라인 댓글 목록을 만드십시오.
import anthropic
import json
import os
client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])
def analyze_file_with_llm(filename, patch):
prompt = (
f'Review this Python code diff for issues.\n'
f'File: {filename}\n\n'
f'Return JSON array: [{{"line": N, "severity": "error|warning|info", '
f'"message": "..."}}]\n'
f'Focus on: security vulnerabilities, bugs, and anti-patterns.\n'
f'Diff:\n{patch[:3000]}'
)
response = client.messages.create(
model='claude-opus-4-5',
max_tokens=1000,
messages=[{'role': 'user', 'content': prompt}]
)
try:
issues = json.loads(response.content[0].text)
return [
{'path': filename, 'line': i.get('line', 1),
'body': f'[{i.get("severity", "info").upper()}] {i.get("message", "")}'}
for i in issues if isinstance(i, dict)
]
except json.JSONDecodeError:
return []전체 PR 검토 에이전트 흐름
모든 요소를 연결하여 완전한 PR 검토 흐름을 구성하십시오. 파일을 가져오고, 검토 가능한 파일만 필터링하고, 각 파일을 LLM으로 분석하고, 댓글을 모은 다음, 한 번의 API 호출로 검토를 제출합니다. LLM 비용을 관리하려면 변경된 파일 중 처음 10개로 제한하십시오.
def run_pr_review(g, repo_name, pr_number):
repo = g.get_repo(repo_name)
pr = repo.get_pull(pr_number)
print(f'Reviewing PR #{pr_number}: {pr.title}')
# Get changed files (limit to 10)
files = [
f for f in list(pr.get_files())[:10]
if f.filename.endswith(('.py', '.js', '.ts'))
and f.patch
]
all_comments = []
for file in files:
print(f' Analyzing: {file.filename}')
comments = analyze_file_with_llm(file.filename, file.patch)
all_comments.extend(comments)
# Determine review event based on findings
critical = [c for c in all_comments if '[ERROR]' in c['body']]
event = 'REQUEST_CHANGES' if critical else 'COMMENT'
summary = (
f'Analyzed {len(files)} files. '
f'Found {len(all_comments)} issues '
f'({len(critical)} critical).'
)
pr.create_review(body=summary, event=event, comments=all_comments[:25])
print(f'Review submitted: {event} with {len(all_comments)} comments')기존 에이전트 검토 확인하기
검토를 게시하기 전에 에이전트 봇이 현재 상태의 이 PR을 이미 검토했는지 확인하십시오. 기존 검토 목록에서 봇의 사용자 이름을 확인하여 중복 검토를 방지하십시오.
def should_review_pr(pr, bot_username):
reviews = list(pr.get_reviews())
# Check if bot already reviewed this PR
bot_reviews = [
r for r in reviews
if r.user and r.user.login == bot_username
]
if not bot_reviews:
return True # No previous review, proceed
# Check if new commits were pushed since last review
last_review_time = max(r.submitted_at for r in bot_reviews)
commits = list(pr.get_commits())
latest_commit_time = commits[-1].commit.author.date if commits else None
if latest_commit_time and latest_commit_time > last_review_time:
print(f'New commits since last review, re-reviewing...')
return True
print(f'PR #{pr.number} already reviewed by {bot_username}')
return False
# --- demo: minimal stand-ins for PyGithub-style PR/review/commit objects ---
import datetime
from types import SimpleNamespace
class _FakeCommit:
def __init__(self, date):
self.commit = SimpleNamespace(author=SimpleNamespace(date=date))
class _FakeReview:
def __init__(self, login, submitted_at):
self.user = SimpleNamespace(login=login)
self.submitted_at = submitted_at
class _FakePR:
def __init__(self, number, reviews, commits):
self.number = number
self._reviews = reviews
self._commits = commits
def get_reviews(self):
return self._reviews
def get_commits(self):
return self._commits
pr_never_reviewed = _FakePR(101, [], [_FakeCommit(datetime.datetime(2026, 8, 1))])
print('PR #101 needs review:', should_review_pr(pr_never_reviewed, 'coddy-bot'))
review_time = datetime.datetime(2026, 8, 1, 10, 0)
pr_up_to_date = _FakePR(102, [_FakeReview('coddy-bot', review_time)],
[_FakeCommit(review_time - datetime.timedelta(hours=1))])
print('PR #102 needs review:', should_review_pr(pr_up_to_date, 'coddy-bot'))
PR 검토 보안 검사 패턴
일반적인 자동화 검토 패턴은 추가된 줄에서 보안 안티 패턴을 검사하는 것입니다. 예를 들면 하드코딩된 비밀 값, 사용 중단된 함수 사용, SQL 문자열 연결 또는 eval() 호출이 있습니다. 추가된 각 줄을 패턴 집합과 대조하고 발견 항목을 인라인 주석으로 표시합니다.
import re
SECURITY_PATTERNS = [
(r'eval\s*\(', 'Dangerous eval() call — code injection risk'),
(r'os\.system\s*\(', 'os.system() is unsafe; use subprocess with a list'),
(r'pickle\.loads?\s*\(', 'pickle.load is unsafe with untrusted data'),
(r'execute\(.*%\s*', 'Possible SQL injection — use parameterized queries'),
(r'md5\s*\(|hashlib\.md5', 'MD5 is cryptographically broken for passwords; use bcrypt'),
]
def scan_patch_for_security(filename, patch):
if not patch:
return []
comments = []
new_line = 0
for line in patch.split('\n'):
if line.startswith('@@'):
import re as _re
m = _re.search(r'\+([0-9]+)', line)
if m:
new_line = int(m.group(1))
elif line.startswith('+') and not line.startswith('+++'):
content = line[1:]
for pattern, message in SECURITY_PATTERNS:
if re.search(pattern, content):
comments.append({'path': filename, 'line': new_line,
'body': f':rotating_light: **Security:** {message}'})
new_line += 1
elif not line.startswith('-'):
new_line += 1
return comments
# --- demo ---
patch = (
'@@ -1,2 +1,3 @@\n'
' def run(cmd):\n'
'+ os.system(cmd)\n'
'+ hashlib.md5(cmd.encode())\n'
)
for c in scan_patch_for_security('run.py', patch):
print(c)
빠른 확인: 검토 이벤트
PR 검토 옵션에 대한 이해도를 테스트해 보세요.
자동화된 PR 검토 요약
이제 에이전트가 프로그래밍 방식으로 풀 리퀘스트를 검토할 수 있습니다.
- repo.get_pull(number) — 메타데이터가 포함된 PR 객체 가져오기
- pr.get_files() —
filename,patch,additions가 포함된 변경된 파일 목록 가져오기 - pr.get_commits() — 커밋 메시지와 통계 확인하기
- pr.create_review(body, event, comments) — 선택적 인라인 주석과 함께 검토 제출하기
- 인라인 주석 형식:
{'path': filename, 'line': N, 'body': markdown} - 관찰 사항에는
'COMMENT'를, 차단해야 하는 문제에는'REQUEST_CHANGES'를 사용하기 - 중복을 피하기 위해 다시 검토하기 전에 기존 검토 확인하기
- API 제한을 준수하려면 파일 분석을 10개 파일과 인라인 주석 25개로 제한하기
AI 튜터와 함께 AI Agents을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 60
- 레슨
- 239
자주 묻는 질문
“자동화된 PR 검토 댓글” 강의는 무료인가요?
네 — “자동화된 PR 검토 댓글” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
“자동화된 PR 검토 댓글”에서 뭘 배우나요?
API로 PR 변경 내용을 읽고 인라인 검토 댓글을 게시합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Agents을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“자동화된 PR 검토 댓글” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- GitHub REST API 개요
- 이슈 조회 및 관리
- 자동화된 PR 검토 댓글
- 커밋 기록 및 변경 내용 분석