한 줄 정의

Code Search는 구글의 거대한 코드베이스를 ‘브라우징’이 아니라 ‘검색’으로 탐색하고 이해하게 해주는 독립된 웹 도구입니다. grep 수준의 텍스트 검색에서 출발해, 심볼 정의·사용처·변경 이력으로 클릭 한 번에 점프하는 상호참조(cross-reference) 도구로 진화했습니다. 핵심은 ‘코드 작성’이 아니라 ‘코드 이해’에 최적화되어 있다는 점이며, 그래서 IDE가 아닌 별도 도구로 만들어졌습니다.

쉽게 말하면

웹이 처음 작았을 때는 야후 같은 디렉터리 — 누군가 정리해둔 카테고리 트리를 클릭해 들어가는 방식 — 로 원하는 페이지를 찾을 수 있었습니다. 그런데 웹페이지가 폭발적으로 늘어나자 디렉터리로는 도저히 따라잡을 수 없게 됐고, ‘카테고리를 둘러보기’에서 ‘검색창에 쳐넣기’로 패러다임이 통째로 넘어갔습니다. 구글 검색이 바로 그 전환의 주인공이었습니다.

Code Search는 구글이 웹에 한 일을 자사 코드베이스에 똑같이 한 것입니다. 코드가 작을 때는 IDE의 파일 트리를 클릭해 둘러보면 됩니다(디렉터리 방식). 하지만 코드베이스가 한 사람의 노트북에 복사조차 안 되는 규모로 커지면, 폴더를 둘러보는 것으로는 답이 안 나옵니다. 그래서 “이 함수 어디 있지?”를 파일 트리에서 헤매는 대신 검색창에 두 글자 쳐서 바로 찾는 것입니다.

이 장 전체는 결국 “코드베이스가 IDE로 감당 못 할 만큼 커지면, 코드 탐색도 웹 검색처럼 풀어야 한다” 는 이야기입니다. 그래서 인덱스·랭킹·지연시간·완벽성 트레이드오프 같은, 원래 웹 검색엔진이 고민하던 문제들이 그대로 코드 검색의 설계 과제로 등장합니다.

왜 중요한가?

개발자가 코드를 작성하는 시간보다 읽고 이해하는 시간이 훨씬 많습니다. 코드를 이해하는 것이야말로 코드를 개발하고 유지보수하는 열쇠이고, 그 이해를 돕는 투자는 측정하기는 어려워도 부정할 수 없는 이익을 돌려줍니다.

코드 검색을 확장하는 데 가장 큰 걸림돌은 전체 코드의 크기입니다. 규모에 따라 해법이 완전히 달라집니다.

  • 수MB짜리 작은 리포지터리 → 별 고민 없이 grep으로 충분합니다.
  • 수백MB → 간단한 로컬 인덱스를 도입하면 검색 속도가 10배 이상 빨라집니다.
  • GB·TB 단위 → 클라우드로 옮겨 서버를 여러 대 띄워야 쓸만한 속도가 나옵니다.

문제는 중앙집중형 검색 솔루션이 이용자 수와 코드량이 늘면 함께 비대해진다는 데 있습니다. 구글은 이 지점을 정면돌파하기로 했고, 그 결과물이 Code Search입니다. 다른 회사들은 같은 기능 대부분을 로컬 IDE에서 수행하는데, 구글이 왜 굳이 독립된 웹 도구로 만들었는지가 이 장의 핵심 질문입니다.

핵심 내용

Code Search가 답하는 질문들 — ‘클릭 한 번으로 다음 질문에’

Code Search의 출발점은 grep과 유사한 검색 도구와 대외용 UI의 조합이었습니다. 이후 상호참조와 심볼 정의로 이동하게 해주는 Kythe(과거 Grok)가 통합되면서, 단순 검색에서 코드 브라우징으로 초점이 옮겨갔습니다. 그러면서 ‘클릭 한 번으로 다음 (예상) 질문에 답해주기’ 가 개발 원칙의 하나로 자리 잡았습니다. 지금은 다음 질문들에 클릭 한두 번으로 답합니다.

  • 이 심볼은 어디에 정의되어 있나요?
  • 이건 어디서 사용되죠?
  • 이 심볼은 어떻게 인클루드하죠?
  • 이 코드는 언제 코드베이스에 추가된 거죠?
  • 전체적으로 CPU 사이클을 얼마나 소모하나요?

UI의 핵심은 웹 검색을 빼닮은 검색창입니다. 타이핑을 시작하면 ‘제안’ 기능이 파일·심볼·디렉터리로 빠르게 안내하고, 더 복잡한 쓰임에는 코드 스니펫이 포함된 결과 페이지를 보여줍니다. 검색 자체는 성능 좋은 ‘파일에서 찾기(find in files)’ 정도로, 여기에 관련성 랭킹·코드 강조·범위 인식·주석 및 문자열 인식이 더해집니다.

파일을 들여다볼 때는 대부분의 토큰이 클릭 가능한 링크입니다. 함수 호출은 함수 정의로, 임포트된 파일 이름은 실제 소스 파일로, 주석에 쓰인 버그 ID는 해당 버그 리포트로 이동합니다. 이는 단순 텍스트 매칭이 아니라 컴파일러 기반 인덱스(Kythe) 가 의미론적 구조를 알고 있기에 가능합니다.

Kythe — 같은 이름의 다른 심볼을 구별해주는 힘

Kythe는 전체 빌드 정보를 활용하여 특정 코드 심볼(예: 함수)을 같은 이름의 다른 심볼과 명확하게 구별해줍니다. 그래서 심볼 이름을 클릭하면 그 심볼이 이용된 모든 위치를 담은 창이 열리고, 지역 변수 이름에 커서를 올리면 그 변수를 사용한 위치만 정확히 강조됩니다. 토큰이 같다고 뭉뚱그리지 않는다는 점이 grep과 결정적으로 다릅니다.

여기에 버전 관리 시스템 Piper가 통합되어 있어 파일의 변경 이력까지 보여줍니다. 이전 버전, 어디가 변경됐는지, 누가 작성했는지를 확인하고, 코드 리뷰 도구로 점프하거나 ‘blame’ 뷰를 볼 수 있습니다. 디렉터리 뷰에서는 지워진 파일까지 보여줍니다.

개발자는 코드 검색으로 무엇을 하나 — 사용 패턴

구글 개발자가 Code Search를 쓰는 이유는 결국 코드에 대한 답을 찾고 코드의 의도를 분명하게 이해하는 것입니다. 실제 사용량을 분석하면 압도적으로 ‘작성’이 아니라 ‘읽기·이해’에 쏠려 있습니다.

질문 유형비중무엇을 하는가주로 쓰는 기능
어떻게?약 1/3다른 사람들이 이 API를 어떻게 썼는지 보고 구체적 사용법 파악검색 + 상호참조
무엇을?약 1/4’코드베이스의 이 부분이 무슨 일을 하는가’ — 탐색적 읽기호출 계층·관련 파일 브라우징
왜?약 16%‘왜 이렇게 동작하지?’ — 기대와 다른 동작을 디버깅특정 시점 코드 상태 탐색
누가 언제?약 8%특정 코드 조각을 누가 언제 추가했는지변경 이력·blame, 코드 리뷰로 점프

여기서 ‘왜?‘와 ‘누가 언제?‘는 모두 시간 축을 다룹니다. 프로덕션 문제를 디버깅할 때는 몇 주·몇 달 전의 코드를, 새로 추가한 코드가 실패할 때는 단 몇 분 전의 변경을 살펴봐야 합니다. Code Search는 둘 다 가능해야 하며, 이 요구가 뒤에서 다룰 ‘변경 이력 전체 인덱싱’의 근거가 됩니다.

또 하나 주목할 점은 선순환입니다. 탐색하기 쉽도록 애초에 코드를 잘 작성하면(계층 구조를 너무 깊지 않게, 범용 타입보다 이름 있는 타입을 쓰는 식으로) Code Search가 더 잘 동작하고, 그 결과 코드를 더 쉽게 이해하게 됩니다.

왜 IDE가 아니라 독립된 웹 도구인가

이 장에서 가장 흥미로운 설계 결정입니다. 네 가지 이유가 맞물려 있습니다.

대규모 코드베이스 지원 — 인덱스를 ‘한 번만’ 만든다

대다수 IDE는 로컬 복사본이 있어야 동작하는데, 구글 코드베이스는 전체를 로컬로 복사하는 것 자체가 불가능합니다. 설령 감당할 규모라도, 로컬에서 검색·상호참조 인덱스를 생성하느라 IDE 구동 시간이 느려져 생산성을 떨어뜨립니다. 핵심 통찰은 비용 구조입니다.

flowchart LR
    subgraph IDE["❌ 각자 로컬 인덱싱 (IDE)"]
        direction TB
        D1["개발자 A<br/>인덱스 생성"]
        D2["개발자 B<br/>인덱스 생성"]
        D3["개발자 N<br/>인덱스 생성"]
    end
    subgraph CS["✅ 중앙 인덱싱 (Code Search)"]
        direction TB
        IDX[("중앙 인덱스<br/>한 번 생성")]
        U1["개발자 A"] --> IDX
        U2["개발자 B"] --> IDX
        U3["개발자 N"] --> IDX
    end
    IDE ==>|중앙화| CS

각자 인덱싱하면 ‘개발자 수 × 코드베이스 크기’만큼 비용이 곱으로 늘지만, 중앙에서 한 번만 생성하면 모두에게 혜택이 돌아갑니다. Code Search의 인덱스는 변경이 서브밋될 때마다 점진적으로 갱신되어 비용이 코드량에 선형으로만 증가합니다.

단, 상호참조 인덱스는 즉각 갱신할 수 없다

검색 인덱스는 변경된 파일의 인덱스만 갱신하면 되므로 점진적·병렬 갱신이 쉽습니다. 그러나 상호참조 인덱스는 다릅니다. 어떤 코드 변경이라도 잠재적으로 코드베이스 전체에 영향을 줄 수 있어서 ‘점진적’으로 풀 수 있는 문제가 아닙니다. 실제로 구글에서는 파일 수천 개에 영향을 주는 변경이 흔하고, 그러면 거의 모든 바이너리를 다시 분석해야 합니다(크로미움 기준 Kythe 인덱스 생성에 약 6시간). 그래서 Kythe 인덱스는 하루에 한 번만 생성되고, 상호참조 인덱스에는 변경이 늦게 반영되어 개발자들이 드물지만 반복적으로 혼란을 겪습니다.

설정 없이 모든 코드 보기

코드베이스 전체에서 최신 코드를 정확히 브라우징할 수 있으면 재사용할 라이브러리나 복사해 쓸 예시 코드를 찾기가 매우 쉽습니다. IDE는 구동 시점에 인덱스를 만들기 때문에 같은 효과를 보려면 프로젝트를 작게 하거나 탐색 범위를 좁혀야 합니다. Code Search 웹 UI는 아무런 설정 없이 어디서 등장하는 코드든 즉시 찾아 검토하게 해줍니다.

기능 특화 — IDE가 아니라는 게 장점

편집이 아닌 코드 ‘탐색’과 ‘이해’에 사용자 경험을 최적화할 수 있습니다. 편집용 텍스트 커서가 필요 없으므로, 심볼을 마우스로 클릭하면 커서를 그 위치로 옮기는 대신 ‘정의로 점프(jump to definition)’‘사용처 모두 보기(show all usages)’ 같은 더 의미 있는 동작을 바로 실행합니다.

다른 도구를 떠받치는 플랫폼

Code Search는 소스 코드 관련 정보를 외부에 제공하는 플랫폼으로 자리 잡았습니다. 구글은 Code Search가 제공하는 소스 파일 링크를 그 파일의 표준 ‘위치’ 로 간주합니다.

  • 로그 뷰어: 로그 문장의 파일명·라인 번호에서 소스 코드로 링크
  • 크래시 리포팅·스택 프레임: 크래시된 바이너리를 빌드한 리포지터리 스냅샷을 알기에 해당 버전 코드에서 정확히 검색. 리팩터링·삭제 후에도 링크가 깨지지 않음
  • 컴파일 에러·테스트 실패 메시지: 문제 위치로 링크. 클라우드 작업 공간 덕분에 커밋하지 않은 코드에도 링크 생성 가능
  • 문서자료·코드랩: live-snippet으로 트렁크의 최신 코드를 문서 페이지에 삽입 → 소스를 더럽힐 필요 없이 항상 최신 코드를 보여줌

마지막으로 검색·상호참조·구문 강조 기능을 API로 제공해, vim·emacs·IntelliJ용 검색·상호참조 플러그인을 만들어 쓰기도 합니다. 로컬 인덱싱을 못 해 포기했던 기능을 플러그인으로 부활시킨 셈입니다.

규모가 만드는 두 가지 지연시간

대화형 도구이므로 반응이 빨라야 합니다. 그런데 지연시간에는 두 종류가 있고, 각각 다른 이유로 중요합니다.

검색 쿼리 지연시간 — ‘1초’를 엔지니어 인건비로 환산하기

짧은 지연시간이 공짜로 주어지지는 않으므로, 투자 여부를 결정하려면 그 가치를 계산할 수 있어야 합니다. 구글 Code Search는 매일 100만 개 이상의 검색을 처리합니다.

손익분기점 계산

100만 쿼리에 검색당 1초씩 지연된다고 가정하면, 매일 약 35명의 엔지니어가 아무것도 안 하고 노는 것과 같습니다. 반면 검색 백엔드를 관리하는 데는 그 1/10인 서너 명이면 충분합니다. 따라서 손익분기점은 하루 약 10만 쿼리(개발자 약 5천 명분)당 지연시간 1초입니다.

게다가 지연시간 증가와 생산성 손실은 선형 관계가 아닙니다. 사람은 지연시간이 200밀리초보다 짧으면 빠르다고 느끼지만, 1초가 넘으면 주의가 분산되기 시작하고, 거기서 10초가 더 흐르면 아예 다른 일을 시작해 생산성이 급격히 떨어집니다. 그래서 자주 쓰이는 기능은 종단간 지연시간을 200밀리초 미만으로 붙잡아 둬야 합니다.

이 지점이 ‘검색이 브라우징을 이기는’ 이유이기도 합니다. 인크루드한 파일이나 심볼 정의를 통한 ‘다음 파일로 이동’은 클릭 한 번이면 되지만, 일반적인 탐색에서는 전통적인 파일 트리보다 원하는 파일을 ‘검색’하는 편이 훨씬 빠릅니다. 코드베이스가 커져 파일 트리가 깊어질수록 그 차이는 극명해집니다.

인덱싱 지연시간 — ‘내가 방금 한 일’이 안 보이면 신뢰가 깨진다

인덱스가 최신이 아니어도 개발자는 보통 눈치채지 못합니다. 전체 코드 중 작은 일부에만 신경 쓰기 때문입니다. 하지만 자신이 방금 수정한 코드가 반영되어 있지 않으면 이야기가 다릅니다. 새 함수·클래스를 추가했는데 검색되지 않으면 완벽한 상호참조에 길든 개발자는 당황하여 다음 단계로 넘어가지 못합니다. 검색 후 대체(search-and-replace) 리팩터링도 마찬가지로, 제거한 코드는 검색 결과에서 곧장 사라져야 합니다.

반대로 코드를 이전 스냅샷(보통 릴리스 시점)으로 되돌릴 수 있으면 좋습니다. 인덱스와 실행 중인 코드가 다르면 진짜 원인을 감추거나 혼선을 줄 수 있기 때문입니다.

검색 인덱스의 진화 — 표준 인프라로의 수렴

Code Search는 현재 약 1.5TB의 콘텐츠를 인덱싱하며 초당 약 200개 쿼리를 처리합니다. 서버단 지연시간은 50밀리초 이하, 인덱싱 지연시간(코드 커밋 후 인덱스에 보일 때까지)의 중간값은 10초 미만입니다.

grep으로 이 성능을 내려면? — 30만 개 CPU 코어

정규식 매칭 라이브러리 RE2는 메모리 상 데이터를 초당 100MB 처리합니다. 1.5TB를 50밀리초 이내로 처리하려면 30만 개의 CPU 코어가 필요합니다. 대부분의 검색은 ‘이 문자열을 포함하는가’ 수준이므로 부분 문자열(substring) 검색으로 최적화하면 초당 약 1GB까지 올라가 코어 수가 10%로 줄지만, 여기에 ‘초당 200개 쿼리 동시 처리’(매 순간 약 10개) 조건을 더하면 다시 30만 개로 불어납니다. 이 추정이 구글 Code Search 팀이 인덱싱 개선에 꾸준히 투자하는 이유를 설명합니다.

인덱스 방식은 세 단계로 진화했습니다.

flowchart LR
    A["트라이그램(trigram)<br/>기반<br/>(러스 콕스가<br/>오픈소스 공개)"] --> B["커스텀 접미사 배열<br/>(suffix array) 기반"]
    B --> C["희소 n-그램<br/>(sparse n-gram) 기반<br/>(현재)"]

현재 방식은 grep보다 500배 이상 효율적이면서 정규식 검색에도 빠르게 응답합니다. 접미사 배열에서 토큰 기반 n-그램으로 전환한 이유가 중요합니다 — 접미사 배열은 커스텀 인덱스를 구축·배포하는 일 자체가 큰 과제였습니다. 그래서 ‘표준’ 기술로 눈을 돌려, 역 인덱스(reverse index) 생성·인코딩과 코어 검색팀이 개발한 서빙 인프라의 장점을 그대로 수용했습니다. 즉각적 인덱싱도 표준 검색 인프라가 이미 제공하던 기능이었습니다.

다만 표준 기술은 구현이 간단해지는 대신 성능이 떨어지는 트레이드오프가 있어서, Code Search는 역 인덱스 생성에만 표준 기술을 쓰고 실제 검색·매칭·점수 산출 코드는 상당히 수정·최적화했습니다. 파일 변경 이력 인덱싱에는 독자적 압축을 적용해 자원 소모를 2.5배 수준으로 억제하면서 전체 이력을 인덱싱했습니다.

저장 매체도 진화했습니다. 초기에는 모든 데이터를 메모리에 뒀지만 인덱스가 커지면서 역 인덱스를 플래시 드라이브로 옮겼습니다. 플래시는 메모리보다 최소 10배 저렴하지만 지연시간이 100배 이상 늘어, 메모리에서 잘 돌던 트라이그램 인덱스(수많은 역 인덱스를 모두 가져와야 함)에는 부적합했습니다. n-그램으로 바꾸면 전체 인덱스 크기는 커지지만 역 인덱스의 수와 크기를 줄여 플래시에 맞출 수 있었습니다.

랭킹 — 코드에 맞춘 시그널

코드베이스가 작으면 결과가 적어 랭킹이 별 의미 없지만, 커지면 짧은 키워드 검색만으로 결과가 수천~백만 개씩 나옵니다. 랭킹이 없으면 사용자가 건초 더미에서 바늘을 찾거나, 결과가 줄도록 쿼리를 계속 다듬어야 합니다. 랭킹은 파일의 여러 특성(시그널, signal)을 점수로 환산하는 함수이며, 시그널은 두 종류입니다.

시그널 종류계산 시점·비용예시
쿼리 독립적오프라인 배치(MapReduce 등), 계산량 많아도 OK파일 조회수, 파일로의 참조량, 파일명 길이, 프로그래밍 언어
쿼리 의존적쿼리마다 계산, 인덱스에서 빠르게 얻을 수 있어야 함함수 정의 일치냐 문자열 리터럴 일치냐, 토큰 정확 일치, 대소문자 일치
쿼리 독립적 시그널 — 조회수와 참조(코드판 페이지랭크)

가장 중요한 두 시그널은 파일 조회수파일로의 참조량입니다. 조회수가 높다는 것은 개발자들이 그 파일을 중요하게 여겨 찾을 가능성이 높다는 뜻입니다.

조회수의 함정 — 탐색과 활용(exploration vs exploitation)

조회수 시그널은 피드백 루프를 만듭니다. 자주 조회돼서 점수를 높이면 더 자주 조회되고, 그 여파로 다른 문서가 검색 순위에 들 가능성은 낮아집니다. 다행히 고득점 항목을 과하게 노출해도 크게 해롭지는 않지만(관련 없으면 무시되고, 일반적 예시가 필요할 때 선택되므로), 새로 만들어져서 아직 정보가 쌓이지 않은 좋은 파일에는 불리하게 작용합니다. A/B 검색 실험이나 훈련 데이터 큐레이션으로 보완합니다.

참조량은 웹의 페이지랭크(PageRank) 와 같은 발상입니다. 웹 링크 대신 include/import 부류의 문장을 참조로 보고, 빌드 의존성(라이브러리·모듈 수준)과 함수·클래스 단위 의존성까지 확장해 ‘우선순위’를 매깁니다. 단, 참조 정보를 안정적으로 추출하기가 어렵습니다 — 초기에는 정규식으로 추출했으나 코드베이스가 복잡해지며 경험법칙이 맞지 않아 Kythe 그래프에서 정확한 정보를 가져와 대체했습니다. 또 Abseil 같은 핵심 라이브러리의 대규모 리팩터링은 여러 단계로 나뉘어 진행되는데, 도중에 간접 참조가 생기면 파일이 이동한 사실이 감춰져 이동된 파일의 랭크가 떨어지고 개발자가 새 위치를 찾기 어려워집니다. 이럴 때는 영향받는 파일들의 점수를 수동으로 높여줍니다.

쿼리 의존적 시그널 — 토큰만 보지 않는다

웹 검색과 달리 Code Search는 토큰만으로 일치를 판단하지 않습니다. 검색어와 콘텐츠가 토큰 수준에서 깔끔하게 일치하면 점수를 더 주고, 대소문자까지 맞으면 보너스를 줍니다. 예를 들어 Point로 검색하면 Point *pappointed to the council.보다 점수를 잘 받습니다. 또 파일 이름과 심볼의 전체 이름(qualified symbol)과도 비교해, 개발자 의도를 짐작하여 일반 콘텐츠보다 심볼·파일명 매치에 가중치를 둡니다.

검출(retrieval)과 다양성 — 점수 매길 후보부터 잘 골라야

점수를 매기기 전에 일치 가능성이 있는 후보를 찾는 단계를 검출(retrieval) 이라고 합니다. 모든 문서를 다 보는 건 비효율적이라 검출된 문서에만 점수를 매기는데, 여기에 함정이 있습니다. 많이 쓰이는 클래스는 사용처가 천 개 단위인데 정의는 단 한 곳입니다. 검색 범위를 정의로 한정하지 않으면, 정의가 든 파일에 도달하기 전에 검출 개수 제한에 걸려버립니다. 보충 검출(supplemental retrieval) 은 원래 쿼리를 ‘정의와 파일 이름만’으로 제한해 다시 검출하고, 그 결과를 기존 검출 결과에 추가하는 기법입니다.

결과 다양성도 중요합니다. 간단한 함수 이름을 검색하면 자바 결과와 파이썬 결과를 함께 보여주는 식입니다. 사용자 의도가 명확하지 않을 때 특히 중요하지만, 분류(함수·클래스·파일명·로컬 결과·사용처·테스트·예제)가 너무 많아 화면에 다 담기 어렵습니다. 그래서 웹 검색의 자동완성과 비슷하게 추천 결과 드롭다운으로 제공합니다.

구글이 택한 트레이드오프

규모 문제를 풀기 위해 내린 선택들입니다. 대부분 완벽성(completeness)을 어디까지 희생할 것인가의 문제입니다.

완벽성: 인덱싱 범위 — 자원 절약보다 신뢰

콘텐츠 일부(바이너리·이미지·동영상, 도구가 생성한 난독화 자바스크립트, 수MB짜리 파일)를 인덱싱에서 제외하면 자원을 아낄 수 있습니다. 하지만 구글은 인덱싱을 많이 하는 쪽을 택했습니다. 제외된 파일 때문에 피드백에 구멍이 생기면 혼란과 생산성 손실로 이어지고, 드물지만 한 번 발생하면 치러야 할 비용이 크기 때문입니다. 검색 도구는 신뢰가 생명이라는 판단입니다.

완벽성: 전부 vs 가장 관련성 높은 결과만

웹 검색은 속도를 위해 완벽성을 희생합니다. Code Search도 함수 정의처럼 단 하나의 결과를 찾을 때는 순위 검색(ranked search)이 적합합니다. 하지만 개발자는 종종 ‘모든’ 결과를 원합니다 — 리팩터링할 때 특정 심볼이 쓰이는 곳을 하나라도 놓치면 안 되기 때문입니다.

해법은 아키텍처에 있습니다. 파일을 우선순위대로 정렬하고 코드베이스 전체를 샤드(shard) 로 나눕니다. 평소에는 각 샤드에서 우선순위 높은 파일만 확인하지만(웹 검색과 동일), 모든 결과를 요청하면 각 샤드로부터 ‘모든’ 결과를 가져옵니다. 이렇게 빈번한 요청의 응답 속도를 희생하지 않으면서 두 가지 쓰임에 모두 대응합니다.

완벽성: 시간 축 — 변경 이력 전체를 인덱싱

코드베이스에는 변경 기록, 즉 시간이라는 차원도 있습니다. 헤드(현재 스냅샷) 외에 무엇을 더 인덱싱하느냐에 따라 복잡도·자원·비용이 급격히 늘어, 현재 버전을 넘어 인덱싱하는 IDE는 아직 없습니다. 그럼에도 Code Search는 Piper의 변경 기록까지 인덱싱합니다. 옛 버전 스냅샷과 삭제된 코드에서도 검색하고, 특정인이 작성한 코드로 한정해 검색할 수도 있습니다.

변경 이력 인덱싱의 실용적 이점 — 낡은 코드를 그냥 지울 수 있다

전에는 낡은 코드를 ‘obsolete code’ 디렉터리로 옮겨놓는 식으로 처리했는데, 그러면 여전히 검색에 걸렸습니다. 변경 이력을 인덱싱하면 낡은 코드를 그냥 삭제해도 필요할 때 과거 이력에서 찾을 수 있으므로, 코드베이스를 깨끗하게 유지하기 쉬워집니다.

작업 공간(workspace)은 글로벌 리포지터리와 성격이 다릅니다 — 개발자 개인 소유이고, 변경된 파일 수가 적으며, 작업 중인 파일은 자주 바뀌고, 존재 기간이 짧습니다. 그래서 작업 공간 인덱스는 현재 상태를 정확히 반영해야 가치가 있으며, 변경 안 된 파일은 글로벌 인덱스를 그대로 재사용하고 변경된 파일만 무차별 검색하는 간단한 머신을 여러 대 둡니다.

표현력: 토큰 vs 부분 문자열 vs 정규식

검색 쿼리를 어디까지 지원하느냐가 리포지터리 규모의 영향력을 좌우합니다. Code Search는 정규식 검색을 지원하는데, 이를 효율적으로 떠받칠 인덱스를 만드는 것이 핵심 과제입니다.

인덱스 방식인덱스 크기표현력한계
토큰 기반작음 (소스 일부만 저장)낮음토큰화로 의미 소실 — function()/function(x) 구분, '=== myClass' 검색 불가. 대소문자·어미(searching/searched) 뭉갬, 공백·구분자 검색 불가
부분 문자열중간중간문자 순서대로 검색 가능하나, 재현 정확도(recall accuracy)가 떨어져 비일치 항목을 걸러내느라 응답이 느려짐
정규식— (별도 인덱스 없음)가장 높음완벽한 정규식 인덱스는 존재하지 않음

구글의 실용적 정책은 이렇습니다. 트라이그램 기반 부분 문자열 인덱스를 만들고, 정규식은 그 위에서 근사합니다. 정규식 오토마타를 부분 문자열 검색들로 변환하는 식인데, 완벽한 정규식 인덱스가 없으니 일부는 무차별 대입식으로 풀어냅니다. 사용자가 복잡한 정규식으로 검색하는 비중은 아주 작기 때문에, 이 근사 정책이 매우 실용적입니다.

비교 / 트레이드오프

관점로컬 IDE 인덱싱Code Search
인덱싱 비용개발자 수 × 코드베이스 크기 (곱)코드량에 선형 (한 번 생성, 공유)
다룰 수 있는 코드로컬 복사 가능한 범위로 한정코드베이스 전체, 설정 불필요
최적화 대상편집 + 탐색탐색·이해 전용
과거 버전현재 스냅샷만전체 변경 이력·삭제된 코드
잘 맞는 상황IDE로 감당되는 작은 프로젝트거대한 코드베이스, 다수 개발자
왜 웹 검색 기술을 그대로 쓰지 않았나

웹 검색과 코드 검색은 출발이 같아 보이지만 결정적으로 다릅니다. 웹 검색은 속도를 위해 완벽성을 기꺼이 희생합니다(상위 결과만 정확하면 됨). 코드 검색은 리팩터링·도구 제작처럼 ‘모든 결과’가 필요한 쓰임이 있어, 완벽성을 함부로 버릴 수 없습니다. 그래서 역 인덱스 생성·서빙 같은 표준 인프라는 그대로 올라타되, 검색·매칭·점수 산출과 ‘모든 결과’ 검출은 코드에 맞게 새로 짰습니다.

내 생각

  • 중앙 인덱스 = 선형 비용 vs 각자 인덱싱 = 곱 비용. CI 캐시를 팀이 공유하는 것, 도커 베이스 이미지를 사내 레지스트리에 한 번 빌드해 돌려쓰는 것과 똑같은 발상입니다. ‘한 번 계산해 N명이 공유’는 인프라 비용 설계의 기본기입니다.

  • 지연시간을 인건비로 환산하는 계산법은 그대로 가져다 쓸 수 있다. ‘쿼리 1초 = 엔지니어 35명분’처럼, 백엔드 API의 p99 지연을 줄이는 작업을 정당화할 때 ‘월 X시간 절약 = 개발자 Y명분’으로 환산하면 SLO 투자 결정이 훨씬 설득력 있어집니다.

  • 인덱싱 지연 문제 = read-your-writes 일관성. “내가 방금 커밋한 게 검색에 안 보이면 도구를 못 믿는다”는 건, 캐시 무효화에서 ‘본인이 쓴 값은 즉시 본인에게 보여야 한다’는 일관성 모델과 정확히 같은 문제입니다.

  • 트라이그램 커스텀 → 표준 역 인덱스로의 회귀가 가장 큰 교훈. 직접 만든 특수 최적화는 초기엔 빠르지만 유지·확장이 발목을 잡습니다. 자체 검색엔진을 만들 것인가 Elasticsearch에 올라탈 것인가의 고민과 동형이고, 구글조차 ‘표준 인프라에 올라타기’를 택했다는 점이 인상적입니다.

  • 검색 도구는 신뢰가 곧 채택률. 빠진 결과 하나가 도구 전체의 신뢰를 깹니다. 자원을 아끼려고 인덱싱을 줄이지 않은 선택은, 모니터링·로깅에서 ‘샘플링으로 비용 아끼다 정작 필요한 순간 데이터가 없는’ 함정과 같은 교훈을 줍니다.

관련 개념

  • Ch16 버전 관리와 브랜치 관리 — Code Search가 통합하는 Piper와 작업 공간이 여기서 나옵니다. 변경 이력 전체를 인덱싱해 ‘누가 언제’를 답하는 기능이 VCS와 직결됩니다
  • Ch10 문서자료live-snippet으로 문서에 트렁크 최신 코드를 삽입하고, Code Search 링크가 코드 참조의 표준이 되는 지점에서 만납니다
  • Ch07 엔지니어링 생산성 측정하기 — ‘지연시간 1초 = 엔지니어 35명분’처럼 도구의 가치를 정량화하는 사고방식이 생산성 측정의 연장선입니다