Claude Code CLI + GitLab CI를 활용한 MR 자동 리뷰 시스템
전체 흐름
MR 생성 → GitLab CI 트리거 → git diff 추출 → Claude CLI -p → MR 노트 등록
설계 결정 사항
API key vs CLI 구독
Anthropic API key(토큰 과금) 방식도 가능하지만, CI에서 MR마다 전체 diff를 전송하면 비용이 예측하기 어렵다.
Claude Pro/Max 구독의 CLI를 사용하면 구독 한도 내에서 추가 비용 없이 사용 가능하다.
→ CLAUDE_CODE_OAUTH_TOKEN으로 구독 인증, claude -p non-interactive 모드로 호출
세션 관리 (--resume 고려)
OpenClaw 등에서는 claude -p --resume sessionId로 세션을 유지하며 컨텍스트를 이어가는 방식을 사용한다.
코드 리뷰는 MR 단위로 독립적인 요청이므로 세션 유지가 불필요하다.
→ 매 MR마다 새 프로세스로 실행, --resume 미사용
최초 구축 가이드
1. Node.js 및 Claude Code CLI 설치
Runner 머신(Rocky Linux 9)에 설치한다.
dnf module enable nodejs:20 -y && dnf install nodejs -y
npm install -g @anthropic-ai/claude-code
claude 실행 후 브라우저 OAuth 로그인 (Claude Pro/Max 구독 사용)
2. OAuth 토큰 추출
cat ~/.claude/.credentials.json
claudeAiOauthToken.accessToken 값 복사
3. GitLab CI Variables 등록
프로젝트 → Settings > CI/CD > Variables
| Key | 설명 | Masked |
|---|---|---|
CLAUDE_CODE_OAUTH_TOKEN |
2번에서 복사한 accessToken | ✅ |
GITLAB_TOKEN |
Personal Access Token, api scope |
✅ |
4. Shell Executor Runner 등록
프로젝트 → Settings > CI/CD > Runners → New project runner
태그: mr-code-review-runner
Runner 머신에서 등록:
gitlab-runner register \
--url https://gitlab.example.com \
--token <발급받은토큰> \
--executor shell \
--non-interactive
gitlab-runner start
5. 프롬프트 파일 작성
프로젝트 루트에 .claude-review-prompt.txt 생성
You are a code reviewer for the Felice project (Kafka monitoring platform), built with Java and Spring Boot.
Review the following git diff and provide feedback in Korean.
[Felice 특화]
- ~~~이 변경된 경우
반드시 상단에 "⚠️ 프론트엔드 팀 전달 필요: [변경 내용 요약]" 형태로 경고
[JPA]
- N+1 쿼리 가능성
- 카르테시안 곱 (복수 컬렉션 fetch join 동시 사용)
- FetchType.EAGER 남용
- 영속성 컨텍스트 범위 밖 지연 로딩
[보안]
- SQL 인젝션, XSS 가능성
- 민감정보 하드코딩 (비밀번호, API 키 등)
- 인증/인가 누락
[예외 처리]
- 예외 삼키기 (catch 블록이 비어있거나 로그만 찍는 경우)
- NPE 가능성
- 부적절한 예외 타입 사용
[성능]
- 컬렉션 루프 안에서 무거운 연산 또는 DB 호출
[동시성]
- 공유 자원 동기화 누락
- Thread-safe하지 않은 자료구조 사용 (예: HashMap → ConcurrentHashMap)
[메모리]
- 순환참조
- Stream/Connection/리스너 등 자원 미닫기 (메모리 릭)
[가독성/유지보수]
- SRP 위반 (메서드/클래스 책임 과다)
- 매직넘버/매직스트링
- 코드 중복
- 복잡한 로직에 주석 없음
- 컨벤션 위반 (Java, Spring Boot 기준)
[테스트]
- 변경된 로직에 테스트 코드 누락
[출력 형식]
- 문제가 없으면 "✅ 특이사항 없음" 한 줄만 출력
- 문제가 있으면 아래 형식으로만 출력, 칭찬이나 요약 없이 문제점만 나열
⚠️ [WARN] 파일명:라인 - 문제 설명
ℹ️ [INFO] 파일명:라인 - 문제 설명
6. .gitlab-ci.yml 수정
workflow rules에 MR 이벤트 추가:
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: always
# ... 기존 rules
stages에 review 추가:
stages:
- build
- deploy
- clean
- sync
- review
clean 잡 수정 (MR 파이프라인 충돌 방지):
clean:local:docker:images:
stage: clean
needs:
- job: build:docker:image:and:push:local
optional: true # 추가
mr-code-review 잡 추가:
mr-code-review:
stage: review
tags:
- mr-code-review-runner
before_script: []
variables:
GIT_DEPTH: 0
only:
- merge_requests
script:
- git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
- DIFF=$(git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD)
- PROMPT=$(cat .claude-review-prompt.txt)
- REVIEW=$(echo "$DIFF" | claude -p "$PROMPT")
- |
curl --request POST \
"https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes" \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"body\": $(echo "$REVIEW" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')}"
allow_failure: true
다른 프로젝트에 추가하는 방법
Runner는 여러 프로젝트에서 공유할 수 있다. 이미 등록된 Runner를 그대로 활용하면 된다.
1. Runner 공유
프로젝트 → Settings > CI/CD > Runners
기존 mr-code-review-runner를 다른 프로젝트에 할당하거나, Runner 관리자가 프로젝트에 추가한다. Runner 머신에 Claude CLI가 이미 설치되어 있으므로 별도 설치 불필요.
2. CI Variables 등록
| Key | 설명 | Masked |
|---|---|---|
CLAUDE_CODE_OAUTH_TOKEN |
공용 계정 accessToken | ✅ |
GITLAB_TOKEN |
본인 Personal Access Token, api scope |
✅ |
3. 프롬프트 및 CI 설정 추가
프로젝트 루트에 .claude-review-prompt.txt 추가 (프로젝트 특성에 맞게 수정)
.gitlab-ci.yml에 workflow rules, stages, mr-code-review 잡 추가. 잡의 tags는 mr-code-review-runner로 지정.
트러블슈팅
Docker executor에서 claude: not found
기존 Runner가 Docker executor라 컨테이너 안에 Claude CLI 없음 → claude: not found
컨테이너 안에서 설치는 가능하나, OAuth 토큰이 없어 구독 인증 불가.
→ Shell executor Runner를 별도 등록해서 로컬에 설치된 Claude CLI 직접 사용
fatal: no merge base (GIT_DEPTH: 1)
기본 설정 GIT_DEPTH: 1로 shallow clone 시 타겟 브랜치와 머지 베이스를 찾지 못함.
→ mr-code-review 잡에 GIT_DEPTH: 0 설정으로 full clone
clean 잡이 MR 파이프라인을 블록
clean 잡이 needs: build:docker:image:and:push:local인데 MR 파이프라인엔 해당 잡이 없어 파이프라인 실패.
→ needs.optional: true 추가
동작 시점
- MR 생성 시 자동 실행
- MR 브랜치에 추가 커밋 push 시 자동 실행
- 일반 브랜치 push, 머지 실행 시에는 동작하지 않음
allow_failure: true— 리뷰 잡 실패해도 MR 블록 안 됨
주의사항
CLAUDE_CODE_OAUTH_TOKEN은 만료될 수 있어 주기적으로 갱신 필요- 공용 계정 사용 시 구독 한도를 팀이 함께 소진. 사용량 증가 시 팀/엔터프라이즈 구독 전환 고려
- 외부 코드 전송이 우려되는 경우 AWS Bedrock 또는 GCP Vertex AI 경유 검토
- 스크린샷 등에 토큰 노출 시 즉시 Runner 삭제 후 재발급