LY Corporation Tech Blog

LY Corporation과 LY Corporation Group(LINE Plus, LINE Taiwan and LINE Vietnam)의 기술과 개발 문화를 알립니다.

일본어 상품 검색 정확도 높이기: Elasticsearch + Kuromoji에서 OpenSearch + Sudachi로

들어가며

안녕하세요. LINE Plus에서 통합 커머스 개발을 맡고 있는 김진성입니다.

2025년 초, 통합 커머스 프로젝트가 시작되었습니다.

서비스가 안정되어 가던 중, 일본 담당자로부터 다양한 불편 사항이 보고되기 시작했습니다. 불편 사항은 관리 툴의 사용성과 개선점 등 여러 항목에 걸쳐 있었는데, 그중 몇 가지 항목에서 공통적인 문제가 언급되었습니다. '상품 관리 도구에서 특정 단어로 상품명을 검색하면 결과가 나오지 않는다'는 내용이었습니다.

사실 이 문제는 새로운 것이 아니었습니다. 통합 커머스 프로젝트 이전부터 Elasticsearch에서 사용하는 일본어 형태소 분석기인 Kuromoji 환경에서 검색이 잘 되지 않는 단어가 발견될 때마다 사용자 사전에 해당 단어를 추가하는 방식으로 대응해 왔습니다. 사용자 사전에 등록된 단어는 올바르게 분석되기 때문에 단기적으로는 문제가 해결됩니다. 그러나 프로젝트 시작 이후 보고되는 검색어의 범위가 점점 넓어지면서, 이 방식의 한계가 분명하게 드러나기 시작했습니다. 신조어, 복합어, 모델명 변형까지 모두 수동으로 사전에 등록하는 것은 근본적인 해결책이 될 수 없었습니다.

문제의 원인을 분석해 보니, 크게 두 가지 요인이 복합적으로 작용하고 있었습니다.

첫 번째는 운영 방식의 문제였습니다. 기존 Kuromoji analyzer는 공식 문서의 기본 설정(default)을 그대로 사용하고 있었고, 상품명 검색 특성에 맞춘 별도의 튜닝 없이 운영되고 있었습니다. Kuromoji는 형태소 분석을 기반으로 복합어를 분해하는 것이 기본 동작인데, 이러한 특성이 상품명 검색에서는 오히려 불리하게 작용하는 경우가 있었습니다. 하나의 의미 단위로 취급되어야 할 상품명이 여러 토큰으로 분해되면서, 사용자의 검색 의도와 다른 결과가 매칭되거나 검색 정확도가 떨어지는 문제가 발생했습니다.

두 번째는 사전의 한계였습니다. Kuromoji에서 널리 사용되는 IPADIC 사전은 2.7.0-20070801 버전을 기준으로 하고 있어 비교적 오래된 데이터를 기반으로 하고 있습니다. 이로 인해 최신 IT 용어, 신제품명, 서비스명, 고유명사 등에 대한 커버리지가 제한적이며, 사전에 포함되지 않은 단어는 예상과 다르게 분해되거나 하나의 의미 단위로 인식되지 못하는 문제가 발생했습니다. 사용자 사전을 통해 이를 보완할 수는 있었지만, 변화 속도가 빠른 상품 도메인에서는 이러한 방식이 지속 가능한 해결책이 되기 어려웠습니다.

결국 이 문제는 단순히 특정 단어를 추가하는 수준을 넘어, 분석 방식과 사전 구성 자체를 검색 환경에 맞게 재설계해야 하는 문제였습니다.

근본적인 해결책을 찾던 중, 일본 기술 커뮤니티에서 Sudachi를 알게 되었습니다. Sudachi는 Works Applications이 개발한 일본어 형태소 분석기로, 복합어 처리와 신조어 대응에서 Kuromoji와는 다른 접근을 취하고 있었습니다. 검증 테스트를 거쳐 실제로 우리 상품 데이터에서 의미 있는 개선이 확인되었고, 이를 적용하기로 결정했습니다.

이 글에서는 그 과정에서 우리가 무엇을 고민했고, 어떤 결정을 내렸는지 공유합니다.

Migration from Elasticsearch with Kuromoji to OpenSearch with Sudachi

기존 구조의 한계

실제 상품 데이터에서 드러난 세 가지 문제

Kuromoji는 일본어 형태소 분석기로서 범용적으로 잘 동작합니다. 그러나 실제 상품 데이터에서는 기본 설정 그대로의 분석 방식과 오래된 사전이 맞물리면서, 반복적으로 문제가 발생했습니다.

참고로 Kuromoji는 Elasticsearch 8.x 이상에서 일부 개선된 기능을 활용할 수 있지만, 사내 검색 플랫폼은 Elasticsearch 7.10.2까지만 지원하고 이후 버전 업그레이드 없이 일몰 방향으로 가고 있어 이러한 선택지는 고려 대상이 아니었습니다.

첫째, 복합어를 의미 단위보다 세분화하여 분해하는 문제

General example comparing Kuromoji and Sudachi tokenization for smartphone case

실제로 현재 설정에서 'スマートフォンケース(스마트폰 케이스)'를 analyze 해보면 Kuromoji는 'スマートフォンケース' / 'スマート' / 'フォン' / 'ケース' 4개의 토큰을 생성합니다. 형태소 단위로 분해되는 것 자체는 Kuromoji의 정상적인 동작입니다. 그러나 상품명처럼 하나의 의미 단위로 검색되는 문자열에서는 이 분해가 검색 품질을 저하시킵니다. 'スマートフォン'으로 검색할 경우 실제로는 'スマート'와 'フォン'으로 분해된 토큰 기반 매칭이 이루어지면서, 의도와 다른 문서가 함께 검색되거나 결과의 정확도가 떨어지는 문제가 발생합니다.

반면 Sudachi는 동일한 문자열을 'スマートフォン' / 'ケース' 2개 토큰으로 분해합니다. Sudachi는 복합어를 하나의 의미 단위로 유지하도록 설계되어 있어, 상품명과 같은 도메인에서도 보다 직관적인 검색 결과를 제공합니다. 이 차이는 단순한 토큰 수의 문제가 아니라, 검색에서 의미 단위를 어떻게 해석할 것인가에 대한 설계 차이에서 비롯됩니다.

둘째, 신조어와 고유명사 대응의 취약성

Kuromoji가 기본으로 사용하는 사전은 'IPADIC'입니다. 범용성과 안정성이 높은 대신, 마지막 릴리스가 2007년 8월(버전 2.7.0-20070801) 이후 업데이트가 중단된 상태여서 최신 단어에 대한 대응에는 한계가 있습니다. 이를 보완하기 위해 일부 서비스에서는 'mecab-ipadic-neologd'와 같이 지속적으로 업데이트되는 사전을 활용하기도 합니다.

그러나 Kuromoji는 MeCab 기반 사전을 직접 연동하는 구조가 아니며, 우리가 운영 중인 Elasticsearch 7.10.2 환경에서는 이러한 최신 사전을 적용하는 데 제약이 있었습니다.

이 문제는 실제 서비스에서도 명확하게 드러났습니다. '新潟県産 こしいぶき(니가타현산 코시이부키)'로 검색했을 때 상품이 전혀 나오지 않는다는 문의가 들어왔습니다. 'こしいぶき'는 니가타현에서 2001년에 등록된 쌀 품종으로, IPADIC 마지막 릴리스(2007년)보다 앞서 등록된 고유명사임에도 사전에 수록되지 않아 의도와 다르게 분석되거나 매칭이 되지 않는 문제가 발생했습니다.

Comparing Kuromoji and Sudachi tokenization for Niigata grown Koshiibuki rice

Kuromoji는 '新潟' / '県' / '産' / 'こす' / 'いぶき'로 분리하여 'こしいぶき'를 전혀 인식하지 못합니다.

Sudachi는 '新潟県' / '産' / 'こしいぶき' 3개 토큰으로 지역명과 품종명을 모두 올바르게 분석합니다. 사용자 사전에 등록하면 그 순간은 해결되지만, 이런 식으로 누락되는 품종명·지명·신조어가 계속 쌓이는 구조 자체가 문제였습니다.

셋째, 영문/숫자 혼합 및 일본어 특수 구분자 처리의 취약성

두 가지 문제가 겹쳐 있습니다. 첫 번째는 모델 번호 형태 상품명입니다. 실제 상품 데이터에는 'AW-10DP3', 'LDA12L-G-10T62P'처럼 영문·숫자·기호가 혼합된 모델 번호가 다수 포함됩니다. 형태소 분석기는 이런 문자열을 의미 단위가 아닌 형태소 단위로 분해하는 특성이 있으며, 이는 Kuromoji뿐 아니라 Sudachi에서도 동일하게 나타납니다.

형태소 분석기(Kuromoji / Sudachi 공통) 처리 결과:
"AW-10DP3"        → AW / 10 / DP / 3
"LDA12L-G-10T62P"  → LDA / 12 / L / G / 10 / T / 62 / P

이처럼 모델 번호가 여러 조각으로 분해되면서, 전체 문자열 기준의 정확한 매칭이 어려워지고, 부분 토큰 기반 매칭으로 인해 의도와 다른 문서가 함께 검색되거나 검색 정확도가 저하되는 문제가 발생합니다.

또한, 머천트마다 동일한 모델을 'AW-10DP3', 'AW_10DP3', 'aw10dp3'처럼 서로 다른 구분자로 등록하는 경우도 많습니다. 이러한 표기 차이는 형태소 분해와 결합되면서 더욱 복잡한 불일치를 만들어냅니다.

두 번째는 일본어 상품명에서 자주 쓰이는 특수 구분자 문제입니다. 일본어 상품명에는 나카구로(・)가 구분자로 자주 등장하는데, 전각(・ U+30FB)과 반각(・ U+FF65) 두 가지 코드포인트가 존재합니다. 입력자가 동일한 문자로 인식하지만, 시스템에서는 서로 다른 문자로 처리됩니다. 이 외에도 전각 스페이스( )나 물결표(〜) 등도 혼용되면서 동일한 상품명이 서로 다른 문자열로 인식되고, 그 결과 토큰화 결과가 달라지거나 검색 불일치가 발생합니다.

일본어 구분자 혼용 사례:
"スマートフォン・ケース"  (나카구로 전각 U+30FB)
"スマートフォン・ケース"  (나카구로 반각 U+FF65)
"スマートフォン ケース"  (전각 스페이스)
"スマートフォン〜ケース"  (물결표)

→ 구분자 종류에 따라 토큰화 결과가 달라지거나 검색 불일치 발생

이러한 문제는 분석기를 Sudachi로 교체하더라도 해결되지 않습니다. tokenizer 선택과 정규화를 별도로 다뤄야 하며, 이것이 뒤에서 설명할 Multi-field 전략의 핵심 동기가 됩니다.

왜 OpenSearch + Sudachi인가

전환 시점이 맞았다

기술적 필요만으로 검색 엔진을 교체하기는 쉽지 않습니다. 이번에는 사내 인프라 환경이 전환의 조건을 자연스럽게 만들어줬습니다.

사내 검색 플랫폼(Verda, 향후 Flava)은 Elasticsearch 7.10.2에 대한 추가 버전 업그레이드 없이 일몰 방향으로 가고 있습니다. 대신 OpenSearch 2.4.1과 2.15.0 두 버전을 지원합니다.

여기서 Sudachi 플러그인의 버전 지원 조건이 결정적이었습니다. 도입 검토 시점 기준으로 Sudachi는 OpenSearch 2.6부터 2.14까지 CI 통합 테스트가 포함된 공식 지원을 제공하고 있었습니다. 사내에서 선택 가능한 두 버전과 대조하면 결론은 명확했습니다.

사내 지원 버전Sudachi 지원 여부도입 검토 시점 (25년 4월경)
OpenSearch 2.4.1X2.6 미만, 지원 범위 밖
OpenSearch 2.15.0O2.14 직후 버전, 실사용 문제 없음

2.15.0은 당시 공식 CI 검증 범위(2.6~2.14)에 직접 포함되지는 않았지만,동일 메이저 라인의 연속 버전이며 실제 검증 과정에서도 호환성 문제는 발견되지 않았습니다. 또한 현재 Sudachi 공식 문서 기준으로는 지원 범위가 2.6~2.19까지 확장되어 있어, 2.15.0은 공식 지원 범위 안에 완전히 포함됩니다.

결과적으로 Elasticsearch 일몰에 따른 OpenSearch 전환 필요성과 Sudachi 2.6+ 지원이라는 두 조건이 맞물리면서 2.15.0 선택이 자연스럽게 결정되었습니다.

Sudachi를 선택한 이유

Sudachi는 Works Applications이 개발한 일본어 형태소 분석기로, Kuromoji 대비 다음과 같은 차별점을 가지고 있습니다.

항목KuromojiSudachi
복합어 인식 중심형태소 단위 분해의미 단위 유지 가능
신조어 대응IPADIC 기반 (업데이트 제한)최신 사전 기반으로 상대적으로 유리

Sudachi는 공식 플러그인 설정에서 split_mode 파라미터를 통해 분석 단위를 제어할 수 있습니다. 기본값은 C 모드이며, 세 가지 모드가 제공됩니다.

모드설명토큰화 결과
C (기본)고유명사·복합어를 하나의 토큰으로 유지選挙管理委員会(선거관리위원회)
B중간 단위로 분리選挙 / 管理 / 委員会
AUniDic 최소 단위(형태소)로 분리選挙 / 管理 / 委員 / 会

기본값인 C 모드는 복합어와 고유명사를 하나의 토큰으로 유지하므로, 상품명처럼 의미 단위 보존이 중요한 경우에 적합합니다. 또한 sudachi_split 토큰 필터를 함께 사용하면, 복합어를 유지하면서도 더 세분화된 토큰을 추가로 생성하여 재현율(recall)을 높일 수 있습니다.

또한 Sudachi는 최신 사전을 기반으로 신조어와 고유명사에 대한 인식 정확도가 높기 때문에, OpenSearch의 synonym 필터와 함께 사용할 때 그 효과가 더욱 잘 나타납니다. IPADIC 기반 환경에서는 사전에 존재하지 않는 단어가 예상과 다르게 분해되면서 동의어 확장이 제대로 동작하지 않는 경우가 많았지만, Sudachi에서는 이러한 단어들이 보다 안정적으로 하나의 토큰으로 인식되므로, '携帯(휴대전화)'와 'スマホ(스마트폰)'처럼 표현이 다른 단어를 동일한 상품으로 연결하는 확장이 자연스럽게 이루어집니다.

Sudachi 프로젝트에서는 사전 기반의 동의어 데이터셋도 제공하고 있어, 이를 OpenSearch의 synonym 필터 설정에 활용함으로써 동의어 확장 구성을 보다 쉽게 할 수 있습니다.

Works Applications는 Sudachi 플러그인을 OpenSearch 호환 버전으로 독립적으로 유지하고 있으며(os-2.6-plus 브랜치), 이를 통해 검색 엔진 전환과 analyzer 교체를 동시에 진행할 수 있었습니다.

핵심 설계 : Multi-field 전략

우리가 운영하는 인덱스는 두 종류입니다. 상품 인덱스는 수많은 머천트가 직접 등록한 상품 정보를 수집해 여러 관련 서비스에 제공하는 인덱스로, 현재 약 8억 건 규모입니다. 카탈로그 인덱스는 머천트 상품을 동일 상품 단위로 묶어 자체적으로 정규화한 카탈로그 데이터를 색인하며, 약 1억 건 규모입니다.

두 인덱스 모두 sudachi_analyzer를 기본 analyzer로 적용합니다. 이를 통해 복합어 인식, 고유명사 처리, 지속적인 사전 업데이트 등 일본어 분석의 기본 품질을 높이고, 상품명에 대한 정확하고 확장된 검색이 가능해집니다. 이것이 이번 전환의 공통 전제입니다.

그러나 Sudachi로 analyzer를 바꾸는 것만으로는 모든 문제를 해결할 수 없었습니다. 두 인덱스는 성격이 다르기 때문에 각 인덱스에 필요한 추가 요구사항도 달랐습니다. 상품 인덱스에서는 Sudachi에 의한 일본어 분석 개선과 함께, 모델 번호가 포함된 형태의 상품명 검색 가능성을 높이는 것이 추가 과제입니다. 동일한 상품이라도 판매하는 머천트마다 상품명을 표기하는 방식에 차이가 있을 수 있는데, 예를 들어 'AW-10DP3', 'LDA12L-G-10T62P' 같은 모델 번호를 포함한 상품명의 경우 어떤 머천트는 'AW-10DP3'로, 다른 머천트는 'AW_10DP3'나 'aw10dp3'으로 등록하는 경우가 있었습니다. 관리자가 어떤 표기로 검색하든 해당 상품을 찾을 수 있어야 합니다. 카탈로그 인덱스에서는 일본어 분석 개선 외에, 자동완성 기능을 제공해 보려 하고 있습니다. 카탈로그명은 운영 과정에서 정제된 데이터이기 때문에, 실제 상품에 대한 카탈로그 매칭 작업 시 카탈로그명 앞 글자 몇 자만 입력해도 후보 카탈로그가 즉시 표시된다면 작업 편의성에 도움이 될 것으로 판단하여 해당 기능 구현에 집중했습니다.

Sudachi는 이러한 영문/숫자 혼합 문자열에 대해서는 분석 정확도가 낮아지는 한계가 있어, 형태소 분석 단계에서 해결할 수 없는 영역이었습니다. 이를 해결하기 위해 하나의 필드를 여러 용도로 색인하는 Multi-field 전략을 도입했습니다. 두 인덱스는 용도가 다른 만큼 서브 필드 구성도 각각의 목적에 맞게 달리했습니다.

상품 인덱스는 머천트마다 구분자 표기가 다른 모델 번호('AW-10DP3', 'AW_10DP3', 'aw10dp3')를 동일 토큰으로 수렴시키는 productName.compact 서브 필드(이하 compact)를 둡니다.

Product index productName multi field structure

카탈로그 인덱스는 관리자의 카탈로그 매칭 작업 편의성을 위해 빠른 자동완성을 제공하는 productName.edge 서브 필드(이하 edge)를 둡니다.

Catalog index productName multi field structure

두 인덱스 모두 keyword 서브 필드에는 커스텀 normalizer인 compact_normalizer를 적용합니다. 정규화된 전체 문자열을 단일 토큰으로 색인해 완전 일치(EXACT) 검색에 사용합니다. 상품 인덱스의 compact 서브 필드는 커스텀 analyzer인 compact_product_name_analyzer를 사용하며, 동일한 정규화 후 whitespace tokenizer로 공백 기준 분리해 색인합니다. 카탈로그 인덱스의 edge 서브 필드는 접두사 토큰을 생성해 자동완성을 주목적으로 하며, 카탈로그명이 정제된 데이터라는 특성을 활용해 일반 검색(PHRASE/MATCH 모드)의 보조 쿼리로도 활용합니다. compact_normalizer, compact_product_name_analyzer는 모두 인덱스 settings에서 직접 정의한 커스텀 설정입니다.

compact_product_name_analyzer: 모델 번호 형태 문자열의 검색 노이즈를 해결한다

compact_product_name_analyzer가 해결하는 핵심 문제는 'AW-10DP3', 'LDA12L-G-10T62P' 같은 모델 번호 형태의 상품명을 검색할 때 발생하는 토큰 분리 노이즈입니다.

sudachi_analyzer는 이미 icu_normalizer로 전각·반각 변환을 처리한 뒤 Sudachi tokenizer에 입력을 넘깁니다. 문제는 그 다음 단계입니다. Sudachi tokenizer는 'AW-10DP3' 같은 영숫자·기호 혼합 문자열을 형태소 단위로 쪼개면서 'AW', '10', 'DP', '3'처럼 의미 단위가 깨진 토큰을 생성합니다. 머천트가 등록한 모델 번호가 이렇게 분리되면, 'AW-10DP3'로 검색해도 해당 상품이 매칭되지 않거나 전혀 다른 상품이 섞여 들어오는 노이즈가 발생합니다.

compact_product_name_analyzer는 이 문제를 tokenizer 선택으로 해결합니다. 인덱스 settings에 직접 정의한 커스텀 analyzer로, icu_normalizer → model_delim_to_space(커스텀 char_filter: -, _, / → 공백 변환) → punct_to_space(커스텀 char_filter: 나카구로·물결표 등 → 공백 변환) → collapse_spaces(커스텀 char_filter: 연속 공백 → 단일 공백)를 거친 뒤 whitespace tokenizer로 분리합니다. 형태소 분석 없이 공백 기준으로만 토큰을 자르기 때문에 모델 번호가 의미 단위로 보존됩니다.

Compact_product_name_analyzer resolving token noise in model number strings

색인과 검색 쿼리 양쪽에 동일한 파이프라인이 적용되므로, 'AW-10DP3'로 검색하든 'aw10dp3'으로 검색하든 동일한 토큰으로 수렴해 같은 상품이 검색됩니다.

다만 compact 필드를 모든 쿼리에 무조건 추가하면 역효과가 생깁니다. 'スマホ'처럼 영숫자 없이 짧은 일본어로 검색할 때 compact 필드까지 함께 검색하면, 의도치 않은 상품이 포함될 수 있습니다. 이를 방지하기 위해 영숫자가 포함된 일정 길이 이상의 검색어에만 compact 필드 검색을 활성화했습니다.

// 길이 > 3 글자 이고, 영숫자가 포함된 경우에만 compact 필드 추가 검색
val isLongEnough = text.length > PRODUCT_NAME_MIN_LENGTH_FOR_COMPACT
val hasAsciiAlphaNum = text.any { c ->
        c in 'a'..'z' || c in 'A'..'Z' || c in '0'..'9'
}

if (isLongEnough && hasAsciiAlphaNum) {
        shouldQueries.add(compactMatchQuery(text))
}

Edge N-gram: 입력 중에 후보를 보여준다

카탈로그 매칭 작업에서 관리자는 상품명 전체를 기억하지 못하는 경우가 많습니다. 'スマートフォン(스마트폰)'이 들어간 카탈로그를 찾고 싶을 때 'スマ' 두 글자만 입력해도 후보가 나타나야 작업이 빠릅니다. 일반 검색(match, match_phrase)은 입력이 완성된 단어 단위에서 동작하기 때문에 이 요구사항을 충족할 수 없습니다.

Edge N-gram은 문자열의 앞에서부터 점진적으로 토큰을 생성해 색인합니다. 덕분에 부분 입력만으로도 매칭이 성립합니다.

색인 대상: "スマートフォン"

[생성된 토큰 (min=2, max=10)]
"スマ", "スマー", "スマート", "スマートフ", "スマートフォ", "スマートフォン"

[검색 동작]
"スマ"  입력 → "スマートフォン" 매칭
"スマー" 입력 → "スマートフォン" 매칭

자동완성에서 관련성 점수는 의미가 없습니다. 접두사가 일치하는 카탈로그를 빠르게 반환하는 것이 전부입니다. constant_score를 적용해 스코어링 계산을 건너뛰면 응답 속도가 눈에 띄게 향상됩니다.

{
    "constant_score": {
        "filter": {
            "match": {
                "name.edge": {
                    "query": "スマ",
                    "operator": "AND"
                }
            }
        },
        "boost": 1.0
    }
}

이 구성으로 관리자가 'スマ'를 입력하는 순간 'スマートフォン'이 들어간 카탈로그 후보가 즉시 표시되고, 추가 입력과 함께 후보가 좁혀집니다. 카탈로그 매칭 작업의 반복적인 검색 동작에서 체감 속도 차이가 직접 발생하는 부분입니다.

한 가지 더 활용할 수 있는 지점이 있습니다. 카탈로그명은 운영 과정에서 정제된 데이터이기 때문에, edge 필드에 색인된 접두사 토큰의 신뢰도가 높습니다. 이 특성을 이용하면 자동완성 전용 외에도, 일반 검색(PHRASE/MATCH 모드)에서 보조 쿼리로 함께 사용해 검색 정확도를 높일 수 있습니다. 비정형 상품명으로 가득한 상품 인덱스에서는 기대하기 어려운 활용 방식입니다.

Multi-field 설계 시 고려: 서브 필드의 비용

Multi-field 전략은 하나의 필드를 여러 용도로 색인하는 만큼, 서브 필드가 늘어날수록 인덱스 크기와 색인 처리 비용이 함께 증가합니다. 특히 Edge N-gram처럼 토큰을 대량으로 생성하는 필드는 데이터 규모에 따라 물리적 디스크 공간과 색인 성능에 직접적인 영향을 미칩니다. 그래서 edge 서브 필드를 어디에 둘 것인가는 단순한 기능 추가 여부가 아니라 트레이드오프를 따져야 하는 결정이었습니다.

자동완성을 위한 edge 서브 필드는 현재 카탈로그 인덱스의 productName 필드에만 적용되어 있습니다. 약 8억 건 규모의 상품 인덱스에는 edge 필드를 두지 않았습니다.

이 결정의 배경은 앞서 설명한 Edge N-gram의 특성에 있습니다. 단어 하나에서 최소~최대 길이만큼 접두사 토큰이 모두 생성되기 때문에, 문서 수가 많을수록 토큰 수 증가는 인덱스 크기와 색인 처리 비용에 직접 영향을 줍니다. 토큰이 많아질수록 Lucene 세그먼트에 기록되는 데이터가 늘고, 세그먼트 merge 빈도와 처리 시간도 함께 증가합니다.

상품 인덱스에서 이 문제는 더 심각하게 나타납니다. 카탈로그명은 운영 과정에서 정제된 데이터지만, 상품명은 수많은 머천트가 자유롭게 입력한 비정형 데이터입니다. 동일한 상품이라도 표기 방식이 제각각이고, 특수문자·모델 번호·긴 수식어가 혼재하는 경우가 많습니다. 이런 상품명에 Edge N-gram을 적용하면 단어 길이와 문자 조합에 따라 토큰 수가 예측하기 어렵게 늘어납니다. 8억 건 규모에서 필드 하나에 Edge N-gram을 잘못 적용하면 인덱스 크기가 즉각 수배로 불어나고, 색인 처리 시간이 늘어나 Kafka 메시지 처리 지연으로 이어질 수 있습니다.

반면 카탈로그 인덱스는 규모가 약 1억 건으로 상대적으로 작고, 카탈로그명은 정제된 데이터여서 토큰 수 증가 폭을 예측할 수 있습니다. 또한 카탈로그 매칭 작업에서 앞 글자 몇 자만 입력해도 후보가 즉시 좁혀진다면 작업 편의성을 높일 수 있다고 기대했습니다.

결국 edge 서브 필드 적용 여부는 '이 필드에서 자동완성이 정말 필요한가'와 '이 데이터 규모와 특성에서 감당 가능한가'를 함께 고려한 결과입니다. 기능을 추가할 수 있다는 것과 추가해야 한다는 것은 다른 문제입니다.

Multi-field 설계를 검색 쿼리로 연결하기

Multi-field 전략으로 각 서브 필드에 용도를 부여했다면, 쿼리도 그 용도에 맞게 제어해야 합니다. 어떤 필드를 언제 사용할지 명시하지 않으면, 잘 설계된 색인 구조가 실제 검색 품질로 이어지지 않습니다.

이를 위해 인덱스별로 전용 검색 모드를 설계했습니다. 관리자가 정확한 상품명을 알고 있을 때, 일부 키워드로 넓게 검색할 때, 자동완성이 필요할 때처럼 각 상황에 맞는 필드와 쿼리를 명시적으로 선택할 수 있도록 했습니다.

상품 인덱스 검색 모드

상품 인덱스의 기본 모드는 PARTIAL입니다. 관리자가 상품명 전체를 정확히 기억하지 못하는 경우가 많고, 머천트마다 표기 방식이 다를 수 있기 때문에 단어 포함 여부 기준의 유연한 검색이 기본이 됩니다. 정확한 상품명 순서까지 맞춰야 할 때는 PHRASE_EXACT_LIKE를, 정규화된 문자열 기준으로 완전히 동일한 상품만 찾아야 할 때는 EXACT를 선택합니다.

모드쿼리사용 시나리오
PARTIAL (기본)match (AND)단어 포함 여부 기준 검색
PHRASE_EXACT_LIKEmatch_phrase단어 순서까지 일치하는 검색
EXACTterm (keyword 필드)정규화 후 완전히 동일한 이름만 검색

compact 필드는 독립된 검색 모드가 아니라 PARTIAL / PHRASE_EXACT_LIKE 쿼리에 조건부로 추가되는 보완 검색입니다. 모델 번호 검색을 위해 설계된 필드인 만큼, 검색어 길이가 3자 초과이고 영숫자가 포함된 경우에만 활성화합니다. 짧은 일본어 검색어에도 compact 필드를 함께 조회하면 의도하지 않은 상품이 섞여 들어오는 노이즈가 발생하기 때문입니다. 각 서브 필드를 그 용도대로만 쓰는 것이 Multi-field 설계의 실질적인 완성입니다.

{
    "bool": {
        "should": [
            {
                "match": {
                    "productName": { "query": "AW-10DP3", "operator": "AND" }
                }
            },
            {
                "match": {
                    "productName.compact": { "query": "AW-10DP3", "operator": "AND" }
                }
            }
        ],
        "minimum_should_match": 1
    }
}

카탈로그 인덱스 검색 모드

모드쿼리사용 시나리오
PHRASE (기본)match_phrase + edge 보조일반 카탈로그 검색, 정확도 우선
MATCHmatch (AND) + edge 보조단어 순서가 달라도 검색 필요 시
EDGEconstant_score + edge자동완성 전용
EXACTterm완전히 동일한 이름만 검색

기본 모드인 PHRASE는 match_phrase로 구문 일치를 우선하되, edge 필드를 보조 쿼리로 함께 사용합니다. 앞서 설명했듯이 카탈로그명은 정제된 데이터이기 때문에 edge 필드의 접두사 토큰 신뢰도가 높습니다. 이 특성을 활용해 완성된 검색어뿐 아니라 입력 중인 키워드로도 카탈로그 후보가 매칭될 수 있도록 edge 필드를 보조로 추가했습니다. MATCH 모드도 같은 이유로 edge 필드를 보조로 활용합니다.

edge 필드를 단독으로 사용하는 모드는 EDGE입니다. constant_score를 적용해 스코어링 계산 없이 접두사 매칭만 수행하며, 자동완성 전용으로 설계되었습니다. PHRASE/MATCH에서의 보조 역할과 달리, EDGE 모드에서는 이 필드만으로 검색 결과를 결정합니다.

아래는 PHRASE 모드의 쿼리 구조입니다. match_phraseedge 보조 쿼리가 should로 조합되어 있습니다.

{
    "bool": {
        "should": [
            {
                "match_phrase": {
                    "name": { "query": "スマートフォンケース", "slop": 0 }
                }
            },
            {
                "match": {
                    "name.edge": { "query": "スマートフォンケース", "operator": "AND" }
                }
            }
        ],
        "minimum_should_match": 1
    }
}

마치며

이번 전환의 출발점은 단순한 기술 교체가 아니었습니다. 일본어 상품 검색이 실제로 잘 동작하게 만드는 것이 목표였고, 그 목표를 향해 각 결정을 내렸습니다.

Sudachi로 analyzer를 바꾸는 것은 첫 번째 단계였습니다. IPADIC 사전이 오래된 탓에 인식하지 못하던 고유명사와 복합어를 올바르게 분석할 수 있게 되었고, 지속적으로 업데이트되는 사전 덕분에 사용자 사전에 단어를 하나씩 추가하던 방식에서 벗어날 수 있었습니다.

그러나 일본어 분석만으로는 충분하지 않았습니다. 실제 상품 데이터에는 머천트마다 다르게 표기된 모델명, 나카구로(・)처럼 전각(U+30FB)과 반각(U+FF65) 두 코드포인트가 존재해 혼재하는 구분자 문제까지 뒤섞여 있습니다. 이 문제들은 형태소 분석 바깥의 영역이었습니다. 'AW-10DP3', 'AW_10DP3', 'aw10dp3'처럼 구분자만 다른 모델명이 형태소 분석기를 거치면 의미 없는 조각으로 쪼개져 검색이 되지 않는 문제를, 인덱스 settings에 직접 정의한 커스텀 analyzer인 compact_product_name_analyzer로 별도로 대응했습니다. 하이픈·언더스코어를 공백으로 치환한 뒤 whitespace tokenizer로 분리하는 방식으로, 어떤 구분자로 등록된 모델명이든 같은 토큰으로 수렴해 동일 상품을 찾을 수 있습니다.

두 인덱스의 서브 필드 구성이 다른 것도 같은 맥락입니다. 8억 건 규모의 상품 인덱스에서는 모델명 표기 방식이 제각각인 현실을 인덱스 레벨에서 흡수하는 compact 서브 필드가 핵심이었고, 1억 건 규모의 카탈로그 인덱스에서는 관리자가 몇 글자만 입력해도 후보가 즉시 좁혀지는 자동완성 경험을 기대하며 edge 서브 필드를 도입했습니다. 왜 이 필드가 필요한지, 인덱스 용도와 데이터 규모를 고려하는 것이 설계의 핵심 원칙이었습니다.

현재는 실제 유입되는 상품을 색인하며 검색 서비스를 운영 중입니다. 검색 로그를 분석하고 검색 모드별 사용 패턴을 모니터링하면서, 일본어 검색이 실제로 나아졌는지 데이터로 확인해 나갈 계획입니다. 완성된 시스템이 아니라, 개선 중인 시스템입니다.