PR diff를 넘어 저장소 전체를 추론하는 AI 코드 리뷰 — 코드베이스 시맨틱 그래프가 크로스-파일 버그를 잡는 원리
코드 리뷰에 AI를 써본 경험이 있다면, 한 번쯤은 이런 상황을 맞닥뜨렸을 겁니다. AI가 수정된 함수 안에서는 아무 문제도 잡아내지 못했는데, 배포하고 나서야 전혀 다른 파일에서 타입 에러가 터지는 경우요. diff를 기반으로 동작하는 도구들의 구조적인 한계입니다. 변경된 줄만 들여다보기 때문에, 그 함수를 실제로 호출하는 쪽이 무너지는 건 보지 못하거든요.
Greptile은 이 문제를 정면으로 파고들었습니다. 접근 방식 자체가 다릅니다. PR이 열렸을 때 diff를 LLM에 넘기는 게 아니라, 저장소 전체를 함수·클래스·모듈·의존성이 연결된 지식 그래프로 만들어놓고, 그 위에서 변경의 파급 효과를 추론합니다. 이 글에서는 Greptile이 코드베이스를 어떻게 그래프로 모델링하는지, 그리고 그 방식이 기존 diff 분석과 실제로 어떻게 다른지를 구체적인 코드 예시와 함께 살펴봅니다. 이 글은 AI 코드 리뷰 도구 시리즈의 첫 편입니다.
그럼 Greptile이 실제로 무엇을 다르게 하는지 뜯어보겠습니다.
핵심 개념
코드베이스를 파일 집합이 아닌 지식 그래프로 보는 관점
전통적인 diff 분석 도구들은 PR에 포함된 변경 줄을 LLM 컨텍스트로 넘깁니다. 빠르고 단순하지만, 수정된 함수가 시스템 전체에서 어떤 역할을 하는지는 알 수 없습니다. 마치 책에서 한 문단만 잘라내 보여주고 "이 문장이 전체 이야기에서 어떤 의미인가요?"라고 묻는 것과 비슷합니다.
Greptile이 구축하는 코드베이스 시맨틱 그래프(Codebase Semantic Graph) 는 저장소 전체를 함수 호출 관계, import 의존성, 패턴 유사성이 연결된 노드-엣지 구조로 변환합니다. 변경이 발생하면 AI는 그래프를 탐색해 "이 변경이 어디까지 영향을 미치는가"를 추론할 수 있게 됩니다.
인덱싱 파이프라인: 코드가 그래프가 되는 과정
저장소를 처음 연결하면 Greptile은 네 단계를 거쳐 그래프를 만듭니다. 각 단계는 이전 단계의 한계를 해결하기 위해 존재합니다.
1단계 — AST 파싱
전체 코드베이스를 추상 구문 트리(Abstract Syntax Tree, AST)로 변환해 함수·변수·클래스 단위로 분해합니다. 코드를 텍스트 덩어리로 다루지 않고 구조로 인식하는 첫 번째 단계입니다.
# 개념 예시 (pseudocode) — 실제 tree-sitter API와 다를 수 있습니다
def parse_to_ast(source_code: str, language: str):
# language-specific parser 로드
parser = get_parser(language)
tree = parser.parse(source_code)
# 결과: 함수명, 파라미터, 반환 타입, 호출 관계가 구조화된 트리
return extract_nodes(tree.root_node)2단계 — 자연어 변환
AST만으로는 부족합니다. 파이썬의 def와 TypeScript의 function처럼 언어마다 문법이 달라, 동일한 로직도 임베딩 공간에서 멀리 떨어져 있거든요. Greptile은 각 AST 노드에 대해 재귀적으로 자연어 설명(docstring)을 생성해 이 노이즈를 흡수합니다. Greptile 팀이 블로그에서 밝힌 내부 측정치에 따르면, 코드 자체보다 자연어 설명이 벡터 임베딩 유사도를 약 12%p 높인다고 합니다.
3단계 — 밀집 벡터 임베딩
생성된 자연어 요약을 함수 단위로 세밀하게 청킹(chunking)한 뒤 임베딩 벡터로 변환합니다. 이 벡터는 벡터 전용 데이터베이스(Chroma, Pinecone 등 — 고차원 벡터를 빠르게 유사도 검색할 수 있도록 특화된 데이터베이스)에 저장됩니다.
4단계 — 그래프 구축
함수 호출 관계와 import 의존성은 코드 구조에서 직접 추출하고, 패턴 유사성은 3단계에서 만든 임베딩 벡터 사이의 코사인 유사도가 일정 임계값 이상인 노드들을 엣지로 연결해 결정합니다. 이렇게 세 종류의 엣지가 합쳐져 최종 그래프 구조가 완성됩니다.
그래프 RAG(Graph RAG): 일반적인 RAG(Retrieval-Augmented Generation)가 유사한 텍스트 청크를 단순 벡터 검색으로 가져오는 반면, 그래프 RAG는 노드 간 연결 관계를 따라 멀티-홉(multi-hop) 탐색이 가능합니다. "이 함수와 유사한 코드"뿐 아니라 "이 함수에 의존하는 모든 코드"를 찾을 수 있다는 차이가 있습니다.
실전 적용
예시 1: 결제 로직 변경이 숨긴 크로스-파일 계약 위반
실무에서 자주 맞닥뜨리는 상황입니다. 결제 서비스의 세금 계산 함수를 수정하는 PR인데, 함수 내부 로직은 깔끔하게 정리됐습니다. diff만 보면 "LGTM"이죠. 저도 처음 이 예시를 봤을 때 invoice.service.ts 쪽은 전혀 신경 못 했습니다.
// tax.service.ts — 수정된 함수
// 변경 전: calculateTax(amount: number): number
// 변경 후: calculateTax(amount: number, region: string): TaxResult
interface TaxResult {
amount: number;
rate: number;
breakdown: Record<string, number>;
}
export function calculateTax(amount: number, region: string): TaxResult {
const rate = getTaxRate(region);
return {
amount: amount * rate,
rate,
breakdown: { base: amount * rate },
};
}diff 분석 도구는 tax.service.ts 안에서 로직이 올바른지만 확인합니다. 하지만 Greptile은 그래프를 탐색해 이 함수를 호출하는 모든 노드를 추적합니다.
// invoice.service.ts — 호출 측 (diff에 포함되지 않음)
// ❌ 반환 타입이 number라고 가정하고 있어 런타임 에러 발생
const tax = calculateTax(invoice.amount); // region 누락, 반환값 타입 불일치
const total = invoice.amount + tax; // TaxResult를 number로 더하려 해서 NaN| 분석 도구 | tax.service.ts 내부 오류 |
invoice.service.ts 계약 위반 |
order.controller.ts 파라미터 누락 |
|---|---|---|---|
| Diff 기반 | 탐지 가능 | 탐지 불가 | 탐지 불가 |
| Greptile | 탐지 가능 | 탐지 가능 | 탐지 가능 |
예시 2: 리팩터링 PR에서 shared library 인터페이스 암묵적 변경
"단순 리팩터링"이라고 PR 설명을 달았는데, 사실 shared library의 공개 인터페이스가 슬쩍 바뀐 경우입니다.
// shared/validators.ts — 리팩터링 전
export function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// 리팩터링 후 — 에러 처리 방식을 throw로 변경
export function validateEmail(email: string): void {
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
throw new ValidationError("Invalid email format");
}
}반환 타입이 boolean에서 void로 바뀌고, 실패 시 동작이 return false에서 throw로 변경됐습니다. diff 분석 도구는 이 파일 안의 변경은 잘 읽습니다. 하지만 이 함수를 소비하는 다른 서비스들은 보지 못하죠.
// auth.service.ts — 호출 측 (diff에 포함되지 않음)
// ❌ boolean 반환을 가정한 코드 — 리팩터링 후 항상 truthy로 동작
if (validateEmail(input)) {
await createUser(input); // 유효하지 않은 이메일로도 사용자가 생성됨
}Greptile은 그래프에서 이 함수를 소비하는 모든 노드를 찾아 "계약이 깨졌다"는 경고를 코멘트로 남깁니다.
예시 3: v3 에이전트의 멀티-홉 조사
Greptile v3부터는 단순한 그래프 검색을 넘어 에이전트가 자율적으로 여러 단계를 거쳐 조사합니다. 아래는 에이전트가 이 과정을 수행하는 동작 원리를 개념적으로 도식화한 것입니다.
PR에서 비정상적인 할인율 계산 발견
↓
git history 역추적 → 관련 커밋 발견
↓
해당 커밋의 원본 PR 설명 읽기 → "핫픽스: 특정 고객사 요청"
↓
코드베이스 내 유사한 할인 계산 패턴 검색
↓
"다른 3곳의 할인 로직은 다른 방식을 사용 중. 일관성 문제 가능성"멀티-홉 조사(Multi-hop Investigation): 하나의 질문에서 출발해 중간 결과를 바탕으로 다음 검색을 이어가는 탐색 방식. "이 함수가 왜 이렇게 생겼는가"를 답하기 위해 git history → PR 설명 → 유사 패턴 순으로 자율적으로 탐색합니다.
diff 분석과 시맨틱 그래프의 결정적 차이
앞선 예시들을 통해 각 도구가 무엇을 다르게 하는지 직접 확인하셨을 겁니다. 정리하면 이렇습니다.
| 항목 | 전통적 Diff 분석 | 시맨틱 그래프(Greptile) |
|---|---|---|
| 분석 범위 | 변경된 줄(line diff) | 저장소 전체 |
| 크로스-파일 맥락 | 변경된 파일 내부 | 호출 체인 전체 추적 |
| Git history 활용 | 제한적 | 멀티-홉 역추적 가능 |
| 리뷰 속도 | 빠름 (수 초) | 상대적으로 느림 |
| 오탐(False Positive) | 낮음 | 상대적으로 높음 |
| 초기 설정 비용 | 없음 | 인덱싱 시간 필요 |
장단점 분석
장점
아래 수치들은 Greptile이 자체 공개한 벤치마크 기준으로, 내부 테스트 환경에서 측정한 결과입니다.
| 항목 | 내용 |
|---|---|
| 버그 탐지율 | 82% vs 경쟁사 44 |
| 크로스-파일 맥락 | 호출 체인, import 의존성, 패턴 유사성 전체를 추적 |
| 아키텍처 회귀 탐지 | "clean" diff에 숨은 인터페이스 계약 위반을 포착 |
| Git history 활용 | 변경의 역사적 맥락까지 참조한 판단 가능 |
| 생태계 통합 | MCP 서버로 제공되어 Claude, Cursor 등 AI 에이전트가 직접 호출 가능 |
단점 및 주의사항
이 목록에서 실제로 가장 뼈아팠던 건 오탐 문제였습니다. 처음 2주는 경고가 너무 많아서 팀원들이 리뷰 알림을 무음으로 바꿔버렸거든요. Greptile 커뮤니티에서도 비슷한 경험을 공유하는 케이스가 꽤 있었고, 독립 벤치마크에서도 Greptile은 11건, CodeRabbit은 2건을 기록해 오탐율 격차가 실제로 크다는 걸 수치로도 확인할 수 있었습니다.
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 높은 오탐율 | 독립 벤치마크에서 11건 (CodeRabbit은 2건) | 팀 규칙 기반 필터 설정, 점진적 임계값 조정 |
| 초기 인덱싱 비용 | 저장소 규모에 따라 수 분~수 시간 소요 | 야간 첫 인덱싱, CI 파이프라인 외부에서 실행 |
| 시맨틱 vs 구조적 의존성 | 함수 시그니처가 달라도 호출 관계가 있는 경우를 놓칠 수 있음 | TypeScript strict mode 등 정적 분석 도구와 병행 사용 |
| 코드베이스 노출 | 전체 저장소를 클라우드 서비스에 전송 | 온프레미스 옵션 사용, 보안 정책 사전 검토 |
| 운영 비용 | 에이전트 기반 분석은 LLM 호출 횟수가 많아 비용 높음 | PR 규모별 분석 깊이 설정, 중요 브랜치에만 적용 |
오탐(False Positive): AI가 실제로는 문제없는 코드를 버그나 위험으로 잘못 판단하는 경우. 오탐이 많으면 개발자가 리뷰 알림을 무시하기 시작하고, 결국 진짜 중요한 경고도 묻히게 됩니다. 탐지율만큼이나 오탐율을 함께 확인해야 하는 이유가 바로 여기에 있습니다.
실무에서 가장 흔한 실수
-
오탐이 많다고 곧바로 도구를 포기하는 것. 초기엔 팀 컨벤션과 무관한 경고가 쏟아질 수 있는데, 이는 인덱스가 코드베이스의 암묵적 규칙을 아직 충분히 학습하지 못한 상태이기 때문입니다. 2~4주 정도 팀 규칙을 피드백으로 쌓아가면 신호 품질이 눈에 띄게 달라집니다.
-
시맨틱 그래프가 정적 분석 도구를 대체한다고 여기는 것. 타입 체커나 린터가 잡는 구조적 오류와 시맨틱 그래프가 잡는 아키텍처적 맥락은 상호 보완 관계입니다. TypeScript strict mode + ESLint + Greptile이 각각 다른 레이어를 커버하는 조합으로 활용하는 것이 좋습니다.
-
전체 저장소에 한꺼번에 적용하는 것. 모노레포(여러 서비스나 패키지를 단일 저장소에서 함께 관리하는 방식)나 대규모 저장소에서는 초기 인덱싱 비용과 오탐 볼륨이 동시에 커집니다. 결제·인증처럼 변경 파급 효과가 큰 핵심 도메인 모듈에 먼저 적용하고, 점진적으로 범위를 넓히는 방식이 현실적입니다.
마치며
AI 코드 리뷰는 "변경된 줄을 잘 읽는" 문제에서 "시스템 전체를 이해하고 변경의 파급 효과를 추론하는" 문제로 옮겨가고 있으며, 시맨틱 그래프 방식은 현재 이 방향에서 가장 구체적인 구현 중 하나입니다. CodeRabbit, GitHub Copilot Code Review 등 경쟁 도구도 같은 방향으로 빠르게 발전하고 있습니다.
오탐율과 인덱싱 비용이라는 현실적인 트레이드오프가 있지만, 크로스-파일 계약 위반이나 아키텍처 회귀를 배포 전에 잡아내는 가치는 팀 규모가 커질수록 더 분명해집니다.
여러분의 저장소에서 제일 먼저 연결해볼 곳은 어디인가요? 지금 바로 시작해볼 수 있는 3단계입니다.
- 공식 사이트에서 현재 플랜을 확인하고, 사이드 프로젝트나 스테이징 저장소 하나를 연결해볼 수 있습니다. GitHub 앱 설치 후 초기 인덱싱이 완료된 뒤 다음 PR부터 크로스-파일 코멘트가 어떤 형태로 달리는지 직접 확인해보시면 좋습니다.
- 기존에 발생했던 크로스-파일 버그 케이스를 모아 회고 테스트로 활용해볼 수 있습니다. "이 버그가 Greptile 코멘트에 잡혔을까?"를 팀이 함께 검증해보시면, 도입 결정에 필요한 실질적인 근거를 얻을 수 있습니다.
- MCP 서버 통합을 통해 Claude나 Cursor에서 코드베이스 쿼리를 직접 날려보시는 것도 좋습니다. "이 함수를 호출하는 모든 곳을 찾아줘"처럼 일상적인 개발 작업에서 시맨틱 그래프의 체감 효용을 먼저 느껴본 뒤 리뷰 자동화로 확장하는 흐름도 자연스럽습니다.
참고 자료
- Graph-based Codebase Context | Greptile 공식 문서
- Greptile v3, an agentic approach to code review | Greptile Blog
- Series A and Greptile v3 | Greptile Blog
- Codebases are uniquely hard to search semantically | Greptile Blog
- AI Code Review Benchmarks 2025 | Greptile
- Greptile: Smarter Code Reviews Through Codebase-Aware AI | DEV Community
- Greptile bags $25M in funding | SiliconANGLE
- Benchmark in talks to lead Series A for Greptile | TechCrunch
- Customer story: Greptile | Anthropic Claude
- CodeRabbit vs Greptile: Which AI Reviewer Catches More Bugs? | DEV Community
- Reliable Graph-RAG for Codebases: AST-Derived Graphs vs LLM-Extracted Knowledge Graphs | arXiv
- State of AI Code Review Tools in 2025 | DevTools Academy