Elasticsearch
Elasticsearch 카테고리의 모든 포스트 - 한국어
21개의 포스트
⚙️Analyzer 커스터마이징 가이드
이 문서는 Elasticsearch에서 custom analyzer를 설계하고,
nori tokenizer
기반으로 불용어 제거, 품사 필터링, 동의어 처리 등을 적용하는 방법을 다룹니다.
1. ⚙️ analyzer 커스터마이징 개요
✅ custom analyzer란?
custom analyzer = tokenizer + 0개 이상의 filter
- tokenizer: 입력 텍스트를 토큰으로 분해
- filters: 토큰을 소문자화, 불용어 제거, 동의어 처리 등 후처리
2. 주요 filter 종류
필터 이름 | 설명 |
---|---|
lowercase | 모든 텍스트를 소문자로 변환 |
stop | 불용어(stopword) 제거 (는 , 이 , 가 등) |
synonym | 동의어 치환 (예: kakao => 카카오 ) |
nori_part_of_speech | 품사(POS) 필터링 (예: 접속사 제거) |
nori_readingform | 한자 → 발음으로 변환 |
3. 예시: nori 기반 커스텀 analyzer
PUT http://localhost:9200/custom-index
{
"settings": {
"analysis": {
"tokenizer": {
"my_nori_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed"
}
},
"filter": {
"my_pos_filter": {
"type": "nori_part_of_speech",
"stoptags": [ "E", "IC", "J", "MAG", "MAJ" ]
},
"my_stop_filter": {
"type": "stop",
"stopwords": [ "의", "가", "이", "은", "는" ]
},
"my_synonym_filter": {
"type": "synonym",
"synonyms": [
"카카오, kakao",
"네이버, naver"
]
}
},
"analyzer": {
"my_korean_analyzer": {
"type": "custom",
"tokenizer": "my_nori_tokenizer",
"filter": [
"lowercase",
"my_pos_filter",
"my_stop_filter",
"my_synonym_filter"
]
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "my_korean_analyzer"
}
}
}
}
4. 커스텀 analyzer 분석 테스트
POST http://localhost:9200/custom-index/_analyze
{
"analyzer": "my_korean_analyzer",
"text": "카카오의 새로운 서비스가 시작되었습니다."
}
📌 예상 결과 (필터 적용됨):
⚡ 검색 성능 향상 팁
이 문서는 Elasticsearch에서 검색 쿼리 성능을 높이기 위한 실전 팁들을 정리한 문서입니다.
대용량 데이터 환경에서도 효율적인 검색을 유지하려면 반드시 알아야 할 핵심 항목들을 포함합니다.
1. 🔍 filter와 must의 분리
✅ must
vs filter
비교
항목 | must | filter |
---|---|---|
점수(score) | 계산함 | 계산 안 함 |
캐시 활용 | 거의 불가능 | ✅ 캐시 가능 |
목적 | relevance 기반 검색 | 조건 필터링 (정확 매칭 등) |
📌 Tip: 가능한 조건은 filter
로 분리하여 쿼리 성능 향상
"bool": {
"must": [
{ "match": { "title": "카카오" } }
],
"filter": [
{ "term": { "status": "published" } }
]
}
2. 📦 _source 필드 최소화
_source
는 document 전체를 반환하는 필드- 필요한 필드만 지정하여 I/O 비용 절감 가능
"_source": ["title", "date"]
📌 예: 수십 개 필드 중 2~3개만 필요할 때 효과적
✨ 검색어 하이라이팅 기능
이 문서는 Elasticsearch에서 검색어가 포함된 부분을 강조(하이라이트)해서 반환하는 기능을 설명합니다.
기본<em>
태그를 사용하며, 다양한 하이라이터 설정도 가능합니다.
✅ 1. highlight란?
검색 결과에서 사용자가 입력한 검색어가 일치한 부분을
특정 태그로 감싸서 강조 표시할 수 있는 기능입니다.
🛠️ 2. 기본 highlight 쿼리 예시
{
"query": {
"match": {
"content": "카카오"
}
},
"highlight": {
"fields": {
"content": {}
}
}
}
반환 예시:
"highlight": {
"content": [
"이번 주 <em>카카오</em> 주식이 급등했다."
]
}
🎨 3. 태그 커스터마이징
"highlight": {
"pre_tags": ["<strong>"],
"post_tags": ["</strong>"],
"fields": {
"content": {}
}
}
<em>
대신<strong>
,<mark>
등 원하는 HTML 태그로 감쌀 수 있음
✂️ 4. fragment_size, number_of_fragments
"highlight": {
"fields": {
"content": {
"fragment_size": 100,
"number_of_fragments": 2
}
}
}
설정 항목 | 설명 |
---|---|
fragment_size | 각 조각의 최대 길이 (문자 수 기준) |
number_of_fragments | 최대 반환 조각 수 |
🧠 5. highlighter 종류
타입 | 설명 |
---|---|
unified | 기본값, 대부분의 경우 추천 |
plain | 빠르지만 기능 제한 |
fvh (Fast Vector Highlighter) | 빠르고 정밀한 하이라이팅 (필드에 term_vector 필요) |
"highlight": {
"type": "unified",
"fields": {
"content": {}
}
}
✅ 요약
항목 | 설명 |
---|---|
기본 태그 | <em> 으로 감싸서 강조 |
커스터마이징 가능 | <strong> , <mark> 등 HTML 태그 지정 가능 |
조각 길이 조절 | fragment_size , number_of_fragments |
하이라이터 선택 옵션 | unified , plain , fvh |
🎯 검색 점수 튜닝 실습 예제
이 문서는
function_score
쿼리를 활용하여 검색 결과의 relevance score를 커스터마이징하는 실습 예제입니다.
클릭수, 좋아요, 최신성 등을 반영한 복합 점수 계산 전략을 설명합니다.
1. ⚙️ function_score 기본 구조
function_score
쿼리는 검색 점수를 수식 기반으로 조절할 수 있습니다.
{
"query": {
"function_score": {
"query": { "match": { "title": "카카오" } },
"functions": [
...
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
query
: 기본 검색 조건functions
: 가중치나 decay 함수 등 적용score_mode
: 여러 함수의 결과를 합산, 평균 등으로 결합boost_mode
: 기존 점수와 함수 점수를 어떻게 합칠지 결정
2. 💡 likes, clicks 기반 가중치 예시
"functions": [
{
"field_value_factor": {
"field": "likes",
"factor": 1.5,
"modifier": "sqrt",
"missing": 0
}
},
{
"field_value_factor": {
"field": "clicks",
"factor": 0.5,
"modifier": "log1p",
"missing": 1
}
}
]
likes
: 제곱근 기반 증가clicks
: 로그 기반 완만한 증가
3. 🕒 createdAt 기반 최신 문서 선호 (decay 함수)
{
"gauss": {
"createdAt": {
"origin": "now",
"scale": "10d",
"decay": 0.5
}
}
}
- 최신 문서일수록 점수가 높고, 오래될수록 자연스럽게 감소
scale
: 감소가 시작되는 거리decay
: 감소 비율
4. ⚖️ boost_mode와 score_mode 조합
score_mode | 의미 |
---|---|
sum | 함수 결과 모두 더함 |
avg | 평균값 사용 |
max | 가장 큰 값만 사용 |
multiply | 모두 곱함 (값이 0이면 전체 점수 0됨) |
first | 첫 번째 함수만 사용 |
boost_mode | 기존 _score 와의 결합 방식 |
---|---|
multiply | _score * function_score |
sum | _score + function_score |
replace | _score 무시하고 function 사용 |
avg | 평균 사용 |
max/min | 큰 값 또는 작은 값 사용 |
✅ 전체 예시 쿼리
{
"query": {
"function_score": {
"query": {
"match": {
"title": "카카오"
}
},
"functions": [
{
"field_value_factor": {
"field": "likes",
"factor": 1.2,
"modifier": "sqrt",
"missing": 0
}
},
{
"field_value_factor": {
"field": "clicks",
"factor": 0.8,
"modifier": "log1p",
"missing": 1
}
},
{
"gauss": {
"createdAt": {
"origin": "now",
"scale": "15d",
"decay": 0.4
}
}
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
📌 요약
요소 | 설명 |
---|---|
function_score | 점수 계산 방식 커스터마이징 |
field_value_factor | 숫자 필드 기반 가중치 반영 |
gauss | 최신성 점수 반영 (decay) |
score_mode | 여러 function 결합 방식 (sum , avg 등) |
boost_mode | 기존 점수와 결합 방식 (multiply , replace 등) |
🎯 매핑 설계 잘하는 팁 & 전략
이 문서는 Elasticsearch에서 매핑(mapping)을 효율적으로 설계하기 위한 팁과 개념들을
http://localhost:9200/contents
기준으로 설명합니다.
1. 🎯 매핑 설계 잘하는 팁
🧹 불필요한 필드 제거
- 저장할 필요가 없는 데이터는 매핑 자체를 하지 않거나
_source
에서 제외 - 검색/필터링/집계에 쓰이지 않는 데이터는 제외하는 것이 성능에 유리
🧷 타입 명확히 지정
데이터 예시 | 적절한 타입 |
---|---|
날짜 | date |
정수 | integer |
가격 | float |
이메일, URL | keyword |
본문, 설명, 제목 | text |
🚫 동적 매핑(auto mapping) 주의
- 설정하지 않으면 자동으로
text + keyword
로 매핑됨 → 원치 않는 분석기 적용 가능성 - 명시적인 매핑 정의 권장
2. ⚖️ text vs keyword
항목 | text | keyword |
---|---|---|
분석 여부 | 분석됨 (tokenizer, filter 적용) | 분석되지 않음 (전체값 그대로) |
검색 목적 | 전문(full-text) 검색 | 정확한 일치, 집계/정렬/필터용 |
예시 필드 | 제목, 내용, 댓글 | ID, 이메일, 태그, 카테고리 |
3. 🔀 multi-field 전략
🧠 하나의 필드를 여러 방식으로 활용
예: title
필드를 검색도 하고, 정렬/집계도 하려면?
📉 쿼리 설계 실수 예시 10가지
Elasticsearch에서 쿼리 성능과 정확도에 영향을 주는 대표적인 실수 10가지를 정리한 문서입니다.
실전 검색 엔진에서 흔히 발생하며, 사전에 방지하거나 리팩터링 시 주의해야 할 포인트입니다.
1. ❌ filter
없이 must
만 사용하는 경우
must
는 relevance score 계산 포함 → 성능 저하- 단순 조건은 반드시
filter
사용
"bool": {
"must": [ { "term": { "status": "active" } } ] // ❌
}
✅ 개선 예시:
"bool": {
"filter": [ { "term": { "status": "active" } } ]
}
2. ❌ from
값을 너무 크게 사용하는 경우
from: 10000
이상은 성능 문제 + 기본 max_result_window 초과 오류- deep pagination 비효율적
✅ 해결 방법:
📊 검색 로그 기반 점수 튜닝 전략
이 문서는 사용자의 검색 로그(search logs) 를 활용하여
검색 점수를 튜닝하고 추천 기능을 강화하는 전략을 설명합니다.
✅ 1. search_logs 인덱스 구성 예시
{
"mappings": {
"properties": {
"query": { "type": "keyword" },
"clicked_doc_id": { "type": "keyword" },
"timestamp": { "type": "date" },
"user_id": { "type": "keyword" }
}
}
}
- 각 로그는 사용자가 어떤 검색어로 어떤 문서를 클릭했는지를 기록
clicked_doc_id
기준으로 클릭 빈도 집계 가능
📈 2. 검색어 → 클릭률 분석
예시:
검색어 | 검색 횟수 | 클릭 횟수 | 클릭률 (%) |
---|---|---|---|
카카오 | 100 | 40 | 40% |
삼성 | 80 | 50 | 62.5% |
- 클릭률이 높은 검색어는 추천 검색어로 활용 가능
- 특정 문서가 어떤 검색어에 자주 클릭되는지 분석해 점수 반영
🔍 3. 인기 키워드 추출
{
"size": 0,
"aggs": {
"popular_queries": {
"terms": {
"field": "query",
"size": 10
}
}
}
}
- 최근 n일 동안 가장 많이 검색된 키워드 집계 가능
✨ 4. 추천어 생성 예시
카
입력 시 → [“카카오”, “카드”, “카메라”] 추천- 인기 키워드를 기반으로 prefix 매칭 추천어 생성 가능
🛠️ 5. 오타 교정 (Did You Mean)
{
"suggest": {
"text": "kakaoo",
"spellcheck": {
"term": {
"field": "query"
}
}
}
}
- 실제 로그에 존재하는 단어를 기준으로 오타 보정 가능
did_you_mean
기능 구현 가능
🚀 6. 클릭 수 기반 추천 점수 반영
방법 1: 검색 쿼리에서 function_score 사용
{
"query": {
"function_score": {
"query": {
"match": {
"title": "카카오"
}
},
"field_value_factor": {
"field": "click_count",
"modifier": "log1p",
"missing": 0
},
"boost_mode": "multiply"
}
}
}
- 클릭 횟수가 높은 문서일수록 가중치가 올라감
- 로그 데이터를 집계해
click_count
필드로 인덱싱해야 함
✅ 요약
전략 항목 | 설명 |
---|---|
search_logs 인덱스 | 검색어, 클릭 문서, 시간, 사용자 ID 등 기록 |
클릭률 기반 분석 | 검색어 → 클릭률 계산 → 인기 검색어 추출 |
추천어 생성 | 자주 검색된 검색어 기반 자동완성 구현 가능 |
오타 교정 | 자주 등장한 검색어 기반 "Did You Mean" 구현 가능 |
점수 반영 | function_score 로 클릭 수 기반 가중치 부여 |
🔁 추천/정확도/최신/인기순 분리 전략
Elasticsearch에서 검색 결과를 다양한 기준으로 정렬하는 전략을 정리한 문서입니다.
사용자의 니즈에 따라 정확도, 인기, 최신, 추천순을 분리하여 검색 품질을 높일 수 있습니다.
1. 🎯 정확도 정렬 (Relevance Based)
전략
multi_match
쿼리로 다양한 필드 검색- 중요한 필드에는
boost
적용
{
"multi_match": {
"query": "카카오",
"fields": ["title^3", "content", "tags^2"],
"type": "best_fields"
}
}
title
필드는^3
→ 가장 높은 점수 기여tags
는^2
→ 중간 가중치
2. 🔥 인기순 정렬 (Popularity Based)
전략
likes
,clicks
등의 수치 필드를 점수에 반영function_score
+field_value_factor
사용
{
"function_score": {
"query": {
"match": {
"title": "카카오"
}
},
"functions": [
{
"field_value_factor": {
"field": "likes",
"factor": 1.5,
"modifier": "sqrt",
"missing": 0
}
},
{
"field_value_factor": {
"field": "clicks",
"factor": 0.5,
"modifier": "log1p",
"missing": 1
}
}
],
"score_mode": "sum",
"boost_mode": "replace"
}
}
3. 🕒 최신순 정렬 (Latest First)
전략
createdAt
필드로 정렬
{
"sort": [
{ "createdAt": { "order": "desc" } }
]
}
- 최신 문서가 먼저 노출됨
- relevance 점수는 무시됨
4. 🧠 추천순 정렬 (Recommendation Ranking)
전략
- 정확도 × 인기 × 최신성 조합
function_score
로 통합 점수 계산
{
"function_score": {
"query": {
"multi_match": {
"query": "카카오",
"fields": ["title^3", "content", "tags^2"]
}
},
"functions": [
{
"field_value_factor": {
"field": "likes",
"factor": 1.2,
"modifier": "sqrt"
}
},
{
"gauss": {
"createdAt": {
"origin": "now",
"scale": "10d",
"decay": 0.5
}
}
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
- 정확도는 기본
_score
likes
: 인기 점수gauss(createdAt)
: 최신성 점수multiply
로 결합 → 추천순
✅ 요약 정리
정렬 기준 | 전략 요약 |
---|---|
정확도순 | multi_match + boost |
인기순 | function_score + likes , clicks |
최신순 | sort by createdAt desc |
추천순 | 정확도 × 인기 × 최신성 조합 (function_score ) |
🔗 동의어 처리 (synonym filter)
이 문서는 Elasticsearch에서 동의어(synonym) 를 처리하여
다양한 표현을 하나의 의미로 검색할 수 있도록 하는 전략을 설명합니다.
✅ 1. 동의어 필터란?
사용자가 “검색"이라고 입력했을 때
“서치”, “찾기”, “검색” 등 여러 표현을 동시에 검색할 수 있도록 만드는 텍스트 확장 필터입니다.
🔄 2. 동의어 처리 예시
입력어 | 확장된 검색어 |
---|---|
검색 | 검색, 서치, 찾기 |
자동차 | 차량, 카, 오토모바일 |
⚙️ 3. analyzer에 동의어 filter 설정 (inline 방식)
{
"settings": {
"analysis": {
"filter": {
"synonym_filter": {
"type": "synonym",
"synonyms": [
"검색, 서치, 찾기",
"자동차, 차량, 오토모바일"
]
}
},
"analyzer": {
"synonym_analyzer": {
"tokenizer": "standard",
"filter": ["lowercase", "synonym_filter"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "synonym_analyzer"
}
}
}
}
📄 4. 파일 기반 동의어 처리
대용량 동의어 사전은 파일로 관리할 수 있습니다.
🔤 자동완성 & ngram 검색 전략
이 문서는 검색어 자동완성 기능을 구현할 때 사용되는 Elasticsearch 전략을 설명합니다.
prefix
,edge_ngram
,completion
세 가지 접근 방식과 각각의 장단점, 설정 방법을 다룹니다.
1. 📌 자동완성 구현 방식 비교
방식 | 장점 | 단점 |
---|---|---|
prefix | 설정 간단, 기본 기능 | 검색 정확도 낮음, 노이즈 발생 |
edge_ngram | 검색어 조합 다양, 부분일치 가능 | 인덱스 용량 증가, 튜닝 필요 |
completion | 속도 빠름, 추천어에 적합 | 기능 제한, 스코어 커스터마이징 어려움 |
2. ✍️ prefix 쿼리 예시
{
"query": {
"prefix": {
"title": "카카"
}
}
}
- 사용자가 “카카” 입력 시 “카카오”, “카카시”, “카카푸” 등 반환
- 정확도보다 간단한 시작 단어 기반 매칭에 적합
3. 🧱 edge_ngram 기반 분석기 설정 예시
매핑 설정
{
"settings": {
"analysis": {
"tokenizer": {
"autocomplete_tokenizer": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20,
"token_chars": ["letter", "digit"]
}
},
"analyzer": {
"autocomplete": {
"tokenizer": "autocomplete_tokenizer",
"filter": ["lowercase"]
},
"autocomplete_search": {
"tokenizer": "standard",
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "autocomplete",
"search_analyzer": "autocomplete_search"
}
}
}
}
autocomplete_tokenizer
:edge_ngram
기반으로 부분 토큰 생성search_analyzer
: 표준 분석기로 검색어 그대로 사용
4. 🔍 검색 쿼리 예시 (edge_ngram)
{
"query": {
"match": {
"title": {
"query": "카카"
}
}
}
}
- 사용자가
카카
라고 검색하면카카오
,카카시
,카카XX
모두 검색 가능 - 토큰화 예시 (카카오):
카
,카카
,카카오
5. ⚡ completion 필드 예시
매핑 설정
{
"mappings": {
"properties": {
"suggest": {
"type": "completion"
}
}
}
}
검색 쿼리
{
"suggest": {
"title-suggest": {
"prefix": "카카",
"completion": {
"field": "suggest"
}
}
}
}
- autocomplete에 특화된 데이터 구조로 빠른 추천
- 단점: 일반적인 match 쿼리와 통합 어려움
✅ 요약 정리
전략 | 설명 |
---|---|
prefix | 간단하지만 정확도 낮고 유연성 부족 |
edge_ngram | 강력한 자동완성 구현 가능, 인덱스 용량 증가 주의 |
completion | 빠른 추천 시스템에 적합, 기능 제한 있음 |
🔰기본 개념
이 문서는 Elasticsearch를 처음 접하는 사람들을 위해 인덱스, 매핑, 구조 등을 설명합니다.
예제 URL은http://localhost:9200/contents
기준입니다.
1. 📌 /contents
는 무엇인가?
http://localhost:9200/contents
에서contents
는 인덱스(index) 이름입니다.- Elasticsearch에서 인덱스는 문서들의 집합입니다.
- RDB로 비유하면 **테이블(table)**에 해당합니다.
2. 🧱 Elasticsearch 구조 요약
Elasticsearch는 다음과 같은 계층 구조로 되어 있습니다:
클러스터
└── indices (복수형, 여러 인덱스)
└── index (예: contents)
└── documents (JSON 형식 데이터)
└── fields (각 데이터의 속성)
RDB 용어 | Elasticsearch 용어 |
---|---|
Database | Cluster |
Table | Index |
Row | Document |
Column | Field |
3. 📄 매핑(Mapping) 개념과 _mapping
의 역할
- **매핑(mapping)**이란 인덱스 내 문서의 필드 구조와 타입을 정의한 것입니다.
- 예를 들어 필드가
text
인지keyword
인지, 날짜인지 숫자인지를 정의합니다. - RDB로 치면 테이블의 스키마(schema) 와 비슷한 개념입니다.
✅ 매핑 확인 방법
GET http://localhost:9200/contents/_mapping?pretty
- 위 요청은
contents
인덱스의 매핑 정보를 확인하는 API입니다.
🔠 대표적인 필드 타입
필드 타입 | 설명 |
---|---|
text | 검색용 텍스트 (analyzer로 형태소 분석 가능) |
keyword | 필터, 정렬, 집계용 문자열 (분석 불가) |
integer | 정수형 숫자 |
float | 실수형 숫자 |
date | 날짜/시간 정보 |
boolean | true/false 값 |
✅ 정리 요약
항목 | 설명 |
---|---|
/contents | 인덱스 이름 (RDB의 테이블과 유사) |
인덱스 구조 | 클러스터 → 인덱스 → 문서 → 필드 |
매핑(Mapping) | 문서의 필드 구조 정의 (타입, 분석기 등) |
_mapping API | 인덱스의 현재 매핑 정보를 확인하는 엔드포인트 |
🚨 실전 매핑 실수 모음
이 문서는 Elasticsearch를 사용할 때 자주 발생하는 매핑 설계 실수 사례들을 정리한 가이드입니다.
실수를 피하기 위한 베스트 프랙티스도 함께 소개합니다.
1. ❌ keyword 필드에 match 사용
문제 설명
keyword
필드는 분석되지 않은 문자열이기 때문에 match
쿼리로는 동작하지 않음.
"match": {
"status": "published" // keyword 필드일 경우 동작 X
}
해결 방법
match
대신term
쿼리를 사용해야 함
"term": {
"status": "published"
}
2. ❌ text 필드로 정렬
문제 설명
text
필드는 분석된 문자열이기 때문에 정렬이 불가능하거나 성능이 매우 나쁨.
Logs Monitoring 정리 (실무 중심)
✅ 1. 전체 아키텍처 흐름
[App/Infra Logs]
↓
Filebeat / Logstash
↓
[Elasticsearch]
↓
Kibana (대시보드)
- Filebeat: 로그 파일 수집기 (Lightweight)
- Logstash: 데이터 처리, 변환, 파싱
- Elasticsearch: 로그 저장/검색/분석
- Kibana: 시각화 및 모니터링 대시보드
✅ 2. Filebeat (로그 수집기)
🔹 역할
- 로그 파일 tailing
- Multiline (스택트레이스 등) 지원
- 시스템 로그, Docker 로그, Nginx, Java 로그 등 수집 가능
🔹 설정 예시
filebeat.inputs:
- type: log
paths:
- /var/log/myapp/*.log
multiline.pattern: '^\['
multiline.negate: true
multiline.match: after
output.elasticsearch:
hosts: ["http://localhost:9200"]
index: "myapp-logs-%{+yyyy.MM.dd}"
💡 실무 팁
multiline
설정 필수 (Java stack trace)fields
옵션으로 서비스명, 환경 태깅- JSON 로그 구조화 추천
✅ 3. Logstash (필요 시 중간 가공)
🔹 역할
- 로그 파싱 및 정제
- Grok 패턴, mutate, date 필터 등
- Kafka → Logstash → ES 구성 가능
🔹 Grok 예시
filter {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:level}\] %{GREEDYDATA:msg}" }
}
date {
match => [ "timestamp", "ISO8601" ]
}
}
💡 실무 팁
- Filebeat에서 JSON 구조면 Logstash 생략 가능
- Kafka 중계/복잡한 parsing 시 유용
✅ 4. Kibana (시각화 및 알림)
🔹 주요 기능
- Discover: 실시간 로그 조회
- Dashboard: 시각화
- Alerting: 이상 탐지 알림
- Lens/TSVB: 시간 기반 분석
💡 실무 팁
timestamp
,level
,logger
,service_name
필드 표준화- Error 로그 대시보드 구성
- 복합 필터링 예시:
log.level: ERROR AND NOT message:"Connection reset"
✅ 5. 성능/보안 로그 모니터링
유형 | 설명 |
---|---|
JVM GC 로그 | 메모리 문제 추적 |
Nginx / Apache | API 응답 시간, 요청 수 분석 |
Kafka / Redis | Broker 상태, throughput 추적 |
Spring Boot | actuator 로그 + slf4j 로그 |
시스템 로그 | CPU, 메모리 사용량 추적 |
인증 로그 | 로그인 시도, 실패 분석 |
✅ 6. 인덱스 전략 및 유지 관리
index.lifecycle
정책 (ILM): 오래된 로그 자동 삭제- 일별 인덱스:
myapp-logs-2025.07.11
- 템플릿 매핑 필수:
@timestamp
,log.level
등 - hot-warm-cold 아키텍처 고려
✅ 7. 실무 구성 예시 (Docker Compose)
services:
elasticsearch:
image: elasticsearch:8.17.6
environment:
- discovery.type=single-node
ports:
- 9200:9200
kibana:
image: kibana:8.17.6
ports:
- 5601:5601
depends_on:
- elasticsearch
filebeat:
image: docker.elastic.co/beats/filebeat:8.17.6
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml
- /var/log:/var/log:ro
✅ 정리 요약
구성 요소 | 역할 | 비고 |
---|---|---|
Filebeat | 로그 수집 | Multiline 지원, 간단 |
Logstash | 로그 정제 | Grok 필터, Kafka 중계 |
Elasticsearch | 저장/검색 | 인덱스 설계 중요 |
Kibana | 시각화/모니터링 | Discover, Alert 활용 |
Query DSL 정리 (실무 중심)
🔍 1. term 쿼리
{ "term": { "status": "active" } }
- 정확히 일치하는 값을 찾을 때 사용
- 형태소 분석되지 않은 필드 (
keyword
,int
,boolean
)에 적합 text
필드에는text.keyword
로 사용해야 정확히 일치- 실무: 상태, ID, 카테고리 코드 등
🔍 2. terms 쿼리
{ "terms": { "status": ["active", "ready"] } }
- 여러 값 중 하나라도 일치하면 검색
IN
조건처럼 사용- 65,536개 이상의 값은 사용 불가 (성능 저하)
🔍 3. match 쿼리
{ "match": { "title": "엘라스틱 서치 강의" } }
- text 필드에 사용
- 형태소 분석됨 (자연어 검색)
- 완전 일치 아님 → 관련된 문서도 나올 수 있음
🔍 4. match_phrase 쿼리
{ "match_phrase": { "title": "엘라스틱 서치 강의" } }
- 정확한 문장 일치 (순서 유지)
- 중간 단어 빠지면 매칭되지 않음
🔍 5. bool 쿼리
{
"bool": {
"must": [ ... ], // 모두 만족 (AND)
"should": [ ... ], // 하나 이상 만족하면 OK (OR 느낌)
"must_not": [ ... ], // 만족하면 제외
"filter": [ ... ] // 점수 영향 없음, 빠름
}
}
✔️ 실전 사용 예
{
"bool": {
"must": [
{ "term": { "status": "active" } },
{ "match": { "title": "엘라스틱" } }
],
"filter": [
{ "range": { "created_date": { "gte": "now-7d/d" } } }
],
"must_not": [
{ "term": { "is_deleted": true } }
]
}
}
🔍 6. range 쿼리
{ "range": { "price": { "gte": 1000, "lte": 5000 } } }
- 숫자나 날짜 필드에 범위 조건 적용
- 실무: 가격, 날짜 필터링
🔍 7. exists 쿼리
{ "exists": { "field": "thumbnail_url" } }
- 필드가 존재하는지 확인
🔍 8. wildcard / regexp / prefix
{ "wildcard": { "title": "엘라*" } }
wildcard
,regexp
,prefix
는 느릴 수 있음- analyzed 되지 않은
keyword
필드에 사용 추천
🔍 9. multi_match
{
"multi_match": {
"query": "검색어",
"fields": ["title", "description"]
}
}
- 여러 필드에 match 쿼리 적용
🔍 10. script 쿼리
{
"script_score": {
"query": { "match_all": {} },
"script": {
"source": "doc['click_count'].value * 0.5 + doc['like_count'].value"
}
}
}
- 스크립트 기반 동적 스코어
- 캐시 안 되고 느릴 수 있음 → 최소 사용 권장
✅ 실무 패턴 정리
목적 | 쿼리 |
---|---|
필터링 + 검색 | bool + must + filter |
정확 조건 | term , terms |
자연어 검색 | match , multi_match |
날짜 조건 | range (filter 사용 권장) |
제외 조건 | must_not |
가중치 적용 | should , script_score |
추천 기능 | should + minimum_should_match |
📌 추천 실전 예시
1. 필터 조건 + 본문 검색
{
"bool": {
"must": [
{ "match": { "content": "엘라스틱서치 설정법" } }
],
"filter": [
{ "term": { "status": "published" } },
{ "range": { "created_date": { "gte": "now-1M/M" } } }
]
}
}
2. 추천어 기반 검색
{
"bool": {
"must": [
{ "term": { "status": "active" } }
],
"should": [
{ "match": { "title": "엘라스틱" } },
{ "match": { "tags": "검색엔진" } }
],
"minimum_should_match": 1
}
}
⚠️ 주의할 점
text
필드에는term
❌ →.keyword
사용match
는 유사도 기반 → 정확 검색은term
filter
는 점수 계산 안 함 → 빠름should
만 있을 경우minimum_should_match
설정 안 하면 모두 미일치해도 통과 가능script
는 느림wildcard
,regexp
는 느림 → 되도록ngram
대체 고려
심화 쿼리 정리 (실무 중심)
1. 🔧 function_score 쿼리
📌 개념
검색 점수(score)에 함수를 적용해서 가중치(weighting)나 랜덤성 부여
✅ 사용 예시
{
"function_score": {
"query": { "match": { "title": "엘라스틱" } },
"functions": [
{
"filter": { "term": { "is_premium": true } },
"weight": 2
},
{
"random_score": {}
}
],
"boost_mode": "multiply"
}
}
💡 실무 활용
- 프리미엄 콘텐츠 우선 노출
- 추천 콘텐츠 랜덤성 부여
- 클릭 수, 조회 수 기반 가중치 조정
⚠️ 주의사항
- 함수마다 성능 차이 큼 (특히 script 포함 시)
boost_mode
,score_mode
조정 필요
2. 🧮 script_score 쿼리
📌 개념
커스텀 스크립트로 score 직접 계산
페이징 전략 정리 (실무 중심)
✅ 1. 기본 페이징: from + size
🔹 개념
SQL의 OFFSET, LIMIT처럼 특정 위치부터 몇 개를 가져옴.
{
"query": { "match_all": {} },
"from": 0,
"size": 10
}
✅ 특징
- 가장 기본적인 방법
- from=0, size=10 → 1페이지, from=10, size=10 → 2페이지
⚠️ 주의사항
- from이 클수록 성능이 나빠짐 (deep pagination)
- 기본 최대는 10,000 (index.max_result_window 초과 시 에러)
💡 실무 팁
- 리스트 조회에는 적합
- 대용량 페이징에는 search_after 또는 scroll 사용
✅ 2. 안정적인 순차 페이징: search_after
🔹 개념
정렬된 필드 기준으로 커서를 넘겨 다음 페이지 요청
🛠️ 매핑 + Analyzer + 검색 쿼리 종합 실습
이 문서는 Elasticsearch에서
posts
인덱스를 대상으로
매핑 설계, custom analyzer 구성, 검색 쿼리 실습을 종합적으로 연습하기 위한 실전 예제입니다.
1. 🎯 목표 요약
- 커스텀 analyzer를 적용한
posts
인덱스 생성 title
,tags
,content
필드를 적절히 text/keyword로 구성- 검색 쿼리에서 match, filter, sort, search_after 등 실제 활용
2. ⚙️ 커스텀 Analyzer + 매핑 예제
PUT http://localhost:9200/posts
{
"settings": {
"analysis": {
"tokenizer": {
"my_nori_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed"
}
},
"filter": {
"my_pos_filter": {
"type": "nori_part_of_speech",
"stoptags": ["E", "IC", "J"]
},
"my_stop_filter": {
"type": "stop",
"stopwords": ["은", "는", "이", "가"]
}
},
"analyzer": {
"my_korean_analyzer": {
"type": "custom",
"tokenizer": "my_nori_tokenizer",
"filter": [
"lowercase",
"my_pos_filter",
"my_stop_filter"
]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "my_korean_analyzer",
"fields": {
"raw": { "type": "keyword" }
}
},
"tags": {
"type": "keyword"
},
"content": {
"type": "text",
"analyzer": "my_korean_analyzer"
},
"author": {
"type": "keyword"
},
"created_at": {
"type": "date"
}
}
}
}
3. 📦 예시 문서 삽입 (Bulk API)
POST http://localhost:9200/posts/_bulk
{ "index": {} }
{ "title": "카카오페이 서비스 출시", "tags": ["fintech", "kakao"], "content": "카카오의 새로운 간편결제 서비스가 시작되었습니다.", "author": "alice", "created_at": "2024-01-01T10:00:00Z" }
{ "index": {} }
{ "title": "네이버 지도 업그레이드", "tags": ["map", "naver"], "content": "지도 검색이 더 빠르고 정확해졌습니다.", "author": "bob", "created_at": "2024-02-15T12:30:00Z" }
{ "index": {} }
{ "title": "구글 검색 AI 도입", "tags": ["ai", "google"], "content": "AI로 더 똑똑한 검색이 가능해졌습니다.", "author": "charlie", "created_at": "2024-03-10T15:00:00Z" }
4. 🔍 검색 쿼리 예제
🎯 4-1. 카카오
를 포함하고 fintech 태그를 가진 문서 검색
POST http://localhost:9200/posts/_search
{
"query": {
"bool": {
"must": [
{ "match": { "content": "카카오" } }
],
"filter": [
{ "term": { "tags": "fintech" } }
]
}
}
}
🧭 4-2. 정렬 + search_after를 사용한 페이징 검색
POST http://localhost:9200/posts/_search
{
"size": 2,
"query": {
"match_all": {}
},
"sort": [
{ "created_at": "desc" },
{ "_id": "asc" }
],
"search_after": ["2024-01-01T10:00:00Z", "doc_id_2"]
}
🔍 4-3. title 정렬 (multi-field 사용)
POST http://localhost:9200/posts/_search
{
"query": {
"match": {
"title": "검색"
}
},
"sort": [
{ "title.raw": "asc" }
]
}
✅ 요약
항목 | 설명 |
---|---|
custom analyzer 설정 | nori tokenizer + 불용어 + 품사 필터 적용 |
multi-field 구성 | title은 text + keyword 함께 사용 (raw 정렬용) |
검색 쿼리 활용 | match, filter, sort, search_after 조합 |
Bulk 삽입 | 대량 데이터 테스트를 위한 빠른 삽입 |
🧠 검색 점수 분석: explain 활용하기
이 문서는
?explain=true
를 사용하여 검색 점수(BM25 등)의 계산 근거를 파악하는 방법을 설명합니다.
어떤 문서가 왜 높은 점수를 받았는지 이해하고, 검색 품질을 향상시키는 데 중요한 역할을 합니다.
✅ 1. explain 파라미터란?
검색 API 호출 시 쿼리 파라미터에 ?explain=true
를 추가하면,
각 문서의 _score
가 어떻게 계산되었는지 자세한 설명이 함께 반환됩니다.
예시 호출
GET /posts/_search?explain=true
{
"query": {
"match": {
"title": "카카오"
}
}
}
📊 2. BM25 점수 계산 요소
BM25는 기본적으로 다음 3가지 요소로 점수를 계산합니다:
🧠 형태소 분석기와 Analyzer 비교
이 문서는 Elasticsearch에서의 형태소 분석 개념과
다양한 analyzer의 차이를 이해하기 위한 실전 예제를 포함하고 있습니다.
5. 🧠 형태소 분석기란 무엇인가?
- 형태소 분석기(morphological analyzer)는 문장을 의미 단위(형태소) 로 분리하는 역할을 합니다.
- Elasticsearch에서는 analyzer를 통해 이 작업을 수행합니다.
⚙️ analyzer 구성 요소
analyzer = tokenizer + filter
- tokenizer: 문장을 기본 단위로 쪼갬 (예: 단어 기준, n-gram 등)
- filter: 소문자 변환, 불용어 제거 등 후처리 작업 수행
🇰🇷 nori: 한글 형태소 분석기
- Elasticsearch에서 제공하는 nori analyzer는 한국어에 특화된 형태소 분석기입니다.
- 띄어쓰기만으로 분리하기 어려운 어미, 조사, 접두어, 복합어 등을 처리 가능
- 예:
"삼성전자의 주가는"
→삼성전자
,의
,주가
,는
6. 🔬 다양한 analyzer 비교
테스트 문장
삼성전자의 주가는 7만 원이다.
1️⃣ standard analyzer (기본)
POST http://localhost:9200/_analyze
{
"analyzer": "standard",
"text": "삼성전자의 주가는 7만 원이다."
}
📌 결과 예시:
🧮 추천순 점수 공식 직접 설계
이 문서는 Elasticsearch에서 추천순 정렬을 위해 점수(score)를 수학적으로 설계하는 방법을 설명합니다.
BM25
점수와 사용자 행동 데이터(likes
,clicks
) 그리고최신성
을 조합하여 추천 점수를 만들 수 있습니다.
✅ 목표 점수 공식
최종 점수 = BM25 × 인기 점수 + 최신성 점수
- BM25: Elasticsearch 기본 검색 점수
- 인기 점수:
likes
,clicks
를 기반으로 계산 - 최신성 점수:
createdAt
필드를 기반으로 decay 적용
1. 🎯 BM25 (기본 점수)
- 기본적으로
match
,multi_match
쿼리를 사용할 경우 BM25 점수가 자동 계산됩니다. - 정확도 기반 검색에 사용됩니다.
{
"multi_match": {
"query": "카카오",
"fields": ["title^3", "content", "tags^2"]
}
}
2. 📈 인기 점수 수식 (log1p, sqrt)
{
"field_value_factor": {
"field": "likes",
"factor": 1.2,
"modifier": "sqrt",
"missing": 0
}
},
{
"field_value_factor": {
"field": "clicks",
"factor": 0.8,
"modifier": "log1p",
"missing": 1
}
}
sqrt
: 좋아요 수의 증가폭을 완화log1p
: 클릭 수가 많아도 완만하게 점수 증가
3. 🕒 최신성 점수 (decay 함수)
{
"gauss": {
"createdAt": {
"origin": "now",
"scale": "10d",
"decay": 0.4
}
}
}
createdAt
기준으로 점수 감소- 최신 문서일수록 점수가 높고 오래된 문서는 낮아짐
4. ⚙️ score_mode / boost_mode 설정
항목 | 설명 |
---|---|
score_mode | 여러 function 점수를 어떻게 결합할지 (sum, avg 등) |
boost_mode | 기존 _score 와 function 결과 결합 방식 |
추천 공식에 맞는 설정
"score_mode": "sum",
"boost_mode": "multiply"
- 점수 함수끼리는
sum
- 그 결과를 BM25
_score
와×
(multiply)
5. 🧪 전체 예시 쿼리
{
"query": {
"function_score": {
"query": {
"multi_match": {
"query": "카카오",
"fields": ["title^3", "content", "tags^2"]
}
},
"functions": [
{
"field_value_factor": {
"field": "likes",
"factor": 1.2,
"modifier": "sqrt",
"missing": 0
}
},
{
"field_value_factor": {
"field": "clicks",
"factor": 0.8,
"modifier": "log1p",
"missing": 1
}
},
{
"gauss": {
"createdAt": {
"origin": "now",
"scale": "10d",
"decay": 0.4
}
}
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
✅ 정리
요소 | 설명 |
---|---|
BM25 | 기본 텍스트 매칭 점수 |
인기 점수 | likes , clicks → sqrt, log1p 적용 |
최신성 점수 | createdAt → decay 적용 |
최종 조합 | score_mode: sum , boost_mode: multiply |
🧰 인덱스(`/contents`)에서 가능한 작업 목록
이 문서는
http://localhost:9200/contents
인덱스를 기준으로
Elasticsearch에서 수행할 수 있는 문서 작업부터 고급 인덱스 관리 기능까지 정리한 문서입니다.
✅ 1. 기본 문서 작업
📥 문서 생성 (Create)
PUT http://localhost:9200/contents/_doc/1
{
"title": "Elasticsearch 기본 개념",
"author": "laon",
"createdAt": "2025-07-11"
}
📤 문서 조회 (Read)
GET http://localhost:9200/contents/_doc/1
📝 문서 수정 (Update)
POST http://localhost:9200/contents/_update/1
{
"doc": {
"author": "laon-ez"
}
}
❌ 문서 삭제 (Delete)
DELETE http://localhost:9200/contents/_doc/1
✅ 2. 매핑 정보 확인
📄 매핑 확인
GET http://localhost:9200/contents/_mapping?pretty
✅ 3. 인덱스 설정 관리
⚙️ 설정 변경 (예: refresh_interval 조정)
PUT http://localhost:9200/contents/_settings
{
"index": {
"refresh_interval": "30s"
}
}
✅ 4. Bulk Insert (대량 삽입)
POST http://localhost:9200/contents/_bulk
{ "index": { "_id": "1" } }
{ "title": "문서 A", "views": 10 }
{ "index": { "_id": "2" } }
{ "title": "문서 B", "views": 5 }
※ 헤더는 Content-Type: application/x-ndjson