Cursor의 코드베이스 인덱싱 시스템에 대한 설명 (Aman Sanger - Cursor.ai co-founder)
TMT1️⃣ Codebase Indexing 작동 방식
-
폴더 스캔 및 해시(Merkle Tree) 생성
- Cursor는 사용자가 연 폴더를 스캔하고, 해당 폴더 내 모든 파일에 대해 Merkle Tree(파일 해시 트리)를 생성함.
.gitignore
또는.cursorignore
파일에 명시된 파일/디렉토리는 인덱싱에서 제외됨.
-
파일 동기화(Syncing)
- 생성된 Merkle Tree는 서버로 동기화됨.
- 10분마다 변경 사항을 감지하고, 변경된 파일의 해시가 기존 해시와 다르면 변경된 파일만 서버에 업로드됨.
-
파일을 분할(Chunking)하고 벡터로 변환(Embedding)
- 서버에서 파일을 작은 청크(chunk) 단위로 나눈 후, 이를 임베딩(벡터)으로 변환.
- 벡터 데이터는 Turbopuffer(Cursor의 벡터 검색 시스템)에 저장됨.
-
파일 경로 및 라인 정보 저장
- 벡터를 검색할 때 파일 경로와 라인 범위를 함께 저장하여 검색 결과를 필터링 가능하게 함.
- 하지만 파일 경로는 난독화(Obfuscation)하여 저장되므로, 원래 코드 파일 경로가 노출되지 않음.
-
인덱싱 속도 향상 (캐싱)
- AWS 캐시 시스템을 사용하여, 이미 인덱싱된 코드베이스라면 해당 코드의 해시 기반으로 빠르게 다시 인덱싱할 수 있음.
- 특히, 같은 코드베이스를 여러 팀원이 사용할 경우, 이전 캐시를 재사용하여 속도를 높임.
2️⃣ Codebase Indexing을 활용한 검색 및 질문 응답
- 사용자가 질문을 하면, 질문을 임베딩(벡터)으로 변환
- Turbopuffer에서 가장 가까운 벡터(Nearest Neighbors)를 검색
- 검색된 코드 청크의 난독화된 파일 경로와 라인 정보를 클라이언트로 전송
- 클라이언트에서 해당 코드 청크를 로컬에서 읽어 서버로 다시 전송
- 이 정보를 바탕으로 질문에 대한 답변을 생성
📌 즉, 서버에는 실제 코드가 저장되지 않고, 클라이언트에서만 원본 코드가 유지됨.
📌 이 방식으로 보안성을 유지하면서도 코드 컨텍스트를 활용할 수 있음.
3️⃣ 보안 관련 사항
🔹 .cursorignore
및 .cursorban
파일
.cursorignore
→ 인덱싱을 방지하지만, 최근에 본 파일이라면 AI 요청에서 포함될 수 있음..cursorban
→ 완전히 차단할 수 있는 기능을 추가할 예정. (.cursorban
이 있으면 AI 요청에도 포함되지 않음.)
🔹 파일 경로 난독화 (Obfuscation)
- 디렉토리 구조를 암호화하여 직접적으로 경로가 노출되지 않음.
- 경로의 각 부분(
'/'
및'.'
로 구분된 부분)은 클라이언트에 저장된 **비밀 키(secret key)**를 사용하여 암호화됨. - 단점: 디렉토리 계층 구조는 대략적으로 추론할 수 있으며, 일부 충돌이 발생할 수도 있음.
🔹 Embedding 역변환(Embedding Reversal) 가능성
- 학계 연구에서 임베딩된 데이터를 역변환하는 것이 가능하다고 밝혀짐.
- 하지만, Cursor의 방식은 긴 벡터에 짧은 문자열을 삽입하는 기존 공격 기법과 다르기 때문에, 직접적인 역변환이 어렵다고 판단됨.
- 그러나, 벡터 DB가 해킹당하면 코드베이스에 대한 정보가 일부 노출될 가능성은 있음.
4️⃣ Git 히스토리 인덱싱
📌 Cursor는 코드베이스뿐만 아니라 Git 히스토리도 인덱싱 가능!
- Git 커밋 정보(SHA, 부모 커밋 정보) 및 파일 이름(난독화된 상태)을 저장.
- 하지만, 커밋 메시지, 파일 내용, diff 정보는 저장하지 않음.
- 파일 이름 난독화 키는 최근 커밋 내용을 기반으로 생성되므로, 같은 Git Repo를 공유하는 사용자들은 같은 데이터를 공유 가능.
5️⃣ 인덱싱 관련 부하(Load) 문제
- Cursor의 인덱싱 시스템은 높은 부하를 경험할 수 있음.
- 따라서 파일이 여러 번 업로드되어야 완전히 인덱싱되는 경우가 있음.
- 네트워크 트래픽(
repo42.cursor.sh
)을 확인하면 예상보다 많은 대역폭을 사용하는 것을 볼 수도 있음.
✅ 결론
💡 Cursor의 Codebase Indexing 기능은 다음과 같은 방식으로 작동한다.
- 코드베이스를 스캔하여 파일 해시(Merkle Tree)를 생성하고 서버와 동기화.
- 파일을 청크 단위로 분할하고 벡터 임베딩하여 Turbopuffer(Vector DB)에 저장.
- 질문이 들어오면 Vector DB에서 검색하여 코드 컨텍스트를 반환.
- 클라이언트에서 해당 코드 청크를 로컬에서 불러와 AI가 질문에 답변하도록 지원.
- 보안을 위해 파일 경로를 난독화하고, 코드 자체는 서버에 저장하지 않음.
- Git 히스토리도 인덱싱하여 커밋 SHA 및 파일명을 추적 가능.
https://x.com/amanrsanger/status/1750023209733464559
1️⃣ Cursor의 코드베이스 인덱싱 시스템은 효율적인 인덱싱 및 업데이트를 제공하면서도 코드가 서버에 저장되지 않습니다.
(1/9)
2️⃣ 첫 번째 단계는 클라이언트에서 머클 트리(Merkle Tree)를 구성하는 것입니다.
우리는 Rust 구현을 TypeScript 프론트엔드에 연결하기 위해 napi
를 사용합니다.
Rust를 선택한 이유는 속도를 극대화하고 머클 트리 구축 시 리소스 사용을 최소화하기 위함입니다.
❓ 그렇다면 왜 머클 트리를 사용할까요?
(2/9)
3️⃣ 클라이언트나 서버에서 예기치 않은 장애가 발생하면, 코드베이스의 상태와 서버에 인덱싱된 코드베이스의 상태가 다를 수 있습니다.
이 문제를 해결하려면 클라이언트 상태를 서버로 보내거나 서버 상태를 클라이언트로 전송해야 합니다.
하지만 약 50,000개 파일이 포함된 대형 코드베이스의 경우를 생각해 보세요.
(3/9)
4️⃣ 각 파일 이름의 크기는 약 32자(32바이트 이상)이며, SHA-256 해시는 추가로 32바이트를 차지합니다.
즉, 파일 이름과 해당 콘텐츠의 해시만으로 3.2MB가 필요합니다.
작은 업데이트가 있을 때마다 3.2MB를 업로드/다운로드해야 한다면?
이는 감당할 수 없는 수준입니다!
(4/9)
5️⃣ 여기서 머클 트리가 등장합니다.
머클 트리는 각 리프(leaf) 노드가 데이터 블록의 해시를 저장하고, 각 부모 노드는 자식들의 해시를 저장하는 데이터 구조입니다.
이 구조를 사용하면 변경이 발생한 위치를 찾는 데 필요한 대역폭이 O(log(n))으로 줄어듭니다.
즉, 클라이언트와 서버의 상태가 불일치하는 부분을 효율적으로 찾아낼 수 있습니다.
(5/9)
6️⃣ 변경된 파일이 있을 경우, Tree-sitter를 활용하여 구문적으로 관련 있는 코드 조각(chunks)을 추출합니다.
이 방식의 장점은 파일에서 한 줄만 변경되더라도 대부분의 코드 조각이 동일하게 유지된다는 점입니다.
❓ 그렇다면 이것이 왜 중요한가요?
(6/9)
**7️⃣ 가장 큰 병목(bottleneck)과 비용 요소는 바로 **임베딩(embedding)입니다.
우리는 DynamoDB를 코드 조각의 임베딩 캐시(embedding cache)로 사용합니다.
✅ 파일을 다시 인덱싱할 때, 한 줄만 변경된 경우?
→ 변경되지 않은 코드 조각은 캐시 히트를 발생시킵니다.
→ 새롭게 임베딩해야 하는 부분은 변경된 코드 조각뿐입니다.
(7/9)
8️⃣ 이런 방식 덕분에, 오픈 소스 저장소를 인덱싱할 때나 팀원이 이미 코드베이스를 인덱싱한 경우 속도가 매우 빠릅니다.
대부분의 코드 조각은 캐시 히트를 발생시키므로,
우리는 벡터 DB에 임베딩을 쓰고 머클 트리를 업데이트하는 작업만 수행하면 됩니다.
(8/9)
9️⃣ 그리고 이 과정에서 어떤 코드도 서버에 저장되지 않습니다!
서버에는 SHA-256 해시, 파일 이름, 500 토큰 크기의 임베딩 데이터만 저장됩니다.
우리는 각 코드베이스별로 임베딩 공간을 선형 변환(linear transformation)하여 저장합니다.
(9/9) 🚀
💡 정리: Cursor 코드베이스 인덱싱의 핵심 아이디어
✅ 머클 트리(Merkle Tree) 기반 동기화 → 변경된 부분만 효율적으로 찾음
✅ Tree-sitter를 활용한 코드 조각 추출 → 변경되지 않은 부분을 재사용 가능
✅ DynamoDB 임베딩 캐시 → 중복된 코드 조각의 재계산을 방지하여 비용 절감
✅ 서버에 코드 저장 없이 SHA-256 해시 & 임베딩만 관리 → 보안 강화
이 방식으로 대형 코드베이스의 인덱싱을 빠르고 효율적으로 수행할 수 있습니다. 🚀