블로그나 문서 사이트의 검색창에 “비밀번호 까먹었어요”라고 쳤는데, 정작 필요한 “계정 재설정 안내” 글이 안 나온 적이 있을 겁니다. 기존 키워드 검색은 글자가 일치해야 찾아 주기 때문입니다. 시맨틱 검색은 글자가 아니라 의미가 가까운 문서를 찾아 줍니다. “비밀번호 분실”과 “계정 재설정”이 다른 단어라도 뜻이 비슷하면 연결합니다. 이 글에서는 시맨틱 검색이 동작하는 원리, 글을 벡터로 바꿔 저장하는 색인 파이프라인, 키워드와 벡터를 함께 쓰는 하이브리드 방식, 그리고 방문자 수천 명 규모의 작은 사이트가 현실적인 비용으로 도입하는 방법까지 실무 관점에서 정리합니다.
키워드 검색은 어디서 막히나
전통적인 검색은 역색인(inverted index)을 씁니다. 문서를 단어로 쪼개 “어떤 단어가 어느 문서에 몇 번 나오는지”를 표로 만들어 두고, 질의에 들어온 단어가 포함된 문서를 점수순으로 돌려줍니다. WordPress 기본 검색, 데이터베이스의 LIKE 조회, Elasticsearch의 BM25가 모두 이 계열입니다. 빠르고 정확한 일치에 강하다는 장점이 분명합니다.
문제는 사람이 같은 뜻을 수십 가지 표현으로 쓴다는 데 있습니다. “환불”과 “결제 취소”, “느려요”와 “로딩이 길어요”는 의미가 같지만 글자가 다릅니다. 키워드 검색은 이 둘을 잇지 못합니다. 동의어 사전을 직접 등록하면 일부 보완되지만, 사람이 만들어 낼 표현을 전부 등록하는 것은 불가능합니다. 반대로 “사과”처럼 한 단어가 과일과 사죄라는 두 뜻을 가질 때, 키워드 검색은 문맥을 구분하지 못하고 둘을 똑같이 취급합니다. 이런 동의어·다의어·오타·구어체 질의에서 키워드 검색의 재현율이 무너집니다.
검색 로그를 들여다보면 “결과 없음”으로 끝난 질의가 의외로 많습니다. 그중 상당수는 정작 답이 되는 글이 사이트에 있는데도 표현이 달라 못 찾은 경우입니다. 시맨틱 검색이 메우려는 빈틈이 바로 여기입니다.
임베딩 — 의미를 좌표로 바꾸는 원리
시맨틱 검색의 핵심은 임베딩(embedding)입니다. 임베딩 모델은 문장을 받아 수백~수천 개의 숫자로 이뤄진 벡터, 즉 고차원 공간의 한 점으로 바꿉니다. 이 변환의 특징은 의미가 비슷한 문장일수록 가까운 점에 놓인다는 것입니다. “비밀번호를 잊었어요”와 “로그인 정보가 기억나지 않아요”는 글자가 거의 겹치지 않아도 벡터 공간에서는 서로 이웃에 자리합니다.
검색은 이 성질을 이용합니다. 사이트의 모든 글을 미리 벡터로 바꿔 저장해 두고, 방문자가 검색어를 입력하면 그 검색어도 같은 모델로 벡터화한 뒤, 저장된 글 벡터 중 가장 가까운 것들을 찾아 돌려줍니다. 가깝다는 기준은 보통 코사인 유사도(두 벡터가 이루는 각도)를 씁니다. 각도가 작을수록 의미가 비슷하다고 봅니다.
여기서 반드시 지켜야 할 원칙이 하나 있습니다. 글을 색인할 때 쓴 임베딩 모델과 질의를 벡터화할 때 쓰는 모델이 같아야 한다는 것입니다. 두 벡터가 같은 좌표계 위에 있어야 거리 비교가 의미를 갖기 때문입니다. 모델을 바꾸면 좌표계가 달라지므로 저장된 벡터를 전부 다시 만들어야 합니다. 임베딩 차원, 입력 토큰 한도, 다국어 지원 여부는 모델마다 다르니 도입 전 제공사 공식 문서를 확인하길 권합니다.
콘텐츠 색인 파이프라인
시맨틱 검색을 붙이는 작업의 대부분은 “글을 검색 가능한 벡터로 만들어 저장하는” 색인 단계에 있습니다. 순서는 다음과 같습니다.
| 단계 | 하는 일 | 유의점 |
|---|---|---|
| 1. 추출 | 글 본문을 HTML·마크다운에서 평문으로 뽑기 | 코드·표는 의미가 깨지지 않게 보존 |
| 2. 분할 | 긴 글을 문단·섹션 단위로 자르기(청킹) | 한 청크에 한 주제가 담기게 |
| 3. 임베딩 | 각 청크를 모델에 넣어 벡터 생성 | 색인·질의 동일 모델 사용 |
| 4. 저장 | 벡터+본문+메타데이터를 벡터 DB에 적재 | 글 ID·제목·URL 함께 저장 |
| 5. 갱신 | 글 수정·삭제 시 해당 벡터만 재생성 | 발행 훅과 연동해 자동화 |
글을 통째로 하나의 벡터로 만들면, 여러 주제가 섞여 임베딩이 흐려지고 검색해 온 결과가 너무 길어집니다. 그래서 보통은 글을 문단이나 H2 섹션 단위로 잘라 각 조각을 따로 임베딩합니다. 조각마다 원본 글의 제목과 URL을 메타데이터로 붙여 두면, 검색 결과에서 “어느 글의 어느 섹션”인지 바로 안내할 수 있습니다.
저장소로는 규모에 맞춰 고르면 됩니다. 글이 수천 건 이하라면 PostgreSQL에 pgvector 확장을 얹는 방식이 운영 부담이 가장 적습니다. 이미 쓰는 DB에 컬럼 하나 늘리는 셈이기 때문입니다. 더 커지면 전용 벡터 DB나 매니지드 검색 서비스로 옮기는 선택지가 있습니다.
-- pgvector 예시: 임베딩 컬럼과 코사인 거리 검색
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE doc_chunks ADD COLUMN embedding vector(1536);
-- 질의 벡터(:q)와 가까운 청크 5개
SELECT post_id, title, url, content
FROM doc_chunks
ORDER BY embedding :q -- <=> : 코사인 거리
LIMIT 5;하이브리드 검색 — 키워드와 벡터를 함께
시맨틱 검색이 키워드 검색을 완전히 대체하지는 못합니다. 정확한 제품명, 오류 코드, 모델 번호처럼 글자 그대로 일치해야 하는 질의에서는 오히려 키워드 검색이 강합니다. “PG-4012 오류”를 의미로 풀면 비슷하지만 다른 오류 글이 섞여 들어올 수 있습니다. 반대로 구어체·동의어 질의에서는 벡터 검색이 강합니다. 두 방식의 강점이 상호 보완적이라, 실무에서는 둘을 합친 하이브리드 검색을 많이 씁니다.
합치는 대표적인 방법이 RRF(Reciprocal Rank Fusion, 상호 순위 융합)입니다. 키워드 검색과 벡터 검색을 각각 돌려 두 개의 순위 목록을 얻은 뒤, 각 문서가 두 목록에서 차지한 순위의 역수를 더해 최종 점수를 냅니다. 어느 한쪽에서만 상위에 든 문서보다, 양쪽에서 고루 상위에 든 문서가 위로 올라옵니다. 점수 스케일이 다른 두 검색을 정규화 없이 합칠 수 있어 구현이 단순합니다.
| 방식 | 강한 질의 | 약한 질의 | 역할 |
|---|---|---|---|
| 키워드(BM25) | 정확한 명사·코드·번호 | 동의어·구어체·오타 | 정밀 일치 보장 |
| 벡터(시맨틱) | 의미·맥락·재진술 | 고유명사 정확 일치 | 재현율 확보 |
| 하이브리드(RRF) | 대부분의 실사용 질의 | — | 두 강점 결합 |
여기에 한 단계를 더 얹기도 합니다. 하이브리드로 후보 20~30개를 추린 뒤, 재순위(re-ranking) 모델로 질의와 각 후보의 적합도를 다시 매겨 상위 몇 개만 남기는 방식입니다. 정확도가 올라가지만 호출 비용과 지연이 늘어나므로, 작은 사이트라면 하이브리드까지만 적용하고 효과를 본 뒤 도입을 검토해도 충분합니다.
작은 사이트의 현실적인 적용
방문자 규모가 크지 않은 블로그나 문서 사이트라면, 처음부터 복잡한 인프라를 깔 필요가 없습니다. 가장 가벼운 출발점은 다음과 같습니다.
- 글 수가 수백~수천 건이면 pgvector나 파일 기반 인덱스(FAISS 등)로 충분합니다. 별도 벡터 DB를 띄우지 않아도 됩니다.
- 임베딩은 글을 발행·수정하는 순간에만 한 번 생성하면 됩니다. 색인은 자주 일어나지 않으므로 비용이 크게 들지 않습니다.
- 검색 시점에는 질의 한 건만 임베딩하면 되므로, 호출량이 방문자의 검색 횟수에 비례할 뿐 폭증하지 않습니다.
- 다국어가 필요 없고 한국어 위주라면, 한국어를 잘 다루는 임베딩 모델인지 미리 확인하면 품질이 안정적입니다.
비용을 더 줄이려면 임베딩 모델을 외부 API 대신 자체 호스팅하는 선택지도 있습니다. 오픈소스 임베딩 모델을 사이트 서버나 별도 인스턴스에 올리면 호출당 비용은 사라지지만, 운영·메모리 부담이 생깁니다. 글이 적고 트래픽이 낮다면 API 종량제가 더 싸고 편한 경우가 많으니, 월 색인량과 검색량을 가늠해 비교해 보는 것이 좋습니다.
검색 품질은 측정해서 개선한다
시맨틱 검색을 붙였다고 끝이 아닙니다. 정말 더 잘 찾아 주는지는 감이 아니라 숫자로 확인해야 합니다. 가장 단순한 출발은, 실제 검색 로그에서 자주 들어온 질의 30~50개를 뽑고 각 질의에 대해 “이 글이 나와야 한다”는 정답 문서를 사람이 지정한 평가 셋을 만드는 것입니다. 그런 다음 검색 설정을 바꿔 가며 같은 질의로 돌려 정답이 상위에 들어오는지를 봅니다.
| 지표 | 측정 내용 |
|---|---|
| Recall@k | 상위 k개 안에 정답 글이 든 질의 비율 — 놓치지 않는가 |
| Precision@k | 상위 k개 중 실제 관련 글의 비율 — 군더더기 없이 정확한가 |
| MRR | 정답이 몇 번째에 나오는지 — 앞쪽에 올리는가 |
| 제로 결과율 | “결과 없음”으로 끝난 질의 비율 — 줄었는가 |
키워드만 쓰던 때와 하이브리드를 적용한 뒤의 같은 지표를 비교하면, 도입 효과가 명확히 드러납니다. 흔히 가장 크게 개선되는 것이 제로 결과율과 Recall입니다. 표현이 달라 못 찾던 질의가 답을 찾기 시작하기 때문입니다. 설정을 바꿀 때는 청크 크기, 검색 개수 k, 키워드·벡터 가중치를 한 번에 하나씩만 바꿔 비교해야 어떤 변수가 효과를 냈는지 알 수 있습니다.
임베딩 모델을 바꾸면 전체 재색인이 필요합니다.
질의 벡터와 색인 벡터는 반드시 같은 모델로 만들어야 거리 비교가 성립합니다. 모델이나 임베딩 차원을 바꾸면 기존 벡터는 새 기준과 호환되지 않으므로, 모든 글을 다시 임베딩해야 합니다. 글이 많다면 재색인에 드는 시간과 비용을 미리 계획에 넣고, 모델 선택은 처음에 신중히 하는 편이 좋습니다.
도입 체크리스트
- 키워드 검색의 제로 결과·오답 질의를 로그에서 먼저 확인해 도입 효과를 가늠했는가
- 색인·질의에 같은 임베딩 모델을 쓰도록 코드를 통일했는가
- 글을 문단·섹션 단위로 청킹하고 제목·URL 메타데이터를 붙였는가
- 글 발행·수정·삭제 훅과 색인 갱신을 연동해 자동화했는가
- 고유명사·코드 질의를 위해 키워드 검색을 버리지 않고 하이브리드로 합쳤는가
- 평가 셋을 만들어 Recall@k·제로 결과율로 도입 전후를 비교했는가
- 월 색인량·검색량을 추산해 API 종량제와 자체 호스팅 비용을 견줘 봤는가
자주 묻는 질문
- 키워드 검색을 완전히 버려야 하나요?
- 아닙니다. 정확한 제품명·오류 코드·번호 같은 질의는 키워드 검색이 더 정확합니다. 시맨틱 검색은 동의어·구어체에 강하므로 둘을 합친 하이브리드가 대부분의 실사용 질의에서 가장 안정적입니다.
- 작은 블로그에도 벡터 DB가 꼭 필요한가요?
- 글이 수천 건 이하라면 PostgreSQL의 pgvector나 파일 기반 인덱스로 충분합니다. 별도 벡터 DB는 데이터가 커지거나 검색량이 많아질 때 검토해도 늦지 않습니다.
- 임베딩 비용이 많이 들지 않을까요?
- 색인은 글을 발행·수정할 때 한 번만 하고, 검색은 질의 한 건만 임베딩하면 됩니다. 트래픽이 낮은 사이트라면 호출량이 적어 종량제로도 부담이 크지 않은 편입니다. 정확한 단가는 제공사 공식 문서를 확인하세요.
- 한국어도 잘 검색되나요?
- 모델에 따라 다릅니다. 한국어를 충분히 학습한 임베딩 모델이나 다국어 모델을 고르면 동의어·구어체 한국어 질의를 잘 잡습니다. 도입 전 한국어 평가 셋으로 품질을 확인하는 것이 안전합니다.
- 검색 품질이 좋아졌는지 어떻게 알 수 있나요?
- 실제 질의와 정답 글을 짝지은 평가 셋을 만들어 Recall@k, MRR, 제로 결과율을 측정합니다. 키워드만 쓰던 때와 하이브리드 적용 후의 같은 지표를 비교하면 효과가 수치로 드러납니다.
검색 노출·구조화 데이터 등 검색엔진이 콘텐츠를 다루는 방식은 Google 검색 센트럴 공식 문서에서 함께 확인할 수 있습니다.
이 글은 블로그·문서 사이트에 시맨틱 검색을 적용하는 원리와 절차를 일반적인 관점에서 정리한 기술 참고 자료입니다. 임베딩 모델의 차원·입력 토큰 한도·단가, 벡터 DB와 검색 라이브러리의 기능은 제공사와 버전·시점에 따라 달라지므로, 실제 구현 전에 사용하는 임베딩 모델과 저장소(예: pgvector, FAISS, 매니지드 검색 서비스)의 공식 문서를 확인하시기 바랍니다. 본문의 코드와 SQL은 개념 설명용 예시입니다.