Neural embedding 3억 개로 웹 검색엔진을 처음부터 만들어본 2개월의 기록
Show HN: Building a web search engine from scratch with 3B neural embeddings
TL;DR Highlight
한 개발자가 2개월 만에 SBERT 임베딩 30억 개, 2.8억 페이지 인덱스를 가진 웹 검색엔진을 혼자 구축한 과정을 상세히 공유한 글로, 벡터 검색 기반 시스템의 실제 아키텍처와 비용 구조를 알 수 있다.
Who Should Read
벡터 검색이나 임베딩 기반 검색 시스템을 직접 구축하려는 백엔드/인프라 엔지니어, 또는 대규모 크롤링·인덱싱 파이프라인의 실전 아키텍처가 궁금한 개발자.
Core Mechanics
- 키워드 매칭 대신 SBERT(Multi-QA-MPNET-base-dot-v1, 768차원) 임베딩으로 의도 기반 검색을 구현했다. 예를 들어 'CORS가 왜 안 되냐'같은 모호한 질문도 S3 문서에서 eventual consistency 관련 정확한 답변을 찾아준다.
- Runpod의 RTX 4090 GPU 250대를 돌려서 초당 10만 개씩 총 30억 개 임베딩을 생성했다. AWS GPU 대비 4.3배 저렴하고, 90% GPU 사용률을 달성한 비동기 파이프라인(폴링→파싱→토큰화→추론→저장)을 직접 설계했다.
- HNSW(Hierarchical Navigable Small World) 인덱스를 64개 샤드로 나눠서 200코어, 4TB RAM, 82TB SSD에 분산 배치했다. 양자화 없이도 쿼리 레이턴시 500ms를 달성했고, 자체 개발한 CoreNN 벡터DB로 RAM을 수 TB에서 128GB까지 줄였다.
- 처음에 PostgreSQL을 썼다가 대량 쓰기에서 MVCC 블로트와 WAL 증폭 문제로 실패했고, 결국 RocksDB + BlobDB로 전환했다. 64개 샤드에 xxHash 기반 consistent hashing을 적용해 초당 30만 ops를 처리한다.
- HTML에서 네비게이션, 광고, 댓글 등 '크롬'을 제거하고 본문만 추출하는 정규화 파이프라인을 만들었다. 문장 단위로 쪼갠 뒤 DistilBERT 분류기로 앞뒤 문맥 의존성을 판단하는 'statement chaining' 기법을 적용해 청크의 의미를 보존했다.
- 비용 비교가 압도적인데, 예를 들어 벡터DB 10억 건 삽입이 Turbopuffer에선 $3,578이지만 Hetzner에서 CoreNN을 돌리면 $150이다. AWS 대비 Hetzner 옥션 서버는 고메모리 서버 42배, NVMe 스토리지 37배 저렴하다.
- 위키피디아/위키데이터 덤프로 지식 그래프를 구축해서 검색 결과에 패널(요약+핵심 사실)을 보여준다. Groq 백엔드로 LLM 기반 AI 어시스턴트도 붙여서 빠른 답변과 재랭킹을 지원한다.
- 향후 방향으로 Common Crawl 데이터 활용, 정적 임베딩(추론 400배 빠름), late chunking, 도메인별 서브엔진, 에이전틱 검색 등을 계획하고 있다. 월 $5 구독자 1만 명이면 인덱스 유지가 가능하다고 추산했다.
Evidence
- 여러 댓글에서 한 사람이 2개월 만에 이 수준의 검색엔진을 만든 것에 경탄했고, '10x 엔지니어가 여유 시간에 Google을 만든 격'이라는 반응이 나왔다. 운영 비용 $50K가 '터무니없이 적다'며 시드 투자하겠다는 댓글도 있었다.
- 벡터 전용 검색의 한계를 지적하는 의견도 있었다. 'garbanzo bean stew'를 검색하니 엉뚱한 콩 레시피가 나왔다는 경험이 공유되었고, BM-25 + 임베딩 하이브리드 검색을 적용했는지 묻는 질문이 나왔다. 키워드가 중요한 경우엔 벡터만으로는 부족하다는 공감대가 형성됐다.
- 크롤링 시 Cloudflare 차단 문제를 겪지 않았는지에 대한 질문이 있었고, 많은 사이트가 Google Bot에만 전체 콘텐츠를 보여주는 것이 Google의 과소평가된 해자(moat)라는 분석이 나왔다. URL 중복 처리(trailing slash, www 유무 등)에 대한 기술적 질문도 이어졌다.
- 커뮤니티 기반 검색엔진 운영 가능성에 대한 열정적인 논의가 있었다. 월 $5 구독자 1만 명이면 운영 가능하다는 계산에 공감하며, 오픈소스로 풀어서 커뮤니티가 운영하자는 의견이 나왔다. 한 제3세계 개발자는 온라인에서 한 번도 돈을 쓴 적 없지만 $50을 기부하겠다고 했다.
- 실제 검색 품질에 대해 'lemmy'를 검색했더니 fediverse가 아닌 liberapay 페이지가 나왔다는 구체적 실패 사례가 공유됐고, 모니터 추천 같은 쿼리에서는 전문 포럼이 아닌 메타 랭킹 사이트가 우선 노출되는 기존 검색엔진과 같은 문제가 있다는 피드백이 있었다.
How to Apply
- 임베딩 기반 검색 시스템을 구축할 때, AWS 대신 Hetzner 옥션 서버(고메모리 42배 저렴)와 Runpod GPU(4.3배 저렴)를 조합하면 비용을 극적으로 줄일 수 있다. 벡터DB도 Turbopuffer 같은 매니지드 서비스 대신 자체 HNSW + RocksDB 조합을 고려해볼 것.
- RAG 파이프라인에서 청킹 품질을 높이려면 단순 토큰 수 분할 대신, 문장 단위 분할 후 제목·앞선 문장 등 구조적 문맥을 청크에 포함시키는 'statement chaining' 방식을 적용하면 검색 정확도가 올라간다.
- 벡터 전용 검색은 키워드 정확도가 떨어지므로, 프로덕션에서는 BM-25(키워드) + 임베딩(의미) 하이브리드 검색을 기본으로 가져가는 게 안전하다. 특히 고유명사나 레시피명처럼 정확한 단어 매칭이 중요한 도메인에서는 필수다.
- 대량 쓰기 워크로드에서 PostgreSQL이 병목이라면 RocksDB + BlobDB 조합을 검토해볼 것. 1KB 이상 값은 BlobDB로 분리하고, 64개 샤드 + xxHash consistent hashing으로 초당 30만 ops급 처리량을 달성할 수 있다.
Code Example
snippet
// RocksDB 최적화 설정 (Rust)
opt.set_max_background_jobs(num_cpus::get() as i32 * 2);
opt.set_bytes_per_sync(1024 * 1024 * 4);
opt.set_enable_blob_files(true);
opt.set_min_blob_size(1024); // 1KB 이상은 BlobDB로 분리
// Block cache: 32GB, write buffer: 256MBTerminology
SBERT문장 전체를 하나의 벡터(숫자 배열)로 변환하는 모델. 두 문장의 벡터를 비교하면 의미적 유사도를 측정할 수 있다.
HNSWHierarchical Navigable Small World. 수십억 개 벡터에서 가장 비슷한 것을 빠르게 찾는 인덱스 구조. 정확도를 약간 희생하고 속도를 확보하는 근사 최근접 이웃 알고리즘이다.
RocksDBFacebook이 만든 고성능 키-값 저장소. 쓰기가 많은 워크로드에 최적화되어 있고, LSM-tree 구조를 사용한다.
BM-25전통적인 키워드 기반 검색 랭킹 알고리즘. TF-IDF의 개선판으로, 단어 빈도와 문서 길이를 고려해서 관련도를 매긴다.
Statement Chaining문장을 쪼갤 때 앞 문장에 의존하는 대명사나 지시어('이것', 'that')가 있으면 앞 문장의 맥락을 함께 붙여주는 기법. 청크가 혼자서도 의미가 통하게 만든다.
Consistent Hashing서버를 추가/제거할 때 데이터 재분배를 최소화하는 분산 기법. 원형 해시 공간에 서버를 배치해서 키가 가장 가까운 서버로 가게 한다.