πŸ–₯️ μ΅œμ’…ν”„λ‘œμ νŠΈ/πŸš€ κΈ°λŠ₯ κ°œμ„ 

[κΈ°λŠ₯ κ°œμ„ ] μžλ™ μ™„μ„± + λΆ€λΆ„ 검색, 같이 μ“°λ©΄ μ•ˆ λ˜λŠ” 이유 (feat. Nori 적용)

carrot0911 2025. 4. 3. 12:11

πŸ” ν•œκΈ€ ν˜•νƒœμ†Œ λΆ„μ„κΈ°λ‘œ 검색 정확도 κ°œμ„ ν•˜κΈ°: Nori + μžλ™μ™„μ„± 뢄리 μ „λž΅

 

πŸ’­ 문제 인식

κΈ°μ‘΄μ—λŠ” μ±„μš© 곡고 검색에 λΆ€λΆ„ 검색(N-gram)κ³Ό μžλ™ μ™„μ„±(Edge N-gram) κΈ°λŠ₯을 ν•¨κ»˜ μ μš©ν•˜κ³  μžˆμ—ˆλ‹€.
기본적인 검색은 κ°€λŠ₯ν–ˆμ§€λ§Œ, μ‚¬μš©μžκ°€ κΈ°λŒ€ν•˜λŠ” μ •ν™•ν•œ 검색 κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜μ§€ λͺ»ν•˜λŠ” κ²½μš°κ°€ μ’…μ’… λ°œμƒν–ˆλ‹€.

πŸ“Œ μ˜ˆμ‹œ

μ‚¬μš©μžκ°€ "μΉ΄λ“œ"λΌλŠ” 단어λ₯Ό ν¬ν•¨ν•œ μ±„μš© 곡고λ₯Ό μ°Ύκ³  싢을 λ•Œ, μžλ™ μ™„μ„± κΈ°λŠ₯이 ν•¨κ»˜ λ™μž‘ν•˜λ©΄μ„œ
카둜 μ‹œμž‘ν•˜λŠ” μ „ν˜€ λ‹€λ₯Έ μ±„μš© κ³΅κ³ λ“€κΉŒμ§€ ν•¨κ»˜ κ²€μƒ‰λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν–ˆλ‹€.

결과적으둜 μ›ν•˜λŠ” 결과보닀 λ„ˆλ¬΄ λ§Žμ€ 데이터가 ν•¨κ»˜ λ…ΈμΆœλ˜λ©΄μ„œ μ‚¬μš©μžλŠ” μ •ν™•ν•œ 검색이 μ–΄λ ΅λ‹€λŠ” 인상을 λ°›κ²Œ λ˜μ—ˆλ‹€.

 

🧱 κΈ°μ‘΄ 인덱슀 ꡬ쑰의 ν•œκ³„

κΈ°μ‘΄ 인덱슀 μ„€μ •μ—μ„œλŠ” title, requiredSkills λ“±μ˜ ν•„λ“œμ— λ‹€μŒ 두 뢄석기λ₯Ό ν•¨κ»˜ μ μš©ν•˜κ³  μžˆμ—ˆλ‹€.

βœ… ngram_analyzer → λΆ€λΆ„ κ²€μƒ‰μš©
βœ… autocomplete_analyzer → μžλ™ μ™„μ„±μš©

"title": {
  "type": "text",
  "fields": {
    "ngram": { "type": "text", "analyzer": "ngram_analyzer" },
    "autocomplete": { "type": "text", "analyzer": "autocomplete_analyzer" }
  }
}

πŸ“Œ 이 κ΅¬μ‘°λŠ” λ‹¨μ–΄μ˜ μΌλΆ€λΆ„λ§Œ μž…λ ₯해도 검색이 κ°€λŠ₯ν•˜λ‹€λŠ” μž₯점이 μžˆμ—ˆμ§€λ§Œ, λ¬Έμ œλŠ” 두 κΈ°λŠ₯이 같은 검색 APIμ—μ„œ λ™μ‹œμ— μž‘λ™ν•˜λ©΄μ„œ 검색 결과의 정밀도가 크게 λ–¨μ–΄μ§€λŠ” ν˜„μƒμ΄ λ°œμƒν–ˆλ‹€λŠ” 점이닀.

μ‚¬μš©μžκ°€ μ •ν™•ν•˜κ²Œ νŠΉμ • ν‚€μ›Œλ“œλ₯Ό κ²€μƒ‰ν•˜λ”λΌλ„ μžλ™ μ™„μ„±μš© 토큰이 ν•¨κ»˜ 쑰회되며 μ›ν•˜μ§€ μ•ŠλŠ” 데이터가 κ³Όλ„ν•˜κ²Œ ν¬ν•¨λ˜λŠ” 문제둜 μ΄μ–΄μ‘Œλ‹€.

 

🧠 ν•΄κ²° λ°©ν–₯: Nori ν˜•νƒœμ†Œ 뢄석기 + κΈ°λŠ₯ 뢄리

ν•œκ΅­μ–΄λŠ” μ˜μ–΄μ™€ 달리 쑰사, μ–΄λ―Έ, 볡합어 λ“± ꡬ성 방식이 λ³΅μž‘ν•˜κΈ° λ•Œλ¬Έμ—
λ‹¨μˆœν•œ N-gram λΆ„μ„κΈ°λ§ŒμœΌλ‘œλŠ” μ •ν™•ν•œ 의미 λ‹¨μœ„ 뢄석에 ν•œκ³„κ°€ μžˆλ‹€.

κ·Έλž˜μ„œ, λ‹€μŒκ³Ό 같은 κ°œμ„  λ°©ν–₯을 μ„€μ •ν–ˆλ‹€.

βœ… Nori ν˜•νƒœμ†Œ 뢄석기λ₯Ό λ„μž…
      πŸ‘‰ ν•œκ΅­μ–΄λ₯Ό ν˜•νƒœμ†Œ λ‹¨μœ„λ‘œ λΆ„μ„ν•˜μ—¬ 보닀 의미 μ€‘μ‹¬μ˜ 검색이 κ°€λŠ₯ν•˜λ„λ‘ κ°œμ„ 
      πŸ‘‰ "κ°œλ°œμžκ°€" → [개발, 자]
      πŸ‘‰ 쑰사/μ–΄λ―Έ 제거 + μ‚¬μš©μž 사전 등둝도 κ°€λŠ₯
βœ… μžλ™ μ™„μ„± κΈ°λŠ₯은 λ³„λ„μ˜ API둜 뢄리
      πŸ‘‰ 검색 정밀도에 영ν–₯을 μ£Όμ§€ μ•Šλ„λ‘ κΈ°λŠ₯적 μ±…μž„μ„ 뢄리
      πŸ‘‰ μ •ν™•ν•œ ν‚€μ›Œλ“œ 검색과 ν˜Όλ™λ˜μ§€ μ•Šκ²Œ 섀계

  

πŸ§ͺ Nori 뢄석기 적용

Elasticsearchμ—μ„œλŠ” ν•œκ΅­μ–΄ ν˜•νƒœμ†Œ 뢄석을 μœ„ν•΄ Nori 뢄석기와 Nori Tokenizerλ₯Ό μ œκ³΅ν•œλ‹€.

ν•­λͺ© Nori 뢄석기 Nori Tokenizer
μ—­ν•  ν•œκΈ€ ν…μŠ€νŠΈλ₯Ό ν˜•νƒœμ†Œλ‘œ λΆ„λ¦¬ν•˜λŠ” μ™„μ„±ν˜• Analyzer ν˜•νƒœμ†Œ λ‹¨μœ„λ‘œ Tokenizing만 μˆ˜ν–‰
μ˜ˆμ‹œ λ°±μ—”λ“œ 개발자
→ [λ°±μ—”λ“œ, 개발, 자]
μžλ°” 개발자
→ [μžλ°”, 개발, 자]

πŸ“Œ 쑰사 및 μ–΄λ―Έ 제거, μ‚¬μš©μž 사전 μ„€μ •, 볡합어 처리 λ“± λ‹€μ–‘ν•œ ν•œκΈ€ 처리 κΈ°λŠ₯ 제곡

 

βš™οΈ λ³€κ²½λœ 인덱슀 ꡬ쑰

"title": {
  "type": "text",
  "fields": {
    "morph": {
      "type": "text",
      "analyzer": "nori_analyzer"
    },
    "autocomplete": {
      "type": "text",
      "analyzer": "autocomplete_analyzer"
    }
  }
}

πŸ” ν•„λ“œ μ„€λͺ…

βœ… morph
      πŸ‘‰ μ •ν™•ν•œ 검색을 μœ„ν•œ Nori ν˜•νƒœμ†Œ 기반 뢄석 적용
      πŸ‘‰ μ‚¬μš©μžκ°€ μž…λ ₯ν•œ ν‚€μ›Œλ“œλ₯Ό 의미 λ‹¨μœ„λ‘œ ν•΄μ„ν•˜μ—¬ μ •λ°€ν•œ 검색 κ°€λŠ₯
βœ… autocomplete
      πŸ‘‰ μžλ™ 완성을 μœ„ν•œ Edge-Ngram 뢄석기 적용
      πŸ‘‰ μž…λ ₯κ°’μ˜ μ•žλΆ€λΆ„μ„ κΈ°μ€€μœΌλ‘œ μžλ™ μ™„μ„± 검색 제곡

πŸ“Œ 이 μ„€μ •μœΌλ‘œ ν•œκΈ€ ν…μŠ€νŠΈλ„ 의미 λ‹¨μœ„λ‘œ μ •ν™•ν•˜κ²Œ 검색할 수 있고, μžλ™ μ™„μ„± κΈ°λŠ₯은 별도 API둜 λΆ„λ¦¬λ˜μ–΄ 검색 결과에 영ν–₯을 μ£Όμ§€ μ•Šλ„λ‘ 뢄리 μ„€κ³„λ˜μ—ˆλ‹€.

 

πŸ”„ μžλ™μ™„μ„± κΈ°λŠ₯ 뢄리 및 κ°œμ„ 

μžλ™μ™„μ„± κΈ°λŠ₯은 Edge N-gram ν•„ν„°λ₯Ό ν™œμš©ν•˜μ—¬ μž…λ ₯ν•œ λ‹¨μ–΄μ˜ μ•žλΆ€λΆ„λ§ŒμœΌλ‘œλ„ 검색이 κ°€λŠ₯ν•˜κ²Œ κ΅¬ν˜„ν–ˆλ‹€.

"filter": {
  "edge_ngram_filter": {
    "type": "edge_ngram",
    "min_gram": 1,
    "max_gram": 25
  }
}

πŸ“Œ μ˜ˆμ‹œ

"ν”„λ‘ νŠΈμ—”λ“œ 개발자" → ["ν”„", "ν”„λ‘ ", "ν”„λ‘ νŠΈ", "ν”„λ‘ νŠΈμ—”", "ν”„λ‘ νŠΈμ—”λ“œ"] 둜 λΆ„ν•΄
βœ… μ‚¬μš©μžκ°€ "ν”„λ‘ "만 μž…λ ₯해도 "ν”„λ‘ νŠΈμ—”λ“œ 개발자"κ°€ ν¬ν•¨λœ μ±„μš© 곡고가 μžλ™μœΌλ‘œ μΆ”μ²œλœλ‹€!

βœ‚οΈ κΈ°λŠ₯ λΆ„λ¦¬λ‘œ 정확도 확보

μ΄μ „μ—λŠ” μžλ™ μ™„μ„± ν•„λ“œκ°€ 일반 검색과 ν•¨κ»˜ λ™μž‘ν•˜λ©΄μ„œ μ›ν•˜μ§€ μ•ŠλŠ” κ²°κ³Όκ°€ ν•¨κ»˜ μ‘°νšŒλ˜λŠ” λ¬Έμ œκ°€ μžˆμ—ˆλ‹€.
이번 κ°œμ„ μ—μ„œλŠ” μžλ™ μ™„μ„± κΈ°λŠ₯을 λ³„λ„μ˜ API둜 λΆ„λ¦¬ν•˜μ—¬ μ •ν™•ν•œ ν‚€μ›Œλ“œ 검색 결과와 ν˜Όν•©λ˜μ§€ μ•Šλ„λ‘ μ²˜λ¦¬ν–ˆλ‹€.

 

 

βœ… κ²°κ³Ό 및 확인

πŸ“Œ κ°œμ„  적용 ν›„, κΈ°λŠ₯은 각각의 λͺ©μ μ— 맞게 λͺ…ν™•ν•˜κ²Œ λΆ„λ¦¬λ˜μ–΄ λ™μž‘ν–ˆλ‹€.

πŸ”Ž 일반 검색 (Nori 뢄석기 적용)
      πŸ‘‰ "μΉ΄λ“œ" ν‚€μ›Œλ“œλ‘œ 검색 μ‹œ μ •ν™•νžˆ "μΉ΄λ“œ"λ₯Ό ν¬ν•¨ν•˜λŠ” μ±„μš© 곡고만 쑰회

✨ μžλ™μ™„μ„± (Edge-Ngram)
       πŸ‘‰ "μΉ΄"
만 μž…λ ₯해도 "μΉ΄λ“œ", "카카였" λ“± κ΄€λ ¨ κ²°κ³Όλ₯Ό 제곡

μ΄μ œλŠ” 검색과 μžλ™ 완성이 μ„œλ‘œ λ°©ν•΄ν•˜μ§€ μ•Šκ³ , 각자의 역할에 μΆ©μ‹€ν•˜κ²Œ λ™μž‘ν•œλ‹€!

 

 

✍ 회고

이번 μž‘μ—…μ„ 톡해 ν•œκΈ€ 검색 ν™˜κ²½μ—μ„œλ„ 정확도와 μ‚¬μš©μž κ²½ν—˜μ„ λͺ¨λ‘ κ³ λ €ν•œ 검색 ꡬ쑰λ₯Ό μ„€κ³„ν•˜κ³  κ΅¬ν˜„ν•  수 μžˆμ—ˆλ‹€.

πŸ’‘ 핡심 정리

βœ… ν˜•νƒœμ†Œ 뢄석(Nori) + μžλ™μ™„μ„±(Edge-Ngram)
βœ… 검색 λͺ©μ λ³„ ν•„λ“œ 뢄리 + κΈ°λŠ₯ API 뢄리

 

이번 κ²½ν—˜μ„ 톡해 Elasticsearch의 μœ μ—°ν•œ 인덱슀 섀계와 ν™•μž₯μ„± 높은 검색 κΈ°λŠ₯을 직접 λ‹€λ€„λ³΄λ©΄μ„œ μ‹€μ „ 감각도 ν‚€μšΈ 수 μžˆμ—ˆλ‹€.
μ•žμœΌλ‘œλŠ” λ‹€μ–‘ν•œ ν…μŠ€νŠΈ 기반 κΈ°λŠ₯μ—μ„œ μ–Έμ–΄ νŠΉμ„±κ³Ό 검색 λͺ©μ μ— λ§žλŠ” ꡬ쑰 섀계가 μ–Όλ§ˆλ‚˜ μ€‘μš”ν•œμ§€ 더 깊이 κ³ λ―Όν•˜κ²Œ 될 것 κ°™λ‹€.