Sourcegraph Cody 리뷰: Copilot이 감당하기에 코드베이스가 너무 클 때
요약
대규모 코드베이스에서 GitHub Copilot이 겪는 컨텍스트 인지 한계를 Sourcegraph Cody가 어떻게 해결하는지 분석합니다. Cody는 전체 리포지토리를 인덱싱하고 심볼 관계를 이해하는 코드 그래프를 통해 정확한 코드 참조와 답변을 제공합니다.
핵심 포인트
- Copilot은 대규모 코드베이스에서 기존 라이브러리를 인지하지 못하고 허구의 코드를 생성할 수 있음
- Cody는 전체 리포지토리를 인덱싱하여 심볼 관계를 이해하는 코드 그래프를 구축함
- 260만 줄 규모의 모노레포에서도 효율적인 인덱싱 및 증분 업데이트 지원
- 단순 검색을 넘어 함수 호출 및 타입 흐름 등 구조적 관계를 파악하여 정확한 답변 제공
저는 Copilot에게 우리 결제 서비스(billing service)에 새로운 결제 수단을 추가해 달라고 요청했습니다. Copilot은 PaymentGateway 인터페이스와 4개의 헬퍼 유틸리티(helper utilities)를 참조하는, 그럴듯해 보이는 PayPal 통합 함수를 생성했습니다. 코드는 깔끔한 TypeScript였습니다. 하지만 그 코드는 완전히 틀렸습니다. 트리 구조상 상위 3개 디렉토리에 이미 존재하는 공유 라이브러리(shared library)의 인터페이스를 새로 만들어 버렸고, 호출한 4개의 헬퍼 함수는 우리가 이미 src/shared/billing/에 동일한 유틸리티를 가지고 있다는 사실을 Copilot이 인지하지 못해 완전히 허구로 만들어낸 것이었습니다.
이것이 바로 Sourcegraph Cody가 방지하도록 설계된 유형의 실패입니다. Cody는 열려 있는 파일과 커서 위치를 통해 추측하는 대신, 전체 리포지토리(repository)를 인덱싱(indexing)하고 그 인덱스를 모든 AI 상호작용을 위한 검색 증강 컨텍스트(retrieval-augmented context)로 사용합니다. 저는 코드베이스를 인식하는 컨텍스트가 AI 코딩의 신뢰성을 유의미하게 향상시키는지 이해하기 위해, 실제 260만 줄 규모의 TypeScript 모노레포(monorepo)를 대상으로 2주 동안 Cody를 실행해 보았습니다.
Cody의 코드 그래프(Code Graph) 작동 방식
Cody는 단순히 코드베이스를 grep(검색)하는 것이 아닙니다. Cody는 심볼 관계(symbol relationships)를 이해하는 검색 인덱스를 구축합니다. 즉, 어떤 함수가 어떤 함수를 호출하는지, 어떤 인터페이스가 어디에서 구현되는지, 어떤 타입(type)이 어떤 모듈을 통해 흐르는지를 이해합니다. Cody에게 질문을 하거나 코드 완성(completion)을 트리거하면, Cody는 이 인덱스를 쿼리하여 가장 관련 있는 코드를 찾고, 해당 코드를 컨텍스트로 포함하여 풍부해진 프롬프트(prompt)를 LLM에 전송합니다.
초기 인덱싱에는 시간이 걸립니다. 제가 테스트한 260만 줄 규모의 모노레포의 경우, 첫 설정 시 인덱서(indexer)가 약 11,000개의 TypeScript 파일을 처리하며 약 14분 동안 실행되었습니다. 이후 파일이 변경됨에 따라 수행되는 증분 업데이트(incremental updates)는 30초 미만으로 완료되었습니다. 인덱스의 디스크 점유 용량은 약 450MB였는데, 이는 이 정도 규모의 코드베이스에 합리적인 수준이며 리포지토리 자체의 크기에 비하면 상당히 적은 양입니다.
실질적인 효과는 Cody의 답변이 실제 코드를 참조한다는 점입니다. 제가 모노레포(monorepo)에서 인증(authentication)이 어떻게 작동하는지 물었을 때, Cody는 5개의 파일을 통해 미들웨어 체인(middleware chain)을 추적하고, JWT 검증 모듈을 식별했으며, 역할 기반 액세스 패턴(role-based access pattern)을 설명했습니다. 이 모든 과정에서 특정 함수와 줄 번호에 대한 인라인 참조(inline references)를 제공했습니다. 반면 Copilot에게 동일한 질문을 했을 때는 일반적인 JWT 흐름을 요약해 주었는데, 이는 방향성 측면에서는 맞았을지 모르나 미들웨어와 라우트 핸들러(route handlers) 사이에 위치한 커스텀 정책 엔진(custom policy engine)을 놓쳤습니다. 이 차이는 '맞게 들리는 답변'과 '정확한 답변'의 차이입니다.
Cody의 컨텍스트 검색(context retrieval)은 심볼(symbols)에 국한되지 않습니다. 코드 패턴과 구조적 관계도 인덱싱합니다. 제가 "마이그레이션할 수 있도록 기존
legacyFetch래퍼(wrapper)를 사용하는 모든 곳이 어디인가요?"라고 물었을 때, Cody는 23개 파일에 걸쳐 47개의 위치를 반환했습니다.legacyFetch에 대한 grep 검색은 31개의 결과만 반환했는데, 이는 함수가 별칭(alias)으로 임포트되었거나 재내보내기(re-exported)된 모듈을 통해 호출된 사용 사례를 놓쳤기 때문입니다. 심볼 인식 검색(Symbol-aware search)은 텍스트 검색이 잡을 수 없는 패턴을 포착합니다.
엔터프라이즈 기능: 멀티 리포(Multi-Repo) 및 온프레미스(On-Premise)
Cody의 엔터프라이즈 티어는 대규모 조직에 특히 중요한 두 가지 기능, 즉 멀티 리포지토리 검색(multi-repository search)과 온프레미스 배포(on-premise deployment)를 추가합니다.
멀티 리포 검색을 사용하면 여러 리포지토리를 인덱싱하고 이를 가로질러 쿼리할 수 있습니다. 단일 기능이 3~4개의 서비스에 걸쳐 있는 마이크로서비스 아키텍처(microservice architecture)에서, 이는 Cody가 API 게이트웨이, 인증 서비스, 비즈니스 로직 서비스, 그리고 데이터베이스 레이어를 통해 요청을 추적할 수 있음을 의미합니다. 각 서비스가 별도의 리포지토리에 있더라도 말입니다. 저는 서로 연결된 4개의 리포지토리로 이를 테스트했으며, "사용자가 구독 등급을 업데이트할 때 엔드 투 엔드(end-to-end)로 어떤 일이 일어나나요?"라고 질문하여 4개의 코드베이스 전체에 걸친 일관된 추적 결과를 얻을 수 있었습니다. 멀티 리포 기능이 없었다면, 저는 4개의 서로 다른 에디터 창에서 같은 질문을 네 번 던지고 답변을 수동으로 짜 맞추어야 했을 것입니다.
Sourcegraph Enterprise를 통해 온프레미스 배포 (On-premise deployment)가 가능하며, 이는 코드 그래프 인덱서 (code graph indexer), Cody AI 게이트웨이 (Cody AI gateway), 그리고 IDE 통합 (IDE integration)을 포함한 전체 스택을 기업 자체 인프라 내부에서 실행합니다. LLM 호출은 자체 호스팅 모델 (self-hosted models)로 라우팅하거나 고객이 제어하는 API 키를 통해 클라우드 제공업체로 라우팅할 수 있어, 보안 팀이 코드와 프롬프트(prompts)가 이동하는 위치를 완전히 제어할 수 있습니다. 이는 국방 계약업체, 금융 기관 및 의료 기업에 필수적인 요구 사항이며, Cody는 제3자 프록시 (third-party proxy) 없이 이를 기본적으로 지원하는 몇 안 되는 AI 코딩 도구 중 하나입니다.
// Cody는 이것이 우리 모노레포 (monorepo)의 표준 인증 패턴임을 정확히 식별했으며,
// 미들웨어 스택 (middleware stack)을 통해 정책 엔진 (policy engine)까지 추적했습니다.
...
IDE 통합 및 일상적 워크플로 (IDE Integration and Daily Workflow)
Cody는 확장을 통해 VS Code, JetBrains, Neovim과 통합됩니다. VS Code 확장이 가장 성숙한 상태이며, 문맥 인식 자동 완성 (context-aware autocomplete), 채팅 패널, 그리고 코드 설명, 수정 및 생성을 위한 인라인 명령 (inline commands)을 제공합니다. 일반적인 AI 확장 프로그램과의 핵심적인 차이점은 모든 상호작용에 Cody가 참조하고 있는 파일이 무엇인지 보여주는 "문맥 (context)" 표시가 포함되어 있으며, 사용자가 해당 문맥을 수동으로 확장하거나 좁힐 수 있다는 점입니다.
자동 완성 경험은 Copilot보다 느립니다. 제 테스트 결과, Copilot이 제안당 약 150300ms인 것에 비해 Cody는 약 400700ms 정도였습니다. 하지만 제안 내용이 실제 코드베이스 (codebase)에 기반하고 있기 때문에 더 정확한 경우가 많습니다. 2주간 매일 사용해 본 결과, 동일한 코드베이스에서 Cody의 완성 수락률 (acceptance rate)은 약 68%였으며, Copilot은 약 55%였습니다. 이 13%포인트의 차이는 Copilot이 생성한 완성 제안 중 그럴듯해 보이지만 프로젝트에 존재하지 않는 함수나 타입을 참조했던 횟수를 반영합니다.
채팅 패널은 파일, 심볼(symbol), 리포지토리(repository)에 대한 @ 멘션(mention)을 지원하며, 이를 통해 코드베이스의 특정 부분으로 질문의 범위를 지정하는 것이 매우 간단합니다. 저는 타겟팅된 질문을 위해 Cody의 컨텍스트(context)를 좁히고자 @file 및 @symbol 멘션을 빈번하게 사용했으며, 전체 코드베이스 컨텍스트가 유용한 광범위한 아키텍처 질문을 할 때는 이를 생략했습니다.
Cody의 한계점
Cody는 Copilot보다 코드를 더 빠르게 생성하지는 않습니다. Cody는 더 정확할 가능성이 높은 코드를 생성하지만, 지연 시간(latency)의 차이는 눈에 띄며 완성 속도가 주요 관심사인 워크플로에서는 이 차이가 중요합니다. 보일러플레이트(boilerplate) 코드를 작성하거나 반복적인 패턴을 채워 넣는 중이라면, Copilot의 더 빠른 완성 기능이 해당 작업에 더 나은 도구입니다. Cody의 속도 우위는 단순한 생성 처리량(throughput)이 아니라 디버깅과 이해에 있습니다.
인덱싱(indexing)에 대한 의존성은 양날의 검입니다. 코드베이스가 작다면(파일 5,000개 미만), 인덱싱 오버헤드(overhead)는 불필요하며 컨텍스트의 이점도 미미합니다. 해당 규모에서는 Copilot의 오픈 파일 및 주변 파일 컨텍스트만으로도 충분히 잘 작동합니다. Cody의 가치 제안은 파일 10,000개 정도의 시점부터 발휘되며, 코드베이스가 커질수록 그 중요성이 점점 더 커집니다. 이 임계값 미만에서는 설정 비용이 미미한 정확도 향상을 위해 지불할 가치가 없습니다.
가격 또한 고려 요소입니다. Cody는 개인 개발자를 위한 Personal 플랜을 무료로 제공하며, 여기에는 코드베이스 인덱싱과 IDE 통합이 포함됩니다. 월 9달러인 Pro 플랜은 무제한 자동 완성(autocomplete)과 더 높은 속도 제한(rate limits)을 추가합니다. Enterprise 가격에는 멀티 리포지토리(multi-repo) 검색, 온프레미스(on-premise) 배포 및 관리자 제어 기능이 포함되며, 연간 계약을 통해 사용자(seat)당 가격이 책정됩니다. 개인 개발자에게는 무료 티어(free tier)가 진정으로 사용 가능할 만큼 실용적입니다. 팀의 경우, 코드베이스 인지 컨텍스트(codebase-aware context)로부터 이점을 얻을 수 있을 만큼 코드베이스가 충분히 크다면 엔터프라이즈 기능이 비용을 정당화할 것입니다.
Cody의 코드 그래프 인덱스(code graph index)는 큰 차이가 있는 브랜치로 전환할 때 처음부터 다시 구축됩니다. 제가 테스트한 모노레포(monorepo)에서는 main 브랜치에서 40개의 파일이 수정된 기능 브랜치(feature branch)로 전환했을 때, 5분간의 증분 재인덱싱(incremental reindex)이 발생했습니다. 이것이 결정적인 결함은 아니지만, 코드가 서로 상이한 브랜치 사이를 빈번하게 전환한다면 알아둘 가치가 있습니다. 인덱스가 따라잡는 데 시간이 필요하며, 재인덱싱 기간 동안의 코드 완성(completions)은 정확도가 떨어질 수 있습니다.
Cody 명령어 및 커스텀 레시피 (Cody Commands and Custom Recipes)
표준적인 채팅 및 자동 완성(autocomplete) 기능 외에도, Cody에는 진정으로 과소평가된 명령어(commands) 시스템이 포함되어 있습니다. 명령어는 특정 컨텍스트(context)를 대상으로 실행되는 사전 구성된 프롬프트(prompts)입니다. 예를 들어, 선택한 코드 설명하기, 현재 파일에 대한 단위 테스트(unit tests) 생성하기, 열려 있는 모듈에서 코드 스멜(code smells) 찾기, 또는 패키지의 공개 API(public API) 문서화하기 등이 있습니다. 이는 완전히 새로운 아이디어는 아니지만, Cody의 구현 방식은 일반적인 구현체들이 하지 못하는 방식으로 코드 그래프(code graph)의 이점을 활용합니다.
세 개의 다른 내부 모듈에 의존하는 함수에 대해 "단위 테스트 생성(Generate Unit Tests)" 명령어를 실행했을 때, Cody는 세 가지 종속성 모두에 대해 올바른 모크(mocks)를 임포트하는 테스트 파일을 생성했습니다. 동일한 함수에 대해 Copilot의 유사한 명령어를 테스트했을 때는, Copilot이 종속성 체인(dependency chain)을 볼 수 없었기 때문에 실제 구현(real implementations)을 호출하는 테스트를 생성했습니다. Cody가 생성한 테스트 파일이 완벽하지는 않았지만(두 개의 엣지 케이스(edge cases)를 놓쳤습니다), 모크 설정(mock setup)이 정확했다는 점이 중요합니다. 모크 설정은 수동으로 작성할 때 가장 많은 시간이 소요되는 부분이기 때문입니다. 저는 모크를 처음부터 작성하는 데 15분을 쓰는 대신, 4분 동안 검토하고 엣지 케이스를 추가하는 데 시간을 보냈습니다.
커스텀 레시피 (Custom recipes)를 사용하면 특정 컨텍스트 요구 사항을 가진 자신만의 명령어를 정의할 수 있습니다. 저희 팀은 모든 풀 리퀘스트 (Pull Request) 전에 API 중단 변경 사항 (breaking API changes)을 스캔하는 레시피를 설정했습니다. 이 레시피는 현재 브랜치의 내보내기 된 타입 (exported types) 및 함수 (functions)를 메인 브랜치와 비교하여 추가, 삭제 및 시그니처 변경 사항을 표시합니다. 이 레시피는 500개 파일의 디프 (diff)를 실행하는 데 약 30초가 소요되며, 다운스트림 소비자 (downstream consumers)를 망가뜨릴 수 있었던 두 번의 실수로 인한 인터페이스 변경을 잡아냈습니다. 이것이 바로 코드베이스를 인식하는 AI (codebase-aware AI)가 가능하게 하는 워크플로이며, 일반적인 어시스턴트 (generic assistants)는 무엇이 변경되었고 무엇이 그것에 의존하는지에 대한 구조적 이해가 부족하기 때문에 단순히 수행할 수 없는 작업입니다.
// API 중단 변경 사항을 감지하기 위한 Cody 커스텀 레시피.
// Cody의 코드 그래프 (code graph)는 의존성 체인 (dependency chain)을 알고 있기 때문에,
// 단순히 디프 텍스트 (diff text)뿐만 아니라 다운스트림 소비자에게 영향을 미치는
// 변경 사항을 표시할 수 있습니다.
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기