努力未来

Elastic search로 검색엔진 고도화하기

홍서현
홍서현Jul 21, 2025


✅ 1단계. SQL 기반 LIKE 검색 → Elasticsearch 도입

📌 기존 방식


SELECT * FROM board WHERE title LIKE '%농구%'

sql

❌ 문제점

  • 성능 저하: 대량 데이터에서 Full Scan
  • 오탈자/띄어쓰기 미지원: 농구는 검색되지만 농규, 농 구는 미검색
  • 정확도 부족: 텍스트 일치 외 유연한 검색 불가능

🎯 개선 목표

  • 빠르고 정확한 검색
  • 한글 형태소 분석 및 오타 보정
  • 관심사/성별/나이 등 사용자 필터링 지원

✅ 2단계. Elasticsearch 기본 검색 적용

✅ 매핑 및 데이터 색인

게시글 데이터를 Elasticsearch에 전송하여 색인합니다.


@Document(indexName = "boards")
public class BoardDocument {
    private String title;
    private String content;
    private List<String> tags;
    ...
}

java

✅ match / multi_match 기반 검색 쿼리


{
  "multi_match": {
    "query": "농구",
    "fields": ["title", "content", "tags"]
  }
}

json

✅ 서비스 구현


SearchHits<BoardDocument> hits = elasticsearchRestTemplate.search(query, BoardDocument.class);

java
  • Pageable 기반으로 페이징 처리
  • 검색어가 포함된 title, content, tags 필드 중심 조회

✅ 3단계. 사용자 필터링 조건 적용

🎯 사용자별 검색 조건 예시

  • 성별 제한: GenderType.MALE, FEMALE
  • 나이 제한: minAge, maxAge
  • 모집 상태: confirmed == false

✅ 구현 전략

  • 도메인 필터링 + 검색 후 후처리
  • 또는 ES bool filter 쿼리 적용 (성능 최적화 시)

if (board.getMinAge() > userAge || board.getGender() != userGender) {
    // 필터링
}

java

또는 ES 내부에서 처리:


"bool": {
  "must": { "multi_match": ... },
  "filter": [
    { "term": { "gender": "MALE" }},
    { "range": { "minAge": { "lte": 26 }}}
  ]
}

json

✅ 4단계. 형태소 분석기 기반 Korean Analyzer 적용

📌 문제

  • 한글은 조사, 어미, 띄어쓰기 등이 많아 정확한 검색이 어렵다.
  • 예: 농구 좋아해요, , 좋아, 하다 등 분리 필요

✅ 해결: Nori 형태소 분석기 적용

🔧 인덱스 생성 시 Custom Analyzer 설정


PUT /boards_korean
{
  "settings": {
    "analysis": {
      "analyzer": {
        "korean_analyzer": {
          "type": "custom",
          "tokenizer": "nori_tokenizer"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "korean_analyzer"
      }
    }
  }
}

json
Elasticsearch 7.x 이상부터 Nori는 기본 탑재

✅ 5단계. N-gram 기반 부분 검색 적용

🎯 목적

  • 부분어 검색 (, 농구, 농규) 지원
  • 오타나 단어 일부만 입력해도 검색되도록

✅ ngram 분석기 적용 예시


"analysis": {
  "tokenizer": {
    "ngram_tokenizer": {
      "type": "nGram",
      "min_gram": 2,
      "max_gram": 3,
      "token_chars": ["letter", "digit"]
    }
  },
  "analyzer": {
    "ngram_analyzer": {
      "type": "custom",
      "tokenizer": "ngram_tokenizer"
    }
  }
}

json

✅ 다중 analyzer 검색 쿼리


{
  "multi_match": {
    "query": "농규",
    "fields": ["title.korean", "title.ngram"]
  }
}

json

✅ 6단계. Fuzziness 적용 (오탈자 보정 기능)

🎯 목적

  • 농규, 놈구 등 오타도 검색되도록
  • Levenshtein 편집 거리 기반 자동 보정

✅ 쿼리 예시

{
  "multi_match": {
    "query": "농규",
    "fields": ["title.korean", "title.ngram"],
    "fuzziness": "AUTO"
  }
}

json
  • "AUTO" 또는 "1", "2" 등 수치 지정 가능
  • AUTO는 단어 길이에 따라 거리 자동 조절

🔄 전체 고도화 흐름 정리

plaintext
복사편집
SQL LIKE 검색
Elasticsearch 기본 검색 (match/multi_match)
도메인 기반 필터링 (나이/성별/모집 여부)
형태소 분석기 적용 (nori)
n-gram 분석기 적용 (부분어 대응)
Fuzziness 적용 (오탈자 대응)

🧠 마무리하며

검색은 단순한 기능이지만, 사용자 만족도와 서비스 완성도를 크게 좌우하는 핵심 요소입니다.

이번 프로젝트에서는 단순 키워드 조회를 넘어, 다음과 같은 점을 직접 구현하며 실전 역량을 키웠습니다:

  • 검색 정확도 향상을 위한 분석기 튜닝
  • 오탈자 대응을 위한 fuzzy search
  • 사용자 조건 기반 필터링
  • 성능과 유연성을 고려한 다단계 쿼리 구성