주뇽's 저장소
엘라스틱서치의 모든 것 - 인덱스 생성 및 설정 가이드 🧰 본문
개요
이번 글에서는 엘라스틱서치 인덱스를 구성하는 전체적인 과정을 단계별로 살펴본다. 우리는 다음 로드맵을 따라 진행한다:
- 인덱스 개념 이해: 엘라스틱서치의 기본 구성 요소
- 인덱스 생성 API: REST 기반 인덱스 생성 방법
- 매핑(Mappings): 필드 정의와 타입 설정
- 설정(Settings): 분석기 구성과 인덱스 옵션
- 한글 처리: Nori 분석기 활용법
- 동의어 처리: 동의어 사전 구성
- 실무 최적화: 인덱스 설계 모범 사례
이전 글에서 다룬 역인덱스 구조와 엘라스틱서치의 기본 개념을 바탕으로, 이번에는 실제로 인덱스를 구성하고 최적화하는 방법을 자세히 알아본다.
1. 인덱스란? 📚
인덱스는 엘라스틱서치에서 문서를 저장하고 검색하기 위한 논리적 단위이다. 관계형 데이터베이스의 테이블과 유사하지만, 검색에 최적화된 구조를 갖고 있다.
🔍 인덱스 특징
- 문서(Document)의 집합
- 역인덱스(Inverted Index) 구조 기반
- JSON 형태로 정의 및 관리
2. 인덱스 생성 API 💻
엘라스틱서치는 모든 작업을 REST API로 처리한다. 인덱스를 생성하려면:
PUT : https://호스트:9200/인덱스이름
이 API에 JSON 형태로 정의된 인덱스 설정을 요청 본문에 담아 보낸다.
3. 인덱스 구성의 핵심 요소 ⚙️
인덱스 정의는 크게 두 가지 영역으로 구성한다:
{
"mappings": { /* 필드 정의 및 타입 지정 */ },
"settings": { /* 분석기 및 인덱스 설정 */ }
}
3.1 필드 매핑(mappings) 🗺️
매핑은 문서의 필드를 정의하고 해당 필드의 데이터 타입과 처리 방법을 결정한다.
기본 매핑 구조:
{
"mappings": {
"properties": {
"필드명1": {
"type": "필드타입",
"analyzer": "사용할분석기",
"index": true/false
},
"필드명2": { /* ... */ }
}
}
}
예시 - 상품 정보 인덱스:
{
"mappings": {
"properties": {
"product_id": {
"type": "long",
"index": false
},
"product_name": {
"type": "text",
"analyzer": "product_analyzer"
},
"description": {
"type": "text",
"analyzer": "product_analyzer"
},
"price": {
"type": "integer"
},
"created_at": {
"type": "date",
"index": false
}
}
}
}
3.2 주요 필드 타입 🏷️
타입 설명 사용 예시
text | 전문 검색용 텍스트 필드 | 제품명, 설명, 블로그 내용 |
keyword | 정확한 일치 검색용 필드 | 카테고리, 태그, 상태값 |
long/integer | 숫자형 필드 | ID, 가격, 수량 |
date | 날짜/시간 필드 | 생성일, 수정일 |
boolean | 참/거짓 값 | 활성화 여부, 판매 가능 여부 |
nested | 중첩 객체 필드 | 댓글, 리뷰, 하위 항목 |
geo_point | 위치 정보 | 매장 위치, 배송지 |
💡 Tip: "index": false를 설정하면 해당 필드는 저장되지만 검색에는 사용되지 않아 색인 크기를 줄일 수 있다. ID나 타임스탬프처럼 검색에 잘 사용되지 않는 필드에 적용한다.
4. 인덱싱 설정(settings) - 분석기의 이해 🔍
4.1 분석기(Analyzer)란?
분석기는 문서의 텍스트를 검색 가능한 토큰으로 변환하는 과정을 담당한다. 이 과정은 세 단계로 이루어진다:
1️⃣ 문자 필터(Char Filter) → 2️⃣ 토크나이저(Tokenizer) → 3️⃣ 토큰 필터(Token Filter)
예시: "엘라스틱서치는 검색에 최적화되어 있습니다!" 라는 문장이 있다면,
- 문자 필터: 특수문자 제거 → "엘라스틱서치는 검색에 최적화되어 있습니다"
- 토크나이저: 형태소 분석 → ["엘라스틱서치", "검색", "최적화", "있다"]
- 토큰 필터: 소문자화, 불용어 제거 → ["엘라스틱서치", "검색", "최적화"]
4.2 분석기 설정 구조
{
"settings": {
"analysis": {
"analyzer": {
"커스텀분석기명": {
"type": "custom",
"char_filter": ["필터1", "필터2"],
"tokenizer": "토크나이저명",
"filter": ["필터1", "필터2"]
}
},
"char_filter": { /* 문자 필터 정의 */ },
"tokenizer": { /* 토크나이저 정의 */ },
"filter": { /* 토큰 필터 정의 */ }
}
}
}
4.3 예시 - 기본 분석기 설정
{
"settings": {
"analysis": {
"analyzer": {
"basic_analyzer": {
"type": "custom",
"char_filter": [],
"tokenizer": "standard_tokenizer",
"filter": ["lowercase_filter"]
}
},
"tokenizer": {
"standard_tokenizer": {
"type": "standard"
}
},
"filter": {
"lowercase_filter": {
"type": "lowercase"
}
}
}
}
}
5. 통합 설정 - 매핑과 분석기 연결 🔄
매핑의 필드와 분석기를 연결하여 완전한 인덱스를 정의할 수 있다:
{
"mappings": {
"properties": {
"product_id": {
"type": "long",
"index": false
},
"product_name": {
"type": "text",
"analyzer": "basic_analyzer"
},
"description": {
"type": "text",
"analyzer": "basic_analyzer"
},
"price": {
"type": "integer"
},
"created_at": {
"type": "date",
"index": false
}
}
},
"settings": {
"analysis": {
"analyzer": {
"basic_analyzer": {
"type": "custom",
"char_filter": [],
"tokenizer": "standard_tokenizer",
"filter": ["lowercase_filter"]
}
},
"tokenizer": {
"standard_tokenizer": {
"type": "standard"
}
},
"filter": {
"lowercase_filter": {
"type": "lowercase"
}
}
}
}
}
6. 분석기 테스트하기 🧪
설정한 분석기가 제대로 동작하는지 확인하는 방법:
GET : https://호스트:9200/인덱스이름/_analyze
테스트 요청 본문:
{
"analyzer": "basic_analyzer",
"text": "엘라스틱서치 분석기 테스트입니다. Hello World!"
}
응답 예시:
{
"tokens": [
{
"token": "엘라스틱서치",
"start_offset": 0,
"end_offset": 7,
"type": "word",
"position": 0
},
{
"token": "분석기",
"start_offset": 8,
"end_offset": 11,
"type": "word",
"position": 1
},
...
{
"token": "hello",
"start_offset": 19,
"end_offset": 24,
"type": "word",
"position": 4
},
{
"token": "world",
"start_offset": 25,
"end_offset": 31,
"type": "word",
"position": 5
}
]
}
7. 한글 처리의 핵심 - Nori 분석기 🇰🇷
7.1 Standard 토크나이저의 한계
앞선 예제에서 사용한 Standard 토크나이저는 한글을 단순히 공백 단위로만 분리한다:
"엘라스틱서치는 검색엔진입니다" → ["엘라스틱서치는", "검색엔진입니다"]
이런 분석으로는 "검색엔진" 같은 키워드로 검색할 때 문제가 발생한다.
7.2 Nori 분석기 소개
Nori는 엘라스틱서치의 공식 한글 형태소 분석기로, 다음과 같이 분석한다:
"엘라스틱서치는 검색엔진입니다" → ["엘라스틱서치", "검색", "엔진", "입니다"]
7.3 Nori 분석기 설정
"tokenizer": {
"nori_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed",
"discard_punctuation": "true",
"lenient": true
}
}
주요 옵션:
- decompound_mode:
- mixed: 합성어 분해 + 원형 유지 (예: "삼성전자" → ["삼성전자", "삼성", "전자"])
- none: 합성어 분해 안함 (예: "삼성전자" → ["삼성전자"])
- discard: 합성어 분해 후 원형 제거 (예: "삼성전자" → ["삼성", "전자"])
- discard_punctuation: 구두점 제거 여부 (기본값: true)
- lenient: 오류 발생 시 건너뛰기 (기본값: false)
7.4 사용자 사전 활용
Nori는 사용자 사전을 통해 형태소 분석 결과를 커스터마이징할 수 있다:
"tokenizer": {
"nori_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed",
"discard_punctuation": "true",
"user_dictionary": "nori/user_dict.txt",
"lenient": true
}
}
사용자 사전 예시 (user_dict.txt):
클라우드컴퓨팅
인공지능 인공 지능
빅데이터 빅 데이터
각 줄은 다음 형식 중 하나를 가진다:
- 단일 단어: 분해하지 않고 그대로 유지
- 공백으로 구분된 단어들: 왼쪽 단어를 오른쪽 형태로 분해
8. 동의어 처리로 검색 품질 높이기 🔄
8.1 동의어 필터 설정
검색 품질을 높이기 위해 동의어 필터를 활용할 수 있다:
"filter": {
"lowercase_filter": {
"type": "lowercase"
},
"synonym_filter": {
"type": "synonym",
"synonyms_path": "analysis/synonym.txt",
"lenient": true
}
}
8.2 동의어 사전 작성 방법
확장(expansion) 형식 - 모든 단어가 서로의 동의어가 된다:
elasticsearch, elastic, es
ai, artificial intelligence, 인공지능
치환(substitution) 형식 - 왼쪽 단어가 오른쪽으로 변환된다:
natural language processing => nlp
machine learning => ml
8.3 주의사항: 동의어와 토크나이저 충돌
Nori 토크나이저에서 분해되는 단어를 동의어 사전에 사용하면 에러가 발생할 수 있다. 이 문제를 해결하려면:
- 먼저 해당 단어를 사용자 사전에 등록하여 분해되지 않도록 설정한다
- 그 후 동의어 사전에 추가한다
잘못된 예:
- Nori가 "클라우드컴퓨팅"을 "클라우드", "컴퓨팅"으로 분해
- 동의어 사전에 "클라우드컴퓨팅, cloud computing"을 추가
- 에러 발생!
올바른 방법:
- 사용자 사전에 "클라우드컴퓨팅" 추가 (분해 방지)
- 동의어 사전에 "클라우드컴퓨팅, cloud computing" 추가
9. 최종 인덱스 설정 - 종합 예제 📋
전자상거래 상품 데이터를 위한 완전한 인덱스 설정 예시:
{
"mappings": {
"properties": {
"product_id": {
"type": "long",
"index": false
},
"product_name": {
"type": "text",
"analyzer": "product_analyzer"
},
"description": {
"type": "text",
"analyzer": "product_analyzer"
},
"category": {
"type": "keyword"
},
"price": {
"type": "integer"
},
"brand": {
"type": "keyword"
},
"tags": {
"type": "keyword"
},
"stock": {
"type": "integer"
},
"created_at": {
"type": "date",
"index": false
}
}
},
"settings": {
"analysis": {
"analyzer": {
"product_analyzer": {
"type": "custom",
"char_filter": [],
"tokenizer": "product_nori_tokenizer",
"filter": [
"lowercase_filter",
"synonym_filter"
]
}
},
"tokenizer": {
"product_nori_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed",
"discard_punctuation": "true",
"user_dictionary": "analysis/user_dict.txt",
"lenient": true
}
},
"filter": {
"lowercase_filter": {
"type": "lowercase"
},
"synonym_filter": {
"type": "synonym",
"synonyms_path": "analysis/synonym.txt",
"lenient": true
}
}
}
}
}
10. 실무 활용 팁 💡
- 인덱스 템플릿 활용
- 비슷한 구조의 인덱스를 자동 생성할 때 유용하다
- 로그 데이터나 시계열 데이터에 특히 효과적이다
- 필드 재사용 (_source, _all 필드 관리)
- _source 필드 비활성화로 디스크 공간 절약이 가능하다
- 불필요한 필드는 "index": false로 설정한다
- 동적 매핑 주의사항
- 운영 환경에서는 가급적 명시적 매핑을 사용한다
- 예상치 못한 필드 타입 변환을 방지한다
- 사용자 사전과 동의어 사전 주기적 업데이트
- 서비스 특성에 맞는 용어를 지속적으로 추가한다
- 검색 로그 분석을 통한 사전 보강을 한다
- 다중 필드 활용
- 같은 데이터를 다른 방식으로 인덱싱한다
- 예: product_name (분석) + product_name.keyword (정확매치)
마무리 📝
엘라스틱서치 인덱스 구성은 단순한 스키마 정의를 넘어, 검색 품질을 결정짓는 중요한 요소이다. 특히 한글과 같은 복잡한 언어를 다룰 때는 적절한 토크나이저와 필터 설정이 필수적이다.
인덱스를 설계할 때는 항상 데이터의 특성과 검색 요구사항을 고려해야 한다. 문서의 양, 검색 패턴, 필요한 분석 수준에 따라 인덱스 설정이 달라질 수 있다.
'데이터베이스 > Elastic Search' 카테고리의 다른 글
🔍 엘라스틱서치(Elasticsearch) – 검색이 되는 이유부터 인덱싱까지 (0) | 2025.04.26 |
---|---|
검색 속도를 끌어올리는 핵심 기술 - 역인덱스와 엘라스틱서치 🔍 (6) | 2025.04.23 |