Home 17. Code Search
Post
Cancel

17. Code Search

Code Search는 구글이 이용하는 코드 브라우징 및 검색 도구로 프런트엔드 UI와 다양한 백엔드 요소로 이루어져 있다. 구글의 거대한 코드베이스를 감당하며 쉽게 확장할 수 있는 도구가 필요해서 만들어졌다.

Code Search는 대규모 코드를 읽고, 이해하고, 탐색하는 데 최적화되었다. 이를 위해 클라우드 기반 백엔드를 적극 활용하여 콘텐츠를 찾고 상호참조를 식별해낸다.

17.1 Code Search UI

Code Search UI의 핵심 요소는 당연히 검색창이다. 웹 검색처럼 타이핑을 시작하면 제안 기능이 작동하여 개발자가 파일, 심볼, 디렉터리로 빠르게 찾아 들어갈 수 있다. 검색 자체는 성능 좋은 파일에서 찾기 정도로 생각할 수 있다. 여기에 관련성 랭킹과 코드 강조, 범위 인식, 주석 및 문자열 인식 같은 개선이 더해져 있다.

또 Code Search는 Piper와도 통합되어 있어서 파일의 변경 이력도 보여준다. 파일의 이전 버전을 확인하고, 어디가 변경되었는지, 누가 작성했는지 확인할 수 있다.

17.2 구글 개발자가 Code Search를 이용하는 방법

Code Search와 비슷한 기능을 제공하는 다른 도구들도 있지만 구글 개발자들은 검색하고 파일 내용을 살펴보기 위해 무엇보다 코드를 이해하기 위해 Code Search UI를 적극 활용한다. 구글 개발자가 Code Search로 하려는 일은 코드에 대한 답을 찾고 코드의 의도를 분명하게 이해하는 것이다.

17.2.1 어디에?

Code Search는 두 가지 방식으로 도와준다.

  1. 결과에 랭킹을 매겨 보여주고 표현력 좋은 질의어를 제공하는 것이다.
  2. 결과를 동료과 공유하기 쉽게 해준다.

17.2.2 무엇을?

Code Search 사용량의 1/4은 전통적인 파일 브라우징에 쓰인다. 코드 베이스의 이 부분이 무슨 일을 하는가?라는 질문에 답하는 거라 볼 수 있다.

17.2.3 어떻게?

약 1/3을 차지하는 쓰임은 다른 사람이 어떻게 썼는지를 보는 것이다. 특정 문제에 이용할 라이브러리를 찾은 다음, 그 안에서 가장 적합한 구현을 선택하게 도와준다.

17.2.4 왜?

Code Search 사용량의 약 16%는 이 코드가 왜 추가되었나? 혹은 왜 이런식으로 동작하지?의 답을 찾는 것이다. 여기서 특정 시점에서 코드베이스의 정확한 상태를 찾고 탐색할 수 있는 기능이 중요하다.

17.2.5 누가 언제?

Code Search 사용량의 약 8%는 특정 코드 조각을 누가 언제 추가했는지를 찾는데 쓰인다. 이때는 버전 관리 시스템과 연계해 동작한다.

17.3 독립된 웹 도구로 만든 이유

다른 회사에서는 Code Search의 기능 대부분을 로컬 IDE에서 수행한다. 구글은 왜 독립된 도구로 만들었을까?

17.3.1 대규모 코드베이스 지원

구글의 코드베이스가 너무 거대해서 코드베이스 전체를 로컬로 복사하는 게 불가능하다. 로컬에서 감당할 수 있는 규모라 해도 로컬 검색과 상호참조 인텍스를 생성하느라 IDE 구동 시간이 느려져서 개발자 생산성을 떨어드린다. 반면 인덱스를 중앙에서 생성할 경우 한 번만 해두면 모두에게 혜택이 돌아온다. 변경된 코드가 서브밋되면 변경된 파일들의 인덱스만 갱신하면 되므로 전역 인덱스는 병렬로 따로 갱신할 수 있다.

상호참조 인덱스는 같은 방식으로 즉각 갱신할 수가 없다. 상호참조 인덱스를 만드는 데 매일 막대한 컴퓨팅 자원을 사용한다.

17.3.2 설정 없이 모든 코드 보기

Code Search의 웹 UI는 아무런 설정(프로젝트 설명, 빌드 환경) 없이도 이용할 수 있다. 어디에서 등장하는 코드든 상관없이 매우 쉽고 빠르게 찾아 검토할 수 있다.

17.3.3 기능 특화

IDE가 아니기 때문에 편집이 아닌 코드 탐색과 이해에 사용자 경험을 최적화할 수 있었다.

17.3.4 다른 도구에 통합

소스 코드 관련 정보를 회부에 제공하는 플랫폼으로 자리 잡았다. 예1) Code Search를 통합한 로그 뷰어, 로그 뷰어는 로그 문장으로부터 해당 소스 코드로 연결해줄 때 Code Search가 제공하는 링크를 이용하는 것이다. 예2) 코드랩이나 다른 문서자료에서도 API, 예시, 구현 코드를 참조하는 경우가 많다.

17.3.5 API 제공

Code Search는 검색, 상호참조, 구문 강조 기능을 API로 제공하여 다른 도구 개발자가 가져다 쓸 수 있다.

17.4 규모가 설계에 미치는 영향

코드 검색을 확장하는 데 가장 큰 걸림돌은 전체 코드의 크기이다. 수 MB 정도의 작은 리포지터리라면 별다른 고민 없이 grep으로 검색해도 된다. 수백 MB로 늘어나면 간단한 로컬 인덱스를 도입해 검색 속도를 10배 이상 높일 수 있다. 코드량이 GB나 TB 단위로 가면 클라우드로 옮겨 서버를 여러 대 띄우면 쓸만한 검색 속도를 유지할 수 있다. 이처럼 중앙집중형 검색 솔루션은 이용자 수와 코드량이 늘면 함께 비대해진다.

17.4.1 검색 쿼리 지연시간

대체로 사람은 지연시간이 200밀리초보다 짧기만 하면 UI가 빠르다고 느낀다. 하지만 1초가 넘어가면 주의가 분산되기 시작하고, 10초가 더 흐르면 전혀 다른 일을 하기 시작하여 생산성을 급격히 떨어뜨릴 가능성이 크다.

Code Search가 처리하는 쿼리의 상당수가 코드베이스를 탐색하는 과정에서 발생한다.

검색에서는 글자 두어 개 타이핑하는 것만으로 원하는 파일을 찾을 수 있다. 이렇게 하기 위해서는 현재 보고 있는 파일 같은 맥락 정보를 검색 백엔드에 제공해야 한다. 맥락 정보를 이용해 검색 범위를 특정 프로젝트로 한정하거나 물리적인 거리순으로 추천하는 것이다.

17.4.2 인덱싱 지연시간

개발자는 자신이 방금 수정한 코드나 그로 인한 변경 사항들을 검토해야 하는데 수정 내용이 반영되어 있지 않으면 혼란을 불러올 수 있다. 작은 수정이든 리팩터링이든 완전히 새로 작성한 코드든 마찬가지이다. 개발자는 자신이 IDE에서 작은 프로젝트를 진행할 때와 똑같이 모든 코드가 최신 버전이기를 기대한다.

검색 후 대체 형태로 이루어지는 리팩터링도 마찬가지이다. 제거한 코드는 검색 결과에서 곧장 사라져야 한다. 편의성 면에서도 그렇지만, 잔재가 남아 있으면 후속 리패터링에 지장을 준다.

문제가 생겼는데 인덱스와 실행 중인 코드가 다르면 진짜 원인을 감추거나 혼선을 줄 수 있다. 실제 구글에서는 상호참조 기능에서 이 문제를 겪는다. 구글 규모에서는 인덱스 생성에 몇 시간이 걸리고, 너무 복잡하여 인덱스를 하나의 버전만 유지하기 때문이다.

17.5 구글은 어떻게 구현했나?

17.5.1 검색 인덱스

현재 Code Search는 약 1.5TB의 콘텐츠를 인덱싱하며 초당 약 200개의 쿼리를 처리한다. 서버단 기준으로 지연시간은 50밀리초 이하이고, 인덱싱 지연시간의 중간 값은 10초 미만이다. grep으로 이 성능을 내려면 자원이 얼마나 들까? 데이터 1.5TB를 지연시간 50밀리초 이내로 처리하려면 30만 개의 CPU 코어를 동원해야 한다.

469~471p

17.5.2 랭킹

코드베이스가 커져 결과 수가 늘어나면서 랭킹 기능이 중요해진다. 구글의 코드베이스에서 짧은 키워드만으로 검색하면 결과가 수천에서 백만 단위까지 나온다. 랭킹 기능이 없다면 사용자가 엄청난 건초더미를 다 뒤져 바늘을 찾아내거나, 결과수가 충분히 적어질 때까지 쿼리를 다음어야 한다.

랭킹을 매기려면 먼저 각 파일의 여러 특성(시그널)을 점수로 환산해줄 함수가 필요하다. 일반적으로 시그널을 2가지로 분류한다. 1)문서(파일)에만 의존적인 쿼리 독립적 시그널, 2)검색 쿼리와 쿼리를 어떻게 문서에 매치시킬지에 의존하는 쿼리 의존적 시그널

쿼리 독립적 시그널

쿼리 독립적 시그널로는 파일 조회수와 파일로의 참조량을 뽑을 수 있다. 파일 조회수가 높다는 것은 개발자들이 그 파일을 중요하게 생각하여 찾고자 할 가능성이 더 높다는 의미이다. 이 시그널의 가장 큰 문제는 피드백 루프가 만들어진다는 점이다. 자주 조회돼서 점수를 높이면 개발자들이 또 조회할 가능성이 커져서, 그로인해 다른 문서가 검색 순위 안에 들어올 가능성은 낮아진다. (탐색과 활용 문재)

**탐색: 새로운 도전 / 활용: 기존 자원을 활용

파일을 가리키는 참조의 수를 이용할 때 웹 링크를 참조로 간주하는 원래의 페이지 랭크 알고리즘과 비슷하게 프로그래밍 언어에서 사용하는 include/import 부류의 문장을 이용하는 것이다.

랭킹에 참조를 이용할 때는 크게 두 가지에 주의해야 한다.

  1. 참조 정보를 안정적으로 추출해낼 수 있어야 한다.
  2. 핵심적인 오픈 소스 라이브러리 등에서 종종 진행하는 대규모 리팩터링도 커다란 문제다. 이런 변경은 여러 단계로 나누어 진행되면서 리팩터링 도중 ‘간접 참조’가 만들어져서 파일이 이동되었다는 사실이 감춰지는 일이 많다. (간접 참조는 이동된 파일의 랭크를 낮춰서 개발자들이 새로운 위치를 찾기 어렵게 한다.)

쿼리 의존적 시그널

쿼리 의존적 시그널은 쿼리마다 계산하는 방식이라서 계산량이 적어야 한다. 즉 인덱스로부터 빠르게 얻을 수 있는 쿼리와 정보만 이용해야 한다.

검출(검색)

Code Search는 문서에 점수를 매기기 전에 검색 쿼리와 일치할 가능성이 있는 후보를 찾는다. 이 단계를 검출이라고 한다. 모든 문서를 다 검색하려면 현실적으로 비효율적이기 때문에 검출된 문서들에만 점수를 매긴다.

검출 단계에서 해결해야 할 주요 과제는 관련이 적은 수많은 파일 중에서 관련도 높은 소수의 파일을 찾는 일이다. 효과가 좋은 해결책으로 ‘보충 검출’이란 것이 있다. 보충 쿼리를 이용하여 검색 대상을 정의와 파일 이름만으로 제한하고, 이렇게 새로 검출한 문서들을 검출 단계의 결과에 추가한다.

결과 다양성

검색은 결과를 다양하게 보여줄 수 있어야 한다. 예) 간단한 함수 이름을 검색할 때 자바 언어에서 찾은 결과와 파이썬 언어에서 찾은 결과를 함께 제공하는 것

17.6 구글이 선택한 트레이드오프

17.6.1 완벽성: 헤드 리포지터리

코드 베이스가 클수록 검색하기는 어려워진다. 인덱싱이 느려지고 비용은 더 들고, 쿼리 응답도 드려진다. 이때 완벽성을 희생하고 콘텐츠 일부를 인덱싱에서 제외하면 이 비용을 줄일 수 있다. 하지만 제외된 파일들 때문에 때론 피드백에 구멍이 생겨서 혼란과 생산성 손실로 이어진다. 그래서 구글은 자원을 절약하기 보다는 인덱싱을 많이 하는 쪽을 선택했다.

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

웹 검색에서는 속도를 위해 완벽성을 희생한다. 수백만 개의 후보 중 함수 정의 같은 단 하나의 결과를 찾을 때는 순위 검색이 적합하다. 하지만 개발자는 종종 모든 결과를 원할때가 있다. 예) 리팩터링을 할 때는 특정 심볼이 쓰이는 곳을 하나라도 놓치면 안 된다.

하나의 아키텍처로 두 마리 토끼를 잡기 위해 구글은 파일들을 우선순위대로 정렬하고 코드베이스 전체를 샤드(조각)로 나눴다. 그리고 보통은 각 샤드에서 우선순위가 높은 파일들과만 일치 여부를 확인하고, 모든 결과를 내어달라고 요청하는 경우 각 샤드로부터 모든 결과를 가져온다.

17.6.3 완벽성: 헤드 vs 브랜치 vs 모든 변경 이력 vs 작업공간

476~477p

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

Code Search는 정규 표현식 검색을 지원한다. 정규 표현식 덕분에 지정한 용어들 모두를 포함하거나 제외할 수 있다. 또 어떤 텍스트에든 이용할 수 있어 문서 검색이나 강력한 의미론적 도구가 없는 프로그래밍 언어로 작성한 코드 검색에 특히 유용하다.

토큰 기반 인텍스는 실제 소스 코드의 일부만 저장하면 되고 표준 검색 엔진들에서 잘 지원하기 때문에 확장성이 좋다. 반대 급부로 활용성은 많이 줄어든다. 또 다른 문제점으로는 코드 식별자가 잘못 정의될 수 있고, 일반적으로 토큰화는 대소문자를 구분하지 않으며 단어들을 뭉개기도 한다. 마지막으로 토큰화 코드에서는 매우 중요한 공백 문자나 다른 구분자를 검색할 수 없게 만든다.

전체 부분 문자열 검색은 문자들의 순서에 상관없이 검색할 수 있다. 단순 텍스트 매칭이 아닌 의미를 이해한 분석과 검색을 지원하는 도구를 말한다. 같은 토큰이라도 변수인지 함수인지, 전역 변수인지 방금 선언한 지역 변수인지 등을 정확하게 구분해준다.

부분 문자열 인덱스가 준비되면 정규 표현식 검색으로 확장하기는 쉬워진다.

17.7 마치며

Code Search는 grep을 대체하여 개발자 생산성을 높이는 핵심 도구로 성장하였으며, 그 과정에서 구글의 웹 검색 기술을 활용했다.

Code Search가 주는 가장 중요한 가치는 명백성이다. 코드를 이해하는 것이야말로 코드를 개발하고 유지보수하는 열쇠이다.

17.8 핵심 정리

  • 개발자들이 코드를 쉽게 이해하도록 도와주면 엔지니어링 생산성을 크게 끌어올릴 수 있다. 구글의 중심에는 Code Search가 있다.
  • Code Search는 다른 도구들의 기반을 제공하며, 모든 문서자료와 개발자 도구가 참조하는 중앙의 표준 저장소라는 가치를 추가로 제공한다
  • Code Search는 대화형 도구이므로 반응이 빨라야 한다. 검색, 브라우징, 인덱싱 모든 면에서 지연 시간이 짧아야 한다.
  • 신뢰할 수 있는 도구여야 널리 쓰인다. 신뢰를 얻으려면 모든 코드를 인덱싱하고, 빠뜨림 없이 결과로 제공하고, 이용자가 원하는 결과를 먼저 보여줘야 한다.
This post is licensed under CC BY 4.0 by the author.

16. 버전 관리와 브랜치 관리

18. 빌드 시스템과 빌드 철학