Elasticsearch 입문 20편 Suggesters. term·phrase·completion·context·search-as-you-type. 자동완성·did you mean·오타 교정.
이 글은 Elasticsearch 입문에서 운영까지 시리즈 38편 중 20편이에요. 19편에서 Search Features (highlight·sort·pagination·search_after·rescore) 를 마무리했다면, 이번엔 검색박스 맨 앞 에서 사용자가 글자를 타이핑하는 그 순간을 잡는 자리예요. Suggesters — 자동완성·오타 교정·"did you mean" — e-commerce 검색박스의 사실상 표준 UX 를 ES 가 어떻게 한 API 로 풀어 주는지 한 호흡에 정리.
이 글은 Elasticsearch 8.x 공식 docs 의 Suggesters 챕터를 한국어 학습 노트로 풀어쓴 자료예요.
Kibana Dev Tools 콘솔에 본문 예제를 직접 한 번 던져 보면 *오타·자동완성* 동작이 눈에 보여서 머리에 훨씬 잘 박혀요.
검색박스 맨 앞 1초 — Suggester 가 잡는 자리
쇼핑몰 검색박스에 "신촌 운동화" 를 입력하려고 "신ㅊ" 까지 친 순간, 그 아래에 "신촌 운동화 매장"·"신촌 카페"·"신촌역 맛집" 후보가 떠야 해요. 자판을 잘못 쳐서 "운돈화" 라고 보내면 "혹시 운동화 를 찾으셨나요?" 가 떠야 하고요.
이 두 자리는 일반 검색 API (match·term·bool) 로는 거의 안 풀려요. 일반 검색은 완성된 단어를 받아 인덱스에서 찾는 게 본업이라, 입력 중인 prefix 나 오타 교정 은 별도 메커니즘이 필요해요. ES 가 이 자리를 위해 따로 만든 게 Suggesters 모듈이에요.
Suggesters 는 검색 응답 안에 suggest 라는 별도 섹션으로 결과를 돌려 줘요. 일반 query 와 같이 한 요청에 묶어 보내도 되고, Suggester 만 단독 으로 호출해도 됩니다. 검색박스 UI 는 키 한 번 누를 때마다 Suggester 요청을 던지는 게 보통이라 — 지연 < 50ms 가 기본 목표예요.
Suggester 4가지 — 자리 분담
ES 의 Suggester 는 크게 네 갈래로 나뉘어요. 각 자리가 풀어 주는 문제가 달라서 어느 자리에 어느 Suggester 가 맞는지 가 학습의 절반.
| Suggester | 입력 | 출력 | 대표 자리 |
|---|---|---|---|
| term | 단어 하나 | 비슷한 단어 후보 | "운돈화 → 운동화" 단어 오타 교정 |
| phrase | 어구 (여러 단어) | 비슷한 어구 후보 | "신촌 운돈화 맡짐 → 신촌 운동화 매장" 문장 단위 교정 |
| completion | prefix | 자동완성 후보 | 검색박스 "신촌" 까지 쳤을 때 떠야 하는 후보 |
| context | prefix + 필터 | 카테고리/지역별 자동완성 | 서울 매장만 또는 카테고리=신발 필터링된 자동완성 |
여기에 8.x 부터 추가된 search_as_you_type 필드 타입 도 있어요. Suggester 가 아니라 매핑 타입 인데 결과적으로 자동완성 자리를 푸는 대안이라 같이 묶어서 보겠어요.
Term Suggester — Levenshtein 거리로 단어 교정
Term Suggester 는 단어 한 개 의 철자 교정 을 해 줘요. "운돈화" 같이 인덱스에 없는 토큰 이 들어왔을 때, 그 토큰과 편집 거리 가까운 인덱스 내 토큰 을 후보로 돌려 주는 식이에요.
POST /products/_search
{
"suggest": {
"my_suggest": {
"text": "운돈화",
"term": {
"field": "name",
"suggest_mode": "missing",
"min_word_length": 2,
"max_edits": 2,
"prefix_length": 1
}
}
}
}
응답은 "운동화 (score 0.83)" 같은 후보 리스트가 와요. score 는 유사도 점수 (0~1) 인데 내부적으로는 Levenshtein 거리 (편집 거리 — 삽입·삭제·치환·전치 비용) 기반이에요.
핵심 옵션 5가지
suggest_mode — 어느 토큰을 교정 대상 으로 볼지 정해요. 셋이 있어요.
missing(기본) — 인덱스에 없는 토큰만 교정. 입력 토큰이 인덱스에 이미 있으면 교정 안 함.popular— 인덱스에 있는 토큰이라도 더 자주 등장하는 비슷한 토큰 으로 교정 후보 돌려줌. "많이 쓰는 단어 우선" 자리에 유용.always— 모든 입력 토큰 에 대해 교정 후보. 디버깅·실험 자리.
max_edits — 최대 편집 거리 (1 또는 2). 2 가 기본이고 2글자까지 틀려도 잡아 줌. 1 로 줄이면 정확도 ↑·재현율 ↓. 3 은 지원 안 함.
prefix_length — 앞쪽 몇 글자는 일치해야 한다 는 제약. 기본 1. "가나다 → 라마바" 처럼 첫 글자부터 다른 것 은 제외하고 "가나다 → 가나라" 만 후보. 검색박스에서 너무 엉뚱한 추천 을 막는 안전장치.
min_word_length — 교정 대상 단어 최소 길이. 기본 4. 한국어는 2~3 정도로 낮춰야 "운동화·매장" 같은 3음절 단어 까지 잡혀요.
string_distance — 거리 알고리즘 선택. internal (기본) · damerau_levenshtein · levenshtein · jaro_winkler · ngram. 한국어는 damerau_levenshtein 이 전치 (typo ㅏ↔ㅓ) 까지 잡아서 자연스러워요.
한계 — 어절 단위만 본다
Term Suggester 는 어절 한 개씩 만 봐요. "운돈화 마짐" 처럼 여러 단어가 동시에 틀린 어구 는 각각 따로 교정해서 문맥 일관성이 깨지는 후보가 나올 수 있어요. 이 자리는 다음의 Phrase Suggester 가 잡아 줍니다.
Phrase Suggester — 어구 단위 교정
Phrase Suggester 는 여러 단어로 이뤄진 어구 전체 의 교정 후보를 돌려 줘요. 내부적으로는 Term Suggester 위에 언어 모델 (n-gram 기반) 을 한 겹 얹어, 후보 어구 조합 중 가장 자연스러운 것 을 골라 주는 구조예요.
POST /products/_search
{
"suggest": {
"phrase_suggest": {
"text": "신촌 운돈화 마짐",
"phrase": {
"field": "name.trigram",
"size": 3,
"gram_size": 3,
"max_errors": 2,
"direct_generator": [{
"field": "name.trigram",
"suggest_mode": "always"
}],
"highlight": {
"pre_tag": "<em>",
"post_tag": "</em>"
}
}
}
}
}
응답은 "신촌 운동화 매장 (score 0.61)" 같은 완성된 어구 가 와요. highlight 를 켜면 어디가 바뀌었는지 <em> 태그로 감싸 줘서 UI 가 "혹시 ○○ 를 찾으셨나요" 자리에 그대로 박을 수 있어요.
핵심 옵션
field — n-gram 분석기로 색인한 필드 를 가리켜야 해요. 그냥 standard 분석한 필드 로는 동작이 부정확. 매핑에서 trigram (3-gram) shingle 토크나이저 로 미리 색인을 깔아 두는 게 표준 셋업이에요.
gram_size — n-gram 크기. 보통 2~3 이 권장. 매핑의 shingle 크기와 맞춰야 정상 동작.
max_errors — 어구 전체에서 허용할 잘못된 토큰 비율. 0~1 사이 실수 또는 정수. 기본 1.0 — 어구 안 모든 토큰이 다 틀려도 OK. 운영에선 0.5 정도가 적당.
confidence — 입력 어구 자체보다 후보 어구가 얼마나 더 자연스러워야 후보로 띄울지 의 임계. 기본 1.0. "입력보다 1배 이상 자연스러운 후보만 추천". 0.7 정도로 낮추면 더 적극 추천.
smoothing_model — 어구 자연스러움 점수 계산법. stupid_backoff (기본·빠름) · laplace · linear_interpolation 셋이 있어요. 대부분 기본값 으로 충분.
Phrase Suggester 매핑 셋업 — trigram 필드
Phrase Suggester 가 제대로 도려면 n-gram (shingle) 분석기로 별도 색인한 필드 가 필요해요. 보통 multi-field 로 깔아요.
PUT /products
{
"settings": {
"analysis": {
"filter": {
"shingle": {
"type": "shingle",
"min_shingle_size": 2,
"max_shingle_size": 3
}
},
"analyzer": {
"trigram": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "shingle"]
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"trigram": {
"type": "text",
"analyzer": "trigram"
}
}
}
}
}
}
이 셋업이 없는 채로 Phrase Suggester 만 호출하면 후보가 거의 안 나오거나 score 가 비정상 으로 나와요. 운영 사고 1순위.
Completion Suggester — FST 로 미리 압축된 자동완성
Completion Suggester 는 prefix 자동완성 의 진짜 자리 예요. 사용자가 "신ㅊ" 까지 친 그 순간 "신촌 매장·신촌역·신촌 카페" 가 떠야 하는데, 이건 기존 인덱스에 검색 으로 풀면 느려도 너무 느려요. ES 는 이 자리를 위해 completion 필드 타입 이라는 별도 자료구조를 깔아요.
completion 필드 = FST (Finite State Transducer)
completion 필드는 내부적으로 FST (Finite State Transducer, 유한 상태 트랜스듀서) 라는 자료구조로 색인돼요. Trie + 압축 의 친척이라고 보면 되고, 메모리에 통째로 올려두고 prefix 매칭을 μs 단위 로 돌려요. 수십만~수백만 후보 까지는 메모리에 다 올라가요.
비교하면 — match_phrase_prefix 쿼리는 역색인 + prefix scan 으로 풀어서 수십 ms, completion 은 FST 메모리 매칭 으로 수 ms 이하. 검색박스 자동완성처럼 키 한 번에 한 번씩 호출하는 자리에선 체감 차이가 큼.
매핑
PUT /products
{
"mappings": {
"properties": {
"name_suggest": {
"type": "completion",
"analyzer": "simple",
"preserve_separators": true,
"preserve_position_increments": true,
"max_input_length": 50
}
}
}
}
name_suggest 가 completion 타입인 점이 핵심. 일반 text 필드 위에 자동완성을 못 함 — completion 으로 전용 필드를 따로 깔아야 동작해요.
색인 — input 과 weight
POST /products/_doc/1
{
"name": "신촌 운동화 매장",
"name_suggest": {
"input": ["신촌 운동화 매장", "운동화 매장 신촌", "신촌 매장"],
"weight": 100
}
}
input 은 이 문서를 어떤 prefix 로 매칭시킬지 정해요. 여러 개 넣으면 각각 다른 시작어로 후보에 노출. "신촌 매장" 으로 시작하든 "운동화 매장 신촌" 으로 시작하든 같은 문서가 떠요.
weight 는 우선순위 가중치. 인기 매장 = 100, 비인기 매장 = 10 식으로 박아 두면 score 순으로 정렬 돼서 인기 후보가 위에 떠요. 0 ~ 양의 정수.
쿼리
POST /products/_search
{
"suggest": {
"product_complete": {
"prefix": "신촌",
"completion": {
"field": "name_suggest",
"size": 10,
"skip_duplicates": true,
"fuzzy": {
"fuzziness": 1
}
}
}
}
}
응답 — prefix 로 시작하는 후보 10개 가 weight 내림차순 으로 와요. skip_duplicates 는 같은 텍스트 중복 제거. fuzzy.fuzziness: 1 을 켜면 1글자 오타까지 허용 하는 자동완성 — "신초" 까지 쳐도 "신촌" 후보가 떠요.
Completion 의 한계 3가지
첫째, prefix 매칭만 함. "운동화 신촌" 입력에 "신촌 운동화 매장" 후보를 띄우려면 input 배열에 양쪽 순서를 다 넣어야 해요.
둘째, 메모리 비용이 큼. FST 가 힙에 통째로 올라가 있어서, 수백만 후보 × 평균 길이 20자 면 수백 MB ~ 수 GB. node 메모리 사이징 에서 미리 잡아 둬야 함.
셋째, 문서 일반 검색에는 못 씀. completion 필드는 자동완성 전용 이라 match 같은 일반 쿼리에선 안 쓰여요. 일반 검색용 text 필드 + 자동완성용 completion 필드 를 나란히 깔아 두는 게 표준 패턴.
Context Suggester — 카테고리·지역 필터링된 자동완성
Context Suggester 는 Completion 의 확장 이에요. "전국 매장" 이 아니라 "서울 매장만" 또는 "카테고리 = 신발 인 매장만" 처럼 컨텍스트 필터 가 걸린 자동완성을 풀어 줘요. e-commerce 에서 "카테고리 → 신발 → 신촌" 으로 점진적으로 좁혀 가는 검색박스의 자리.
매핑 — contexts 선언
PUT /stores
{
"mappings": {
"properties": {
"name_suggest": {
"type": "completion",
"contexts": [
{
"name": "category",
"type": "category"
},
{
"name": "location",
"type": "geo",
"precision": 6
}
]
}
}
}
}
contexts 안에 컨텍스트 한 묶음씩 선언해요. type: category 는 문자열 라벨 필터, type: geo 는 지오 해시 기반 위치 필터. 한 필드에 여러 컨텍스트를 동시에 걸 수도 있어요.
색인 — 각 input 에 context 박기
POST /stores/_doc/1
{
"name": "신촌 운동화 매장",
"name_suggest": {
"input": ["신촌 운동화 매장", "신촌 매장"],
"weight": 100,
"contexts": {
"category": ["shoes"],
"location": { "lat": 37.5560, "lon": 126.9370 }
}
}
}
이 매장은 카테고리 shoes + 신촌 위치 컨텍스트가 박힌 후보가 돼요.
쿼리 — context 필터
POST /stores/_search
{
"suggest": {
"ctx_complete": {
"prefix": "신",
"completion": {
"field": "name_suggest",
"size": 5,
"contexts": {
"category": ["shoes"],
"location": [{
"lat": 37.555, "lon": 126.937,
"precision": 6
}]
}
}
}
}
}
요청에 contexts 를 넣어 카테고리 shoes 인 후보만 + 해당 위치 근처만 자동완성. 카테고리 미설정 으로 호출하면 전체 매장 이 와요. category 만 넣으면 해당 카테고리 전국, location 만 넣으면 전체 카테고리 근처.
Context 가 비싼 자리
Context Suggester 는 Completion 보다 메모리·디스크 비용이 더 큼. 각 (input, context 조합) 마다 별도 FST 엔트리가 생겨서, 카테고리 10개 × 지역 100개 × input 100만 = 10억 같은 폭증 자리가 나올 수 있어요. 운영에서 컨텍스트 카디널리티 를 의식해서 설계해야 합니다.
Search-as-you-type 타입 — Completion 의 대안
8.x 부터 권장되는 또 다른 자동완성 방식이 search_as_you_type 필드 타입이에요. Suggester 가 아니라 매핑 타입 이라 일반 match 쿼리 로 자동완성을 풀어 줘요. 별도 Suggester API 를 안 써도 됩니다.
매핑
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "search_as_you_type",
"max_shingle_size": 3
}
}
}
}
search_as_you_type 으로 깔면 내부적으로 원본 + name._2gram + name._3gram + name._index_prefix 같은 여러 보조 필드를 자동 생성 해요. 색인 시점에 *prefix · shingle * 을 미리 다 깔아 두는 셈.
쿼리
POST /products/_search
{
"query": {
"multi_match": {
"query": "신촌 운동",
"type": "bool_prefix",
"fields": [
"name",
"name._2gram",
"name._3gram"
]
}
}
}
type: bool_prefix 가 각 토큰을 prefix 로 매칭 + 마지막 토큰만 prefix 같은 자동완성 패턴을 자동 풀어 줘요. 응답은 일반 검색의 hits 로 와요 (suggest 섹션 아님).
Completion vs Search-as-you-type 어느 쪽?
| Completion Suggester | search_as_you_type | |
|---|---|---|
| 자료구조 | FST (메모리) | 일반 역색인 + prefix 보조 필드 |
| 응답 속도 | < 5ms | 10~30ms |
| 메모리 비용 | 큼 | 보통 |
| 일반 검색 가능 | X (자동완성 전용) | O (한 필드로 둘 다) |
| 가중치 (weight) | 지원 | sort/function_score 로 우회 |
| context (필터) | 지원 (category·geo) | 일반 쿼리 filter 로 |
| 한국어 자모 분리 | 약함 (analyzer 별도) | analyzer 자유 |
| 8.x 권장 | 여전히 표준 | 대안으로 추천 (Elastic 공식) |
거친 결정 기준 — μs 단위 응답이 절대 필요한 검색박스 면 completion, 그렇지 않고 일반 검색과 한 필드로 묶고 싶으면 search_as_you_type. 한국어 분석기를 자유롭게 쓰고 싶을 때 도 search_as_you_type 이 편해요 (다음 절).
한국어 자동완성 — 자모 분리의 함정
이 자리가 한국 회사에서 가장 자주 사고 나는 자리예요. 사용자가 "신ㅊ" 까지 친 (= "신촌" 의 "ㅊ" 만 친) 상태에서 "신촌" 후보가 떠야 하는데, ES 기본 분석기로는 어절 단위 만 잡아서 "신ㅊ" 에는 매칭이 안 돼요.
해결은 한글 자모 분리 분석기 + Completion (또는 search_as_you_type) 조합이에요. 11편(Korean Analyzer) 에서 다룬 Nori + 자모 토크나이저를 깔거나, 사내에서 자주 쓰는 javacafe-elastic-jamo 같은 자모 분리 플러그인 을 색인 시점에 적용해 "신촌" → "ㅅㅣㄴㅊㅗㄴ" 으로 분해해서 색인합니다.
쿼리 시점에도 입력을 자모 분리 해서 던지면 "신ㅊ" → "ㅅㅣㄴㅊ" 가 "ㅅㅣㄴㅊㅗㄴ" 의 prefix 가 돼서 매칭. 이 셋업 없는 기본 standard 분석기 + completion 은 한국 사용자 자동완성 UX 50% 가 깨진 상태 라고 봐도 무방.
자주 만나는 사고
사고 1 — Completion 필드를 안 깔고 일반 text 필드에 자동완성 시도
원인 — text 필드에 completion Suggester 를 던지면 400 매핑 에러 또는 결과 0개 가 나와요. Completion 은 전용 completion 타입 필드 가 있어야 동작.
해결 — 일반 검색용 text 필드 + 자동완성용 completion 필드 를 나란히 깔아 두는 게 표준. 색인 시점에 두 필드에 같은 텍스트 를 박아요. multi-field 로 묶거나 별도 두 필드로.
사고 2 — Context Suggester 에서 context 누락하면 결과 0개
원인 — contexts 를 매핑에 선언했는데 쿼리 시점에 context 를 안 보내면 결과가 0개 로 나와요. 기본값 = "전체" 가 아니라 기본값 = "필터 미일치".
해결 — 명시적으로 contexts 를 보내거나, 매핑에서 default: { value: "all" } 같은 기본 context 를 한 묶음 박아 두고 색인 시점에 모든 문서에 "all" 컨텍스트 를 추가로 박아요. "context 안 보내면 전체 매장" 처럼 자연스럽게 동작하게.
사고 3 — 한국어 자모 분리 없이 Completion 만 깐다
원인 — "신ㅊ" → "신촌" 자동완성이 안 떠요. 사용자가 어절을 완성해야만 자동완성이 돌아서 UX 50% 깨짐.
해결 — 자모 분리 분석기 (Nori 의 jamo 옵션 또는 자모 분리 플러그인) 를 색인 + 쿼리 양쪽에 적용. 11편(Korean Analyzer) 에서 깊이.
사고 4 — Phrase Suggester 가 결과 0개 — trigram 필드 미설정
원인 — Phrase Suggester 의 field 가 standard 분석기 필드 를 가리키면 n-gram 통계 가 없어서 후보가 거의 안 나옴.
해결 — shingle (n-gram) 분석기로 색인한 별도 필드 (name.trigram 같은) 를 multi-field 로 깔고, Suggester field 가 그 필드를 가리키게. 위 매핑 예제 참고.
사고 5 — Completion FST 메모리 폭증
원인 — 후보 1,000만 × 평균 30자 같은 큰 인덱스에 Completion 을 깔면 힙 메모리 수 GB 가 FST 로 점유돼서 노드 OOM 또는 GC 폭증.
해결 — Completion 필드는 카디널리티 < 100만 자리에 한정. 큰 자리는 search_as_you_type 또는 별도 자동완성 인덱스 분리. 노드 _nodes/stats 에서 completion.size_in_bytes 로 추적.
사고 6 — Suggester 응답 임계 (threshold) 미세팅으로 노이즈 추천
원인 — Term Suggester 의 score 가 0.3 같은 낮은 후보까지 검색박스에 띄우면 "신촌 → 신문지" 같은 엉뚱한 추천 이 노출.
해결 — 클라이언트 단에서 score 임계 (예 0.6 이상) 를 박아 필터링, 또는 confidence (Phrase Suggester) 값을 올려요. 검색박스 "혹시 ○○ 를 찾으셨나요" 가 오해 되는 추천이면 안 띄우는 게 낫다 가 운영 원칙.
사고 7 — Suggester 와 일반 query 를 한 요청에 묶었는데 size 0 을 안 박음
원인 — Suggester 만 필요한데 size 를 안 박으면 기본 10건의 hits 까지 같이 와서 네트워크·CPU 가 낭비. 검색박스의 keystroke 단위 호출에선 체감 응답이 무거워짐.
해결 — Suggester 단독 요청은 항상 "size": 0 박기. hits 가 안 와요. 응답 페이로드가 수십 KB → 수 KB 로 줄어요.
운영 권장 패턴
운영에서 자동완성 응답 < 50ms 를 목표로 잡고 거꾸로 설계해요. p99 가 100ms 를 넘기 시작하면 Completion → search_as_you_type 전환 또는 자동완성 인덱스 분리 가 답.
자동완성용 인덱스는 검색용 인덱스와 별도로 분리 하는 게 안전해요. 상품 데이터 1억 건 인덱스 에 Completion 까지 같이 깔면 Completion FST 만 수 GB 가 돼서 재색인·rollover 가 거대 해져요. 자동완성 후보 100만 건 별도 인덱스 가 가볍고 빠름.
가중치(weight) 는 인기도 점수 와 같이 박아 둬요. 판매량·검색 클릭률 같은 비즈니스 지표를 주 1회 ETL 로 weight 에 반영하면, "많이 검색되는 매장이 위에" 가 자동으로 됩니다. weight = 클릭수 / 1000 같은 단순 정규화로 시작하면 충분.
Suggester 자체는 비동기·디바운스 로 호출해요. 검색박스에서 키 입력마다 그대로 던지지 말고, 300ms debounce + 직전 요청 abort 로 서버 부하를 1/3 로 줄일 수 있어요. 클라이언트 측 패턴이지만 운영 부담의 큰 부분을 잡아 줍니다.
마지막으로 추천 품질 모니터링 을 표준 셋업으로 박아요. 자동완성 클릭률 과 추천 후 일반 검색으로 진입한 비율 을 Kibana 대시보드로 띄워 두면, Suggester 가 도움이 되고 있는지 가 숫자로 보여요. 클릭률 < 5% 면 추천 임계·가중치 를 재조정해야 한다는 신호.
시험 직전 한 번 더 — 압축 노트
- Suggester = 검색박스 맨 앞 1초 자리. 자동완성·오타 교정·did you mean.
- 4갈래 — term · phrase · completion · context. 8.x 부터 search_as_you_type 필드 타입이 추가 대안.
- Term — 단어 한 개 오타 교정. Levenshtein 거리 기반.
suggest_mode(missing·popular·always) ·max_edits(1~2) ·prefix_length. - Phrase — 어구 단위 교정. n-gram 기반 언어 모델. trigram 필드 매핑이 전제.
gram_size·max_errors·confidence·smoothing_model. - Completion — prefix 자동완성 전용. FST 자료구조로 μs 응답.
completion필드 타입 필요.input(다중) ·weight·fuzzy·skip_duplicates. - Context — Completion + 필터.
category(라벨) ·geo(위치) 타입. 매핑·색인·쿼리 세 자리 모두에 context 박아야. - search_as_you_type — 일반
match로 자동완성. prefix + shingle 보조 필드 자동 생성. 일반 검색과 한 필드로 묶고 싶을 때. - 한국어 자동완성 = 자모 분리 분석기 + Completion(또는 search_as_you_type) 조합. Nori 의 jamo 또는 자모 분리 플러그인.
- 7대 사고 — completion 필드 미설정 · context 누락 0건 · 자모 분리 없음 · trigram 필드 없음 · FST OOM · 노이즈 추천 임계 미세팅 · size:0 누락.
- 운영 — 자동완성 인덱스 분리 · weight 에 인기도 ETL · debounce 300ms · 클릭률 대시보드.
- 응답 목표 — Completion < 5ms · search_as_you_type < 30ms · 전체 자동완성 응답 < 50ms.
- Term + Phrase 는 "did you mean" 자리. Completion + Context 는 자동완성 후보 자리. 둘이 다른 자리 임을 혼동 X.
시리즈 다른 편
- 이전 글 = 19편 Search Features — highlight·sort·search_after·rescore
- 다음 글 = 21편 Vector Search — dense_vector·kNN·임베딩 색인
- 11편 = Korean Analyzer — Nori·mecab-ko·사용자 사전 (자모 분리 자동완성의 전제)
- 13편 = Full-text Queries — match·match_phrase·multi_match
- 17편 = Aggregations Bucket — terms·date_histogram·range
- 22편 = RAG 인프라 — hybrid 검색·rerank·LLM 통합
- 32편 = Spring Data Elasticsearch — Repository·Template·POJO
- 38편 = 시리즈 마무리 — 결정 트리·체크리스트·자격증
한 줄 정리 — Suggester = ES 가 검색박스 맨 앞 1초 를 잡기 위해 따로 깐 자동완성·오타 교정 전용 모듈. term·phrase 는 교정, completion·context 는 자동완성. 한국어는 자모 분리 + Completion 조합이 표준.