본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 29. · 17 Views
Elasticsearch 검색 쿼리 기초 완벽 가이드
Elasticsearch의 핵심 검색 쿼리들을 초급 개발자 눈높이에서 설명합니다. Query DSL의 기본 구조부터 match, term, range, bool 쿼리까지 실무에서 바로 활용할 수 있는 예제와 함께 배워봅니다.
목차
1. Query DSL 이해
어느 날 김개발 씨는 회사에서 새로운 프로젝트를 맡게 되었습니다. 상품 검색 기능을 구현해야 하는데, 선배가 "Elasticsearch 써봐"라고 했습니다.
처음 접하는 기술이라 막막했던 김개발 씨는 일단 공식 문서를 펼쳤습니다. 그런데 Query DSL이라는 낯선 용어가 눈에 들어왔습니다.
Query DSL은 Domain Specific Language의 약자로, Elasticsearch에서 검색 쿼리를 작성하기 위한 전용 언어입니다. 마치 SQL이 관계형 데이터베이스를 다루는 언어인 것처럼, Query DSL은 Elasticsearch를 다루는 언어라고 생각하면 됩니다.
JSON 형식으로 작성되며, 이 문법만 익히면 복잡한 검색 조건도 자유자재로 표현할 수 있습니다.
다음 코드를 살펴봅시다.
// Elasticsearch Query DSL의 기본 구조
const searchQuery = {
// query: 모든 검색의 시작점
query: {
// 검색 유형을 지정합니다 (match, term, bool 등)
match: {
// 검색할 필드와 검색어를 지정합니다
title: "elasticsearch 입문"
}
},
// 반환할 결과 수 (선택사항)
size: 10,
// 시작 위치 - 페이징용 (선택사항)
from: 0
};
김개발 씨는 입사 6개월 차 백엔드 개발자입니다. 지금까지는 MySQL만 사용해왔는데, 이번에 처음으로 Elasticsearch를 접하게 되었습니다.
SQL은 익숙한데, 이 새로운 검색 엔진은 어떻게 다뤄야 할까요? 선배 개발자 박시니어 씨가 옆자리에서 힌트를 줍니다.
"Elasticsearch는 Query DSL이라는 걸 써. SQL의 SELECT 문이라고 생각하면 돼." 그렇다면 Query DSL이란 정확히 무엇일까요?
쉽게 비유하자면, Query DSL은 마치 도서관 사서에게 책을 찾아달라고 요청하는 메모와 같습니다. "컴퓨터 관련 책 중에서 2023년 이후에 출판된 것, 초급자용으로 부탁해요"라고 적어서 전달하면, 사서가 정확히 그 조건에 맞는 책을 찾아주는 것입니다.
Query DSL도 마찬가지로, 우리가 원하는 검색 조건을 JSON 형식의 메모로 작성하면 Elasticsearch가 그에 맞는 문서를 찾아줍니다. Query DSL이 없던 시절에는 어땠을까요?
사실 검색 엔진을 직접 구현하려면 정말 많은 것을 고려해야 합니다. 텍스트를 어떻게 분석할지, 유사도는 어떻게 계산할지, 여러 조건을 어떻게 조합할지 등 복잡한 로직을 모두 직접 작성해야 했습니다.
이는 개발 시간도 오래 걸리고 버그도 많이 발생하는 원인이 되었습니다. 바로 이런 문제를 해결하기 위해 Elasticsearch는 Query DSL을 제공합니다.
Query DSL을 사용하면 복잡한 검색 로직을 간단한 JSON으로 표현할 수 있습니다. 또한 Elasticsearch가 내부적으로 최적화를 수행하므로 성능도 보장됩니다.
무엇보다 표준화된 문법 덕분에 팀원 간 협업이 쉬워진다는 큰 장점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
가장 바깥쪽에 query라는 키가 있습니다. 이것이 모든 검색 쿼리의 시작점입니다.
그 안에 match라는 검색 유형이 지정되어 있습니다. match는 전문 검색을 수행하는 쿼리인데, 이에 대해서는 다음 장에서 자세히 다루겠습니다.
마지막으로 size와 from은 페이징을 위한 옵션으로, 검색 결과 중 몇 개를 어디서부터 가져올지 지정합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 쇼핑몰의 상품 검색 기능을 구현한다고 가정해봅시다. 사용자가 "나이키 운동화"라고 검색하면, Query DSL로 이 검색어를 담은 쿼리를 만들어 Elasticsearch에 전송합니다.
Elasticsearch는 수백만 개의 상품 데이터 중에서 관련성 높은 상품을 순식간에 찾아서 반환해줍니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 query 키를 빠뜨리는 것입니다. JSON 구조가 복잡해지면 어디에 query를 넣어야 하는지 헷갈릴 수 있습니다.
항상 최상위에 query 객체가 있어야 한다는 점을 기억하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.
Query DSL의 기본 구조를 이해한 김개발 씨는 자신감이 생겼습니다. "생각보다 어렵지 않네요.
JSON만 잘 작성하면 되는 거잖아요!" 이제 기본 구조를 알았으니, 다음 장에서는 실제로 검색을 수행하는 다양한 쿼리 유형들을 하나씩 살펴보겠습니다.
실전 팁
💡 - Query DSL은 항상 JSON 형식으로 작성하며, 최상위에 query 객체가 위치해야 합니다
- size와 from 옵션을 활용하면 페이징 처리를 쉽게 구현할 수 있습니다
- 복잡한 쿼리를 작성하기 전에 Kibana의 Dev Tools에서 먼저 테스트해보는 것이 좋습니다
2. match 쿼리 전문 검색
김개발 씨가 상품 검색 기능을 구현하기 시작했습니다. 사용자가 "편한 운동화"라고 검색하면 "편한"과 "운동화"가 포함된 상품을 모두 찾아야 합니다.
그런데 데이터베이스의 LIKE 검색으로는 뭔가 부족한 느낌이 들었습니다. 이때 선배가 말했습니다.
"그건 match 쿼리로 해야 해."
match 쿼리는 Elasticsearch에서 가장 많이 사용되는 전문 검색(Full-text Search) 쿼리입니다. 입력된 검색어를 분석기로 토큰화한 후, 해당 토큰들이 포함된 문서를 찾아냅니다.
마치 구글 검색처럼 자연어 검색이 가능하며, 검색어와 정확히 일치하지 않아도 관련된 문서를 찾아줍니다.
다음 코드를 살펴봅시다.
// match 쿼리 - 전문 검색의 기본
const matchQuery = {
query: {
match: {
// description 필드에서 "편한 운동화" 검색
description: {
query: "편한 운동화",
// operator: "and"면 모든 단어 포함 필수
// operator: "or"면 하나라도 포함되면 검색 (기본값)
operator: "or"
}
}
}
};
// 결과: "편한", "운동화" 중 하나라도 포함된 문서 반환
김개발 씨는 이전 프로젝트에서 MySQL의 LIKE 검색을 사용한 경험이 있습니다. '%운동화%'처럼 와일드카드를 사용했었죠.
그런데 이번에는 뭔가 다른 검색이 필요했습니다. 사용자가 "편안한 러닝화"라고 검색했을 때, "편한 운동화"라는 상품도 검색 결과에 나와야 합니다.
의미상 비슷한 내용이니까요. LIKE 검색으로는 이게 불가능했습니다.
그렇다면 match 쿼리는 어떻게 이 문제를 해결할까요? 쉽게 비유하자면, match 쿼리는 마치 똑똑한 비서와 같습니다.
"내일 오후에 김 부장님과 미팅 잡아줘"라고 말하면, 비서는 "내일", "오후", "김 부장님", "미팅"이라는 핵심 단어를 파악하고 일정을 잡아줍니다. match 쿼리도 마찬가지로 검색어를 핵심 단어들로 쪼개서 이해합니다.
이 과정을 **토큰화(Tokenization)**라고 합니다. "편한 운동화"라는 검색어가 들어오면, Elasticsearch의 분석기가 이를 "편한"과 "운동화"로 쪼갭니다.
그리고 이 두 토큰 중 하나라도 포함된 문서를 찾아서 반환합니다. 기본 동작이 OR 조건이기 때문입니다.
만약 두 단어가 모두 포함된 문서만 찾고 싶다면 어떻게 할까요? 바로 operator 옵션을 사용하면 됩니다.
operator를 "and"로 설정하면, "편한"과 "운동화"가 모두 포함된 문서만 검색됩니다. 반대로 "or"로 설정하면 둘 중 하나만 있어도 검색됩니다.
기본값은 "or"입니다. 위의 코드를 살펴보면, description 필드에서 검색을 수행하고 있습니다.
query 속성에 검색어를 넣고, operator 속성으로 검색 조건을 지정합니다. 이렇게 간단한 JSON만으로도 강력한 전문 검색이 가능해집니다.
실무에서 match 쿼리는 정말 자주 사용됩니다. 블로그 글 검색, 상품 검색, 뉴스 기사 검색 등 사용자가 자유롭게 텍스트를 입력하는 모든 검색 기능에 match 쿼리가 활용됩니다.
특히 한국어처럼 형태소 분석이 필요한 언어에서는 적절한 분석기와 함께 사용하면 더욱 정확한 검색 결과를 얻을 수 있습니다. 주의할 점도 있습니다.
match 쿼리는 검색어를 분석하기 때문에, 대소문자나 띄어쓰기에 유연합니다. 하지만 이 유연함이 때로는 단점이 될 수 있습니다.
정확히 일치하는 값만 찾고 싶을 때는 match 쿼리가 적합하지 않습니다. 그런 경우에는 다음 장에서 배울 term 쿼리를 사용해야 합니다.
김개발 씨는 match 쿼리를 적용한 후 검색 품질이 확연히 좋아진 것을 느꼈습니다. "오, 이제 '나이키신발'이라고 붙여 써도 '나이키 신발'이 검색되네요!"
실전 팁
💡 - match 쿼리는 전문 검색에 사용하며, 분석기가 검색어를 토큰화합니다
- operator 옵션으로 AND/OR 조건을 조절할 수 있으며, 기본값은 OR입니다
- 정확한 값 매칭이 필요하면 match 대신 term 쿼리를 사용하세요
3. term 쿼리 정확 검색
김개발 씨가 match 쿼리로 검색 기능을 잘 구현했습니다. 그런데 새로운 요구사항이 들어왔습니다.
"상품 상태가 정확히 'available'인 것만 보여줘야 해요." match 쿼리를 사용했더니 'unavailable'도 함께 검색되는 문제가 생겼습니다. 분석기가 'available'이라는 단어를 포함한 모든 것을 찾아버렸기 때문입니다.
term 쿼리는 분석 과정 없이 입력값과 정확히 일치하는 문서를 찾는 쿼리입니다. 카테고리, 상태값, ID 같은 정형화된 데이터를 검색할 때 사용합니다.
match 쿼리와 달리 검색어를 토큰화하지 않으므로, 입력한 그대로의 값을 찾습니다.
다음 코드를 살펴봅시다.
// term 쿼리 - 정확한 값 매칭
const termQuery = {
query: {
term: {
// status 필드가 정확히 "available"인 문서만 검색
status: {
value: "available"
}
}
}
};
// terms 쿼리 - 여러 값 중 하나와 일치
const termsQuery = {
query: {
terms: {
// category가 "shoes" 또는 "clothing" 중 하나인 문서
category: ["shoes", "clothing"]
}
}
};
김개발 씨는 난감한 상황에 처했습니다. 상품 상태를 필터링해야 하는데, match 쿼리로는 원하는 결과가 나오지 않았습니다.
"available"을 검색했는데 "unavailable"도 같이 나오다니요. 박시니어 씨가 상황을 파악하고 설명해줍니다.
"match 쿼리는 검색어를 분석하잖아. 그래서 'available'이라는 토큰이 포함된 모든 문서를 찾은 거야.
이럴 때는 term 쿼리를 써야 해." 그렇다면 term 쿼리는 match 쿼리와 무엇이 다를까요? 쉽게 비유하자면, match 쿼리가 "이 단어가 포함된 책을 찾아줘"라면, term 쿼리는 "책 번호가 정확히 A-1234인 책을 가져다줘"와 같습니다.
term은 분석 없이 값을 있는 그대로 비교합니다. 이것은 keyword 타입 필드와 깊은 관련이 있습니다.
Elasticsearch에서 문자열 필드는 크게 text 타입과 keyword 타입으로 나뉩니다. text 타입은 분석기를 거쳐 토큰화되고, keyword 타입은 원본 그대로 저장됩니다.
term 쿼리는 주로 keyword 타입 필드에 사용합니다. 위의 코드에서 첫 번째 쿼리를 보겠습니다.
status 필드에서 정확히 "available"인 문서만 찾습니다. 이 쿼리는 "AVAILABLE"이나 "Available"은 찾지 않습니다.
대소문자까지 정확히 일치해야 하기 때문입니다. 두 번째 terms 쿼리는 조금 다릅니다.
하나의 값이 아닌 여러 값 중 하나와 일치하는 문서를 찾습니다. SQL의 IN 절과 비슷하다고 생각하면 됩니다.
category가 "shoes"이거나 "clothing"인 문서를 모두 찾아줍니다. 실무에서 term 쿼리는 필터링에 주로 사용됩니다.
쇼핑몰에서 "브랜드: 나이키"를 선택하거나, "배송 가능 상품만 보기"를 체크하는 기능을 생각해보세요. 이런 필터는 정확한 값 매칭이 필요하므로 term 쿼리가 적합합니다.
주의할 점이 있습니다. text 타입 필드에 term 쿼리를 사용하면 예상치 못한 결과가 나올 수 있습니다.
text 필드는 저장될 때 분석기를 거쳐 소문자로 변환되거나 토큰화될 수 있기 때문입니다. 예를 들어 "Nike Shoes"가 "nike", "shoes"로 저장되면, term 쿼리로 "Nike Shoes"를 검색해도 찾을 수 없습니다.
김개발 씨가 term 쿼리로 상태 필터링을 구현하자, 정확히 원하는 결과만 나왔습니다. "아하, 전문 검색은 match, 정확한 필터링은 term이군요!"
실전 팁
💡 - term 쿼리는 keyword 타입 필드에 사용하고, match 쿼리는 text 타입 필드에 사용하세요
- 대소문자, 띄어쓰기까지 정확히 일치해야 하므로 데이터가 어떻게 저장되어 있는지 확인하세요
- 여러 값 중 하나를 찾으려면 terms 쿼리를 사용하세요
4. range 쿼리 범위 검색
김개발 씨가 상품 검색에 가격 필터를 추가해야 했습니다. "1만원에서 5만원 사이의 상품만 보여주세요"라는 요구사항이었습니다.
정확한 값이 아닌 범위로 검색해야 하는데, term 쿼리로는 해결이 안 됩니다. 이럴 때 필요한 것이 바로 range 쿼리입니다.
range 쿼리는 숫자, 날짜, 문자열 등의 필드에서 특정 범위에 해당하는 문서를 찾습니다. gte(이상), gt(초과), lte(이하), lt(미만) 연산자를 조합하여 원하는 범위를 정의할 수 있습니다.
SQL의 BETWEEN이나 비교 연산자와 유사한 역할을 합니다.
다음 코드를 살펴봅시다.
// range 쿼리 - 숫자 범위 검색
const priceRangeQuery = {
query: {
range: {
price: {
gte: 10000, // 10000 이상 (greater than or equal)
lte: 50000 // 50000 이하 (less than or equal)
}
}
}
};
// range 쿼리 - 날짜 범위 검색
const dateRangeQuery = {
query: {
range: {
created_at: {
gte: "2024-01-01",
lt: "2024-02-01",
format: "yyyy-MM-dd" // 날짜 형식 지정
}
}
}
};
쇼핑몰에서 가격 범위로 필터링하는 기능, 익숙하시죠? "3만원 ~ 5만원" 슬라이더를 조절하면 해당 가격대의 상품만 보여주는 기능 말입니다.
김개발 씨도 바로 이 기능을 구현해야 했습니다. 박시니어 씨가 range 쿼리를 알려줍니다.
"range는 범위를 지정하는 쿼리야. 숫자뿐만 아니라 날짜도 범위 검색이 가능해." 그렇다면 range 쿼리의 핵심 연산자를 알아볼까요?
range 쿼리에는 네 가지 핵심 연산자가 있습니다. gte는 greater than or equal, 즉 '이상'입니다.
gt는 greater than, '초과'입니다. lte는 less than or equal, '이하'입니다.
lt는 less than, '미만'입니다. 쉽게 비유하자면, 도서관에서 책을 찾을 때와 같습니다.
"2020년부터 2023년까지 출판된 책"이라고 하면 2020년, 2021년, 2022년, 2023년 모두 포함됩니다. 이것이 gte와 lte를 사용한 경우입니다.
"2020년 이후부터 2023년 이전"이라고 하면 2021년, 2022년만 해당됩니다. 이것이 gt와 lt를 사용한 경우입니다.
위의 첫 번째 코드는 가격 범위 검색입니다. price 필드가 10000 이상이고 50000 이하인 문서를 찾습니다.
10000원짜리 상품도 포함되고, 50000원짜리 상품도 포함됩니다. 만약 50000원은 제외하고 싶다면 lte 대신 lt를 사용하면 됩니다.
두 번째 코드는 날짜 범위 검색입니다. created_at 필드가 2024년 1월 1일 이상이고 2024년 2월 1일 미만인 문서를 찾습니다.
즉, 2024년 1월에 생성된 문서만 검색됩니다. format 옵션으로 날짜 형식을 지정할 수 있어서 편리합니다.
실무에서 range 쿼리는 정말 다양하게 활용됩니다. 가격 필터링, 날짜 기간 조회, 재고 수량 필터링, 리뷰 점수 필터링 등 숫자나 날짜가 관련된 거의 모든 필터에 range 쿼리가 사용됩니다.
특히 대시보드에서 "최근 7일간의 데이터"를 보여줄 때 자주 쓰입니다. 날짜 검색에서 유용한 팁이 있습니다.
Elasticsearch는 now라는 특별한 키워드를 지원합니다. "now-7d"는 7일 전, "now-1M"은 한 달 전을 의미합니다.
이를 활용하면 동적으로 최근 기간의 데이터를 조회할 수 있습니다. 김개발 씨는 range 쿼리로 가격 필터를 구현했습니다.
슬라이더의 최소값과 최대값을 gte와 lte에 넣어주기만 하면 되니 정말 간단했습니다.
실전 팁
💡 - gte/lte는 경계값을 포함하고, gt/lt는 경계값을 제외합니다
- 날짜 검색 시 now 키워드와 -7d, -1M 같은 표현을 활용하면 동적 기간 조회가 가능합니다
- 숫자 필드는 integer나 long 타입으로, 날짜 필드는 date 타입으로 매핑되어 있어야 합니다
5. bool 쿼리 조합
김개발 씨가 드디어 복잡한 검색 조건을 구현해야 할 때가 왔습니다. "나이키 브랜드이면서, 가격이 5만원 이하이고, 상품 설명에 '러닝'이 포함된 상품을 찾아주세요." 하나의 쿼리 유형으로는 이 요구사항을 충족할 수 없었습니다.
여러 조건을 조합해야 했습니다.
bool 쿼리는 여러 개의 쿼리를 논리적으로 조합하는 복합 쿼리입니다. must(AND), should(OR), must_not(NOT), filter(AND, 점수 무시) 네 가지 절을 사용하여 복잡한 검색 조건을 표현할 수 있습니다.
실무에서 가장 많이 사용되는 쿼리 유형 중 하나입니다.
다음 코드를 살펴봅시다.
// bool 쿼리 - 여러 조건 조합
const boolQuery = {
query: {
bool: {
// must: 반드시 만족해야 함 (AND)
must: [
{ match: { description: "러닝" } }
],
// filter: 반드시 만족 + 점수에 영향 없음
filter: [
{ term: { brand: "nike" } },
{ range: { price: { lte: 50000 } } }
],
// must_not: 만족하면 안 됨 (NOT)
must_not: [
{ term: { status: "soldout" } }
]
}
}
};
지금까지 김개발 씨는 match, term, range 쿼리를 각각 배웠습니다. 하지만 실제 서비스에서는 이 쿼리들을 조합해서 사용해야 하는 경우가 대부분입니다.
어떻게 해야 할까요? 박시니어 씨가 핵심을 짚어줍니다.
"bool 쿼리가 다른 쿼리들을 감싸는 그릇 역할을 해. 마치 레고 블록을 조립하듯이 쿼리를 조합할 수 있어." bool 쿼리는 마치 엑셀의 필터 기능과 비슷합니다.
엑셀에서 여러 열에 조건을 걸어 필터링하는 것처럼, bool 쿼리도 여러 필드에 다양한 조건을 걸 수 있습니다. "브랜드는 나이키이면서 가격은 5만원 이하"처럼요.
bool 쿼리에는 네 가지 절(clause)이 있습니다. must는 반드시 만족해야 하는 조건입니다.
여러 개를 넣으면 모두 AND로 연결됩니다. should는 만족하면 좋은 조건입니다.
하나라도 만족하면 검색 결과에 포함되고, 더 많이 만족할수록 점수가 높아집니다. must_not은 만족하면 안 되는 조건입니다.
해당 조건에 맞는 문서는 결과에서 제외됩니다. filter는 must와 비슷하지만 점수 계산에 영향을 주지 않습니다.
위의 코드를 자세히 살펴보겠습니다. must 절에는 match 쿼리가 들어있습니다.
description 필드에 "러닝"이 반드시 포함되어야 합니다. filter 절에는 두 개의 조건이 있습니다.
brand가 정확히 "nike"이어야 하고, price가 50000 이하여야 합니다. must_not 절에는 status가 "soldout"인 문서를 제외하는 조건이 있습니다.
왜 brand와 price 조건은 must가 아닌 filter에 넣었을까요? 이것은 성능과 관련이 있습니다.
filter 절은 점수 계산을 하지 않으므로 더 빠릅니다. 또한 필터 결과가 캐시되어 재사용됩니다.
점수에 영향을 줄 필요가 없는 단순 필터링 조건은 filter에 넣는 것이 좋습니다. 실무에서 bool 쿼리는 거의 모든 검색에 사용됩니다.
쇼핑몰의 상품 검색을 예로 들면, 검색어는 must에, 카테고리나 브랜드 필터는 filter에, 품절 상품 제외는 must_not에 넣는 식으로 구성합니다. 이렇게 구조화하면 유지보수도 쉽고 성능도 좋습니다.
bool 쿼리는 중첩도 가능합니다. bool 쿼리 안에 또 다른 bool 쿼리를 넣을 수 있습니다.
예를 들어 "(A AND B) OR (C AND D)" 같은 복잡한 조건도 bool 쿼리의 중첩으로 표현할 수 있습니다. 김개발 씨는 bool 쿼리의 강력함에 감탄했습니다.
"이거 하나면 어떤 복잡한 조건도 다 표현할 수 있겠네요!"
실전 팁
💡 - 점수에 영향을 줄 필요 없는 필터 조건은 must 대신 filter를 사용하여 성능을 높이세요
- 복잡한 조건은 bool 쿼리를 중첩하여 표현할 수 있습니다
- must_not은 해당 문서를 완전히 제외하므로, 의도치 않게 필요한 결과가 빠지지 않도록 주의하세요
6. must should filter 차이
김개발 씨가 bool 쿼리를 사용하면서 문득 궁금해졌습니다. "must랑 filter가 둘 다 AND 조건이라면서요?
그럼 뭐가 다른 거예요?" 또 should는 어떨 때 쓰는 건지, 검색 점수는 어떻게 계산되는 건지 헷갈리기 시작했습니다. 이 차이를 명확히 이해해야 진정한 Elasticsearch 마스터가 될 수 있습니다.
must, should, filter는 모두 bool 쿼리의 절이지만, 점수 계산과 필수 조건 여부에서 차이가 있습니다. must는 필수 조건이며 점수에 영향을 주고, filter는 필수 조건이지만 점수에 영향을 주지 않습니다.
should는 선택 조건으로, 만족할수록 점수가 높아집니다.
다음 코드를 살펴봅시다.
// must vs filter vs should 비교
const comparisonQuery = {
query: {
bool: {
// must: 필수 + 점수 계산 O
// "운동화"가 많이 언급될수록 점수 높음
must: [
{ match: { title: "운동화" } }
],
// filter: 필수 + 점수 계산 X (빠름, 캐시됨)
// 단순히 조건만 체크
filter: [
{ term: { category: "shoes" } },
{ range: { price: { lte: 100000 } } }
],
// should: 선택 + 점수 계산 O
// "프리미엄"이 있으면 상위 노출
should: [
{ match: { description: "프리미엄" } }
]
}
}
};
김개발 씨는 bool 쿼리를 사용하면서 점점 의문이 생겼습니다. must와 filter 둘 다 "반드시 만족해야 한다"는 점에서는 같은데, 왜 두 개가 따로 존재하는 걸까요?
박시니어 씨가 칠판에 그림을 그리며 설명을 시작합니다. "핵심은 **검색 점수(score)**야.
Elasticsearch는 각 문서가 검색 조건에 얼마나 잘 맞는지 점수를 매기거든." 검색 점수란 무엇일까요? 쉽게 비유하자면, 시험 점수와 비슷합니다.
"운동화"를 검색했을 때, 제목에 "운동화"가 세 번 나오는 상품과 한 번 나오는 상품이 있다면, 세 번 나오는 상품이 더 관련성이 높겠죠? Elasticsearch는 이런 관련성을 점수로 계산하고, 점수가 높은 순서대로 결과를 정렬합니다.
must는 이 점수 계산에 참여합니다. must 절의 조건을 얼마나 잘 만족하는지에 따라 점수가 달라집니다.
match 쿼리로 "운동화"를 검색하면, "운동화"라는 단어가 많이 나오거나 중요한 위치에 있을수록 점수가 높아집니다. 반면 filter는 점수 계산을 하지 않습니다.
filter는 단순히 "이 조건을 만족하느냐, 안 하느냐"만 판단합니다. 예/아니오의 이분법적 판단만 하므로 계산이 단순하고 빠릅니다.
게다가 결과가 캐시되어서, 같은 필터 조건이 반복되면 더욱 빠르게 응답합니다. 그렇다면 언제 must를 쓰고 언제 filter를 쓸까요?
규칙은 간단합니다. 검색 결과의 순서에 영향을 줘야 하면 must, 단순히 거르기만 하면 filter입니다.
"운동화"라는 검색어는 관련성이 중요하므로 must에 넣습니다. "카테고리가 신발"이라는 조건은 단순 필터링이므로 filter에 넣습니다.
should는 조금 특별합니다. should는 필수 조건이 아닙니다.
should 조건을 만족하지 않아도 검색 결과에 포함될 수 있습니다. 하지만 만족하면 점수가 올라갑니다.
이것을 "부스팅"이라고 합니다. 실무에서 should는 이렇게 활용됩니다.
예를 들어 쇼핑몰에서 "운동화"를 검색했을 때, 프리미엄 상품이나 인기 상품을 상위에 노출하고 싶다면 should를 사용합니다. "프리미엄" 태그가 있으면 점수를 높여서 상위에 보여주는 식입니다.
주의할 점이 하나 있습니다. must나 filter가 없고 should만 있으면, should 조건 중 최소 하나는 만족해야 검색 결과에 포함됩니다.
이때 minimum_should_match 옵션으로 최소 몇 개를 만족해야 하는지 지정할 수 있습니다. 김개발 씨가 정리합니다.
"must는 필수이면서 점수에 영향, filter는 필수이지만 점수 무관, should는 선택이면서 점수 부스팅이군요!"
실전 팁
💡 - 검색어 매칭처럼 관련성 순위가 중요한 조건은 must에, 단순 필터링은 filter에 넣으세요
- should를 활용하면 특정 조건을 만족하는 문서를 상위에 노출시킬 수 있습니다
- filter는 캐시되므로 자주 사용되는 필터 조건에 적극 활용하면 성능이 향상됩니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Grafana 대시보드 완벽 가이드
실시간 모니터링의 핵심, Grafana 대시보드를 처음부터 끝까지 배워봅니다. Prometheus 연동부터 알람 설정까지, 초급 개발자도 쉽게 따라할 수 있는 실전 가이드입니다.
분산 추적 완벽 가이드
마이크로서비스 환경에서 요청의 전체 흐름을 추적하는 분산 추적 시스템의 핵심 개념을 배웁니다. Trace, Span, Trace ID 전파, 샘플링 전략까지 실무에 필요한 모든 것을 다룹니다.
CloudFront CDN 완벽 가이드
AWS CloudFront를 활용한 콘텐츠 배포 최적화 방법을 실무 관점에서 다룹니다. 배포 생성부터 캐시 설정, HTTPS 적용까지 단계별로 알아봅니다.