검색은 어떻게 동작할까? 역인덱스부터 사전, TF-IDF, 임베딩까지
쇼핑몰 검색창에 "울 니트"를 입력하면 1초도 안 돼서 결과가 나옵니다. 수십만 개의 상품 중에서 어떻게 이렇게 빨리, 그리고 그럴듯한 순서로 상품을 찾아올까요? 이번 글에서는 정보 검색의 기본 개념인 역인덱스, 사전, TF-IDF, 임베딩을 정리하고, 검색어가 입력됐을 때 어떤 흐름으로 결과가 나오는지 살펴보겠습니다.
1. 검색은 두 단계로 나뉩니다: 매칭과 랭킹
검색은 크게 두 단계로 이루어집니다.
| 단계 | 하는 일 | 질문 |
|---|---|---|
| 매칭 | 검색어에 해당하는 문서를 후보로 골라냅니다 | 어떤 문서가 결과에 들어가는가? |
| 랭킹 | 골라낸 후보의 순서를 정합니다 | 그중 무엇을 먼저 보여줄 것인가? |
"니트"를 검색해서 5만 개의 상품이 매칭되더라도, 사용자는 보통 앞의 몇십 개만 봅니다. 그래서 무엇을 찾아오는지만큼, 무엇을 먼저 보여주는지도 중요합니다.
이 글에서 다룰 개념 중 역인덱스와 사전은 주로 매칭, TF-IDF는 랭킹의 기초 점수, 임베딩은 매칭과 랭킹 모두에 쓰입니다.
2. 역인덱스: 빠르게 찾기 위한 목록
가장 단순한 검색 방법은 모든 문서를 처음부터 끝까지 읽으면서 검색어가 있는지 확인하는 것입니다. 하지만 문서가 수십만 개라면 너무 느리겠죠.
그래서 검색 엔진은 역인덱스(Inverted Index)를 미리 만들어 둡니다. 책 뒤편의 "찾아보기"처럼, 단어마다 그 단어가 들어간 문서 목록을 정리해 두는 방식입니다.
예를 들어 상품이 4개 있다고 해보겠습니다.
| 상품 | 상품명 |
|---|---|
| D1 | 울 니트 가디건 |
| D2 | 면 니트 |
| D3 | 울 코트 |
| D4 | 니트 원피스 |
이걸 역인덱스로 바꾸면 다음과 같습니다.
울 → D1, D3
니트 → D1, D2, D4
가디건 → D1
면 → D2
코트 → D3
원피스 → D4이제 "울 니트"를 검색하면 문서를 하나씩 읽을 필요 없이, "울"과 "니트"의 목록만 꺼내서 합치면 됩니다.
- 두 단어가 모두 들어간 문서(AND): D1
- 두 단어 중 하나라도 들어간 문서(OR): D1, D2, D3, D4
문서가 아무리 많아도 단어의 목록만 찾으면 되기 때문에 검색이 빠릅니다. 대부분의 검색 엔진이 이 구조를 기본으로 사용합니다.
3. 형태소 분석과 사전: 단어를 어떻게 나눌 것인가
역인덱스를 만들려면 먼저 문장을 단어 단위로 쪼개야 합니다. 이 과정을 토큰화, 한국어에서는 보통 형태소 분석이라고 부릅니다.
한국어는 띄어쓰기가 불규칙하고 조사가 붙기 때문에 쪼개는 방식이 특히 중요합니다. 그리고 이 쪼개는 규칙을 보완하는 것이 바로 사전입니다.
| 사전 | 하는 일 | 예시 |
|---|---|---|
| 사용자 사전 | 쪼개지면 안 되는 단어를 한 단어로 유지합니다 | "퍼플코어"가 "퍼플" + "코어"로 쪼개지지 않게 |
| 동의어 사전 | 같은 뜻의 다른 표기를 함께 찾게 합니다 | 후리스 = 플리스, 가디건 = 카디건 |
| 불용어 사전 | 검색에 의미 없는 단어를 제거합니다 | "무료배송", "당일출고" |
| 오타 교정 | 잘못 입력한 단어를 바로잡습니다 | 트랜치코트 → 트렌치코트 |
사전은 관문이 아니라 손질 단계입니다
사전을 "통과하면 검색되고, 아니면 안 되는" 관문처럼 생각하기 쉽지만, 실제로는 검색어를 찾기 좋게 다듬는 단계입니다. 해당하는 규칙이 있으면 바꾸고, 없으면 그대로 둔 채 항상 역인덱스로 넘어갑니다.
예를 들어 "퍼플코어 플리스"를 검색했다고 해보겠습니다.
사전이 없을 때
"퍼플코어 플리스"
→ [퍼플] [코어] [플리스]
→ 역인덱스에서 찾기"퍼플코어"의 의미가 깨지고, 상품명에 "후리스"라고 적힌 상품은 글자가 달라서 찾지 못합니다.
사전이 적용될 때
"퍼플코어 플리스"
① 사용자 사전: [퍼플코어] [플리스]
② 동의어 사전: [퍼플코어] [플리스 또는 후리스]
③ 역인덱스에서 찾기이제 "후리스"로 등록된 상품도 함께 찾아집니다.
문서 쪽에도 같은 사전이 적용됩니다
중요한 점은 검색어만 다듬는 것이 아니라, 문서를 색인할 때도 같은 사전으로 다듬는다는 것입니다.
상품명 "[당일출고] 퍼플코어 후리스 가디건"
→ 불용어 제거: [당일출고] 삭제
→ 사용자 사전 적용: [퍼플코어] [후리스] [가디건]
→ 역인덱스에 저장검색어와 문서가 같은 규칙으로 쪼개져야 서로 맞아떨어지기 때문입니다.
사전을 바꾸면 어떻게 되나요?
사전의 종류에 따라 반영 방식이 다릅니다.
- 사용자 사전을 바꾸면 문서를 쪼개는 방식 자체가 달라지므로, 이미 색인된 문서를 다시 색인(재색인) 해야 반영됩니다.
- 동의어 사전은 검색할 때 검색어를 확장하는 방식으로 적용하면 재색인 없이 바로 반영할 수 있습니다.
그래서 사전 하나를 바꾸는 일도 검색어 처리와 색인 양쪽에 영향을 주기 때문에, 반영하기 전에 다른 검색 결과를 망가뜨리지 않는지 확인하는 과정이 중요합니다. 예를 들어 "아우터 = 코트 = 패딩"처럼 너무 넓게 묶으면, "코트"를 검색했는데 패딩이 섞여 나오는 문제가 생길 수 있습니다.
4. TF와 IDF: 어떤 문서가 더 관련 있을까
역인덱스로 후보를 찾았다면, 이제 순서를 정해야 합니다. 가장 기본적인 방법이 TF-IDF입니다.
TF (Term Frequency, 단어 빈도)
한 문서 안에 그 단어가 몇 번 나오는가입니다. 많이 나올수록 그 문서가 해당 단어와 관련이 높다고 봅니다.
IDF (Inverse Document Frequency, 역문서 빈도)
그 단어가 전체 문서 중 얼마나 드문가입니다. 모든 문서에 다 나오는 단어는 구분하는 힘이 약하고, 드물게 나오는 단어일수록 중요하다고 봅니다.
IDF = log(전체 문서 수 ÷ 그 단어가 들어간 문서 수)예시로 계산해 보기
앞의 상품 4개로 계산해 보겠습니다. (계산을 쉽게 하기 위해 log는 밑이 10인 상용로그를 쓰고, TF는 모두 1로 두겠습니다.)
| 단어 | 들어간 문서 수 | IDF |
|---|---|---|
| 니트 | 3개 | log(4/3) ≈ 0.125 |
| 울 | 2개 | log(4/2) ≈ 0.301 |
| 가디건 | 1개 | log(4/1) ≈ 0.602 |
"니트"는 여러 상품에 흔하게 나오니 IDF가 낮고, "울"은 상대적으로 드무니 IDF가 높습니다.
이제 "울 니트"를 검색했을 때 각 상품의 점수(TF × IDF의 합)는 다음과 같습니다.
| 상품 | 상품명 | 점수 |
|---|---|---|
| D1 | 울 니트 가디건 | 0.301 + 0.125 = 0.426 |
| D3 | 울 코트 | 0.301 |
| D2 | 면 니트 | 0.125 |
| D4 | 니트 원피스 | 0.125 |
두 단어가 모두 들어간 D1이 1등인 건 자연스럽습니다. 그런데 흥미로운 점은 "울 코트"가 "면 니트"보다 위에 온다는 것입니다. "울"이 더 드문 단어라 점수에 크게 기여했기 때문이죠.
사용자는 아마 니트를 찾고 있었을 텐데 코트가 먼저 나오는 셈입니다. 이처럼 단어의 중요도만으로는 사용자의 의도를 완벽하게 반영하기 어렵습니다. 그래서 실제 검색에서는 카테고리, 인기도, 클릭률 같은 다른 신호를 함께 사용해 순서를 정합니다.
BM25: TF-IDF의 개선판
많은 검색 엔진이 기본으로 사용하는 BM25는 TF-IDF의 두 가지 약점을 보완한 방식입니다.
- 단어 반복에 상한을 둡니다. 상품명에 "니트 니트 니트 겨울니트"처럼 같은 단어를 도배해도 점수가 끝없이 오르지 않습니다.
- 문서 길이를 보정합니다. 긴 문서는 단어가 우연히 포함될 확률이 높으므로 점수를 조금 낮춥니다.
다만 TF-IDF와 BM25는 모두 글자가 정확히 일치해야 점수를 줍니다. "후리스"와 "플리스"처럼 표기가 다르면 같은 단어로 보지 못하기 때문에, 앞에서 본 동의어 사전이 필요합니다.
5. 임베딩: 글자가 아니라 의미로 찾기
키워드 검색의 한계는 단어가 일치하지 않으면 찾지 못한다는 점입니다. 상품명에 "하객"이라는 단어가 없으면, "하객룩"을 검색했을 때 단정한 원피스가 나오기 어렵습니다.
임베딩(Embedding)은 이 문제를 다른 방식으로 접근합니다. 텍스트를 의미를 담은 숫자 벡터로 바꾸고, 의미가 비슷한 것끼리 가까운 위치에 놓습니다.
"하객룩" → [0.82, 0.11, 0.67, …]
"단정한 원피스" → [0.79, 0.15, 0.63, …] ← 가까움
"트레이닝 바지" → [0.05, 0.91, 0.12, …] ← 멂
(숫자는 이해를 돕기 위한 예시입니다)검색할 때는 검색어도 벡터로 바꾼 뒤, 가장 가까운 문서 벡터를 찾습니다. 이를 kNN(k-Nearest Neighbors) 검색이라고 하고, 실제로는 속도를 위해 근사적으로 찾는 ANN(Approximate Nearest Neighbor) 방식을 주로 사용합니다. 가까움은 보통 코사인 유사도로 측정합니다.
키워드 검색과 임베딩 검색 비교
| 키워드 검색 (BM25 등) | 임베딩 검색 | |
|---|---|---|
| 기준 | 글자가 일치하는가 | 의미가 가까운가 |
| 강점 | 정확한 상품명, 브랜드, 속성 검색 | 상황이나 스타일처럼 의도를 담은 검색 |
| 약점 | 표기가 다르면 찾지 못함 | 정확한 조건을 놓치거나, 결과의 이유를 설명하기 어려움 |
두 방식은 서로의 약점을 보완하기 때문에, 실무에서는 둘을 함께 사용하는 하이브리드 검색을 많이 사용합니다. 키워드로 정확하게 맞는 것을 찾고, 임베딩으로 의미상 가까운 것을 보완하는 방식입니다.
6. 전체 흐름 정리
지금까지의 내용을 하나의 흐름으로 정리하면 다음과 같습니다.
미리 해두는 일 (색인)
문서(상품) 등록
↓
형태소 분석 + 사전 적용 (불용어 제거, 사용자 사전, 표기 정리)
↓
역인덱스에 저장 (+ 필요하면 임베딩 벡터도 저장)검색하는 순간
검색어 입력
↓
① 사전으로 다듬기 사용자 사전, 동의어, 불용어, 오타 교정
↓
② 후보 찾기 (매칭) 역인덱스로 키워드 매칭 + 임베딩으로 의미 매칭
↓
③ 순서 정하기 (랭킹) BM25 같은 관련성 점수 + 인기도, 클릭률 등
↓
결과 노출계속 개선하는 일
검색 기록이 쌓임 (무엇을 검색했고, 결과가 몇 건이었고, 무엇을 눌렀는지)
↓
결과가 없거나 만족스럽지 않은 검색어 분석
↓
사전 보완 (동의어 추가, 신조어 등록 등)
↓
다시 색인과 검색에 반영검색 품질은 한 번 만들고 끝나는 것이 아니라, 사용자가 실제로 무엇을 찾았는지 보고 사전과 데이터를 계속 다듬어 가는 과정이라고 할 수 있습니다.
마치며
정리하면 검색은 다음과 같은 요소들이 함께 작동하는 구조입니다.
- 역인덱스는 빠르게 찾을 수 있게 해주고
- 사전은 검색어와 문서가 같은 기준으로 맞아떨어지게 해주며
- TF-IDF와 BM25는 어떤 문서가 더 관련 있는지 점수를 매기고
- 임베딩은 글자가 달라도 의미가 비슷한 것을 찾아줍니다
각각이 완벽하지 않기 때문에 서로를 보완하며 사용되고, 결국 좋은 검색이란 사용자가 입력한 단어 너머의 의도를 얼마나 정확하게 연결하느냐에 달려 있다고 생각합니다.
Comments