작동하는 간단한 검색 엔진 만들기
(karboosx.net)- 기존 데이터베이스를 활용해 외부 서비스 없이 동작하는 검색 엔진 구조를 구현, 토큰화·가중치·스코어링을 중심으로 구성
- 핵심 아이디어는 모든 텍스트를 토큰화 후 저장하고, 검색 시 동일한 방식으로 토큰을 매칭해 관련도를 계산하는 방식
- Word, Prefix, N-Gram 토크나이저를 조합해 정확 일치·부분 일치·오타 대응을 모두 처리하며, 각 토크나이저는 고유한 가중치를 가짐
- 가중치 시스템과 SQL 기반 스코어링 알고리듬을 통해 문서 길이, 토큰 다양성, 평균 품질 등을 종합 평가
- 확장성과 투명성이 높아, 새로운 토크나이저나 문서 타입 추가, 가중치 조정, 스코어링 수정이 자유로운 구조
직접 검색 엔진을 만드는 이유
- Elasticsearch나 Algolia 같은 외부 서비스는 강력하지만 복잡한 API 학습과 인프라 관리 부담이 존재
- 단순히 기존 데이터베이스와 통합되어 작동하고, 디버깅이 쉬운 검색 기능이 필요할 때 직접 구축이 유용
- 목표는 외부 의존성 없이 관련성 높은 결과를 반환하는 단순한 검색 엔진
핵심 개념: 토큰화와 매칭
- 기본 원리는 모든 텍스트를 토큰화(tokenize)하여 저장하고, 검색 시 동일한 방식으로 토큰을 생성해 매칭
- 인덱싱 단계에서 콘텐츠를 토큰 단위로 분리하고 가중치와 함께 저장
- 검색 단계에서는 쿼리를 동일하게 토큰화하여 일치하는 토큰을 찾고 점수를 계산
- 스코어링 단계에서 저장된 가중치를 활용해 관련도 점수를 산출
데이터베이스 스키마 설계
- 두 개의 테이블 사용:
index_tokens와index_entries-
index_tokens: 고유 토큰과 토크나이저별 가중치 저장 -
index_entries: 토큰과 문서를 연결하며, 필드·문서·토크나이저 가중치를 반영한 최종 점수 저장
-
- 최종 가중치 계산식:
field_weight × tokenizer_weight × ceil(sqrt(token_length)) - 인덱스는 문서 조회, 토큰 조회, 필드별 쿼리, 가중치 필터링을 위해 설정
토큰화 시스템
- WordTokenizer: 단어 단위 분리, 짧은 단어 제거, 정확 일치용 (가중치 20)
- PrefixTokenizer: 단어 접두사 생성, 자동완성 및 부분 일치용 (가중치 5)
- NGramsTokenizer: 고정 길이 문자 조합 생성, 오타 및 부분 일치 대응 (가중치 1)
- 모든 토크나이저는 소문자 변환, 특수문자 제거, 공백 정규화를 공통 수행
가중치 시스템
- 필드 가중치: 제목·본문·키워드 등 중요도 반영
- 토크나이저 가중치: Word > Prefix > N-Gram 순
- 문서 가중치: 위 두 요소와 토큰 길이를 결합해 계산
-
ceil(sqrt())함수를 사용해 긴 토큰의 영향력을 완화하며, 필요 시 로그나 선형 함수로 조정 가능
인덱싱 서비스
-
IndexableDocumentInterface를 구현한 문서만 인덱싱 가능 - 문서 생성·수정 시 이벤트 리스너나 명령어(
app:index-document,app:reindex-documents)로 인덱싱 수행 - 절차:
- 기존 인덱스 제거 후 새 토큰 생성
- 각 필드에 대해 모든 토크나이저 실행
- 토큰 존재 여부 확인 후 생성(
findOrCreateToken) - 계산된 가중치로
index_entries에 배치 삽입(batch insert)
- 중복 방지, 성능 향상, 업데이트 대응 구조
검색 서비스
- 쿼리를 동일한 토크나이저로 처리해 인덱싱과 동일한 토큰 세트 확보
- 토큰 중복 제거 후 길이순 정렬(긴 토큰 우선), 최대 300개로 제한
- SQL 쿼리를 통해 토큰과 문서를 조인하고, 관련도 점수 계산 및 정렬
- 결과는
SearchResult(documentId, score)형태로 반환
스코어링 알고리듬
- 기본 점수:
SUM(sd.weight) -
토큰 다양성 보정:
LOG(1 + COUNT(DISTINCT token_id)) -
평균 가중치 보정:
LOG(1 + AVG(weight)) -
문서 길이 패널티:
1 / (1 + LOG(1 + token_count)) - 정규화(normalization) : 최대 점수로 나누어 0~1 범위로 조정
- 최소 토큰 가중치 필터(
st2.weight >= ?)를 통해 의미 없는 저가중치 일치 제거
결과 처리
- 검색 결과는 문서 ID와 점수로 반환되며, 저장소(repository)를 통해 실제 문서로 변환
-
FIELD()함수를 사용해 검색 결과 순서를 유지한 채 문서 조회
시스템 확장성
- 새로운 토크나이저는
TokenizerInterface구현으로 추가 가능 - 새로운 문서 타입은
IndexableDocumentInterface구현으로 등록 - 가중치나 스코어링 로직은 SQL 수정만으로 조정 가능
결론
- 이 구조는 단순하지만 실제로 작동하는 검색 엔진으로, 외부 인프라 없이도 충분한 성능 제공
- 명확한 로직, 완전한 제어, 쉬운 디버깅이 장점
- 복잡한 시스템보다 직접 이해하고 제어 가능한 코드가 더 가치 있다는 점을 강조
Hacker News 의견
-
검색의 기본 아이디어는 단순하고 흥미로운 문제 영역임
하지만 대량의 데이터를 다루는 것과 모호한 쿼리를 처리하는 것이 진짜 어려움
DBMS 기반 접근은 작은 웹사이트 수준에서는 괜찮지만, 영어 위키피디아 규모에서는 금방 한계에 부딪힘
입문용으로는 SeIRP e-book이 좋은 무료 자료- 대량 데이터는 당연히 어렵지만, 모호한 쿼리를 다루는 건 “가장 관련 있는 결과를 어떻게 정할 것인가”의 하위 문제라고 생각함
정답이 명확하지 않다는 점이 특히 까다로움
Google은 광고를 ‘가장 관련 있는 결과’로 보여주기도 해서, Marginalia Search는 좋은 대조 사례임
혹시 TREC 논문들을 참고해본 적 있는지 궁금함 - 요즘은 오히려 SEO 스팸 회피가 더 큰 문제라고 생각함
검색 엔진은 광고 수익을 노리는 적대적 플레이어들과 끊임없이 싸워야 함
품질 지표를 계속 바꿔가며 그들이 역이용하지 못하게 하는 끝없는 고양이와 쥐의 게임이 됨 - SQLite로 단일 서버(약 천 달러 수준)에서 텍스트 중심 비즈니스 프로세스를 돌린다면, 실질적으로 다룰 수 있는 문서 저장소의 규모가 어느 정도인지 궁금함
1쿼리당 5초, 분당 12쿼리 정도의 속도를 기준으로 어느 정도의 코퍼스를 검색할 수 있을지 알고 싶음 - Marginalia Search를 정말 좋아함
- 검색의 어려움은 단순히 데이터 크기뿐 아니라, 어떤 결과를 반환할지 결정하는 문제라고 생각함
예를 들어 Gilligan’s Island 위키 문서와 팬 블로그 중 어느 쪽이 더 “좋은” 결과인지 판단하기 어려움
여기에 랭크 조작이나 키워드 스터핑까지 더해지면, 확장성 문제보다 훨씬 복잡한 도전이 됨
- 대량 데이터는 당연히 어렵지만, 모호한 쿼리를 다루는 건 “가장 관련 있는 결과를 어떻게 정할 것인가”의 하위 문제라고 생각함
-
검색은 정말 어려운 일임
Apple, Microsoft, OpenAI 같은 자원과 기술력이 넘치는 회사조차 검색 기능 품질이 낮음
이는 단순한 기술 문제가 아님- 대부분의 회사가 검색을 잘 구현하지 못하는 이유는, 기업 문화와 개발 방식이 검색 개발과 충돌하기 때문임
검색 품질을 높이려면 랭킹 파라미터를 세밀하게 조정해야 하는데, 이런 일은 스프린트나 Jira 같은 관리 체계로는 계획하기 어려움
결국 개발자에게 신뢰와 자율성이 필요한 영역임 - 하지만 어떤 회사들은 단순히 검색이 우선순위가 아니기 때문에 품질이 낮은 것
AI 모델에는 수십억을 투자하지만, 웹앱이나 검색은 부차적이라서 그런 결과가 나오는 것임
- 대부분의 회사가 검색을 잘 구현하지 못하는 이유는, 기업 문화와 개발 방식이 검색 개발과 충돌하기 때문임
-
10년 전쯤 검색 엔진 설계로 박사과정을 하던 동료와 함께 일한 적이 있음
그는 검색과 데이터베이스 통합에 대해 매우 열정적으로 이야기했고, 덕분에 많은 걸 배웠음
언젠가 Apache Solr와 Lucene의 내부를 깊이 파보고 싶음- 나도 내 분야 이야기를 몇 시간이고 할 수 있을 정도로 좋아하지만, 대형 시스템의 세부 구현에 관심 있는 사람은 많지 않음
-
예전에는 오픈소스 검색 솔루션이 없어서 직접 만들어야 했음
그 경험으로 얻은 교훈은 “직접 검색 엔진을 만들지 말라”는 것임
수년간 수많은 인력이 이 문제에 매달려왔고, 직접 만들면 끝없는 유지보수 지옥에 빠짐
“오타 교정 기능 추가해달라”, “내년엔 분류 체계도 넣자” 같은 요구가 이어지면 끝남 -
예전에 Virginia University의 David Evans 교수가 진행한 검색 엔진 구축 강의를 정말 즐겼음
“고전적인 검색 엔진”을 직접 만들어보는 건 매우 재미있는 프로젝트였음
강의 링크와 YouTube 재생목록 참고 가능- 나도 그 강의를 들었는데, 초보 프로그래머에게도 흥미롭고 밀도 높은 수업이었음
-
내가 자주 쓰는 검색 엔진들은 2~3글자 약어나 단어를 무시하는 게 불만임
“mp3”나 “PHP” 같은 짧은 단어를 검색할 때 제거해버리면 정말 불편함 -
Toby Segaran의 Programming Collective Intelligence를 읽고 검색, 추천, 분류기 등 다양한 아이디어에 영감을 받았음
- 나도 그 책을 좋아했지만, 저자가 나중에 유튜브에서 “이제는 구식이니 쓰지 말라”고 말한 걸 봤음
- 정말 좋은 책이었는데, 2025년에 해당하는 최신판이 있다면 궁금함
-
흥미로운 글이었음
인기 검색 엔진들이 사용하는 토크나이저 최적화 수준이 얼마나 높은지 궁금해짐 -
이 시스템이 얼마나 확장성 있게 작동할지 궁금함
Elasticsearch는 권장 스케일을 넘어도 꽤 인상적인 성능을 보임 -
간단한 텍스트 검색 엔진은 만들기 어렵지 않음
하지만 좋은 검색 엔진을 만드는 건 전혀 다른 이야기임
단순히 BM25 같은 알고리즘을 구현하는 수준으로는 부족함
내가 컨설팅한 회사들 대부분이 자체 솔루션을 쓰다가 결국 Elasticsearch나 Opensearch로 옮김
자체 구현은 처음엔 단순하지만, 시간이 지나면 랭킹 문제와 성능 저하로 복잡해짐
“느리다”, “엉뚱한 결과가 나온다” 같은 증상이 반복됨
Elasticsearch는 이미 10년 전부터 이런 문제를 해결해왔고, 지금은 훨씬 더 발전함
“설정이 어렵다”는 말도 있지만, 요즘은 대부분 자동 구성되고, 관리형 서비스도 많음
Postgres보다 오히려 다루기 쉬움
결국 중요한 건 인덱스 매핑 최적화임
“그런 고급 기능은 필요 없다”고 말하는 사람도 있지만, 실제로는 검색 품질이 비즈니스 생존에 직결됨
제대로 된 검색을 원한다면 결국 이런 복잡함을 감수해야 함- 나도 Elasticsearch를 기본으로 쓰고 있음
최근 HN에서 자주 언급되는 SeekStorm 같은 신흥 대안도 흥미로워 보이지만, 실제 프로덕션 사례는 아직 못 봤음 - “불필요한 기능은 없다”는 말에 공감함
특히 dynamic mapping을 끄고 불필요한 필드 인덱싱을 막는 팁이 유용했음 -
ManticoreSearch에 대해서는 어떻게 생각하는지 궁금함
Lucene보다 오래된 프로젝트로 알고 있음
- 나도 Elasticsearch를 기본으로 쓰고 있음