๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ–ฅ๏ธ ์ตœ์ข…ํ”„๋กœ์ ํŠธ/๐Ÿš€ ๊ธฐ๋Šฅ ๊ฐœ์„ 

[๊ธฐ๋Šฅ ๊ฐœ์„ ] ์ž๋™ ์™„์„ฑ + ๋ถ€๋ถ„ ๊ฒ€์ƒ‰, ๊ฐ™์ด ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ  (feat. Nori ์ ์šฉ)

by carrot0911 2025. 4. 3.

๐Ÿ” ํ•œ๊ธ€ ํ˜•ํƒœ์†Œ ๋ถ„์„๊ธฐ๋กœ ๊ฒ€์ƒ‰ ์ •ํ™•๋„ ๊ฐœ์„ ํ•˜๊ธฐ: 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์˜ ์œ ์—ฐํ•œ ์ธ๋ฑ์Šค ์„ค๊ณ„์™€ ํ™•์žฅ์„ฑ ๋†’์€ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ ์ง์ ‘ ๋‹ค๋ค„๋ณด๋ฉด์„œ ์‹ค์ „ ๊ฐ๊ฐ๋„ ํ‚ค์šธ ์ˆ˜ ์žˆ์—ˆ๋‹ค.
์•ž์œผ๋กœ๋Š” ๋‹ค์–‘ํ•œ ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ๊ธฐ๋Šฅ์—์„œ ์–ธ์–ด ํŠน์„ฑ๊ณผ ๊ฒ€์ƒ‰ ๋ชฉ์ ์— ๋งž๋Š” ๊ตฌ์กฐ ์„ค๊ณ„๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ค‘์š”ํ•œ์ง€ ๋” ๊นŠ์ด ๊ณ ๋ฏผํ•˜๊ฒŒ ๋  ๊ฒƒ ๊ฐ™๋‹ค.