코드 리뷰는 미루면 쌓이고, 쌓이면 대충 보게 된다. 리뷰어가 두세 명뿐인 팀에서 PR이 하루 열 개씩 올라오면 “LGTM”이 사실상 자동 응답이 되는 순간이 온다. 그렇다고 사람을 늘릴 수도 없다. 이럴 때 NPE 가능성이나 SQL 인젝션처럼 기계가 더 잘 잡는 것들만이라도 자동으로 걸러주면, 사람은 설계와 도메인 판단에 집중할 수 있다. 이번 포스팅에서는 알리바바가 오픈소스로 공개한 open-code-review를 GitHub Actions에 붙여 PR마다 AI 코드 리뷰를 자동으로 받는 방법을 설치부터 노이즈 제어까지 정리하고자 한다.

AI 코드 리뷰 도구, 왜 하필 하이브리드인가

AI 코드 리뷰 도구를 고를 때 핵심 질문은 “LLM에게 얼마나 맡기느냐”다. diff를 통째로 프롬프트에 넣고 리뷰를 받는 방식은 만들기는 쉽지만, 코멘트가 엉뚱한 줄에 붙거나 매번 다른 소리를 한다. open-code-review가 택한 방식은 하이브리드다. 파일 선택, 번들링, 규칙 매칭, 코멘트 위치 계산은 결정론적 파이프라인이 담당하고, 판단이 필요한 부분만 LLM 에이전트에게 넘긴다.
이 구조 덕분에 “몇 번째 줄에 어떤 문제가 있다”는 위치 정확도가 프롬프트 품질과 무관하게 유지된다. 저장소에는 NPE, 스레드 안전성, XSS, SQL 인젝션 같은 항목이 파인튜닝된 내장 룰셋으로 들어 있어, 모델이 매번 처음부터 판단하지 않아도 된다. 알리바바 사내 규모에서 검증됐다는 점과 Apache-2.0 라이선스라는 점도 사내 도입 시 결재를 통과시키기 쉬운 조건이다. 실제로 저장소는 스타 1만 5천 개를 넘겼고 지금도 활발히 갱신되고 있다.
선택지를 넓게 보면 상용 도구도 있다. 어느 쪽이 맞는지는 팀 상황에 따라 갈린다.

| 기준 | open-code-review | CodeRabbit | Claude Code Action |
|---|---|---|---|
| 비용 | 무료(LLM 토큰 비용만) | 유료 구독 | API 비용 + 플랜 조건 |
| 라이선스 | Apache-2.0 오픈소스 | 상용 | 상용 |
| LLM 선택 | OpenAI/Anthropic 호환 자유 | 벤더 고정 | Anthropic 고정 |
| 온프레미스 | 가능(self-hosted runner) | 제한적 | 제한적 |
| CI 연동 예시 | GitHub, GitLab, Gerrit, Bitbucket, GitFlic 등 | 주요 호스트 전반 | GitHub 중심 |
표에서 갈리는 지점은 결국 통제권이다. 사내망에서 LLM 트래픽을 밖으로 내보낼 수 없거나, 모델을 직접 고르고 싶거나, 도구 비용 없이 토큰 값만 내고 싶다면 open-code-review 쪽이 맞다. 반대로 설정에 시간을 쓰기 싫고 결제로 해결하고 싶다면 상용 도구가 빠르다. CI 연동 예시는 저장소의 examples 디렉터리에 플랫폼별로 들어 있으니, 쓰는 호스트가 있는지 먼저 확인하면 도입 판단이 빨라진다.
로컬에서 먼저 돌려보고 판단하기
CI에 붙이기 전에 로컬에서 한 번 돌려보는 편이 좋다. 리뷰 품질이 팀 코드베이스에 맞는지 확인하지 않고 전사 적용하면, 쓸모없는 코멘트가 PR마다 쌓여 오히려 신뢰를 잃는다. 설치는 npm 전역 설치 한 줄이면 끝난다.
npm install -g @alibaba-group/open-code-review
ocr versionShellScript설치하면 ocr 명령을 쓸 수 있다. npm 외에 설치 스크립트(install.sh, install.ps1)나 GitHub Release 바이너리로도 받을 수 있고, Go로 작성돼 있어 소스 빌드도 가능하다.
다음은 LLM 공급자와 모델을 지정한다. 대화형 설정으로 고르거나, 환경 변수로 직접 지정할 수 있다.
# 대화형 설정
ocr config provider
ocr config model
# 또는 환경 변수로 직접 지정
export OCR_LLM_URL="https://api.anthropic.com"
export OCR_LLM_TOKEN="sk-ant-..."
export OCR_LLM_MODEL="claude-sonnet-5"
export OCR_USE_ANTHROPIC="true"ShellScript여기서 OCR_USE_ANTHROPIC가 중요하다. Anthropic 계열이면 true, OpenAI 호환 API면 false로 두는데, 이 값에 따라 요청 포맷이 갈리므로 헷갈리면 인증은 통과해도 응답 파싱에서 깨진다. 사내 게이트웨이처럼 OpenAI 호환 엔드포인트를 쓰는 경우엔 false가 맞다.
이제 실제로 리뷰를 돌려본다. 작업 중인 변경사항, 브랜치 간 diff, 특정 커밋 등 대상을 골라 실행할 수 있다.
# 현재 변경사항(staged/unstaged) 리뷰
ocr review
# 브랜치 간 비교 — 실제 PR과 가장 가까운 형태
ocr review --from main --to feature/payment-refactor
# 특정 커밋 하나만
ocr review --commit abc123
# 저장소 전체 파일 스캔(diff가 아닌 전수 검사)
ocr scanShellScript--from/--to 조합이 PR 리뷰와 가장 비슷한 결과를 준다. 팀에서 최근에 머지한 PR 브랜치를 대상으로 몇 번 돌려보면, 실제로 잡아낼 문제를 잡는지 아니면 스타일 지적만 늘어놓는지 금방 감이 온다. 이 단계에서 별로라고 판단되면 CI에 붙이지 않는 게 낫다.
GitHub Actions에 붙여 PR마다 자동 리뷰 받기

로컬 검증이 끝났다면 CI 연동은 워크플로 파일 하나로 끝난다. open-code-review는 재사용 가능한 composite action을 공식 제공하므로, 설치·diff 계산·코멘트 게시를 직접 스크립트로 짤 필요가 없다. .github/workflows/ocr-review.yml을 만들고 아래처럼 작성한다.
name: OpenCodeReview PR Review
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write # 인라인 코멘트 게시에 필요
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: alibaba/open-code-review@main
with:
llm_url: ${{ secrets.OCR_LLM_URL }}
llm_auth_token: ${{ secrets.OCR_LLM_AUTH_TOKEN }}
llm_model: ${{ vars.OCR_LLM_MODEL }}
llm_use_anthropic: ${{ vars.OCR_LLM_USE_ANTHROPIC }}
language: 한국어YAML이 워크플로는 PR이 열리거나 갱신될 때마다 액션을 실행한다. 액션 내부에서 저장소 체크아웃, ocr 설치, git merge-base 계산, ocr review --format json 실행, PR 인라인 코멘트 게시까지 모두 처리하므로 별도 체크아웃 스텝조차 필요 없다. language 입력에 한국어를 주면 리뷰 코멘트가 한국어로 달린다.
토큰과 모델명은 저장소 설정에 등록한다. Settings → Secrets and variables → Actions에서 OCR_LLM_URL과 OCR_LLM_AUTH_TOKEN은 Secrets로, OCR_LLM_MODEL과 OCR_LLM_USE_ANTHROPIC는 Variables로 넣으면 된다. 시크릿 등록 방법이 낯설다면 GitHub Actions 시크릿 공식 문서를 참고하면 된다. 재현성이 중요한 팀이라면 @main 대신 버전 태그나 커밋 SHA로 고정하는 편이 안전하다.
여기서 눈여겨볼 부분은 트리거가 pull_request가 아니라 pull_request_target이라는 점이다. 포크에서 올라온 PR에서도 시크릿에 접근하려면 이 트리거가 필요한데, GitHub 공식 문서의 이벤트 설명에서도 경고하듯 일반적으로는 위험한 선택으로 알려져 있다. 액션이 PR 코드를 실행하지 않고 diff만 읽기 때문에 성립하는 구조이며, 실제로 액션은 신뢰된 base를 체크아웃한 뒤 PR head의 blob만 따로 가져와 작업 트리에 신뢰할 수 없는 파일을 풀어놓지 않는다. 직접 워크플로를 개조할 때 이 전제를 깨지 않도록 주의해야 한다.
코멘트 폭탄을 막는 노이즈 제어 설정

AI 코드 리뷰 자동화가 실패하는 가장 흔한 이유는 품질이 아니라 양이다. PR 하나에 코멘트가 40개씩 달리면 팀은 곧 봇을 무시하기 시작한다. open-code-review는 이 문제를 severity 라우팅과 증분 리뷰라는 두 가지 스위치로 다룬다.
- uses: alibaba/open-code-review@main
with:
llm_url: ${{ secrets.OCR_LLM_URL }}
llm_auth_token: ${{ secrets.OCR_LLM_AUTH_TOKEN }}
llm_model: ${{ vars.OCR_LLM_MODEL }}
llm_use_anthropic: ${{ vars.OCR_LLM_USE_ANTHROPIC }}
# medium 이하 심각도는 인라인 대신 요약 코멘트로 몰아넣는다
route_severity_below: medium
# 스타일·문서 지적도 요약으로 이동
route_categories: style,documentation
# 이미 코멘트가 달린 위치는 다시 달지 않는다
incremental: 'true'
# 요약 코멘트는 새로 달지 않고 기존 것을 갱신
sticky_summary: 'true'YAMLroute_severity_below: medium은 medium과 low 심각도 지적을 인라인에서 빼내 PR 요약 코멘트로 보낸다. 지적 자체를 버리는 게 아니라 위치만 옮기는 fail-open 정책이라, 놓치는 것 없이 화면만 조용해진다. route_categories는 같은 일을 카테고리 기준으로 한다. bug, security, performance, maintainability, test, style, documentation, other 중에서 고를 수 있다.
incremental: 'true'는 push가 반복될 때 특히 효과가 크다. 이미 봇 코멘트가 달린 (파일, 라인 범위)와 겹치는 지적은 건너뛰므로, 커밋을 열 번 밀어 넣어도 같은 잔소리가 열 번 쌓이지 않는다. 겹침 판정은 IoU 임계값(incremental_overlap_threshold, 기본 0.6)으로 조절한다. 기존 코멘트를 지우지 않는 비파괴 방식이라 리뷰 히스토리도 보존된다.
이 값들은 문자열로 넘겨야 한다. 'true'처럼 따옴표를 씌우지 않으면 YAML이 boolean으로 해석해 문자열 비교 조건에 걸리지 않는 경우가 있으니, 예시대로 따옴표를 유지하는 편이 안전하다.
팀 규칙을 봇에게 가르치는 커스텀 룰

내장 룰셋만으로는 팀 고유의 관례를 잡지 못한다. “Controller에서 Entity를 그대로 반환하지 말 것”, “@Transactional은 Service 계층에만” 같은 규칙은 리뷰에서 반복적으로 지적되는데도 매번 사람이 말해야 한다. open-code-review는 저장소 루트의 .opencodereview/rule.json에 규칙을 적어두면 별도 플래그 없이 자동으로 읽어간다.
{
"include": ["src/**/*.{java,kt}"],
"exclude": ["**/generated/**", "**/*Dto.java"],
"rules": [
{
"path": "**/controller/**/*.java",
"rule": "Controller에서 Entity를 그대로 반환하지 말 것. 응답은 반드시 DTO로 변환한다."
},
{
"path": "**/*mapper*.xml",
"rule": "SQL 인젝션 위험, 파라미터 오류, 태그 누락을 점검할 것."
}
]
}JSON파일은 세 필드로 구성된다. exclude는 리뷰에서 제외할 glob 패턴이고, include는 내장 기본 제외 패턴(테스트 파일 등)을 우회해 강제로 포함시킬 패턴이다. 핵심은 rules 배열로, path glob이 매칭되는 파일에 대해 해당 rule 문장을 프롬프트로 넘긴다. 선언 순서대로 평가해 처음 매칭된 항목이 이기므로, 구체적인 규칙을 위에 두어야 한다.
규칙은 네 단계 우선순위로 해석된다. --rule 플래그가 가장 높고, 그다음이 프로젝트의 .opencodereview/rule.json, 사용자 전역 ~/.opencodereview/rule.json, 마지막이 바이너리에 내장된 시스템 기본값이다. 상위 계층 파일이 없으면 조용히 다음 단계로 넘어가므로, 아무것도 만들지 않아도 내장 규칙으로 동작한다.
CI에서 저장소 표준 규칙 대신 다른 파일을 쓰고 싶다면 rule 입력으로 경로를 넘긴다.
- uses: alibaba/open-code-review@main
with:
llm_url: ${{ secrets.OCR_LLM_URL }}
llm_auth_token: ${{ secrets.OCR_LLM_AUTH_TOKEN }}
llm_model: ${{ vars.OCR_LLM_MODEL }}
llm_use_anthropic: ${{ vars.OCR_LLM_USE_ANTHROPIC }}
rule: .opencodereview/ci-rule.json
review_concurrency: '4'YAML룰 파일을 저장소에 함께 두면 규칙이 코드처럼 리뷰와 버전 관리를 거친다는 점이 크다. 새 규칙을 추가할 때도 PR로 논의할 수 있어, 암묵지로 남아 있던 팀 관례가 문서화되는 부수 효과가 있다. review_concurrency는 병렬 처리 수로, 큰 PR에서 리뷰 시간을 줄이는 대신 LLM 요청이 몰리므로 레이트 리밋과 균형을 봐야 한다.
규칙을 손본 뒤에는 실제로 어떤 파일이 리뷰 대상이 되는지 미리 확인하는 편이 좋다. 토큰을 쓰지 않고 필터 결과만 출력하는 옵션이 있다.
ocr review --previewShellScript이 명령은 LLM을 호출하지 않고 파일 필터 결과만 보여준다. 참고로 **/*_test.go, **/*Test.java, **/*.test.{js,ts} 같은 테스트 파일은 기본 제외 목록에 들어 있어, 테스트 코드도 리뷰받고 싶다면 include에 명시적으로 넣어야 한다.
리뷰 결과를 후속 처리하고 싶다면 액션이 내보내는 출력값을 쓴다. comments_total, comments_inline, comments_skipped, comments_routed, comments_failed, summary_comment_url이 제공되므로, 심각도 높은 지적이 일정 수를 넘으면 잡을 실패시키거나 슬랙으로 알리는 식의 게이트를 붙일 수 있다.
사내망에서 LLM 트래픽을 밖으로 안 내보내려면

보안 정책상 코드가 외부 API로 나가면 안 되는 조직이라면 self-hosted runner에서 돌리는 선택지가 있다. GitHub 호스팅 러너 대신 사내 러너를 쓰고, LLM 엔드포인트도 사내 게이트웨이나 로컬 모델로 지정하면 코드와 토큰이 회사 밖으로 나가지 않는다.
jobs:
review:
runs-on: self-hosted
container:
image: node:24
steps:
- name: Trust workspace
run: git config --global --replace-all safe.directory '*'
- uses: alibaba/open-code-review@main
with:
llm_url: ${{ secrets.INTERNAL_LLM_GATEWAY }}
llm_auth_token: ${{ secrets.INTERNAL_LLM_TOKEN }}
llm_model: ${{ vars.OCR_LLM_MODEL }}
llm_use_anthropic: 'false'YAML컨테이너 안에서 실행할 때는 git이 작업 디렉터리 소유권을 문제 삼아 “dubious ownership” 오류를 내므로, safe.directory 설정을 먼저 해줘야 한다. 이때 --add가 아니라 --replace-all을 쓰는 이유는 여러 러너에서 반복 실행할 때 전역 git 설정에 항목이 무한정 쌓이지 않게 하기 위해서다.
사내 게이트웨이가 OpenAI 호환 규격이라면 llm_use_anthropic을 'false'로 두면 된다. open-code-review 프로젝트 자체도 자기네 CI를 self-hosted runner에서 node:24 컨테이너로 돌리고 있어, 저장소의 워크플로 파일이 실전 참고 자료가 된다.
FAQ
AI 코드 리뷰가 사람 리뷰어를 대체할 수 있는가?
대체가 아니라 분업으로 보는 편이 맞다. AI 코드 리뷰는 NPE 가능성, 스레드 안전성, SQL 인젝션처럼 패턴이 명확한 결함을 지치지 않고 잡아낸다. 반면 이 설계가 도메인에 맞는지, 이 추상화가 6개월 뒤에도 유지될지 같은 판단은 여전히 사람의 몫이다. 기계가 잘하는 것을 넘겨서 사람이 설계 리뷰에 시간을 쓰게 만드는 것이 목적이다.
open-code-review는 무료인가, 비용이 얼마나 드는가?
도구 자체는 Apache-2.0 오픈소스라 라이선스 비용이 없다. 실제 비용은 리뷰에 쓰는 LLM 토큰 값이며, 어떤 모델을 연결하느냐에 따라 달라진다. 사내 게이트웨이나 로컬 모델을 연결하면 외부 과금 없이 운영할 수도 있다. 상용 도구처럼 개발자 시트당 월 구독료를 내는 구조가 아니라는 점이 가장 큰 차이다.
포크에서 올라온 PR도 리뷰할 수 있는가?
가능하다. 공식 예시 워크플로가 pull_request_target 트리거를 쓰는 이유가 바로 포크 PR에서도 시크릿에 접근하기 위해서다. 이 트리거는 보통 위험하지만, 액션이 PR 코드를 실행하지 않고 신뢰된 base를 체크아웃한 뒤 diff만 읽기 때문에 성립한다. 워크플로를 직접 수정할 때 PR 코드를 빌드하거나 실행하는 스텝을 추가하면 이 전제가 깨지므로 주의해야 한다.
리뷰 코멘트가 너무 많이 달리면 어떻게 줄이는가?
route_severity_below와 route_categories로 낮은 심각도나 스타일·문서 지적을 인라인에서 요약 코멘트로 옮기고, incremental: 'true'로 이미 코멘트가 달린 위치의 중복 지적을 막는다. 두 정책 모두 지적을 삭제하지 않고 위치만 바꾸거나 건너뛰는 방식이라, 노이즈는 줄이면서 놓치는 항목은 만들지 않는다.
리뷰 대상 diff는 어떻게 정해지는가?
액션은 git merge-base로 base 브랜치와 head 커밋의 공통 조상을 구한 뒤, 그 지점부터 head까지를 ocr review --from <merge-base> --to <head>로 리뷰한다. base 브랜치에 다른 커밋이 머지돼도 그 변경까지 리뷰 대상에 끌려들어오지 않는다는 뜻이다. 로컬에서 같은 형태로 확인하려면 ocr review --from main --to <브랜치>를 쓰면 된다.
테스트 코드는 왜 리뷰에서 빠지는가?
**/*_test.go, **/*Test.java, **/*.test.ts 같은 테스트 파일 패턴이 내장 기본 제외 목록에 들어 있기 때문이다. 테스트 코드도 리뷰받고 싶다면 .opencodereview/rule.json의 include에 해당 패턴을 명시하면 기본 제외를 우회한다. 어떤 파일이 실제로 리뷰 대상이 되는지는 ocr review --preview로 토큰 소비 없이 미리 확인할 수 있다.
마치며

예전 팀에서 리뷰 자동화를 처음 붙였을 때, 첫 주에 봇을 꺼달라는 요청을 받았다. 코멘트가 너무 많아서였다. 도구가 나빠서가 아니라 전부 인라인으로 쏟아붓게 설정한 내 잘못이었는데, severity 라우팅과 증분 옵션을 켜고 나서야 팀이 봇 코멘트를 다시 읽기 시작했다. 자동화는 켜는 것보다 시끄럽지 않게 만드는 게 어렵다는 걸 그때 배웠다. AI 코드 리뷰를 도입한다면 첫날부터 노이즈 제어를 같이 설정하기를 권한다. 지금까지 AI 코드 리뷰를 GitHub Actions에 자동화하는 방법에 대해서 정리해 보았다.
