Elasticsearch 검색 엔진 구축
Elasticsearch를 활용해 고성능 검색 시스템을 구축합니다. 인덱싱, 검색 쿼리, 집계 분석, 한글 형태소 분석, Kibana 시각화까지 모든 것을 다룹니다.
학습 항목
본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
이미지 로딩 중...
Elasticsearch 소개와 아키텍처 완벽 가이드
Elasticsearch의 기본 개념부터 분산 아키텍처, 노드와 클러스터, 샤드와 레플리카까지 초급 개발자를 위해 쉽게 풀어낸 입문 가이드입니다. ELK Stack과 Lucene 엔진의 기초까지 다룹니다.
목차
1. Elasticsearch란?
어느 날 김개발 씨는 회사에서 새로운 프로젝트를 맡게 되었습니다. 쇼핑몰의 상품 검색 기능을 개선하라는 미션이었습니다.
기존 데이터베이스의 LIKE 검색으로는 수백만 건의 상품을 빠르게 찾아내기 어려웠습니다. 선배 박시니어 씨가 말했습니다.
"Elasticsearch를 한번 알아보는 게 어때요?"
Elasticsearch는 한마디로 초고속 검색을 위해 태어난 분산 검색 엔진입니다. 마치 도서관의 유능한 사서가 수백만 권의 책 중에서 원하는 책을 순식간에 찾아주는 것과 같습니다.
관계형 데이터베이스와 달리 텍스트를 분석하고 인덱싱하여 전문 검색에 최적화되어 있습니다.
다음 코드를 살펴봅시다.
// Elasticsearch에 문서 저장하기
PUT /products/_doc/1
{
"name": "무선 블루투스 이어폰",
"description": "고음질 노이즈 캔슬링 지원",
"price": 89000,
"category": "전자제품",
"created_at": "2024-01-15"
}
// 검색 쿼리 실행
GET /products/_search
{
"query": {
"match": {
"description": "노이즈 캔슬링"
}
}
}
김개발 씨는 입사 1년 차 백엔드 개발자입니다. 오늘 받은 미션은 쇼핑몰 검색 기능 개선이었습니다.
기존에는 MySQL의 LIKE 검색을 사용하고 있었는데, 상품이 100만 개를 넘어가면서 검색 속도가 눈에 띄게 느려지기 시작했습니다. "LIKE '%노이즈캔슬링%' 쿼리가 3초나 걸리네요..." 김개발 씨가 한숨을 쉬었습니다.
선배 박시니어 씨가 다가와 모니터를 살펴봅니다. "아, 이건 관계형 데이터베이스의 한계야.
전문 검색에는 Elasticsearch를 써야 해." 그렇다면 Elasticsearch란 정확히 무엇일까요? 쉽게 비유하자면, Elasticsearch는 마치 도서관의 초고속 사서와 같습니다.
일반 데이터베이스가 책장에서 책을 하나하나 꺼내 제목을 확인하는 방식이라면, Elasticsearch는 이미 모든 책의 내용을 분석해서 색인 카드를 만들어 놓은 것입니다. 누군가 "노이즈 캔슬링에 관한 책 찾아주세요"라고 하면, 색인 카드만 뒤져서 0.1초 만에 관련 책들을 찾아낼 수 있습니다.
Elasticsearch가 등장하기 전에는 어땠을까요? 개발자들은 MySQL이나 PostgreSQL의 LIKE 검색에 의존해야 했습니다.
하지만 이 방식은 데이터가 늘어날수록 기하급수적으로 느려집니다. 더 큰 문제는 "노이즈캔슬링"을 검색했을 때 "노이즈 캔슬링"(띄어쓰기 있음)은 찾지 못한다는 것이었습니다.
오타나 동의어 검색은 꿈도 꾸지 못했습니다. 바로 이런 문제를 해결하기 위해 Elasticsearch가 등장했습니다.
Elasticsearch는 Apache Lucene이라는 검색 라이브러리를 기반으로 만들어진 분산 검색 엔진입니다. 2010년 Shay Banon이 처음 개발했고, 현재는 Elastic 회사에서 관리하고 있습니다.
오픈소스로 시작했지만 지금은 전 세계 수많은 기업에서 사용하는 검색의 표준이 되었습니다. 위의 코드를 살펴보겠습니다.
먼저 PUT 요청으로 products라는 인덱스에 문서를 저장합니다. 여기서 인덱스는 관계형 데이터베이스의 테이블과 비슷한 개념입니다.
JSON 형식으로 데이터를 저장하며, 각 필드는 자동으로 분석되어 검색 가능한 형태로 인덱싱됩니다. 검색할 때는 GET 요청과 함께 쿼리를 보냅니다.
match 쿼리는 가장 기본적인 전문 검색 쿼리로, 입력한 텍스트를 분석하여 관련된 문서를 찾아줍니다. "노이즈 캔슬링"이라고 검색하면 "노이즈캔슬링", "노이즈 캔슬링", 심지어 "캔슬링 노이즈" 같은 문서도 찾아낼 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 쇼핑몰에서는 상품 검색에, 뉴스 사이트에서는 기사 검색에, 로그 분석 시스템에서는 수억 건의 로그를 실시간으로 검색하는 데 사용합니다.
Netflix, Wikipedia, Stack Overflow 같은 대형 서비스들도 Elasticsearch를 적극 활용하고 있습니다. 하지만 주의할 점도 있습니다.
Elasticsearch는 실시간 데이터베이스가 아닙니다. 문서를 저장하면 약간의 지연(기본 1초) 후에 검색이 가능해집니다.
또한 트랜잭션을 지원하지 않으므로 금융 거래 같은 데이터에는 적합하지 않습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 눈이 반짝였습니다. "와, 그래서 검색이 그렇게 빠른 거였군요!" Elasticsearch의 기본 개념을 이해한 김개발 씨는 본격적으로 검색 시스템 구축에 나섰습니다.
실전 팁
💡 - Elasticsearch는 검색에 특화된 도구입니다. 주 데이터베이스로 사용하지 말고, 검색용 보조 저장소로 활용하세요.
- RESTful API를 사용하므로 어떤 언어에서든 HTTP 요청만으로 쉽게 연동할 수 있습니다.
2. ELK Stack 소개
김개발 씨가 Elasticsearch를 도입하려고 조사하던 중, 자꾸 ELK Stack이라는 단어가 등장했습니다. "Elasticsearch만 쓰면 되는 거 아닌가요?" 김개발 씨가 물었습니다.
박시니어 씨가 웃으며 대답했습니다. "Elasticsearch는 혼자서도 훌륭하지만, 친구들과 함께하면 더 강력해져."
ELK Stack은 Elasticsearch, Logstash, Kibana 세 가지 도구의 조합을 말합니다. 마치 요리사(Logstash)가 재료를 다듬고, 냉장고(Elasticsearch)에 보관하고, 손님에게 예쁜 접시(Kibana)에 담아 제공하는 것과 같습니다.
최근에는 Beats가 추가되어 Elastic Stack이라고도 부릅니다.
다음 코드를 살펴봅시다.
// Logstash 설정 예시 (logstash.conf)
input {
file {
path => "/var/log/application/*.log"
start_position => "beginning"
}
}
filter {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:content}" }
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "application-logs-%{+YYYY.MM.dd}"
}
}
김개발 씨는 Elasticsearch 공부를 시작하면서 이상한 점을 발견했습니다. 검색 엔진 하나를 배우려고 했는데, 자꾸 Logstash니 Kibana니 하는 단어들이 따라붙는 것이었습니다.
"선배, ELK가 뭐예요? 동물원에서 보던 그 사슴 말인가요?" 박시니어 씨가 피식 웃었습니다.
"비슷해. 그 사슴처럼 우아하고 빠르거든.
Elasticsearch, Logstash, Kibana의 앞글자를 딴 거야." 그렇다면 이 세 가지 도구는 각각 어떤 역할을 할까요? 쉽게 비유하자면, 레스토랑 주방을 떠올려 보세요.
Logstash는 재료를 손질하는 주방보조입니다. 각종 소스에서 들어오는 데이터를 수집하고, 깔끔하게 정제해서 다음 단계로 넘깁니다.
Elasticsearch는 거대한 냉장 창고입니다. 정제된 재료를 체계적으로 보관하고, 필요할 때 순식간에 꺼내줍니다.
Kibana는 예쁜 접시와 플레이팅을 담당합니다. 데이터를 시각적으로 아름답게 표현해서 사람들이 이해하기 쉽게 보여줍니다.
ELK Stack이 등장하기 전에는 어땠을까요? 개발자들은 로그를 분석하려면 서버에 직접 접속해서 grep 명령어로 파일을 뒤져야 했습니다.
서버가 10대면 10번 접속해야 했고, 100대면... 상상만 해도 끔찍합니다.
로그 데이터를 시각화하려면 또 다른 도구를 찾아야 했고, 각 도구들을 연결하는 것도 큰 일이었습니다. 바로 이런 고통을 해결하기 위해 ELK Stack이라는 조합이 탄생했습니다.
위의 코드는 Logstash 설정 파일입니다. input 섹션에서 어디서 데이터를 가져올지 정의합니다.
파일, 데이터베이스, 메시지 큐 등 다양한 소스를 지원합니다. filter 섹션에서는 데이터를 정제합니다.
grok 패턴을 사용하면 비정형 로그를 구조화된 데이터로 변환할 수 있습니다. output 섹션에서 정제된 데이터를 Elasticsearch로 보냅니다.
Kibana는 웹 브라우저에서 접속하는 시각화 도구입니다. Elasticsearch에 저장된 데이터를 차트, 그래프, 대시보드로 만들 수 있습니다.
SQL을 몰라도 클릭 몇 번으로 복잡한 분석이 가능합니다. 최근에는 Beats라는 경량 데이터 수집기가 추가되었습니다.
Logstash가 무거운 트럭이라면, Beats는 가벼운 오토바이입니다. Filebeat는 로그 파일을, Metricbeat는 시스템 메트릭을, Packetbeat는 네트워크 패킷을 수집합니다.
이제는 Elastic Stack이라고 부르는 것이 더 정확합니다. 실제 현업에서는 어떻게 활용할까요?
가장 흔한 사용 사례는 로그 분석입니다. 수백 대의 서버에서 발생하는 로그를 한 곳에 모아서 실시간으로 분석합니다.
에러가 발생하면 Kibana 대시보드에서 즉시 확인하고, 어떤 서버에서 문제가 생겼는지 파악할 수 있습니다. 보안 팀에서는 침입 탐지에, 비즈니스 팀에서는 사용자 행동 분석에 활용합니다.
하지만 주의할 점도 있습니다. ELK Stack 전체를 운영하려면 상당한 리소스가 필요합니다.
작은 프로젝트에서는 과한 선택일 수 있습니다. 또한 각 컴포넌트의 버전 호환성을 맞추는 것도 신경 써야 합니다.
김개발 씨는 고개를 끄덕였습니다. "아, 그래서 다들 ELK Stack이라고 묶어서 말하는 거였군요.
검색만 하려면 Elasticsearch만 써도 되지만, 전체 파이프라인을 구축하려면 세 가지가 다 필요하네요."
실전 팁
💡 - 처음 시작할 때는 Elasticsearch와 Kibana만으로 시작하세요. Logstash는 데이터 파이프라인이 복잡해질 때 도입해도 늦지 않습니다.
- Elastic Cloud를 사용하면 인프라 관리 부담 없이 바로 시작할 수 있습니다.
3. 분산 아키텍처 이해
김개발 씨가 Elasticsearch를 테스트 서버에 설치하고 기능 검증을 마쳤습니다. 이제 운영 환경에 적용하려는데, 선배가 걱정스러운 표정을 지었습니다.
"혹시 서버 한 대에서 돌리려고 해?" 김개발 씨가 고개를 끄덕이자, 박시니어 씨가 말했습니다. "Elasticsearch의 진짜 힘은 분산 아키텍처에서 나와."
분산 아키텍처란 여러 대의 컴퓨터가 하나의 시스템처럼 동작하는 구조를 말합니다. 마치 이사할 때 혼자 짐을 나르면 하루 종일 걸리지만, 친구 10명이 함께하면 한 시간 만에 끝나는 것과 같습니다.
Elasticsearch는 태생부터 분산 환경을 위해 설계되었습니다.
다음 코드를 살펴봅시다.
// 클러스터 상태 확인
GET /_cluster/health
{
"cluster_name": "my-application",
"status": "green",
"number_of_nodes": 3,
"number_of_data_nodes": 3,
"active_primary_shards": 5,
"active_shards": 10
}
// 노드 정보 확인
GET /_cat/nodes?v
// ip heap.percent cpu load_1m node.role master name
// 192.168.1.1 45 12 0.08 dim * node-1
// 192.168.1.2 38 15 0.12 dim - node-2
// 192.168.1.3 42 10 0.05 dim - node-3
김개발 씨는 개발 서버에서 Elasticsearch가 잘 동작하는 것을 확인하고 뿌듯해했습니다. 검색 속도도 빠르고, API도 직관적이었습니다.
이제 운영 환경에 똑같이 구축하면 되겠다고 생각했습니다. "선배, 운영 서버에 Elasticsearch 설치하려고요.
사양 좋은 서버 한 대면 되겠죠?" 박시니어 씨가 고개를 저었습니다. "음, 그건 Elasticsearch의 장점을 절반도 못 쓰는 거야.
분산 아키텍처를 모르면 안 돼." 분산 아키텍처란 정확히 무엇일까요? 쉽게 비유하자면, 분산 아키텍처는 마치 체인 편의점과 같습니다.
편의점이 한 곳만 있으면 손님이 몰릴 때 줄이 길어지고, 그 가게가 문을 닫으면 물건을 살 수 없습니다. 하지만 같은 물건을 파는 편의점이 동네 곳곳에 있다면, 손님이 분산되고, 한 곳이 문을 닫아도 다른 곳에서 살 수 있습니다.
Elasticsearch가 분산 아키텍처를 채택한 이유는 무엇일까요? 첫째, 확장성 때문입니다.
데이터가 늘어나면 서버를 추가하기만 하면 됩니다. 기존 시스템을 중단하거나 데이터를 마이그레이션할 필요가 없습니다.
Elasticsearch가 알아서 데이터를 새 서버로 분배합니다. 둘째, 고가용성 때문입니다.
서버 한 대가 고장 나도 서비스가 멈추지 않습니다. 다른 서버들이 그 역할을 대신합니다.
24시간 365일 운영해야 하는 서비스에서 이 점은 매우 중요합니다. 셋째, 성능 때문입니다.
검색 요청이 들어오면 여러 서버가 동시에 처리합니다. 100만 건을 검색할 때, 서버 한 대가 100만 건을 뒤지는 것보다 서버 10대가 10만 건씩 뒤지는 것이 훨씬 빠릅니다.
위의 코드에서 클러스터 상태를 확인할 수 있습니다. status가 green이면 모든 것이 정상입니다.
yellow는 일부 복제본이 없는 상태, red는 일부 데이터에 접근할 수 없는 심각한 상태입니다. number_of_nodes는 클러스터에 참여한 서버 수를 보여줍니다.
노드 정보를 보면 각 서버의 IP, 메모리 사용량, CPU 사용량을 확인할 수 있습니다. master 열에 별표(*)가 있는 노드가 현재 마스터 노드입니다.
마스터 노드는 클러스터의 두뇌 역할을 하며, 어떤 데이터가 어디에 있는지 관리합니다. 실제 현업에서는 어떻게 구성할까요?
소규모 서비스라면 3대로 시작하는 것이 일반적입니다. 대규모 서비스는 수십에서 수백 대까지 확장합니다.
Netflix는 수천 대의 노드로 구성된 Elasticsearch 클러스터를 운영한다고 합니다. 하지만 주의할 점도 있습니다.
분산 시스템은 단일 서버보다 복잡합니다. 네트워크 지연, 노드 간 동기화, 분산 잠금 등 새로운 문제가 생깁니다.
작은 규모에서 시작해서 점진적으로 확장하는 것이 좋습니다. 김개발 씨는 깨달음을 얻었습니다.
"아, Elasticsearch는 처음부터 여러 대가 협력하도록 설계된 거군요. 한 대로 쓰는 건 스포츠카로 동네 마트만 다니는 것 같네요."
실전 팁
💡 - 운영 환경에서는 최소 3대의 노드로 구성하세요. 홀수로 구성해야 마스터 선출 시 분할 뇌 문제를 피할 수 있습니다.
- 클러스터 상태를 모니터링하는 대시보드를 꼭 구축하세요. 문제가 생기기 전에 미리 감지할 수 있습니다.
4. 노드와 클러스터 개념
분산 아키텍처를 이해한 김개발 씨는 본격적으로 클러스터를 구축하기로 했습니다. 그런데 문서를 읽다 보니 마스터 노드, 데이터 노드, 코디네이팅 노드 등 다양한 용어가 나왔습니다.
"노드 종류가 이렇게 많아요?" 김개발 씨가 당황하자, 박시니어 씨가 차근차근 설명을 시작했습니다.
노드는 Elasticsearch가 실행되는 하나의 서버 인스턴스이고, 클러스터는 이런 노드들이 모인 그룹입니다. 마치 회사에서 각자 역할이 있는 직원들(노드)이 모여 하나의 팀(클러스터)을 이루는 것과 같습니다.
각 노드는 자신의 역할에 맞는 일을 수행합니다.
다음 코드를 살펴봅시다.
// elasticsearch.yml - 마스터 노드 설정
cluster.name: my-cluster
node.name: master-node-1
node.roles: [ master ]
network.host: 192.168.1.1
// elasticsearch.yml - 데이터 노드 설정
cluster.name: my-cluster
node.name: data-node-1
node.roles: [ data ]
network.host: 192.168.1.2
// elasticsearch.yml - 코디네이팅 노드 설정
cluster.name: my-cluster
node.name: coord-node-1
node.roles: [ ]
network.host: 192.168.1.3
김개발 씨는 Elasticsearch 클러스터를 구축하기 위해 문서를 읽기 시작했습니다. 그런데 생각보다 노드의 종류가 많았습니다.
마스터 노드, 데이터 노드, 인제스트 노드, 코디네이팅 노드... 머리가 복잡해졌습니다.
"선배, 노드 종류가 왜 이렇게 많아요? 그냥 다 같은 역할 하면 안 되나요?" 박시니어 씨가 설명을 시작했습니다.
"회사를 생각해봐. 모든 직원이 똑같은 일을 하면 효율적일까?
기획자, 개발자, 디자이너가 각자 전문 분야를 맡는 게 낫잖아." 그렇다면 각 노드의 역할은 무엇일까요? 마스터 노드는 클러스터의 두뇌입니다.
회사로 치면 CEO나 관리자 역할입니다. 어떤 데이터가 어디에 저장되어 있는지, 어떤 노드가 살아있는지, 새 노드가 추가되면 어떻게 데이터를 분배할지 결정합니다.
마스터 노드가 죽으면 다른 마스터 후보 노드 중 하나가 새 마스터로 선출됩니다. 데이터 노드는 실제 데이터를 저장하고 검색을 수행합니다.
회사로 치면 실무를 담당하는 직원입니다. CPU와 메모리, 디스크 I/O를 가장 많이 사용하므로 좋은 하드웨어가 필요합니다.
클러스터에서 가장 많은 비중을 차지하는 노드입니다. 인제스트 노드는 데이터가 저장되기 전에 전처리를 담당합니다.
Logstash처럼 데이터를 변환하거나 가공하는 역할입니다. 간단한 전처리는 인제스트 노드에서 처리하고, 복잡한 파이프라인은 Logstash를 사용합니다.
코디네이팅 노드는 요청을 받아서 적절한 노드로 전달하고, 결과를 취합합니다. 회사로 치면 고객 응대를 담당하는 안내 데스크입니다.
직접 데이터를 저장하거나 검색하지는 않지만, 대규모 클러스터에서 부하를 분산하는 데 유용합니다. 위의 코드는 각 역할별 노드 설정 예시입니다.
cluster.name이 같으면 자동으로 같은 클러스터에 합류합니다. node.roles에서 역할을 지정하고, 빈 배열로 두면 코디네이팅 전용 노드가 됩니다.
실제 운영 환경에서는 어떻게 구성할까요? 소규모 클러스터에서는 모든 노드가 모든 역할을 수행해도 됩니다.
하지만 규모가 커지면 역할을 분리하는 것이 좋습니다. 마스터 노드는 가볍게 유지하고, 데이터 노드에 리소스를 집중합니다.
마스터 노드가 바빠지면 클러스터 전체가 불안정해질 수 있기 때문입니다. 주의할 점이 있습니다.
마스터 후보 노드는 반드시 홀수로 유지해야 합니다. 짝수면 스플릿 브레인 문제가 발생할 수 있습니다.
네트워크가 단절되었을 때 양쪽에서 각각 자신이 마스터라고 주장하면 데이터 불일치가 발생합니다. 3대, 5대, 7대처럼 홀수로 구성하면 과반수를 확보한 쪽만 마스터를 선출합니다.
김개발 씨가 이해했다는 듯 고개를 끄덕였습니다. "아, 회사처럼 역할 분담이 중요하군요.
관리자, 실무자, 안내 데스크 각자 전문 분야가 있는 거네요. 처음에는 모두가 멀티플레이어로 시작하고, 규모가 커지면 전문화하는 거고요."
실전 팁
💡 - 처음에는 모든 노드가 모든 역할을 수행하도록 구성하세요. 규모가 커지면 그때 역할을 분리해도 됩니다.
- 마스터 후보 노드는 반드시 3대, 5대, 7대처럼 홀수로 구성하세요.
5. 샤드와 레플리카
김개발 씨가 클러스터를 구축하고 데이터를 넣기 시작했습니다. 그런데 인덱스를 생성할 때 샤드 수와 레플리카 수를 설정하라는 옵션이 나왔습니다.
"기본값으로 해도 되나요?" 김개발 씨가 물었습니다. 박시니어 씨가 진지한 표정으로 말했습니다.
"이건 나중에 바꾸기 어려워. 지금 제대로 이해하고 가자."
샤드는 인덱스를 물리적으로 나눈 조각이고, 레플리카는 샤드의 복사본입니다. 마치 피자를 여러 조각으로 나누고(샤딩), 인기 있는 토핑은 여분으로 더 준비해두는 것(복제)과 같습니다.
샤드로 데이터를 분산하고, 레플리카로 안정성을 확보합니다.
다음 코드를 살펴봅시다.
// 인덱스 생성 시 샤드와 레플리카 설정
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
// 샤드 분배 상태 확인
GET /_cat/shards/products?v
// index shard prirep state docs store ip node
// products 0 p STARTED 1000 1.2mb 192.168.1.1 node-1
// products 0 r STARTED 1000 1.2mb 192.168.1.2 node-2
// products 1 p STARTED 1200 1.4mb 192.168.1.2 node-2
// products 1 r STARTED 1200 1.4mb 192.168.1.3 node-3
// products 2 p STARTED 800 0.9mb 192.168.1.3 node-3
// products 2 r STARTED 800 0.9mb 192.168.1.1 node-1
김개발 씨는 인덱스를 생성하려고 문서를 읽다가 멈췄습니다. number_of_shards?
number_of_replicas? 기본값으로 두면 될 것 같은데, 왠지 중요해 보이는 설정이었습니다.
"선배, 이 설정 그냥 넘어가면 안 될까요? 기본값이 있잖아요." 박시니어 씨가 고개를 저었습니다.
"샤드 수는 인덱스 생성 후에 변경할 수 없어. 나중에 바꾸려면 전체 데이터를 재색인해야 해.
지금 제대로 이해하고 가자." 그렇다면 샤드란 무엇일까요? 쉽게 비유하자면, 샤드는 피자 조각입니다.
피자 한 판(인덱스)을 혼자 먹기엔 크니까 여러 조각(샤드)으로 나눕니다. 여러 명이 동시에 먹을 수 있고(병렬 처리), 각 조각을 다른 접시에 나눠 담을 수도 있습니다(분산 저장).
레플리카는 인기 많은 메뉴의 여분입니다. 원본 샤드(프라이머리 샤드)가 있고, 이것의 복사본이 레플리카 샤드입니다.
프라이머리가 있는 노드가 죽으면 레플리카가 프라이머리로 승격됩니다. 또한 검색 요청을 프라이머리와 레플리카가 나눠서 처리하므로 검색 성능도 향상됩니다.
위의 코드를 살펴보겠습니다. 프라이머리 샤드 3개, 레플리카 1개로 설정하면 총 6개의 샤드가 생깁니다.
프라이머리 3개 + 각각의 레플리카 3개입니다. 샤드 분배 상태를 보면 p는 프라이머리, r은 레플리카를 의미합니다.
중요한 점은 프라이머리와 레플리카가 다른 노드에 배치된다는 것입니다. 같은 노드에 있으면 그 노드가 죽었을 때 원본과 복사본이 함께 사라지기 때문입니다.
코드의 출력 결과를 보면 샤드 0의 프라이머리는 node-1에, 레플리카는 node-2에 있습니다. 샤드 수는 어떻게 정해야 할까요?
경험적으로 샤드 하나의 크기가 10GB에서 50GB 사이가 되도록 설정합니다. 너무 작으면 오버헤드가 커지고, 너무 크면 복구 시간이 오래 걸립니다.
예상 데이터가 100GB라면 샤드 3~5개가 적당합니다. 레플리카 수는 보통 1로 시작합니다.
안정성이 중요하면 2로 늘리고, 개발 환경에서는 0으로 설정해도 됩니다. 레플리카를 늘리면 저장 공간이 그만큼 더 필요하다는 점을 기억하세요.
주의할 점이 있습니다. 샤드 수는 나중에 변경할 수 없습니다.
처음에 너무 적게 설정하면 데이터가 늘어났을 때 확장이 어렵습니다. 반대로 너무 많이 설정하면 관리 오버헤드가 커집니다.
미래 성장을 고려해서 여유 있게, 하지만 과하지 않게 설정하는 것이 중요합니다. 김개발 씨가 정리했습니다.
"샤드는 데이터를 나눠서 병렬로 처리하기 위한 거고, 레플리카는 백업과 성능 향상을 위한 거군요. 처음 설정이 중요하니까 신중하게 결정해야겠어요."
실전 팁
💡 - 샤드 하나의 크기가 10-50GB가 되도록 계획하세요. 예상 데이터 크기를 기준으로 역산합니다.
- 노드 수보다 샤드 수가 많아야 부하가 분산됩니다. 노드 3대에 샤드 3개면 각 노드에 1개씩 배치됩니다.
- 레플리카는 언제든 늘릴 수 있으니, 처음에는 1로 시작해도 됩니다.
6. Lucene 엔진 기초
김개발 씨가 Elasticsearch를 공부하면서 자꾸 Lucene이라는 단어를 만났습니다. "Elasticsearch가 Lucene 기반이라는데, Lucene은 뭐예요?" 박시니어 씨가 미소 지었습니다.
"Elasticsearch의 심장이지. 이걸 알면 왜 검색이 그렇게 빠른지 이해할 수 있어."
Lucene은 Java로 작성된 오픈소스 검색 라이브러리로, Elasticsearch의 핵심 엔진입니다. 마치 자동차의 엔진처럼 눈에 보이지 않지만 모든 검색 기능을 실제로 수행합니다.
역색인, 토큰화, 점수 계산 등 검색의 핵심 기술이 모두 Lucene에서 나옵니다.
다음 코드를 살펴봅시다.
// 텍스트 분석 API로 토큰화 과정 확인
POST /_analyze
{
"analyzer": "standard",
"text": "The Quick Brown Fox Jumps"
}
// 결과: ["the", "quick", "brown", "fox", "jumps"]
// 역색인 구조 예시 (개념적 표현)
// Term | Document IDs
// ----------|-------------
// quick | [1, 5, 23]
// brown | [1, 8]
// fox | [1, 5, 8, 15]
// jumps | [1, 23]
김개발 씨는 Elasticsearch 문서를 읽을 때마다 Lucene이라는 단어를 마주쳤습니다. "Lucene 세그먼트", "Lucene 인덱스", "Lucene 분석기"...
도대체 Lucene이 뭐길래 이렇게 자주 언급될까요? "선배, Lucene이 뭐예요?
Elasticsearch랑 다른 건가요?" 박시니어 씨가 설명했습니다. "Elasticsearch가 자동차라면, Lucene은 그 안의 엔진이야.
네가 운전할 때 엔진을 직접 만지진 않지만, 차가 움직이는 건 전부 엔진 덕분이잖아." 그렇다면 Lucene은 정확히 무엇일까요? Apache Lucene은 Doug Cutting이 1999년에 만든 Java 검색 라이브러리입니다.
오픈소스로 공개되어 있고, 전 세계 수많은 검색 시스템의 기반이 됩니다. Elasticsearch뿐 아니라 Apache Solr, Atlassian의 Jira와 Confluence도 Lucene을 사용합니다.
Lucene이 빠른 이유는 역색인(Inverted Index) 구조 때문입니다. 일반적인 데이터베이스는 문서를 순차적으로 저장합니다.
"fox"라는 단어를 찾으려면 모든 문서를 하나씩 열어서 확인해야 합니다. 반면 역색인은 단어를 키로, 그 단어가 포함된 문서 목록을 값으로 저장합니다.
"fox"를 검색하면 바로 [1, 5, 8, 15] 문서에 있다는 것을 알 수 있습니다. 위의 코드에서 analyze API를 사용하면 텍스트가 어떻게 토큰화되는지 확인할 수 있습니다.
"The Quick Brown Fox Jumps"라는 문장은 ["the", "quick", "brown", "fox", "jumps"]로 나뉩니다. 대문자가 소문자로 바뀌고, 공백 기준으로 단어가 분리됩니다.
이렇게 분리된 각 **토큰(term)**이 역색인에 저장됩니다. 검색할 때도 같은 분석 과정을 거칩니다.
"Quick Fox"를 검색하면 ["quick", "fox"]로 변환되고, 역색인에서 두 단어가 모두 포함된 문서를 찾습니다. Lucene은 TF-IDF(Term Frequency-Inverse Document Frequency) 알고리즘으로 관련성 점수를 계산합니다.
검색어가 문서에 많이 나올수록, 그리고 전체 문서에서 드물게 나오는 단어일수록 높은 점수를 받습니다. "the" 같은 흔한 단어보다 "elasticsearch" 같은 특수한 단어가 더 중요하게 취급됩니다.
Lucene의 데이터는 **세그먼트(segment)**라는 불변 파일에 저장됩니다. 문서가 추가되면 새 세그먼트가 생성되고, 주기적으로 여러 세그먼트가 하나로 합쳐집니다(merge).
세그먼트가 불변이기 때문에 캐싱이 효율적이고, 동시 읽기도 안전합니다. 실제로 Elasticsearch를 사용할 때 Lucene을 직접 다룰 일은 거의 없습니다.
하지만 성능 튜닝을 할 때는 Lucene의 동작 원리를 알아야 합니다. 왜 refresh가 필요한지, 왜 merge가 일어나는지, 왜 삭제해도 디스크 공간이 바로 늘지 않는지 이해할 수 있습니다.
김개발 씨가 감탄했습니다. "아, 그래서 검색이 그렇게 빠른 거였군요.
책 내용을 하나하나 읽는 게 아니라, 이미 만들어진 색인 카드에서 찾는 거네요. 도서관 사서가 카드 목록을 뒤지는 것처럼요." 박시니어 씨가 고개를 끄덕였습니다.
"맞아. 그리고 그 색인 카드를 만드는 게 바로 Lucene이야.
Elasticsearch는 Lucene을 여러 대의 서버에서 분산 실행하고, REST API로 쉽게 사용할 수 있게 포장한 거지."
실전 팁
💡 - analyze API를 활용해서 텍스트가 어떻게 토큰화되는지 확인하세요. 검색이 기대와 다르게 동작할 때 원인을 찾는 데 유용합니다.
- 역색인의 원리를 이해하면 왜 와일드카드 검색(*)이 느린지, 왜 정확한 일치 검색이 빠른지 알 수 있습니다.
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!