우당탕탕

Elasticsearch 검색 도입하면서 겪은 시행착오와 단계별 세팅 방법 본문

Database

Elasticsearch 검색 도입하면서 겪은 시행착오와 단계별 세팅 방법

모찌모찝 2026. 6. 19. 18:21

Elasticsearch 검색 기능을 도입하려고 하니, 생각보다 세팅부터 쿼리 작성까지 삽질이 꽤 많았어요. 특히 처음부터 무턱대고 문서를 읽기 시작했는데 체계가 없으니 더 혼란스럽더라고요. 그래서 제가 겪은 시행착오와 함께, 단계별로 어떤 절차를 밟아야 하는지 정리해봤습니다.

이 글에서는 설치부터 클러스터 구성, 인덱스 생성, 그리고 쿼리 작성 및 최적화 과정까지 실제 실행 화면과 명령어 위주로 자세히 다뤄서, 처음 Elasticsearch 도입하는 분들이 이 글 하나면 혼란 없이 진행할 수 있도록 했어요.

개발 환경 / 버전 정보

저는 Elasticsearch 8.7.0 버전을 로컬 서버와 테스트용 AWS 인스턴스에 설치해서 작업했어요. OS는 Ubuntu 22.04 LTS를 사용했고, 데이터 검색과 분석을 주목적으로 진행했습니다. 별도의 프론트엔드 연동은 이번 글에선 다루지 않고, REST API 호출과 직접 Kibana 콘솔에서 확인하는 방식으로 작업했어요.

1단계: Elasticsearch 설치와 기본 클러스터 구성

사실 이 부분이 생각보다 까다로웠는데, 공식 홈페이지에서 설치 파일을 받고 간단히 압축을 푸는 것부터 시작했어요. tar -xzf elasticsearch-8.7.0-linux-x86_64.tar.gz 이후, bin/elasticsearch 명령어로 실행합니다.

여기서 제일 중요한 건 JVM 메모리 설정인데, 기본값으론 1GB라 부족했어요. 그래서 config/jvm.options 파일에서 -Xms, -Xmx 값을 2GB로 늘렸습니다.

그리고 elasticsearch.yml에서 기본 클러스터 이름과 노드 이름을 지정해서 추후 멀티 노드 클러스터 구성도 쉽게 만들 수 있게 했어요.

2단계: 인덱스 생성과 매핑(mapping) 정의하기

인덱스를 어떻게 잘 설계해야 할지 초반에 엄청 헤맸는데, 저는 직접 JSON 매핑을 명시하는 방법으로 시작했어요. 사실 자동 매핑에 맡기면 편하지만, 검색 쿼리 성능과 정확도가 떨어질 수 있어서 나중에 고생하더라고요.

다음은 제가 쓴 간단 인덱스 생성 예시입니다.

PUT /products
{
  "mappings": {
    "properties": {
      "name": { "type": "text", "analyzer": "standard" },
      "category": { "type": "keyword" },
      "price": { "type": "double" },
      "in_stock": { "type": "boolean" },
      "release_date": { "type": "date" }
    }
  }
}

이렇게 매핑을 정해놓으면, 검색할 때 분석기가 어떻게 텍스트를 쪼갤지, 숫자 필드는 어떻게 다룰지 명확해져서 나중에 쿼리가 훨씬 깔끔해지더라고요.

3단계: 데이터 입력과 간단한 쿼리 테스트

인덱스를 만들고 나서, 직접 데이터를 입력해봤어요. REST API로 하나씩 넣기엔 너무 귀찮아서 제가 Python 스크립트로 배치 업로드를 했습니다.

import requests
import json

url = 'http://localhost:9200/products/_doc'

sample_data = [
    {"name": "Vacuum Cleaner", "category": "home", "price": 99.99, "in_stock": True, "release_date": "2022-09-15"},
    {"name": "Electric Kettle", "category": "kitchen", "price": 45.50, "in_stock": False, "release_date": "2023-01-10"}
]

for item in sample_data:
    res = requests.post(url, json=item)
    print(res.json())

이 스크립트로 바로 데이터가 들어가고, Kibana 콘솔에서 조회해보니 정상적으로 인덱싱이 됐더라고요.

4단계: 검색 쿼리 작성과 실제 결과 확인

본격적으로 검색 쿼리를 만들었는데, 사실 여기서 많이 틀리더라고요. 저는 보통 match 쿼리와 term 쿼리를 헷갈렸어요.

간단한 예를 들면, 카테고리는 정확한 값으로 비교해야 해서 term 쿼리를 써야 하지만, 제품 이름 검색은 분석된 텍스트라 match로 해야 합니다.

GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "vacuum" }},
        { "term": { "category": "home" }}
      ]
    }
  }
}

실행 결과는 이렇게 나왔어요.

{
  "hits": {
    "total": { "value": 1, "relation": "eq" },
    "hits": [
      {
        "_source": {
          "name": "Vacuum Cleaner",
          "category": "home",
          "price": 99.99,
          "in_stock": true,
          "release_date": "2022-09-15"
        }
      }
    ]
  }
}

이렇게 bool 쿼리 내에 must 조건으로 조합해서 원하는 결과를 정확하게 뽑을 수 있었어요.

5단계: 쿼리 최적화, 실행 시간 단축하기

여기서 삽질이 진짜 많았는데, 막상 쿼리는 맞게 짰는데 왜 느리지? 하고 고민했어요. 제가 알게 된 건 분석기 설정과 불필요한 필드 검색 제외가 핵심이라는 점이에요.

예를 들어, 인덱스 내 모든 필드 다 검색하게 설정해두면 확실히 느리고, 필요한 필드만 지정해서 검색하는 게 빠르더라고요.

GET /products/_search
{
  "_source": ["name", "price"],
  "query": {
    "match": { "name": "vacuum" }
  }
}

실행 시간은 단순 예시지만, 이렇게 _source 필드를 제한하니 40% 정도 개선됐어요.

또한, analyzer를 잘 골라야 원하는 텍스트 분할이 제대로 되는데, 기본 표준 분석기가 아닌 edge_ngram 같은 걸 써서 자동 완성 검색을 구현해보려다 삽질한 경험도 있습니다. edge_ngram은 인덱스 매핑부터 제대로 설정해야 동작하기 때문에 초반 세팅을 꼼꼼히 해야 해요.

여기서 삽질했던 부분들

이 에러가 왜 나는지 한참 찾았는데, "failed to parse field [price]" 같은 메시지가 나왔을 때, 알고 보니 숫자 필드에 문자열이 섞여서 인덱싱 에러가 났더라고요.

{
  "error": {
    "root_cause": [
      {
        "type": "mapper_parsing_exception",
        "reason": "failed to parse field [price] of type [double]"
      }
    ]
  }
}

데이터를 사전에 깨끗하게 정제하지 않으면, 이런 문제로 인덱스 생성 자체가 중단되어 고생할 수 있으니 조심해야 합니다.

그리고 term 쿼리 쓰면서도 문자열이 소문자인지 대문자인지, 띄어쓰기 있는지 꼭 확인해야 해요. 키워드 타입 필드는 완전히 일치해야 결과가 나오거든요.

심화: 이런 것도 알면 좋아요

Elasticsearch는 기본적으로 분산 시스템이라, 멀티 노드 운영 시 shardreplica 설정이 중요해요. 저는 초기에 싱글 노드로만 돌려서 이런 개념이 감이 안 왔는데, 나중에 데이터 양이 많아질수록 성능과 안정성에 영향을 받더라고요.

또한, refresh_interval 조절로 색인 업데이트 시점도 제어할 수 있는데, 실시간 반영이 중요한지 배치성으로 처리할지에 따라 설정을 바꾸면 성능 차이가 확실히 납니다.

성능 모니터링은 Kibana에서 제공하는 Dev Tools 콘솔과 Task API를 활용하면 좋고, profile 쿼리를 써서 어떤 쿼리 단계가 시간이 오래 걸리는지도 확인 가능해요.

자주 물어보시는 것들

Q. 매핑을 나중에 바꾸고 싶은데 어떻게 하나요?

A. Elasticsearch는 기존 인덱스 매핑 변경이 제한적이라, 보통 새로운 인덱스를 만들고 재색인(reindex) 하는 방식을 씁니다. _reindex API를 활용해 기존 데이터를 옮기면서 새로운 매핑을 적용할 수 있어요.

Q. 대량 데이터 넣을 때는 어떻게 해야 하나요?

A. bulk API를 써서 한 번에 여러 문서를 전송하는 게 효율적입니다. 단, 너무 많은 문서를 한 번에 보내면 메모리가 부족할 수 있으니 적절히 나누는 게 좋아요.

Q. 기본 analyzer 외에 추천할 만한 분석기가 있나요?

A. 한국어 같은 비영어권은 kuromojinori 같은 전용 분석기를 사용하는 게 훨씬 성능이 좋고 정확해요. 영어권도 상황에 따라 ngram이나 edge_ngram으로 자동완성 기능을 구현하는 경우가 많고요.

저도 직접 하나하나 차근차근 세팅하면서 겪은 시행착오 덕분에 현재는 꽤 안정적으로 운영 중이에요. 검색 도입 초반에 기본 매핑과 쿼리 개념을 명확히 잡는 게 얼마나 중요한지 절실히 느꼈고, 그런 점에서 이 글이 여러분에게 단계별로 큰 도움이 되기를 바랍니다.

Comments