Meilisearch란 — 2026년 7월 기준 완전 분석
Meilisearch는 2018년 프랑스에서 시작한 오픈소스 검색 엔진입니다. Rust로 작성해 속도가 극단적으로 빠르고, 단일 바이너리(또는 Docker 이미지)로 배포가 간단합니다. 2026년 7월 기준 GitHub Star 50,000개를 넘었고, MIT 라이선스로 상업적 사용도 완전 무료입니다.
| 항목 | Meilisearch | Elasticsearch | Typesense | Algolia |
|---|---|---|---|---|
| 라이선스 | MIT 무료 | SSPL (제한적) | GPL-3.0 | 클라우드 SaaS |
| 메모리 사용 | ~200MB (Rust) | 2GB+ (JVM 필수) | ~512MB (C++) | 클라우드 |
| 설치 복잡도 | ⭐ 매우 쉬움 | ⭐⭐⭐⭐ 어려움 | ⭐⭐ 쉬움 | 없음 (SaaS) |
| 한국어 지원 | ✅ 내장 | ✅ 플러그인 필요 | ✅ 내장 | ✅ |
| 오타 허용 | ✅ 기본 내장 | ⚠️ 별도 설정 | ✅ 기본 내장 | ✅ |
| 하이브리드 검색 | ✅ v1.6부터 | ✅ | ✅ | ✅ |
| 셀프호스팅 | ✅ 완전 지원 | ✅ 가능 | ✅ 가능 | ❌ 불가 |
📖 핵심 개념 — 인덱스(Index)와 문서(Document)
| 개념 | 설명 | 비유 |
|---|---|---|
| Index | 문서들의 집합. 각자 독립적인 설정을 가짐 | MySQL의 테이블 |
| uid | 인덱스의 고유 식별자. 영문·숫자·하이픈·언더스코어만 허용 | 테이블명 |
| Document | 인덱스 안의 개별 항목. JSON 객체 | 테이블의 행(row) |
| primaryKey | 각 문서를 고유하게 식별하는 필드명. 인덱스당 하나만 설정. 같은 값이면 덮어씌워짐 | Primary Key |
| Task | 문서 추가/삭제, 설정 변경은 모두 비동기 Task로 처리. /tasks로 완료 여부 확인 | 비동기 작업 큐 |
Elasticsearch는 JVM 기반이라 최소 2GB RAM이 필요합니다. Meilisearch는 Rust로 작성되어 200MB RAM 미만으로 운영 가능합니다. R730의 128GB 중 0.15%만 사용하는 수준입니다. 단일 바이너리 배포라 운영도 단순합니다.
Docker Compose로 설치
📁 디렉토리 구조 생성
R730의 디렉토리 규칙(/mnt/data/01_ai/)에 따라 배치합니다. 설정 파일과 데이터 파일을 구분해서 관리합니다.
# 설정 파일이 위치할 디렉토리 mkdir -p /mnt/data/01_ai/meilisearch # 인덱스 데이터 영구 저장 디렉토리 mkdir -p /mnt/data/01_ai/meilisearch/data cd /mnt/data/01_ai/meilisearch
🐳 docker-compose.yaml 작성
아래 파일을 /mnt/data/01_ai/meilisearch/docker-compose.yaml로 저장합니다.
services:
meilisearch:
image: getmeili/meilisearch:v1.13
container_name: meilisearch
restart: unless-stopped
ports:
- "7700:7700"
environment:
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
- MEILI_ENV=production
- MEILI_DB_PATH=/meili_data
- MEILI_NO_ANALYTICS=true
volumes:
# ./data 는 이 파일이 있는 디렉토리 기준 상대 경로
# 실제 경로: /mnt/data/01_ai/meilisearch/data/
- ./data:/meili_data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7700/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
- ai-common-net
networks:
ai-common-net:
external: truevolumes ./data:/meili_data — docker-compose.yaml이 있는 디렉토리 기준 상대 경로입니다. 컨테이너를 삭제해도 /mnt/data/01_ai/meilisearch/data/ 안의 인덱스 데이터는 보존됩니다.
MEILI_ENV=production — 운영 환경에서 반드시 설정합니다. 미설정 시 Master Key 없이도 API 접근이 허용되어 보안 위험이 생깁니다.
ai-common-net — n8n, Dify, Open WebUI 등 기존 홈랩 스택과 같은 네트워크를 공유해, 컨테이너명 meilisearch로 직접 통신합니다.
🔑 .env 파일 생성
아래 파일을 /mnt/data/01_ai/meilisearch/.env로 저장합니다.
# 32자 이상 강한 키 필수 — 아래 명령어로 생성 # openssl rand -hex 32 MEILI_MASTER_KEY=여기에_강한_마스터키_입력_최소_32자_이상
Master Key는 64자 hex(openssl rand -hex 32 결과값)를 권장합니다. 한번 설정 후 변경하면 기존 API 키가 모두 무효화됩니다. .env 파일은 반드시 .gitignore에 포함하고 외부에 절대 노출하지 마세요.
🚀 기동 및 확인
cd /mnt/data/01_ai/meilisearch docker compose up -d # 정상 기동 확인 — {"status":"available"} 이 나오면 성공 curl http://localhost:7700/health # 로그 실시간 확인 docker compose logs -f meilisearch
브라우저에서 http://192.168.1.253:7700 접속 → Master Key 입력 → Meilisearch Web UI(Dashboard) 진입. 인덱스 목록, 문서 검색, 설정 관리를 GUI로 할 수 있습니다.
인덱스 설계 완전 가이드
인덱스를 잘 설계하면 검색 속도와 품질이 극적으로 향상됩니다. 문서를 추가하기 전에 아래 설정들을 먼저 이해하세요.
| 설정 항목 | 역할 | 설계 권장 |
|---|---|---|
| searchableAttributes | 검색 대상 필드 목록. 순서가 가중치 — 앞에 있을수록 검색 결과 상위 노출 | 반드시 명시적 설정. 기본값(전체 필드)은 성능·품질 모두 저하 |
| filterableAttributes | 필터 파라미터에 사용할 필드. 여기 없는 필드는 filter로 사용 불가 | 나중에 추가하면 전체 재인덱싱 발생 → 처음부터 미리 설계 필수 |
| sortableAttributes | sort 파라미터에 사용할 필드 | 날짜·조회수·가격 등 정렬에 쓸 필드 미리 지정 |
| displayedAttributes | 검색 결과에 포함할 필드. 기본은 전체 포함 | 민감한 내부 필드를 결과에서 제외할 때 사용 |
filterableAttributes를 나중에 추가하면 기존 문서 전체가 재인덱싱됩니다. 문서가 많을수록 시간이 길어지므로, 처음 인덱스 설계 시 필터로 쓸 필드를 모두 미리 정의하는 것이 중요합니다.
실전 인덱스 3종 완전 구성
홈랩에서 실제로 유용하게 쓸 수 있는 3개 인덱스를 처음부터 끝까지 구성합니다. 인덱스 생성 → Settings 설정 → 문서 추가 예시까지 복사해서 바로 사용할 수 있는 코드로 제공합니다.
| 인덱스 uid | primaryKey | 주요 용도 | 핵심 filter 필드 | 핵심 sort 필드 |
|---|---|---|---|---|
blog-posts | post_id | 블로그 포스팅 전문 검색 | category, tags, status | published_at, views |
news-feed | article_id | 뉴스·RSS 피드 검색 | source, category, lang | published_at, crawled_at |
products | sku | 상품·자료 검색 | category, brand, in_stock, price | price, rating |
📝 인덱스 1 — blog-posts
curl -X POST 'http://localhost:7700/indexes' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_MASTER_KEY' \
--data-binary '{
"uid": "blog-posts",
"primaryKey": "post_id"
}'curl -X PATCH 'http://localhost:7700/indexes/blog-posts/settings' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_MASTER_KEY' \
--data-binary '{
"searchableAttributes": [
"title",
"excerpt",
"content",
"tags",
"category"
],
"filterableAttributes": [
"category",
"tags",
"status",
"author",
"published_at"
],
"sortableAttributes": [
"published_at",
"views",
"updated_at"
],
"displayedAttributes": [
"post_id",
"title",
"excerpt",
"category",
"tags",
"author",
"published_at",
"url",
"thumbnail"
]
}'curl -X POST 'http://localhost:7700/indexes/blog-posts/documents' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_ADMIN_KEY' \
--data-binary '[
{
"post_id": 1,
"title": "Plane 셀프호스팅 완전 가이드 2026",
"excerpt": "Jira 대체 오픈소스 프로젝트 관리 툴 Plane을 홈서버에 설치하는 방법",
"content": "...(본문 전체 또는 요약)...",
"category": "홈랩",
"tags": ["Plane", "Docker", "셀프호스팅", "프로젝트관리"],
"author": "agibop",
"status": "published",
"published_at": 1751788800,
"views": 1250,
"url": "https://agibop.com/plane-selfhost-complete-guide-2026/",
"thumbnail": "https://agibop.com/wp-content/uploads/plane-guide.jpg"
}
]'📰 인덱스 2 — news-feed
n8n으로 RSS를 주기적으로 수집해 Meilisearch에 인덱싱합니다. KOSPI 시황, 테크 뉴스, 경쟁사 블로그를 한 곳에 모아 전문 검색합니다. source로 특정 사이트만 필터링하거나, published_at으로 최신순 정렬하는 것이 핵심 패턴입니다.
curl -X POST 'http://localhost:7700/indexes' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_MASTER_KEY' \
--data-binary '{
"uid": "news-feed",
"primaryKey": "article_id"
}'curl -X PATCH 'http://localhost:7700/indexes/news-feed/settings' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_MASTER_KEY' \
--data-binary '{
"searchableAttributes": [
"title",
"summary",
"content",
"tags",
"source_name"
],
"filterableAttributes": [
"source",
"source_name",
"category",
"tags",
"lang",
"published_at"
],
"sortableAttributes": [
"published_at",
"crawled_at"
],
"displayedAttributes": [
"article_id",
"title",
"summary",
"source",
"source_name",
"category",
"tags",
"lang",
"published_at",
"url",
"thumbnail"
]
}'curl -X POST 'http://localhost:7700/indexes/news-feed/documents' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_ADMIN_KEY' \
--data-binary '[
{
"article_id": "reuters-2026-07-06-001",
"title": "Fed signals rate cut as inflation cools to 2.1%",
"summary": "Federal Reserve officials signal potential rate cuts...",
"content": "...(전문)...",
"source": "reuters.com",
"source_name": "Reuters",
"category": "economy",
"tags": ["Fed", "금리", "인플레이션", "미국경제"],
"lang": "en",
"published_at": 1751788800,
"crawled_at": 1751789400,
"url": "https://reuters.com/...",
"thumbnail": "https://..."
}
]'🛍️ 인덱스 3 — products
curl -X POST 'http://localhost:7700/indexes' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_MASTER_KEY' \
--data-binary '{
"uid": "products",
"primaryKey": "sku"
}'curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_MASTER_KEY' \
--data-binary '{
"searchableAttributes": [
"name",
"description",
"brand",
"tags",
"category"
],
"filterableAttributes": [
"category",
"brand",
"in_stock",
"price",
"rating"
],
"sortableAttributes": [
"price",
"rating",
"created_at"
],
"displayedAttributes": [
"sku",
"name",
"description",
"brand",
"category",
"price",
"in_stock",
"rating",
"thumbnail"
]
}'API 키 관리 완전 가이드
| 키 종류 | 용도 | 노출 범위 | 권한 |
|---|---|---|---|
| Master Key | 키 관리, 인덱스 생성/삭제, 설정 변경 | 서버 측에서만 (.env) | 모든 권한 |
| Admin API Key | 문서 추가/수정/삭제, 설정 변경 | 백엔드 서버, n8n | 읽기+쓰기 |
| Search API Key | 검색 쿼리만 | 프론트엔드, 클라이언트 | 읽기(검색)만 |
# Master Key로 API 키 목록 조회 curl 'http://localhost:7700/keys' \ -H 'Authorization: Bearer 여기에_MASTER_KEY' # 응답에서 확인: # "Default Admin API Key" → 문서 추가/삭제 등 백엔드·n8n에서 사용 # "Default Search API Key" → 검색 전용, 프론트엔드에 노출 가능
Master Key가 노출되면 인덱스 전체 삭제, 데이터 탈취 등 심각한 보안 사고가 발생합니다. 프론트엔드 JavaScript에는 반드시 Search API Key(읽기 전용)만 사용하세요.
검색 API 실전 활용
curl -X POST 'http://localhost:7700/indexes/blog-posts/search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_SEARCH_KEY' \
--data-binary '{
"q": "Docker",
"limit": 10,
"offset": 0
}'# news-feed: 영문 economy 카테고리, 최신순 정렬 curl -X POST 'http://localhost:7700/indexes/news-feed/search' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer 여기에_SEARCH_KEY' \ --data-binary '{ "q": "Fed rate cut", "filter": "lang = en AND category = economy", "sort": ["published_at:desc"], "limit": 5 }' # blog-posts: 홈랩 카테고리 + 조회수 높은 순 curl -X POST 'http://localhost:7700/indexes/blog-posts/search' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer 여기에_SEARCH_KEY' \ --data-binary '{ "q": "Prometheus", "filter": "category = 홈랩 AND status = published", "sort": ["views:desc"], "limit": 5 }'
curl -X POST 'http://localhost:7700/indexes/blog-posts/search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 여기에_SEARCH_KEY' \
--data-binary '{
"q": "Meilisearch",
"attributesToHighlight": ["title", "excerpt"],
"highlightPreTag": "<mark>",
"highlightPostTag": "</mark>",
"attributesToCrop": ["content"],
"cropLength": 150,
"limit": 5
}'사용자가 “Dokcer”라고 오타를 내도 “Docker” 결과를 자동으로 반환합니다. 5자 미만은 1자 오타, 5자 이상은 2자 오타까지 허용합니다. 한국어도 동일하게 적용됩니다.
Meilisearch Dashboard(Web UI) 활용
Meilisearch는 설치 시 Web UI(Dashboard)를 내장합니다. http://192.168.1.253:7700 접속 후 Master Key를 입력하면 GUI로 모든 관리 작업을 할 수 있습니다.
| Dashboard 기능 | 위치 | 활용법 |
|---|---|---|
| 인덱스 목록 | 좌측 패널 | 생성된 인덱스 확인. 클릭하면 해당 인덱스의 문서/설정으로 이동 |
| 문서 검색 | 검색창 | 실시간 검색 결과 확인. 인덱스 설정 후 검색 품질 즉시 테스트 |
| Documents 탭 | 각 인덱스 내 | 저장된 문서 목록 확인. 개별 문서 삭제 가능 |
| Settings 탭 | 각 인덱스 내 | searchableAttributes, filterableAttributes 등 GUI로 확인·수정 |
| Tasks 탭 | 상단 메뉴 | 문서 추가/삭제, 설정 변경 작업의 성공/실패 상태 확인 |
| Keys 탭 | 상단 메뉴 | API 키 목록 확인 (Master Key 접속 시에만 표시) |
Dashboard → Tasks 탭 → 해당 Task 클릭 → error 메시지 확인. 가장 흔한 실패 원인은 ① primaryKey 필드가 문서에 없음 ② filterableAttributes에 없는 필드로 filter 시도 ③ 잘못된 JSON 형식입니다.
n8n 자동화 — 문서 자동 인덱싱
n8n과 Meilisearch를 연동하면 WordPress 글 발행 → 자동 인덱싱, RSS 수집 → news-feed 자동 업데이트를 자동화할 수 있습니다. n8n 컨테이너와 meilisearch 컨테이너가 같은 ai-common-net 네트워크에 있으므로 http://meilisearch:7700으로 내부 통신합니다.
📰 WordPress 발행 → blog-posts 자동 인덱싱
Method: POST URL: http://meilisearch:7700/indexes/blog-posts/documents (ai-common-net 내부 통신, 컨테이너명으로 접근) Headers: Authorization: Bearer [ADMIN_KEY] Content-Type: application/json Body (JSON): [ { "post_id": {{ $json.id }}, "title": "{{ $json.title.rendered }}", "excerpt": "{{ $json.excerpt.rendered | stripHtml | truncate(200) }}", "content": "{{ $json.content.rendered | stripHtml | truncate(2000) }}", "category": "{{ $json.categories[0].name }}", "tags": {{ $json.tags | map('name') | json }}, "author": "agibop", "status": "published", "published_at": {{ $json.date | toTimestamp }}, "url": "{{ $json.link }}", "thumbnail": "{{ $json.featured_media_src_url }}" } ]
📡 RSS 수집 → news-feed 자동 인덱싱
워크플로우: Cron(30분) → RSS Feed Read → Set → HTTP Request Method: POST URL: http://meilisearch:7700/indexes/news-feed/documents Body (JSON): [ { "article_id": "{{ $json.guid }}", "title": "{{ $json.title }}", "summary": "{{ $json.contentSnippet | truncate(300) }}", "source": "{{ $json.link | extractDomain }}", "source_name": "{{ $node['RSS Feed'].json.feedTitle }}", "category": "{{ $json.categories[0] ?? 'general' }}", "tags": {{ $json.categories | json }}, "lang": "{{ $json.lang ?? 'ko' }}", "published_at": {{ $json.pubDate | toTimestamp }}, "crawled_at": {{ $now | toTimestamp }}, "url": "{{ $json.link }}" } ]
같은 article_id(primaryKey)를 가진 문서를 다시 추가하면 기존 문서를 덮어씁니다. 30분마다 RSS를 수집해도 중복 없이 최신 상태를 유지합니다. n8n에서 별도 중복 체크 로직이 필요 없습니다.
업그레이드 완전 가이드
마이너 업그레이드(v1.12 → v1.13)는 이미지 태그만 바꾸고 재시작하면 됩니다. 메이저 업그레이드(v1.x → v2.x)는 반드시 Dump → 이미지 교체 → 재임포트 절차를 따라야 합니다. 절차를 무시하면 “incompatible database version” 오류가 발생하고 기존 데이터에 접근 불가 상태가 됩니다.
cd /mnt/data/01_ai/meilisearch # 1. docker-compose.yaml에서 이미지 태그 수정 # image: getmeili/meilisearch:v1.12 → v1.13 # 2. 새 이미지 Pull 및 재시작 docker compose pull docker compose up -d # 3. 정상 기동 확인 curl http://localhost:7700/health
### Step 1: 현재 버전에서 Dump 생성 curl -X POST 'http://localhost:7700/dumps' \ -H 'Authorization: Bearer 여기에_MASTER_KEY' # 반환된 taskUid로 완료 여부 확인 (status: "succeeded" 대기) curl 'http://localhost:7700/tasks/여기에_TASK_UID' \ -H 'Authorization: Bearer 여기에_MASTER_KEY' # Dump 파일 위치 확인 # 호스트 경로: /mnt/data/01_ai/meilisearch/data/dumps/ ls /mnt/data/01_ai/meilisearch/data/dumps/ ### Step 2: docker-compose.yaml 이미지 버전 변경 # image: getmeili/meilisearch:v1.x → v2.x ### Step 3: One-off 컨테이너로 Dump 임포트 docker compose run --rm \ -e MEILI_IMPORT_DUMP="/meili_data/dumps/20260706-120000.dump" \ meilisearch ### Step 4: 임포트 완료 후 정상 기동 docker compose up -d
트러블슈팅 실전 5가지
원인: 인덱스에 설정된 primaryKey 필드가 문서 JSON에 없음
해결: 추가하려는 문서에
post_id(또는 설정한 primaryKey)가 포함되어 있는지 확인원인: filterableAttributes에 등록되지 않은 필드로 filter 사용
해결: 해당 필드를 filterableAttributes에 추가 후 재인덱싱 완료 대기
원인: 메이저 버전 업그레이드 시 Dump → 재임포트 절차 건너뜀
해결: 이전 버전으로 롤백 → Dump 생성 → 새 버전으로 임포트 (STEP 09 참고)
http://meilisearch:7700 접근 불가원인: n8n과 meilisearch 컨테이너가 같은 Docker 네트워크에 없음
원인 2: 인덱스를 아직 생성하지 않음
해결: Dashboard 로그인 시 반드시 Master Key 사용
# meilisearch 컨테이너의 네트워크 확인 docker inspect meilisearch | grep -A 10 "Networks" # ai-common-net에 없다면 수동 연결 docker network connect ai-common-net meilisearch # 연결 후 n8n에서 재시도
blog-posts · news-feed · products 3개 인덱스를 설계하고
n8n으로 자동 인덱싱까지 구성하면 완전한 검색 인프라가 완성됩니다.



