![[ElasticSearch] 애널라이저(Analyzer)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbqzyU%2FbtsOlL7BMkw%2FeFkl4yEI2DVU3v3Ndrhso1%2Fimg.png)
이 글은 인프런의 지식 공유자 박재성님의 강의를 듣고 개인적으로 정리하는 글임을 알립니다.
애널라이저(Analyzer)
이론
역인덱스(Inverted Index)로 저장하는 과정에서 문자열(text)을 토큰으로 변환시켜주는 장치를 보고 애널라이저(Analyzer)라고 부른다.
애널라이저(Analyzer)는 내부적으로 캐릭터 필터(character filter), 토크나이저(tokenizer), 토큰 필터(token filter)라는 걸 활용해 문자열을 토큰으로 변환시킨다.
standard analyzer
Elasticsearch에서 제공하는 애널라이저로는 standard, simple, whitespace 등 다양한 종류가 있다.
그 중 기본값으로 설정되어 있는 애널라이저는 standard analyzer이다.
애널라이저는 3가지(character filter, tokenizer, token filter)로 이루어져있다.
standard analyzer는 charcater filter는 설정되어 있지 않고, tokenizer는 standard로 설정되어 있고, token filter는 lowercase인 애널라이저를 뜻한다.
- tokenizer(standard) : 공백 또는 ,, ., !, ?와 같은 문장 부호를 기준으로 문자열을 자름
- token filter(lowercase) : 소문자로 변환
standard analyzer 표현 방식
Elasitcsearch에서 standard analyzer를 표현하는 방식은 크게 2가지가 있다.
1번의 방식으로 표현하든 2번의 방식으로 표현하든 똑같이 작동한다.
// 방법 1 "analyzer": "standard" // 방법 2 "char_filter": [], "tokenizer": "standard", "filter": ["lowercase"] |
캐릭터 필터(character filter)
캐릭터 필터(character filter)는 문자열을 토큰으로 자르기 전에 문자열을 다듬는 역할을 한다.
다양한 종류의 필터가 존재하며, 여러 개의 필터를 적용시킬 수 있다.
[예시]
- html_strip 필터 적용 (HTML 태그를 제거)
<h1>아이폰 15 사용 후기</h1> → 아이폰 15 사용 후기 - mapping 필터, pattern replace 필터 등이 더 있다.
토크나이저(tokenizer)
토크나이저(tokenizer)는 문자열을 토큰으로 자르는 역할을 한다.
[예시]
- standard 토크나이저 (공백, 콤마(,) 점(.), !, ?와 같은 문장 부호를 기준으로 자름)
The Brown-Foxes jumped over the roof.
→ [The, Brown, Foxes, jumped, over, the, roof] - classic 토크나이저, keyword 토크나이저, pattern 토크나이저 등이 더 있다.
토큰 필터(token filter)
토큰 필터(token filter)는 잘린 토큰을 최종적으로 다듬는 역할을 한다.
다양한 종류의 필터가 존재하며, 여러 개의 필터를 적용시킬 수 있다.
[예시]
- lowercase 필터 적용 (소문자로 변환)
[The, Brown, Foxes, jumped, over, the, roof]
→ [the, brown, foxes, jumped, over, the, roof] - stop 필터 적용 (a, the, is와 같은 특별한 의미를 가지지 않는 단어 제거)
[the, brown, foxes, jumped, over, the, roof]
→ [brown, foxes, jumped, roof] - stemmer 필터 적용 (단어의 원래 형태로 변환)
[brown, foxes, jumped, roof]
→ [brown, fox, jump, roof]
- asciifolding 필터, synonym 필터, edge_ngram 필터, kstem 필터 등이 더 있다.
실습
애널라이저가 토큰을 어떻게 분리하는지 확인
문법(Analyze API)
// 방법 1 GET /_analyze { "text": "_________", "analyzer": "standard" } // 방법 2 (standard analyer의 구성을 직접 명시) GET /_analyze { "text": "_________", "char_filter": [], "tokenizer": "standard", "filter": ["lowercase"] } |
위 2가지 방식은 완전히 똑같이 작동한다.
GET /_analyze { "text": "Apple 2025 맥북 에어 13 M4 10코어", "analyzer": "standard" } |
토큰으로 분리한 결과를 보면 standard tokenizer(공백, 콤마(,), 점(.), !, ?와 같은 문장 부호를 기준으로 문자열을 자름)와 lowercase token filter(소문자로 변환)가 적용된 채로 토큰이 생성된 것을 확인할 수 있다.
토큰 필터 - lowercase
아무 설정 없이 인덱스를 생성하면 standard analyzer가 설정된다.
기본으로 설정되는 standard analyzer가 아닌, Analyzer의 구성 요소를 직접 설정하는 Custom Analyzer를 활용해서 인덱스를 생성할 수 있다.
// 인덱스 생성 + 매핑 정의 + Custom Analyzer 적용 PUT /products { "settings": { "analysis": { "analyzer": { "products_name_analyzer": { "char_filter": [], "tokenizer": "standard", "filter": [] } } } }, "mappings": { "properties": { "name": { "type": "text", "analyzer": "products_name_analyzer" } } } } |
products 인덱스에서 name 필드에 products_name_analyzer라는 Custom Analyzer를 적용시켰다.
Custom Analyzer에는 standard tokenizer만 설정했고, lowercase token filter(소문자로 변환)은 설정하지 않았다.
POST /products/_create/1 { "name": "Apple 2025 맥북 에어 13 M4 10코어" } |
이렇게 인덱스에 데이터를 삽입하고
GET /products/_search { "query": { "match": { "name": "apple" } } } |
검색을 하면 아무것도 뜨지 않는것을 확인할 수 있다.
이는 apple이 아닌 Apple로 검색해야하는 것임을 뜻한다.
즉, 대소문자를 명확히 구분해서 검색을 해야하는 것이다.
다시 lowercase를 적용하고 "Apple 2025 맥북 에어 13 M4 10코어"라는 도큐먼트를 삽입하면
// 인덱스 생성 + 매핑 정의 + Custom Analyzer 적용 PUT /products { "settings": { "analysis": { "analyzer": { "products_name_analyzer": { "char_filter": [], "tokenizer": "standard", "filter": ["lowercase"] } } } }, "mappings": { "properties": { "name": { "type": "text", "analyzer": "products_name_analyzer" } } } } POST /products/_create/1 { "name": "Apple 2025 맥북 에어 13 M4 10코어" } |
apple 또는 Apple 모두 검색이 되는것을 확인할 수 있다.
이는 도큐먼트를 생성할 때 Analyzer가 문자열을 토큰으로 분리해 역인덱스를 생성하지만, 검색을 할 때도 Analyzer가 검색어로 입력한 문자열을 토큰으로 분리해 검색하기 때문이다.
이 때문에 Apple이라고 검색어를 입력하더라도 lowercase token filter에 의해 apple로 바뀐 채로 검색을 하게 된다. 그래서 Apple이라고 검색했는데도 불구하고 도큐먼트가 조회된 것이다.
결론적으로 Analyzer에서 lowercase token filter를 활용함으로써 대소문자에 상관없이 데이터를 검색할 수 있게 된 것이다.
캐릭터 필터 - html_strip
대부분의 게시글 서비스는 굵게, 기울임, 링크 등을 포함해서 작성할 수 있게 되어 있다.
그러려면 HTML 태그를 포함해서 그대로 DB에 저장해야 하는 경우가 많다.
스탠다드 애널라이저를 적용하여 인덱스를 생성하고, HTML태그를 포함한 도큐먼트를 저장하려면 아래와 같다.
PUT /boards { "mappings": { "properties": { "content": { "type": "text" } } } } // id를 자동 생성해서 도큐먼트 저장하기 POST /boards/_doc { "content": "<h1>Running cats, jumping quickly — over the lazy dogs!</h1>" } |
검색을 해보면 잘 되는것을 확인할 수 있다.
GET /boards/_search { "query": { "match": { "content": "running" } } } |
하지만 "h1"이라는 키워드에도 게시글이 조회됐다.
즉, HTML 태그에 대해서도 검색이 되다보니 검색 품질이 떨어지게 된다.
GET /boards/_search { "query": { "match": { "content": "h1" } } } |
토큰으로 저장될 필요 없는 HTML 태그까지도 역인덱스에 저장하다보니 디스크 공간을 낭비하게 된다.
그리고 역인덱스에 불필요한 토큰까지 저장되므로 검색 속도도 떨어지게 된다.
이러한 이유 때문에 토큰으로 저장할 때 HTML 태그는 제거한 뒤에 저장을 한다.
HTML 태그를 제거한 뒤에 토큰을 만들어 저장하기 위해 html_strip character filter를 추가해서 인덱스를 생성
// 인덱스 생성 + 매핑 정의 + Custom Analyzer 적용 PUT /boards { "settings": { "analysis": { "analyzer": { "boards_content_analyzer": { "char_filter": ["html_strip"], "tokenizer": "standard", "filter": ["lowercase"] } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "boards_content_analyzer" } } } } |
데이터 삽입
POST /boards/_doc { "content": "<h1>Running cats, jumping quickly — over the lazy dogs!</h1>" } |
이렇게 하면 running이라는 키워드로는 검색이 잘 되지만, h1이라는 키워드로는 검색이 안되는 걸 확인할 수 있다.
따라서 HTML 태그가 포함된 데이터를 검색에 사용할 때는 character filter로 html_strip 적용을 고려할 필요가 있다.
토큰 필터 - stop(불용어 제거)
영어로 작성된 게시글을 보면 검색어로 잘 사용하지 않는 a, an, the, or과 같은 불용어(= 의미없는 단어)가 많이 포함되어 있다.
역인덱스의 효율적인 저장과 활용을 위해 불용어를 제거하고 관리하는 방법은 stop 토큰 필터를 사용하는 것이다.
// 인덱스 생성 + 매핑 정의 + Custom Analyzer 적용 PUT /boards { "settings": { "analysis": { "analyzer": { "boards_content_analyzer": { "char_filter": [], "tokenizer": "standard", "filter": ["lowercase", "stop"] } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "boards_content_analyzer" } } } } // 잘 생성됐는 지 확인 GET /boards |
데이터 삽입
POST /boards/_doc { "content": "The cat and the dog are friends." } |
Analyze API를 사용해서 분석
GET /boards/_analyze { "field": "content", "text": "The cat and the dog are friends." } |
토큰으로 저장될 필요 없는 불용어(the, and, are)를 제거한 뒤에 역인덱스에 저장했다.
그래서 the, and, are로 검색했을 때 아무 데이터도 조회되지 않는다.
- 불용어(a, the, are, is 등)를 활용해 검색할 일이 없는 데이터라면, 역인덱스의 효율성을 위해 token filter로 `stop`을 활용하도록 하자.
- 만약 노래 제목(ex. 비틀즈의 Let It Be)과 같이 불용어를 포함해서 검색하는 게 중요할 때는 token filter로 `stop`을 적용시키지 않아야 한다.
토큰 필터 - stemmer(단어 기본형태 저장)
영어 단어를 보면 play, playing, played, player 등 다양한 형태로 사용된다.
검색을 할 때 단어의 형태에 상관없이 검색이 가능하다면 훨씬 편하게 원하는 데이터를 조회할 수 있을 것이다.
stemmer 필터는 단어를 어간(기본형태) 으로 변환하여 저장하거나 검색에 활용할 수 있게 만들어주는 텍스트 정규화 필터이다.
// 인덱스 생성 + 매핑 정의 + Custom Analyzer 적용 PUT /boards { "settings": { "analysis": { "analyzer": { "boards_content_analyzer": { "char_filter": [], "tokenizer": "standard", "filter": ["lowercase", "stemmer"] } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "boards_content_analyzer" } } } } |
데이터 삽입
POST /boards/_doc { "content": "Running cats, jumping!" } |
Analyze API로 확인
GET /boards/_analyze { "field": "content", "text": "Running cats, jumping!" } |
응답값을 보면 영단어의 기본 형태로 저장되어 있는 걸 확인할 수 있다.
여기서 jumped라고 검색을 하더라도 데이터가 잘 조회된다.
이유는 도큐먼트를 생성할 때 Analyzer가 문자열을 토큰으로 변환해 역인덱스를 생성하지만, 검색을 할 때도 마찬가지로 Analyzer가 검색어로 입력한 문자열을 토큰으로 변환해 검색하기 때문이다.
이 때문에 jumped이라고 검색어를 입력하더라도 stemmer에 의해 jump로 바뀐 채로 검색을 하게 된다.
그래서 jumped라고 검색했는데도 불구하고 도큐먼트가 조회된다.
토큰 필터 - synonym(동의어 검색)
synonym 필터는 색인(indexing) 혹은 검색(query) 과정에서 설정된 단어를 다른 단어로 확장 또는 대체한다.
예를 들어 다음과 같은 동의어 설정이 있다면
laptop, notebook |
- 사용자가 notebook을 검색하더라도 laptop이 포함된 문서를 검색할 수 있다.
- 또는 laptop이 색인된 도큐먼트가 notebook이라는 검색어로도 매칭될 수 있다.
// 인덱스 생성 + 매핑 정의 + Custom Analyzer 적용 PUT /products { "settings": { "analysis": { "filter": { "products_synonym_filter": { "type": "synonym", "synonyms": [ "notebook, 노트북, 랩탑, 휴대용 컴퓨터, laptop", "samsung, 삼성" ] } }, "analyzer": { "products_name_analyzer": { "char_filter": [], "tokenizer": "standard", "filter": [ "lowercase", "products_synonym_filter" ] } } } }, "mappings": { "properties": { "name": { "type": "text", "analyzer": "products_name_analyzer" } } } } |
데이터 삽입
POST /products/_doc { "name": "Samsung Notebook" } |
Analyze API를 활용해 분석
GET /products/_analyze { "field": "name", "text": "Samsung Notebook" } |
분리된 토큰을 살펴보면 동의어까지 포함해서 토큰이 등록된 걸 확인할 수 있다.
이 때문에 동의어로 검색을 하더라도 원하는 데이터가 잘 조회된다.
'Back-End > ElasticSearch' 카테고리의 다른 글
[ElasticSearch] 한글 검색 최적화(Nori Analyzer) (0) | 2025.06.01 |
---|---|
[ElasticSearch] 데이터 타입(data type)과 매핑(mapping) (0) | 2025.05.31 |
[ElasticSearch] 역인덱스(Inverted Index) (0) | 2025.05.31 |
[ElasticSearch] 인덱스, 도큐먼트, 매핑, 필드 (0) | 2025.05.30 |
[ElasticSearch] 작동 방식 및 Kibana (0) | 2025.05.30 |