배경
팀 프로젝트를 진행하면서 PR 코드리뷰에 드는 시간이 점점 부담이 됐다.
사람이 직접 리뷰하기 전에, 아키텍처 패턴 위반이나 명백한 버그 같은 것들을 먼저 걸러줄 수 있다면 리뷰 품질이 올라가지 않을까 하는 생각이 들었다.
그래서 git push 하는 순간 AI가 자동으로 코드를 분석하고 Slack으로 리뷰를 보내주는 환경을 만들었다.
고려했던 방법들
처음에는 PR이 올라오면 GitHub Actions에서 자동으로 리뷰가 달리는 걸 원했다.
방법을 찾아보니 크게 세 가지였다
1. Option A. GitHub Actions + Claude API 직접 호출
PR 오픈 시 Action이 트리거 → git diff 추출 → Claude API 호출 → PR에 리뷰 코멘트 게시
가장 깔끔한 자동화지만, Anthropic API 크레딧이 별도로 필요하다. Claude Code Max 구독과 완전히 다른 과금 체계다.
2. Option B. GitHub Actions + Claude Code CLI
CI 환경에서 Claude Code CLI를 직접 실행하는 방식. 하지만 Claude Code는 인터랙티브 툴이라 headless 환경에서 불안정하고, Max 구독을 CI에서 재사용하는 공식 방법이 없다.
3. Option C. 외부 서비스 (CodeRabbit 등)
GitHub App 설치만으로 즉시 사용 가능. 대신 모델 선택 자유도가 낮고 월 구독 비용이 추가된다.
최종 선택: pre-push hook + Claude Code CLI
추가 비용 없이 구현할 수 있는 현실적인 방법으로 로컬 pre-push hook을 선택했다.
- Claude Code Max 구독을 그대로 활용
- 팀원 각자의 Claude Code가 각자의 토큰으로 실행
- GitHub Actions 같은 서버 인프라 불필요
단점은 PR에 자동 코멘트가 달리지 않는다는 것인데, Slack 연동으로 보완했다.
구현
1. 기존 pre-push hook 구조
이미 push 전에 테스트와 빌드를 검증하는 hook이 hooks/pre-push에 있었다.
2. Claude 리뷰 로직 추가
빌드 검증 이후, push 직전에 Claude 리뷰를 실행한다.
간단한 android 용 룰을 프롬프트로 설정했다.
# Claude Code AI 코드 리뷰 (설치된 경우에만)
if command -v claude > /dev/null 2>&1; then
echo "🤖 Claude 코드 리뷰 중..."
UPSTREAM=$(git rev-parse --abbrev-ref --symbolic-full-name @{upstream} 2>/dev/null || echo "origin/develop")
DIFF=$(git diff "${UPSTREAM}"...HEAD -- "*.kt" 2>/dev/null | head -n 500)
if [ -z "$DIFF" ]; then
echo "ℹ️ 변경된 Kotlin 파일 없음 — 리뷰 스킵"
else
TMPFILE=$(mktemp)
cat > "$TMPFILE" << 'EOF'
You are reviewing Android Kotlin code for a project using MVI pattern + Clean Architecture.
Architecture rules:
- Composable layers must follow: Destination → Screen → View
- View composables must NOT have default values for the modifier parameter
- Each screen has a contract/ package with XxxEvent, XxxState, XxxEffect
- Two BaseVM implementations coexist (BaseVM with runningFold, BaseViewModel with MutableStateFlow) — flag inconsistency within a feature
Review for:
1. Architecture pattern violations
2. Potential bugs or missing error handling
3. Memory leaks (context leaks, unregistered listeners, coroutine scope issues)
4. Code quality issues
Respond in Korean. Format:
🔴 심각: [내용]
🟡 주의: [내용]
🟢 양호: [내용]
If nothing to flag, just say "🟢 전반적으로 양호합니다"
Diff:
EOF
printf '%s\n' "$DIFF" >> "$TMPFILE"
REVIEW=$(claude -p "$(cat "$TMPFILE")" 2>/dev/null)
rm -f "$TMPFILE"
if [ -n "$REVIEW" ]; then
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🤖 Claude 코드 리뷰 결과"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "$REVIEW"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "⚠️ 참고용 리뷰입니다. Push는 계속 진행됩니다."
echo ""
# Slack 전송 (local.properties에 slack.webhook.url이 설정된 경우)
SLACK_WEBHOOK_URL=$(grep "^slack.webhook.url=" local.properties 2>/dev/null | cut -d'=' -f2-)
if [ -n "$SLACK_WEBHOOK_URL" ]; then
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
AUTHOR=$(git config user.name 2>/dev/null)
ESCAPED_REVIEW=$(echo "$REVIEW" | sed 's/"/\\"/g' | tr '\n' ' ')
PAYLOAD=$(printf '{"text":"🤖 *Claude 코드 리뷰* — %s (%s)\n%s"}' "$BRANCH" "$AUTHOR" "$ESCAPED_REVIEW")
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" > /dev/null
echo "📨 Slack으로 리뷰 결과 전송됨"
fi
else
echo "⚠️ Claude 리뷰 실패 — 스킵"
fi
fi
else
echo "⚠️ Claude Code 미설치 — 리뷰 스킵"
fi
3. Slack 연동
터미널을 닫으면 리뷰 결과를 놓칠 수 있다. Slack으로 전송해 나중에도 확인할 수 있게 했다.
# Slack 전송 (local.properties에 slack.webhook.url이 설정된 경우)
SLACK_WEBHOOK_URL=$(grep "^slack.webhook.url=" local.properties 2>/dev/null | cut -d'=' -f2-)
if [ -n "$SLACK_WEBHOOK_URL" ]; then
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
AUTHOR=$(git config user.name 2>/dev/null)
ESCAPED_REVIEW=$(echo "$REVIEW" | sed 's/"/\\"/g' | tr '\n' ' ')
PAYLOAD=$(printf '{"text":"🤖 *Claude 코드 리뷰* — %s (%s)\n%s"}' "$BRANCH" "$AUTHOR" "$ESCAPED_REVIEW")
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" > /dev/null
echo "📨 Slack으로 리뷰 결과 전송됨"
fi
웹훅 URL은 환경변수나 ~/.zshrc에 넣는 방법도 있지만, local.properties에 저장하는 방식을 선택했다.
(어디에 넣을지는 자유)
4. hook 파일을 팀과 공유하는 방법
.git/hooks/는 git으로 공유가 안 된다. 그래서 hooks/ 폴더를 레포에 두고 팀원들이 직접 연결하는 방식을 쓴다.
실제 동작 결과


'Computer > Android&iOS' 카테고리의 다른 글
| git-push > AI 리뷰 2탄 (0) | 2026.05.06 |
|---|---|
| AAB로도 해결 안 됐던 문제 — audio-asset-pack 도입기 (0) | 2026.04.13 |
| Android) strings.xml 을 Dev(Debug), Staging, Release 로 따로 설정하는 방법 (0) | 2026.04.01 |
| [Android] ADB MCP + Claude Code 로 qa 자동화 하기 (0) | 2026.02.27 |
| 안드로이드 웹뷰 로그인이 자꾸 풀린다면? (0) | 2026.01.14 |