서론
얼마전 서비스 기업 페이지 관련 기능이 출시되어, 그 중 기업 검색 기능을 구현했었다.
다소 복잡한 요구사항을 만족하기 위해 Elasticsearch 인덱스 설정을 이것 저것 시도해보았는데, 그 과정에서 얻게된 경험을 정리해보고자 한다.
본론
요구사항은 다음과 같다.
- 한 글자 검색 가능 (예: 삼 > 삼성전자, 성 > 삼성전자)
- 중간 검색 가능 (예: 전자 > 삼성전자)
- (추가된 사항) 검색 품질을 위해 검색어를 한 글자로 쪼개어 검색하지는 않을 것 (예: 현대 > 대한통운 X)
analyzer별 간단한 설명
- ngram: min_gram과 max_gram 사이의 모든 조합으로 토큰화
- edge_ngram: 문자열의 앞부분부터 일정 길이까지의 조합으로 토큰화
- nori: 한글 형태소 단위로 토큰화
- keyword: 입력 전체를 하나의 토큰으로 처리
- whitespace: 공백 기준으로 분리, 공백을 포함한 단어 단위로 처리
시도1
처음에는 다큐먼트와 검색어에 모두 min gram =1 인 ngram analyzer를 적용하였다.
{elasticsearch_host}/{index_name}/_analyzer 로 원하는 데이터를 바디에 포함하여 요청해보면 다음과 같이 토큰화된다.
- 네이버 > 네, 이 ,버 ,네이, 이버, 네이버
- 이병건 > 이, 병, 건, 이병, 병건, 이병건
즉, 이병건으로 검색했을때 '이' 가 겹치는 네이버가 나오게 되어 검색품질이 떨어지는 결과가 있었고, 또한 너무 많은 토큰이 생기는 문제도 있었다.
시도2
다음으로 다큐먼트와 검색어에 nori 로 토큰화한뒤 여기에 edge_ngram을 적용하는 방법을 시도해보았다. 이 설정은 다음과 같이 토큰화한다
- 삼성전자 > 삼성, 전자 > 삼, 삼성, 전, 전자
이 경우에는 성 으로 검색했을때 삼성전자가 나오지 않는 문제가 있었다.
시도3
다음으로 다큐먼트를 nori 로 토큰화한뒤 여기에 ngram을 적용하였다. 검색품질을 높이기 위해 검색어에는 keyword 를 적용했다. 토큰화는 다음과 같다
- nori + ngram: 현대오토에버 > 현대, 오토, 에버 > 현, 대, 현대, 오, 토, 오토, 에, 버, 에버
- keyword: 현대오토에버 > 현대오토에버
이 경우에는 현대오토에버 를 검색했을때 현대오토에버가 검색되지 않는 문제가 있었다.
시도4
다음으로 다큐먼트를 nori 로 토큰화한뒤 여기에 ngram을 적용하였다. 검색어에는 nori만 적용했다. 결과적으로 원하는 동작을 확인할 수 있었다.
- 삼성전자 > 삼성, 전자 > 삼, 성, 삼성, 전, 자, 전자
- 사성전자 > 사성, 전자 > 사, 성, 사성, 전, 자, 전자
- 삼성 > 삼성
이 경우 삼성을 검색하면 사성전자는 나오지 않는다. 비록 성전 으로 삼성전자를 검색할 수는 없지만, 이는 다소 불필요한 검색결과라고 생각한다. 결론적으로, 형태소 분석기인 nori를 통해 의미 단위 토큰을 유지하면서, ngram을 적용하여 부분 검색(1글자·중간 검색)을 지원함으로써 인덱스 비용을 최소화하면서도 주어진 요구사항을 만족하고, 검색품질을 유지할 수 있었다.
그 외
이외에도 주식회사를 검색에 사용하지 않고, 영 대소문자를 동일하게 처리하는 필터를 추가하여 검색 성능을 높일 수 있었다.
'배운 것들 > 그 외' 카테고리의 다른 글
| Nest.js의 인터셉터를 이용하여 인풋 필드에 권한 체크를 추가하기 (2) | 2025.12.24 |
|---|---|
| 스탬피드 캐싱 방지하기 (6) | 2025.07.24 |
| GraphQL 요청당 필드 캐싱으로 인한 이슈 해결 (1) | 2025.06.27 |
| Elasticsearch 를 활용한 프로젝트를 경험하면서 느낀 점 (2) | 2025.06.26 |
| TypeScript 프로젝트에서 외부 파일을 이용할 때 경로 처리 (3) | 2025.04.12 |