견적서에 "검색 기능"이라고 한 줄만 적혀 있는 경우가 많습니다. 옆에 붙은 금액도 대체로 크지 않습니다. 그런데 개발이 시작되면 이 한 줄에서 일정과 비용이 가장 크게 흔들립니다.

이 한 줄 안에 성격이 다른 세 가지가 섞여 있기 때문입니다. 키워드를 넣고 엔터를 치는 단순 검색, 조건을 걸어 목록을 좁히는 필터, 글자를 칠 때마다 후보를 띄우는 자동완성입니다. 셋은 만드는 방법도, 운영비가 늘어나는 방식도 다릅니다.

단순 검색과 필터와 자동완성은 원가가 다릅니다

단순 검색은 한 번 요청하면 한 번 응답하는 구조입니다. 요청 수를 예측하기 쉽고, 조금 느려도 사용자가 기다려 줍니다. 세 가지 중 가장 싼 쪽입니다.

필터는 요청 수는 비슷하지만 조건 조합이 늘어납니다. 자동완성은 조합은 단순한 대신 요청 수 자체가 몇 배로 뜁니다. 값이 올라가는 축이 서로 다릅니다.

검색 한 줄에 자동완성이 포함되는지 아닌지가 견적 차이의 대부분을 만듭니다.

단순 검색은 데이터가 쌓이는 순간 인덱스를 못 쓰게 됩니다

PostgreSQL 공식 문서는 B-tree 인덱스를 LIKE에 쓸 수 있는 조건을 적어 두었습니다. 패턴이 상수이고 문자열 앞쪽에 고정되어 있어야 합니다. 'foo%'는 인덱스를 타지만 '%bar'는 타지 못합니다.

문제는 실무에서 원하는 검색이 대부분 '%검색어%' 형태라는 점입니다. 어디에 있어도 찾아 달라는 요구이고, 이때는 테이블을 끝까지 훑습니다. 데이터가 적을 때는 드러나지 않다가 쌓이면서 느려집니다.

PostgreSQL에서는 pg_trgm 확장과 GIN 인덱스로 이 문제를 풉니다. 다만 공식 문서는 추출할 트라이그램이 없는 패턴은 결국 인덱스 전체 스캔으로 떨어진다고 못 박습니다. 트라이그램은 연속한 세 글자를 뜻하므로, 두 글자짜리 검색어는 이 방식의 효과를 거의 못 봅니다.

한글 검색은 여기서 한 단계가 더 붙습니다

MySQL 공식 문서는 기본 전문 검색 파서가 공백으로 단어 경계를 판단한다고 설명하고, 이 방식이 단어를 띄어 쓰지 않는 언어에서는 한계라고 밝힙니다. 그래서 중국어·일본어·한국어용 ngram 파서를 따로 제공하며, 기본 토큰 크기는 2, 즉 두 글자 단위입니다.

Elasticsearch는 한국어 형태소 분석에 nori 플러그인과 mecab-ko-dic 사전을 씁니다. 공식 문서 기준으로 이 플러그인은 모든 노드에 설치하고 노드마다 재시작해야 합니다. 사전에 기대는 방식이라 신조어와 상품명은 개발이 끝난 뒤에도 계속 손봐야 합니다. 견적에 형태소 분석기 설정과 사전 관리가 있는지 확인하세요.

필터는 조건 하나가 늘 때마다 인덱스를 다시 잡는 일입니다

화면만 보면 필터는 체크박스 하나 추가하는 일로 보입니다. 그런데 검색어와 필터를 함께 걸면 데이터베이스는 성격이 다른 두 종류의 인덱스를 한 질의에서 같이 다뤄야 합니다.

PostgreSQL 공식 문서는 GIN이 필요한 컬럼과 B-tree가 맞는 컬럼을 함께 조건으로 쓸 때, 인덱스 두 개를 합치는 것보다 btree_gin 확장으로 멀티컬럼 GIN 인덱스를 만드는 편이 효율적일 수 있다고 안내합니다. 조건 조합이 바뀌면 이 판단을 다시 해야 합니다.

조건이 다섯 개면 켜고 끄는 경우의 수만 32가지이고, 정렬과 페이지 이동이 붙으면 확인할 경로는 다시 몇 배가 됩니다. 조건 옆에 결과 건수까지 보여 달라는 요구도 자주 나오는데, 이 숫자는 목록을 가져오는 일과 별개로 조건마다 다시 세어야 하는 값입니다.

자동완성은 글자 하나가 요청 하나입니다

자동완성은 입력이 바뀔 때마다 결과를 가져옵니다. 다섯 글자를 치면 요청이 다섯 번 나갑니다. 사용자 눈에는 검색 한 번이지만 서버가 받는 것은 다섯 번입니다.

Algolia 공식 문서도 요청 수와 청구액을 줄이는 방법으로 디바운스를 안내합니다. 입력이 멈춘 뒤 잠깐 기다렸다가 보내는 방식입니다. 최소 입력 글자 수를 두세 글자로 정하는 것도 같은 목적입니다.

한글은 자모가 합쳐지며 글자가 완성되기 때문에 입력 이벤트가 더 잘게 나옵니다. 조합이 끝나는 시점을 따로 처리하지 않으면 요청 수는 영문보다 더 늘어납니다.

자동완성은 요금제 종류에 따라 청구서가 다르게 움직입니다

Algolia는 요청 수로 과금합니다. 오늘 기준 공식 가격 페이지에서 무료 구간은 월 검색 요청 1만 건과 레코드 10만 건이고, Grow 요금제는 초과분에 검색 요청 1,000건당 0.5달러, 레코드 1,000건당 0.4달러를 받습니다. Grow Plus는 검색 요청 1,000건당 1.75달러입니다.

여기에 앞의 숫자를 대입해 보겠습니다. 월 10만 번 검색되는 서비스가 엔터 방식이면 요청은 10만 건이고, Grow 기준 초과분 요금은 45달러입니다. 같은 서비스에 자동완성을 붙여 평균 다섯 글자가 나가면 요청은 50만 건, 요금은 245달러가 됩니다. 기능 하나로 다섯 배가 됩니다.

Typesense Cloud는 반대로 움직입니다. 요청당 과금이 없고, 선택한 사양의 시간당 클러스터 요금과 GB 단위 대역폭 요금만 받습니다. 요청이 늘면 청구 항목이 아니라 필요한 서버 사양이 올라갑니다. Meilisearch Cloud는 사용량 기준과 리소스 기준 중에서 고릅니다.

자동완성을 붙일 계획이라면 요금이 요청 수를 따라가는지 서버 사양을 따라가는지부터 확인하세요.

견적을 받기 전에 이 항목들을 먼저 정하세요

아래 항목이 비어 있는 상태로 받은 검색 견적은 대부분 다시 계산됩니다. 개발사에 넘기기 전에 먼저 답을 맞춰 두세요.

  • 검색 대상이 제목까지인지, 본문과 첨부 파일 내용까지인지
  • 오타와 유사어를 같은 결과로 볼지
  • 필터 조건 개수와, 조건 옆 결과 건수 표시 여부
  • 자동완성 적용 여부와 최소 입력 글자 수
  • 1년 뒤 데이터가 몇 배로 늘어나는지

특히 마지막 항목이 중요합니다. 지금 데이터로는 어떤 방식이든 빠르기 때문입니다.

이럴 때는 자동완성을 넣지 마세요

주문번호나 사업자등록번호처럼 정확한 값으로 찾는 검색에는 자동완성이 도움이 되지 않습니다. 사용자가 이미 찾을 값을 알고 있어 후보를 띄울 이유가 없습니다. 내부 관리자 화면도 대부분 여기에 해당합니다.

검색 결과의 품질이 아직 잡히지 않았을 때도 미루는 편이 낫습니다. 이 상태에서 자동완성을 붙이면 틀린 결과를 더 빨리 보여 주는 기능이 됩니다. 순서는 단순 검색의 정확도, 그다음 필터, 마지막이 자동완성입니다.

검색 설계와 운영에 관한 다른 글은 실무 가이드개발 노트에 있습니다. 인덱스 동작은 PostgreSQL 공식 문서에서 확인할 수 있고, 프로젝트 상담은 Codeforest로 문의하세요.

결론: 검색은 한 줄이 아니라 세 줄로 나눠서 잡으세요

단순 검색은 인덱스 설계에서, 필터는 조건 조합에서, 자동완성은 요청 수에서 값이 올라갑니다. 축이 다르기 때문에 한 항목으로 묶어 견적을 내면 반드시 어긋납니다. 세 줄로 나눠 각각의 범위와 금액을 받아 보세요.

한글 검색에는 형태소 분석기와 사전 관리라는 항목이 하나 더 붙습니다. 이 항목이 견적에 없다면 아직 계산되지 않은 것입니다. 자동완성을 넣기로 했다면 요금이 요청 수에 연동되는 서비스인지부터 확인하세요.