Elasticsearch 보강편. 매핑 메커닉이 아니라 '요구사항을 보고 어떤 타입을 고를지' 설계 사고법 — text vs keyword 결정, copy_to 통합 검색, 명시 매핑 원칙.
이 글은 Elasticsearch 입문에서 운영까지 시리즈의 보강편이에요. 8편 매핑·9편 필드 타입에서 매핑이 어떻게 동작하는지 메커닉을 다뤘죠. 그런데 막상 검색 서비스를 설계하려고 하면 다른 벽에 부딪혀요 — "이 필드는 어떤 타입으로 잡지?" 메커닉을 안다고 좋은 매핑이 나오는 건 아니거든요. 이 글은 요구사항에서 출발해 매핑을 결정하는 검색 문서 모델링 사고법을 풀어요.
문서 모델링은 메커닉을 안다고 되는 게 아니에요
text와 keyword의 차이는 8편에서 다 배웠어요. 그런데도 실무에서 매핑을 잘못 잡는 일이 흔해요. 왜냐하면 타입 선택은 문법 문제가 아니라 "이 필드를 어떻게 쓸 거냐"는 설계 문제거든요.
핵심 질문은 늘 같아요 — "이 필드로 무엇을 할 것인가?" 전문(full-text) 검색을 할 건지, 정확히 일치하는 값으로 필터할 건지, 정렬할 건지, 집계할 건지. 같은 "상품명" 필드라도 검색하려면 text, 정렬·집계하려면 keyword가 맞아요. 그래서 문서 모델링은 필드 목록이 아니라 요구사항 목록에서 시작해요.
문서 모델링의 출발 — 요구사항에서 타입 결정
자주 쓰는 결정 기준을 표로 정리하면 이래요.
| 이 필드로 할 일 | 타입 | 이유 |
|---|---|---|
| 전문 검색(형태소 분석) | text |
분석기로 토큰화해 부분·유사 검색 |
| 정확 일치·필터·정렬·집계 | keyword |
분석 안 함, 값 그대로 비교 |
| 범위 검색·정렬(숫자) | integer·long·double |
수치 연산·범위 |
| 날짜 범위·정렬 | date |
기간 필터·시계열 |
| 검색 + 정렬 둘 다 필요 | text + keyword(멀티필드) |
한 필드를 두 용도로 |
마지막 줄이 자주 쓰는 패턴이에요 — "상품명으로 검색도 하고 정렬도 한다" 면, text로 두고 그 안에 .keyword 서브필드를 둬요(멀티필드). 검색은 name, 정렬·집계는 name.keyword로 가는 거죠. 여기서 시험 함정 하나 — 정렬·집계를 text에 직접 하면 에러가 나거나 무거워요. 그건 keyword의 일이에요.
copy_to — 통합 검색 필드 만들기
검색창 하나로 제목·본문·태그·작성자 를 한꺼번에 검색하고 싶을 때가 많죠. 매번 여러 필드를 OR로 묶어 질의하는 대신, copy_to 로 여러 필드를 하나의 검색 전용 필드에 모아 둘 수 있어요.
{
"mappings": {
"properties": {
"title": { "type": "text", "copy_to": "search_all" },
"body": { "type": "text", "copy_to": "search_all" },
"tags": { "type": "text", "copy_to": "search_all" },
"search_all": { "type": "text" }
}
}
}
이렇게 하면 색인할 때 title·body·tags 값이 자동으로 search_all에도 복사돼요. 검색은 search_all 한 필드만 질의하면 끝이라 쿼리가 단순해지고 빨라져요. 통합 검색박스의 정석 패턴이에요. (search_all은 _source에 따로 저장 안 하고 검색용으로만 둬도 돼요.)
dynamic 끄고 명시 매핑
Elasticsearch는 매핑을 안 정해 두면 들어온 문서를 보고 타입을 자동 추론해요(dynamic mapping). 편하지만 운영에선 위험해요. 여기서 정말 중요한 함정 —
- 문자열이 들어오면 자동으로
text+keyword멀티필드로 만들어 불필요한 필드가 폭발해요(매핑 explosion). - 어쩌다
"123"이 먼저 들어오면 숫자로 추론했다가 나중에 문자열이 와서 타입 충돌이 나기도 해요.
그래서 운영 인덱스는 dynamic: "strict" 로 두고 필드를 명시적으로 선언하는 게 안전해요. strict면 매핑에 없는 필드가 들어올 때 조용히 추가하는 대신 에러로 막아 줘서, 의도치 않은 필드 증식을 차단해요. "매핑은 자동이 아니라 설계" 라는 원칙이에요.
_source와 안 쓰는 필드 정리
마지막으로 저장 최적화 하나. Elasticsearch는 원본 문서를 _source에 통째로 저장해요. 검색 결과로 원문을 돌려줄 때 쓰죠. 그런데 검색·집계에만 쓰고 원문으로 돌려줄 일이 없는 큰 필드라면, 굳이 _source에 다 안고 갈 필요가 없어요. 다만 _source를 잘라내면 39편 재색인이나 update가 제한되니, 정말 안 쓰는 게 확실한 필드에만 신중히 적용해요. 모델링 단계에서 "이 필드는 검색용인가, 표시용인가"를 가르는 습관이 인덱스 크기를 좌우합니다.
시험·면접 직전 압축 노트 — 검색 문서 모델링
- 타입 선택 = 문법이 아니라 "이 필드로 무엇을 할 것인가" 설계 문제
- 문서 모델링은 필드 목록이 아니라 요구사항(검색·필터·정렬·집계)에서 출발
- 전문 검색 =
text(분석기 토큰화) / 정확 일치·필터·정렬·집계 =keyword - 숫자 범위 =
integer/long/double, 날짜 =date - 검색+정렬 둘 다 =
text+.keyword멀티필드 (검색name, 정렬name.keyword) text에 직접 정렬·집계 = 에러/고비용 →keyword로copy_to= 여러 필드를 하나의 통합 검색 필드(search_all)로 모음 → 통합 검색박스 정석- dynamic mapping = 타입 자동 추론 → 매핑 폭발·타입 충돌 위험
- 운영 인덱스 =
dynamic: "strict"+ 명시 매핑 (없는 필드 에러로 차단) _source= 원본 저장(원문 반환용) — 안 쓰는 큰 필드만 신중히 제외- 모델링 습관 = 각 필드가 "검색용인가 표시용인가" 구분
공식 문서: Mapping과 copy_to에서 타입과 옵션의 자세한 사양을 확인할 수 있어요.
시리즈 다른 편
같이 읽으면 좋은 글: