이 레포가 무엇을 하는 물건인가.
open-code-review(줄여서 OCR, 명령어도 ocr)는 Git의 변경 내역(diff)을 읽어 AI가 줄 단위로 정확한 리뷰 코멘트를 달아주는 명령줄 도구입니다. "어떤 파일을 볼지, 코멘트를 어느 줄에 붙일지"처럼 절대 틀리면 안 되는 단계는 일반 프로그래밍 로직(결정론적 엔지니어링)이 책임지고, "이 코드에 버그가 있는가?"라는 판단만 LLM 에이전트에게 시킵니다.
터미널에서 ocr review 한 줄을 치면, 지금 작업 중인 변경사항(스테이징·미스테이징·새 파일 전부)을 모아 LLM에게 보내고, 결과를 "몇 번째 줄의 이 코드가 왜 문제이고 어떻게 고치면 되는지" 형태로 돌려받습니다. Anthropic(Claude)·OpenAI 호환 API 어느 쪽이든 모델 주소와 키만 설정하면 바로 동작합니다.
출신이 특이합니다. 처음부터 오픈소스로 만든 프로젝트가 아니라, 알리바바 그룹의 공식 사내 AI 코드리뷰 비서로 2년간 운영되며 수만 명의 개발자에게 쓰이고 수백만 건의 결함을 찾아낸 뒤, 그 운영 데이터를 바탕으로 다듬어 2026년 5월에 공개된 물건입니다. 공개 3주 만에 ★4.6k를 모으며 TrendShift 상위권에 올랐습니다.
트렌딩 이유 · 범용 에이전트 대비 무엇이 다른가.
Claude Code 같은 범용 코딩 에이전트에 "리뷰해줘"라고 시켜본 사람이라면 누구나 겪는 세 가지 고질병이 있습니다. README가 첫머리에서 정면으로 지적하는 내용입니다.
① 커버리지 구멍 — 변경 파일이 많으면 에이전트가 "대충 중요한 것만" 골라 보고 나머지를 건너뜁니다. ② 위치 드리프트 — "이 줄에 문제 있어요"라고 하는데 실제 코드 위치와 줄 번호가 어긋납니다. ③ 품질 널뛰기 — 자연어 프롬프트 기반이라 디버깅이 어렵고, 프롬프트 한 줄 차이로 리뷰 품질이 크게 흔들립니다.
근본 원인은 하나 — 순수 언어 구동 구조에는 절차를 강제할 "딱딱한 제약"이 없다는 것.
틀리면 안 되는 단계(파일 선정·관련 파일 묶기·규칙 매칭·코멘트 위치 잡기)는 일반 코드가 100% 보장하고, LLM은 "판단과 동적 컨텍스트 수집"이라는 잘하는 일에만 투입합니다. 파일 묶음마다 독립된 컨텍스트의 서브에이전트가 붙으므로 대형 변경에서도 빠짐없이, 병렬로 돌아갑니다.
| 비교 항목 | 범용 에이전트 + 프롬프트 | open-code-review |
|---|---|---|
| 리뷰 대상 선정 | LLM이 알아서 (누락 발생) | 코드가 결정 — 필터·번들링 로직이 보장 |
| 코멘트 위치 | LLM이 말한 줄 번호 그대로 (드리프트) | 슬라이딩 윈도우 매칭 + 실패 시 LLM 재배치 2단계 |
| 리뷰 규칙 | 프롬프트에 통째로 욱여넣기 | 파일 경로별 규칙 매칭 — 자바엔 자바 규칙만 |
| 대형 변경 | 컨텍스트 한도에 걸려 부실해짐 | 파일 묶음별 서브에이전트 분할 정복 + 동시 실행 |
| 검증 근거 | — | 알리바바 사내 2년 · 수만 사용자 운영 데이터 |
여기에 에이전트의 도구 세트 자체를 대규모 운영 데이터로 설계했다는 점이 흥미롭습니다. 도구 호출 빈도 분포, 도구별 반복률, 새 도구가 전체 호출 체인에 주는 영향을 분석해서 — 범용 에이전트의 만능 도구함 대신 코드리뷰 전용으로 깎아낸 6개 도구만 남겼습니다. 도구가 적을수록 LLM이 헤맬 공간도 줄어듭니다.
범용 에이전트에게 리뷰를 맡기는 건 만능 수리공에게 "집 전체 점검해줘"라고 하는 것과 같습니다 — 실력은 있지만 어느 방을 봤는지, 빠뜨린 곳은 없는지 알 수 없죠. OCR은 점검표를 들고 방마다 배정된 전문 검사원을 보내는 방식입니다. 어떤 방(파일)을 누가 보는지는 회사 규정(코드)이 정하고, 검사원(LLM)은 방 안에서의 판단만 합니다.
Go 단일 바이너리 + 외부 의존성 최소 구성.
프론트엔드 프레임워크도, 데이터베이스도 없습니다. Go 1.25로 빌드한 단일 실행 파일이 전부이고, 결과 열람용 웹 뷰어조차 Go 표준 라이브러리의 HTML 템플릿으로 만들었습니다. "CLI 도구는 이렇게 만든다"의 교과서 같은 구성입니다.
| 레이어 | 기술 / 버전 | 역할 |
|---|---|---|
| 언어·런타임 | Go 1.25 | 전체 구현. 단일 바이너리로 컴파일되어 설치가 곧 복사. |
| LLM 연동 | anthropic-sdk-go · openai-go | Claude(Anthropic)와 OpenAI 호환 API 양쪽 모두 공식 SDK로 지원. |
| 토큰 계산 | tiktoken-go (cl100k_base 내장) | 프롬프트가 모델 한도를 넘는지 사전 계산. 대화가 길어지면 압축 트리거. |
| 경로 매칭 | doublestar v4 | **/*mapper*.xml 같은 글롭 패턴으로 파일별 규칙 매칭. |
| 관측성 | OpenTelemetry (OTLP gRPC) | 스팬·메트릭 수출. 사내 대규모 운영의 흔적 — 기본은 꺼짐. |
| 배포 | npm 래퍼 + GitHub Releases | npm i -g @alibaba-group/open-code-review 한 줄 설치. npm 패키지가 OS별 바이너리를 받아주는 방식. |
| 웹 뷰어 | Go html/template + 순수 CSS | ocr viewer로 리뷰 세션 기록(JSONL)을 브라우저에서 열람. |
diff 한 덩어리가 줄 단위 코멘트가 되기까지.
전체 그림부터 봅시다. ocr review를 치면 데이터가 아래 라인을 따라 흐릅니다. 회색 글씨로 표시한 왼쪽 단계들은 전부 일반 Go 코드가 결정하고, LLM은 가운데 "에이전트 루프" 안에서만 일합니다.
"내가 자바 파일 3개를 고치고 ocr review를 쳤다"는 상황 하나를 입구부터 출구까지 추적해 봅니다. 이 한 줄기만 이해하면 나머지는 변주입니다.
① 수집 — internal/diff가 git을 호출해 스테이징·미스테이징·새 파일의 변경분을 Unified Diff (유니파이드 디프, +/- 줄로 표현한 변경 기록) 형태로 모읍니다. ② 필터 — 바이너리, 테스트 파일(*Test.java 등), 지원하지 않는 확장자를 코드 레벨에서 걸러냅니다. LLM에게 "이건 보지 마"라고 부탁하는 게 아니라 아예 보내지 않습니다. ③ 규칙 매칭 — **/*.java 패턴이 걸리므로 내장 점검표 java.md(NPE·스레드 안전성·N+1 쿼리 등)가 이 파일의 리뷰 기준으로 붙습니다. ④ 번들링 — 서로 관련된 파일(예: message_en.properties와 message_zh.properties)은 한 묶음으로 묶고, 묶음마다 서브에이전트를 배정해 기본 8개까지 동시에 돌립니다.
⑤ 에이전트 루프 — 변경이 50줄을 넘으면 본 리뷰 전에 PLAN_TASK라는 별도 LLM 호출로 "위험 지점과 조사 계획"을 JSON으로 먼저 뽑습니다. 그다음 MAIN_TASK 에이전트가 diff와 점검표를 받아, 필요하면 file_read(주변 코드 읽기)·code_search(저장소 검색)·file_read_diff(다른 변경 파일 보기)로 컨텍스트를 모으고, 문제를 확신하면 code_comment 도구를 호출합니다. 도구 호출은 최대 30회로 제한되고, 대화가 길어지면 MEMORY_COMPRESSION_TASK가 지금까지의 발견을 요약해 컨텍스트를 비웁니다.
⑥ 위치 결정 — 이 레포의 백미. LLM은 줄 번호를 자주 틀리기 때문에, OCR은 줄 번호를 아예 신뢰하지 않습니다. 대신 code_comment가 "문제가 된 코드 조각 원문(existing_code)"을 받아 슬라이딩 윈도우 알고리즘으로 diff 텍스트에서 그 조각과 일치하는 연속 줄을 찾아 코멘트를 박습니다. 텍스트 매칭이 실패하면? RE_LOCATION_TASK라는 작은 LLM 호출이 "diff에서 코멘트가 가리키는 최소 범위를 글자 그대로 다시 뽑아라"를 수행해 한 번 더 시도합니다. ⑦ 출력 — 결과는 터미널(text) 또는 CI용 json으로 나가고, LLM과 주고받은 전 과정이 JSONL로 남아 ocr viewer에서 복기할 수 있습니다. (에러 처리·토큰 한도 분기 같은 곁가지는 여기선 생략합니다.)
task_template.json 한 파일에 들어 있습니다. 프롬프트를 코드에서 분리하면 버전 관리·실험·튜닝이 쉬워집니다 — "대본은 대본집에, 배우는 무대에".⑥의 위치 결정은 책에 포스트잇 붙이기와 같습니다. "37쪽에 오타"라고 기억하면(줄 번호 방식) 개정판에서 바로 어긋나지만, "'그는 말했다'로 시작하는 문단"이라고 기억하면(코드 조각 매칭) 쪽수가 바뀌어도 찾아갈 수 있습니다. 그래도 못 찾으면 사서(RE_LOCATION_TASK)에게 "이 메모가 가리키는 문장을 원문에서 그대로 뽑아줘"라고 한 번 더 부탁하는 거죠.
어떤 폴더가 무슨 일을 하나 — 표준적인 Go CLI 레이아웃.
| 위치 | 왜 들여다볼 가치가 있나 |
|---|---|
internal/agent/agent.go | 에이전트 루프의 모든 것 — 서브태스크 분배, 동시 실행 워커풀, 플랜 단계 진입 조건. |
internal/diff/relocation.go | 코멘트 위치 매칭 실패 시 LLM으로 재배치하는 2차 방어선. |
internal/config/template/task_template.json | 프로덕션에서 검증된 코드리뷰 프롬프트 원문. 그 자체로 프롬프트 엔지니어링 교재. |
internal/config/rules/rule_docs/ | 자바·코틀린·TS·C++ 등 언어별 리뷰 점검표. "무엇을 지적하고 무엇을 지적하지 말라"까지 명시. |
internal/tool/code_comment.go | 슬라이딩 윈도우 위치 매칭의 실제 구현. |
눈여겨볼 관례 하나 — 이 프로젝트에서 "정상"은 설정이 코드가 아니라 JSON·md 데이터로 존재하는 것입니다. 규칙도, 프롬프트도, 도구 스키마도, 제외 패턴도 전부 파일입니다. Go 코드는 그 데이터를 해석하는 엔진 역할만 합니다.
이 레포에서 배울 만한 것 + 어디를 보면 되는지.
task_template.json수만 명에게 2년 검증된 프롬프트가 통째로 공개돼 있습니다. 시스템 프롬프트를 읽어보면 배울 게 많습니다: 역할 → 능력 → 엄격한 집중 규칙 → 응답 제한의 4단 구성, "다른 파일에서 문제를 발견해도 무시하라(컨텍스트 도구는 이해용일 뿐)" 같은 탈선 방지 조항, "삭제된 코드엔 코멘트하지 말라" 같은 노이즈 차단 조항. 막연히 "잘 리뷰해줘"가 아니라 행동 경계를 촘촘히 긋는 방식입니다.
tools.json에 정의된 도구는 단 6개: task_done(종료 선언), code_comment(코멘트 제출), file_read(파일 읽기), code_search(저장소 검색), file_read_diff(다른 변경 파일 보기), file_find(파일 찾기). 흥미로운 건 도구마다 plan_task/main_task 플래그로 어느 단계에서 쓸 수 있는지를 제한한다는 점 — 플랜 단계에선 조사 도구만, 본 리뷰에선 제출 도구까지. 에이전트를 만들 때 "도구를 더 줄까?"보다 "이 단계에 이 도구가 꼭 필요한가?"를 먼저 묻게 됩니다.
LLM 출력에서 가장 잘 깨지는 게 숫자(줄 번호)입니다. OCR은 ① 코드 조각 원문을 받아 슬라이딩 윈도우로 diff에서 텍스트 매칭(internal/diff/resolver.go) → ② 실패 시 "이 코멘트가 가리키는 줄을 diff에서 글자 그대로 다시 뽑아라"라는 전용 LLM 태스크로 재시도(relocation.go)합니다. LLM의 약점을 LLM에게 사과받는 대신 시스템으로 보완하는 사고방식 — 어떤 AI 제품을 만들든 써먹을 수 있는 패턴입니다.
리뷰 규칙이 CLI 플래그 → 프로젝트 설정(.opencodereview/rule.json, git 커밋 가능) → 사용자 전역 설정 → 내장 기본값 순서로 해석됩니다. 각 계층 안에서는 선언 순서대로 첫 매칭 승리(first-match-wins). ESLint·Git 설정 등 좋은 개발 도구들이 공유하는 보편 패턴인데, 이 레포는 그 구현을 작고 읽기 좋은 Go 코드로 보여줍니다.
// 프로젝트 규칙 예시 — .opencodereview/rule.json
{
"rules": [
{ "path": "**/*mapper*.xml", ← 글롭 패턴: 이 경로에 매칭되면
"rule": "SQL 인젝션·파라미터 오류·닫는 태그 누락을 확인" }
],
"exclude": ["**/generated/**"] ← 자동 생성 코드는 리뷰 제외
}
복사해서 경로와 규칙만 우리 프로젝트에 맞게 바꾸면 바로 동작합니다.
agent.go의 CommentWorkerPool은 "작업을 던지고(Submit) 다 끝나길 기다려 결과를 모으는(Await)" 고전적 워커풀입니다. 고루틴·채널·sync를 책으로만 봤다면, LLM 호출처럼 느리고 실패할 수 있는 작업을 동시 8개로 제어하는 실전 코드로 배우기 좋습니다. --concurrency 플래그가 이 풀의 크기를 조절합니다.
돌리려면 무엇이 필요한가 — 진입 장벽이 매우 낮다.
| 항목 | 요구사항 |
|---|---|
| OS | macOS(Intel/Apple Silicon) · Linux(x86_64/ARM64) · Windows(x86_64/ARM64) — 전 플랫폼 바이너리 제공 |
| 설치 경로 | npm 설치 시 Node.js 필요. 바이너리 직접 다운로드 시 의존성 0. 소스 빌드 시 Go 1.25+ |
| 필수 도구 | git (diff 수집에 사용) |
| LLM API | Anthropic 또는 OpenAI 호환 엔드포인트 + API 키 — 이것만이 진짜 요구사항. Claude Code 환경변수(ANTHROPIC_BASE_URL 등)를 그대로 재활용 가능 |
| 하드웨어 | 특별한 요구 없음 — 무거운 연산은 전부 LLM API 서버에서. GPU 불필요 |
로컬에서 모델을 돌리는 게 아니라 API를 호출하는 구조라, 비용은 하드웨어가 아니라 API 토큰 요금으로 나갑니다. 큰 diff일수록 토큰을 많이 쓰므로 --preview로 리뷰 대상을 먼저 확인하는 습관이 유용합니다.
난이도별로 손에 익히는 단계.
npm install -g @alibaba-group/open-code-review → ocr config set으로 LLM 4개 키 설정 → ocr llm test로 연결 확인 → 아무 git 프로젝트에서 일부러 어설픈 코드(예: null 체크 빠진 함수)를 만들고 ocr review. 코멘트가 정확히 그 줄을 가리키는지 확인해 보세요.
ocr review --preview는 LLM 호출 없이 "어떤 파일이 리뷰될지"만 보여줍니다. 테스트 파일이 왜 빠지는지, ocr rules check src/Foo.java로 어떤 점검표가 매칭되는지 추적해 보면 결정론적 파이프라인의 앞 절반(②③)이 손에 잡힙니다.
프로젝트 루트에 .opencodereview/rule.json을 만들어 자기 프로젝트 특화 규칙(예: "API 핸들러는 반드시 입력 검증") 2~3개를 글롭 패턴과 함께 작성하고, 위반 코드를 일부러 커밋해 잡아내는지 검증해 보세요. 4계층 우선순위(전역 설정에 같은 패턴을 넣으면 어느 쪽이 이기는지)도 실험해 볼 것.
리뷰 후 ocr viewer를 띄워 localhost:5483에서 세션을 열어보세요. 시스템 프롬프트, 에이전트가 어떤 도구를 어떤 순서로 불렀는지, code_comment에 넘긴 코드 조각이 전부 보입니다. 에이전트 디버깅이 무엇인지 체감하는 가장 빠른 길입니다.
examples/github_actions/ocr-review.yml을 참고해 자기 저장소의 PR마다 ocr review --from origin/main --format json이 돌게 하고, JSON 출력을 파싱해 PR 코멘트로 다는 스크립트까지 연결해 보세요. 토큰 비용 제어(--concurrency·대상 브랜치 제한)도 함께 고민하면 실무 수준입니다.
한 주씩 따라가는 계획.
| 주차 | 주제 | 학습 자료 |
|---|---|---|
| 1주차 | 설치·기본 사용 + Unified Diff 형식 읽는 법 | README · git diff 출력 직접 해부 · 실습 1~2 |
| 2주차 | 에이전트 설계 해부 — 프롬프트·도구·플랜 단계 | task_template.json · tools.json 정독 · 실습 4 |
| 3주차 | 결정론 파이프라인 — 규칙 체인·필터·번들링 | internal/config/rules · internal/agent/agent.go · 실습 3 |
| 4주차 | 위치 매칭 알고리즘 + Go 동시성 | internal/diff/ · CommentWorkerPool · Go 공식 동시성 문서 |
| 5주차 | 실전 이식 — CI 연동, 또는 내 에이전트에 패턴 적용 | examples/ · 실습 5 |
본문에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| Unified Diff | 코드 변경을 -(삭제)/+(추가) 줄로 표현하는 표준 형식. git이 쓰는 변경 기록 언어. |
| 결정론적 파이프라인 | 같은 입력이면 항상 같은 결과가 나오는 일반 코드 처리 단계. LLM의 확률적 출렁임과 대비되는 개념. |
| 서브에이전트 | 전체 작업의 한 조각(파일 묶음)만 맡는, 독립 컨텍스트의 작은 에이전트. |
| 슬라이딩 윈도우 매칭 | 창문을 한 칸씩 밀며 텍스트에서 일치 구간을 찾는 기법. 코멘트를 정확한 줄에 붙이는 데 사용. |
| 플랜 단계 (PLAN_TASK) | 큰 변경(50줄+)에서 본 리뷰 전에 위험 지점·조사 계획을 JSON으로 뽑는 사전 LLM 호출. |
| 컨텍스트 압축 | 대화가 토큰 한도에 가까워지면 지금까지의 발견을 요약본으로 바꿔 자리를 비우는 기법. |
| 4계층 규칙 체인 | CLI 플래그 → 프로젝트 → 전역 → 내장 순으로 설정을 해석하는 우선순위 구조. |
| 글롭 패턴 (glob) | **/*.java처럼 와일드카드로 파일 경로를 매칭하는 표기법. doublestar 라이브러리가 처리. |
| 세션 JSONL | 한 줄에 JSON 하나씩 쌓는 로그 형식. 리뷰 중 LLM과 주고받은 모든 메시지가 이 형식으로 저장됨. |
| DNS 리바인딩 방어 | 악성 웹페이지가 localhost 서비스에 몰래 접근하는 공격을 Host 헤더 허용 목록으로 차단. ocr viewer에 내장. |