Elasticsearch 마스터 노트 시리즈 3편. Analyzer가 text 필드를 토큰화하는 3단계(char filter·tokenizer·token filter), Standard Analyzer 동작, 한국어 분석기 Nori의 형태소 분석, Custom Analyzer 구성, N-Gram 분석으로 부분 검색, _analyze API로 결과 확인까지.
이 글은 Elasticsearch 마스터 노트 시리즈의 세 번째 편입니다. 2편(Mapping)에서 text vs keyword를 봤다면, 이번엔 text가 어떻게 처리되나 — Analyzer.
"Spring Boot Framework" 가 어떻게 ["spring", "boot", "framework"]로 변환? 이 변환이 검색 결과를 결정. 한국어 환경 = Nori 분석기.
처음 Analyzer가 어렵게 느껴지는 이유
처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, **3 단계 (char filter·tokenizer·token filter)**가 막연합니다. 둘째, 한국어는 영어와 달라 표준 분석기가 안 맞습니다.
해결법은 한 가지예요. "text → 토큰 리스트로 변환". 분석기 = 텍스트 → 검색 가능한 단어들. 한국어 = 형태소 분석 (Nori).
Analyzer 3 단계
[Original Text]
↓
[Character Filter] (특수문자 처리·HTML 제거)
↓
[Tokenizer] (단어 분리)
↓
[Token Filter] (소문자·stopword·stemming)
↓
[Tokens]
각 단계 독립적·구성 가능.
Standard Analyzer — 기본
"Spring Boot 2024!"
↓ Tokenizer (공백·구두점 분리)
["Spring", "Boot", "2024"]
↓ Lowercase Token Filter
["spring", "boot", "2024"]
여기서 시험 함정이 하나 있어요. Standard Analyzer는 영어 친화. 한국어는 공백만으로 분리 → "검색엔진" 같은 명사 분리 X.
_analyze API — 결과 확인
GET /_analyze
{
"analyzer": "standard",
"text": "Spring Boot Framework"
}
# 결과
{
"tokens": [
{ "token": "spring", "position": 0 },
{ "token": "boot", "position": 1 },
{ "token": "framework", "position": 2 }
]
}
검색 시 어떻게 토큰화되는지 미리 확인. 디버깅 핵심.
빌트인 Analyzer
| 분석기 | 동작 |
|---|---|
| Standard | 기본 (공백·구두점·소문자) |
| Simple | 알파벳 외 분리·소문자 (숫자 제거) |
| Whitespace | 공백만 분리 (소문자 X) |
| Keyword | 분리 X (전체 1 토큰) |
| Stop | Standard + 불용어 제거 |
| Pattern | 정규식 기반 |
| English | 영어 stemming·불용어 |
| Language | French·German 등 다국어 |
English Analyzer
GET /_analyze
{
"analyzer": "english",
"text": "running runs ran"
}
# 결과 — stemming
[
{ "token": "run" },
{ "token": "run" },
{ "token": "ran" }
]
running·runs → run (어간 추출).
한국어 — Nori Analyzer
설치
# 플러그인 설치 (Elasticsearch)
bin/elasticsearch-plugin install analysis-nori
또는 Docker:
elasticsearch:
image: elasticsearch:8.x
environment:
- "ES_PLUGINS=analysis-nori"
사용
GET /_analyze
{
"analyzer": "nori",
"text": "Spring Boot로 만드는 검색엔진"
}
# 결과 — 형태소 분석
[
{ "token": "spring" },
{ "token": "boot" },
{ "token": "만들" },
{ "token": "검색" },
{ "token": "엔진" }
]
여기서 정말 중요한 시험 함정 — 한국어 = Nori 필수. Standard Analyzer는 한국어 형태소 X. "검색엔진"이 분리 안 되어 "검색"만으로 검색 시 X.
Nori 옵션
PUT /products
{
"settings": {
"analysis": {
"analyzer": {
"korean_analyzer": {
"type": "nori",
"decompound_mode": "mixed",
"user_dictionary": "user_dict.txt"
}
}
}
}
}
| Decompound Mode | 의미 |
|---|---|
| none | 복합어 분해 X ("검색엔진" 그대로) |
| discard | 분해된 토큰만 ("검색", "엔진") |
| mixed | 원본 + 분해 ("검색엔진", "검색", "엔진") |
여기서 시험 함정이 하나 있어요. mixed가 일반 권장. 사용자가 "검색엔진"으로도 "검색"으로도 찾을 수 있음.
사용자 정의 사전
# user_dict.txt
스프링부트
검색엔진,검색,엔진
특정 명사 명시. 도메인 용어·신조어에 유용.
Custom Analyzer
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"char_filter": ["html_strip"],
"tokenizer": "standard",
"filter": ["lowercase", "stop", "snowball"]
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "my_custom_analyzer"
}
}
}
}
3 단계 자유 조합.
Character Filter
| 필터 | 동작 |
|---|---|
| html_strip | HTML 태그 제거 |
| mapping | 문자 매핑 (& → and) |
| pattern_replace | 정규식 치환 |
GET /_analyze
{
"char_filter": ["html_strip"],
"tokenizer": "standard",
"text": "<p>Hello <b>World</b></p>"
}
# → "Hello World"
Tokenizer 종류
| Tokenizer | 동작 |
|---|---|
| standard | 단어 + 공백 + 구두점 |
| whitespace | 공백만 |
| keyword | 분리 X |
| pattern | 정규식 |
| ngram | N-gram |
| edge_ngram | Edge N-gram |
| path_hierarchy | 경로 계층 |
| nori_tokenizer | 한국어 형태소 |
Token Filter
lowercase — 소문자
uppercase — 대문자
stop — 불용어 제거
stemmer — 어간 추출 (영어)
synonym — 동의어
unique — 중복 제거
asciifolding — 악센트 제거 (é → e)
Stop (불용어)
PUT /my_index
{
"settings": {
"analysis": {
"filter": {
"english_stop": {
"type": "stop",
"stopwords": ["the", "a", "an", "and", "or"]
}
},
"analyzer": {
"my_analyzer": {
"tokenizer": "standard",
"filter": ["lowercase", "english_stop"]
}
}
}
}
}
Synonym (동의어)
"filter": {
"my_synonym": {
"type": "synonym",
"synonyms": [
"스마트폰, 휴대폰, 핸드폰",
"노트북 => 랩탑"
]
}
}
스마트폰·휴대폰·핸드폰 모두 같은 토큰으로. 노트북 → 랩탑만 (단방향).
N-Gram — 부분 검색
PUT /my_index
{
"settings": {
"analysis": {
"tokenizer": {
"my_ngram": {
"type": "ngram",
"min_gram": 2,
"max_gram": 3
}
},
"analyzer": {
"ngram_analyzer": {
"tokenizer": "my_ngram"
}
}
}
}
}
GET /my_index/_analyze
{
"analyzer": "ngram_analyzer",
"text": "spring"
}
# → ["sp", "spr", "pr", "pri", "ri", "rin", "in", "ing", "ng"]
여기서 시험 함정이 하나 있어요. N-Gram = 부분 매칭 가능. "spri"로도 "spring" 찾기. 다만 인덱스 크기 폭증·검색 속도 ↓. 자동완성·작은 필드에만.
Edge N-Gram — 자동완성
PUT /my_index
{
"settings": {
"analysis": {
"tokenizer": {
"edge_ngram": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 10
}
}
}
}
}
GET /my_index/_analyze
{
"tokenizer": "edge_ngram",
"text": "spring"
}
# → ["sp", "spr", "spri", "sprin", "spring"]
앞에서부터만. 자동완성에 표준.
search_analyzer — 검색 시 다른 분석기
"name": {
"type": "text",
"analyzer": "edge_ngram_analyzer", # 인덱싱 시
"search_analyzer": "standard" # 검색 시
}
저장 = N-Gram 다양하게, 검색 = 표준 (사용자 입력 그대로).
여기서 정말 중요한 시험 함정 — 인덱싱 분석 vs 검색 분석 분리. 자동완성·N-Gram에 표준. 안 분리하면 검색어도 N-Gram 분해 → 의도 다른 결과.
운영 권장 패턴
한국어 + 영어 혼합:
- nori_tokenizer (한국어 형태소)
- lowercase
- stop (한국어·영어 불용어)
- synonym (도메인 동의어)
자동완성:
- edge_ngram (인덱싱)
- standard (검색)
정확 매칭:
- keyword 타입 (분석 X)
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 3편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- Analyzer 3 단계 — Char Filter / Tokenizer / Token Filter
_analyzeAPI로 결과 확인 (디버깅 핵심)- 빌트인 — Standard·Simple·Whitespace·Keyword·Stop·English·Language
- Standard = 영어 친화 (한국어 X)
- 한국어 = Nori 필수
- 플러그인 —
analysis-nori - Decompound Mode — none·discard·mixed (권장)
- mixed = 원본 + 분해 (검색엔진·검색·엔진)
- 사용자 정의 사전 —
user_dictionary(도메인 용어·신조어) - Custom Analyzer = 3 단계 자유 조합
- Char Filter — html_strip·mapping·pattern_replace
- Tokenizer — standard·ngram·edge_ngram·nori_tokenizer
- Token Filter — lowercase·stop·stemmer·synonym·asciifolding
- Synonym — 양방향 (
,) / 단방향 (=>) - N-Gram = 부분 검색 (인덱스 폭증·자동완성에)
- Edge N-Gram = 자동완성 표준 (앞에서만)
search_analyzer= 검색 시 다른 분석기- 자동완성 = edge_ngram(인덱싱) + standard(검색)
- 한국어 + 영어 혼합 = nori + lowercase + stop + synonym
시리즈 다른 편
- 1편 — 기본 개념·Cluster·Shard
- 2편 — Mapping·데이터 타입
- 3편 — Analyzer·Tokenizer·한국어 (현재 글)
- 4편 — Query DSL
- 5편 — Full-text Search·Relevance
- 6편 — Aggregations
- 7편 — Bulk API·Reindex
- 8편 — Spring Data Elasticsearch
- 9편 — 검색 엔진 프로젝트 설계
- 10편 — Security·인증·인가·TLS
공식 문서: Analysis / Nori Plugin 에서 더 깊이.
다음 글(4편)에서는 Query DSL — JSON 기반 검색 쿼리, term/match/bool/range 등 핵심 쿼리, filter context까지 풀어 갑니다.