PR로 머지 기록들을 남기는데 혼자 작업을 하기에 개인 프로젝트라 내가 올리고 머지 승인하다보니 의미가 없다고 생각이 들었음.
그러다가 누군가 PR을 올릴 때, PR에 대한 반응을 자동적으로 만들었다는 글을 보고 그렇다면 AI를 붙여서 리뷰를 받을 수 있지 않을까 생각했음.
결론은 openAI를 연결하기로 했음
코드 작업시 claude와 같이 작업을 진행하고 있는데, 리뷰도 같은 모델로 하는 경우 긍정적 편향이 있을 수 있다고 하는 내용을 어디선가 보았음.
그래서 '아! 그렇다면 다른 AI로 리뷰를 받는게 낫겠다'고 판단!
아래의 이유로 'gpt-5.1-codex-mini' 결정
그 결과가 'gpt-5.1-codex-mini' 였음.
흐름은 아래와 같이 PR이 등록되거나 추가 커밋으로 수정되는 경우 시작하게 됨.
PR 오픈 or 추가 커밋으로 수정
→ git diff로 변경 코드 추출 (pr_diff.txt)
→ OpenAI API에 diff + 프롬프트 전송
→ 리뷰 텍스트 반환
→ GitHub PR 코멘트로 자동 등록
Settings > Secrets에 OPENAI_API_KEY 등록.github/
├── workflows/
│ └── pr-review.yml
└── scripts/
└── ai_review.js
pr-review.ymlGitHub Action 워크플로우를 설정하는 파일로 아래와 같이 작성되어 있음.
# pr-review.yml
name: AI Code Review
# PR 이벤트 트리거
on:
pull_request:
types: [opened, synchronize, reopened] # 생성·커밋 추가·재오픈 시 실행
# GitHub 권한 설정
permissions:
contents: read # 코드 읽기
pull-requests: write # PR에 리뷰 코멘트 작성
jobs:
ai-review:
runs-on: ubuntu-latest
steps:
# 전체 커밋 히스토리 포함해서 체크아웃 (diff 계산에 필요)
- uses: actions/checkout@v4
with:
fetch-depth: 0
# base 브랜치와 현재 브랜치의 diff를 파일로 저장
- name: Get PR diff
run: |
git fetch origin ${{ github.base_ref }}
git diff origin/${{ github.base_ref }}...HEAD > pr_diff.txt
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# AI 리뷰 스크립트 실행에 필요한 패키지 설치
- name: Install dependencies
run: npm install openai
# OpenAI API로 diff 분석 후 PR에 리뷰 코멘트 게시
- name: Run AI review
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.number }}
run: node .github/scripts/ai_review.js
ai_review.js워크플로우 설정 파일인 pr-review.yml에서 실행시키는 파일로 api를 통해 프롬프트를 보내고 받는 동작을 실행하는 파일임.
아래의 내용을 고려하여 작성되었음.
참고
영어: 1토큰 ≈ 4글자
한글: 1토큰 ≈ 1~2글자
AI가 MAX_CHARS 설정을 제안했는데, 아래의 이유들로 수락함
80,000자 기준 근거
명확한 계산 기준 없이 경험적 수치. 대략80,000 ÷ 4 = 약 20,000 토큰
// ai_review.js
import { readFileSync } from "fs";
import OpenAI from "openai";
// diff 텍스트가 너무 길면 잘라내기 위한 최대 글자 수
const MAX_CHARS = 80_000;
// GitHub API를 호출해 PR에 코멘트를 게시하는 함수
async function postComment(body) {
const { GITHUB_TOKEN, GITHUB_REPOSITORY, PR_NUMBER } = process.env;
const url = `https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments`;
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
"Content-Type": "application/json",
},
body: JSON.stringify({ body }),
});
if (!response.ok) throw new Error(`GitHub API error: ${response.status}`);
}
async function main() {
// 워크플로우에서 저장한 diff 파일 읽기
let diff = readFileSync("pr_diff.txt", "utf-8");
// 변경사항이 없으면 리뷰 생략
if (!diff.trim()) {
console.log("변경사항 없음, 리뷰 스킵");
return;
}
// diff가 너무 길면 앞부분만 사용 (토큰 초과 방지)
const truncated = diff.length > MAX_CHARS;
if (truncated) diff = diff.slice(0, MAX_CHARS);
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 리뷰 기준과 출력 포맷을 지정한 프롬프트
const prompt = `You are a senior React/TypeScript developer. Please review the PR diff below and respond in Korean.
Project: React 19 + TypeScript + Firebase + Tailwind CSS advent calendar app
Review criteria:
1. TypeScript type safety (overuse of any, missing types, etc.)
2. React patterns (hooks rules, unnecessary re-renders, component structure)
3. Firebase usage (Timestamp conversion, error handling, security rule violations)
4. Security (XSS, exposed sensitive info, auth bypass possibilities)
5. Bug risks (runtime errors, missing edge cases)
6. Code quality (readability, DRY, duplicate logic)
7. Naming conventions (variables, functions, components — clarity, consistency, and intent)
8. Performance (unnecessary useEffect, missing optimization, memoization needs)
9. Error boundaries (missing try/catch, insufficient async error handling)
10. Component size (oversized components, need for separation)
Format:
- 🔴 **Critical** — must fix
- 🟡 **Warning** — recommended fix
- 🟢 **Suggestion** — improvement ideas
- ✅ **Good** — well done
If overall looks good, a short compliment is fine. Keep feedback concise and actionable.
\`\`\`diff
${diff}
\`\`\`
${truncated ? "\n> ⚠️ diff가 너무 커서 앞부분만 리뷰되었습니다." : ""}`;
const model = "gpt-5.1-codex-mini"
const context = [
{ role: 'user', content: prompt }
];
// OpenAI Responses API로 리뷰 생성
const response = await client.responses.create({
model: model,
input: context,
max_output_tokens: 2000,
});
// 리뷰 결과를 PR 코멘트로 게시
const review = response.output_text;
await postComment(`## AI 코드 리뷰\n\n${review}\n\n---\n*Powered by OpenAI ${model}*`);
console.log("리뷰 코멘트 등록 완료");
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
프로젝트 도입 결과 리뷰를 보고 수정을 한 경우도 있고 추후 수정 계획인 것도 있는 등 보완해야 할 것들을 짚어줘서 코드의 퀄리티를 높일 수 있겠구나 느꼈음.
그리고 생각하지 못한 부분을 짚어줘서 배우는 것도 많음.
현재는 아래의 플로우로 작업 진행중!
PR 올림
→ 코멘트 달림
→ 코멘트 내용 판단
→ 코멘트가 지금 바로 수정해야 할 크리티컬한 경우 바로 수정 진행
→ 크리티컬하지 않거나 생각해야 할 부분이 있다면 보류
→ 판단 내용을 코멘트 남기고 추가 작업이 필요한 경우에는 작업
→ 머지하여 작업 완료
프로젝트에 도입하니 동작을 잘하고 생각보다 좋은 리뷰를 많이 주어서 PR의 의미가 생겨나는 것 같았음.
그래서 다른 저장소들에도 도입해야겠다고 생각하고 학습하고 실습하는 저장소에 이를 붙이기로 함.
다만 그곳은 기능을 따로 브랜치로 만들지 않아 PR을 올리지 않고 메인으로 바로 올리기에 지금 현재의 형태가 아닌 푸시할 경우에 코멘트로 작성하는 것으로 작업을 생각하였음.
관련 내용은 추후 2탄으로 가져오겠음.
전체 코드를 보지 않아서 이미 설정되어 있는 경우도 짚어주는 경우가 있는데, 설정 파일은 함께 프롬프트로 전달할지 고민 중.
사용하면서 수정할 부분은 아예 코멘트가 없는 경우도 있어서 답변이 없는 경우 예외처리를 추가 필요.