Elasticsearch 마스터 — Mapping·데이터 타입

2026-05-03확률과 통계 마스터 노트

Elasticsearch 마스터 노트 시리즈 2편. Mapping이 Elasticsearch의 스키마인 이유, text vs keyword의 결정적 차이, Dynamic Mapping의 함정과 명시 매핑 권장, 13 데이터 타입(text·keyword·long·integer·float·double·boolean·date·geo_point·nested·object 등), Multi-field로 한 필드 다중 인덱싱, Mapping 변경 한계까지.

이 글은 Elasticsearch 마스터 노트 시리즈의 두 번째 편입니다. 1편(기초)에서 큰 그림을 봤다면, 이번엔 데이터 구조 정의 — Mapping.

text vs keyword 한 글자 차이가 검색 동작 결정. Dynamic Mapping은 편하지만 함정. 이 두 인식이 운영의 시작.

처음 Mapping이 어렵게 느껴지는 이유

처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, text vs keyword가 막연합니다. 둘째, Dynamic Mapping의 함정이 직관적이지 않습니다.

해결법은 한 가지예요. "text = 분석·검색 / keyword = 정확 매칭·집계" 한 줄. 검색은 text, 집계·정렬·필터는 keyword. 이 매핑만 잡으면 끝.

Mapping이란

GET /products/_mapping
{
  "products": {
    "mappings": {
      "properties": {
        "name": { "type": "text" },
        "price": { "type": "integer" },
        "tags": { "type": "keyword" }
      }
    }
  }
}

각 필드의 타입 정의. RDB의 스키마 비슷하지만 더 유연.

Dynamic Mapping — 자동 추론

# Mapping 정의 안 하고 문서 추가
POST /products/_doc
{
  "name": "Spring Boot",
  "price": 30000,
  "tags": ["spring", "java"],
  "released_at": "2024-01-15"
}

# Elasticsearch가 자동 매핑:
# name        → text + keyword (multi-field)
# price       → long
# tags        → text + keyword
# released_at → date

여기서 정말 중요한 시험 함정 — Dynamic Mapping = 운영 위험. 첫 문서가 정확한지 확신 X. 잘못 추론 시 변경 어려움. 운영 = 명시 Mapping.

명시 Mapping

PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "fields": {
          "keyword": { "type": "keyword", "ignore_above": 256 }
        }
      },
      "price": { "type": "integer" },
      "tags": { "type": "keyword" },
      "released_at": { "type": "date" }
    }
  }
}

명시적·예측 가능.

text vs keyword — 가장 중요한 차이

text — 분석·검색

"description": { "type": "text" }
  • Analyzer가 동작 — 토큰화·소문자·stopword 제거
  • Full-text Search
  • 정렬·집계 X
  • 예: "Spring Boot Framework" → ["spring", "boot", "framework"]

keyword — 정확 매칭

"category": { "type": "keyword" }
  • 분석 X — 원본 그대로
  • 정확 매칭만
  • 정렬·집계 O
  • 필터·aggregation 표준
  • 예: "Spring Boot" → ["Spring Boot"]

비교

# text 검색 — 단어 매칭
GET /products/_search
{
  "query": {
    "match": { "description": "spring" }
  }
}
# → "Spring Boot Framework" 매칭

# keyword 검색 — 정확 매칭
GET /products/_search
{
  "query": {
    "term": { "category": "IT" }
  }
}
# → "IT" 정확히. "it"는 매칭 X.

여기서 정말 중요한 시험 함정 — 검색 = text / 집계·필터·정렬 = keyword. 잘못 선택 시 동작 안 함. 자주 헷갈리는 부분.

Multi-field — 한 필드 다중 인덱싱

같은 데이터를 다르게 인덱싱:

"title": {
  "type": "text",
  "fields": {
    "keyword": { "type": "keyword" },
    "raw":     { "type": "text", "analyzer": "english" }
  }
}
# Full-text 검색 (분석)
GET /_search
{
  "query": { "match": { "title": "spring" } }
}

# 정확 매칭 (keyword)
GET /_search
{
  "query": { "term": { "title.keyword": "Spring Boot Book" } }
}

# 영어 분석기 (stemming 등)
GET /_search
{
  "query": { "match": { "title.raw": "running" } }
  # → "run"·"ran" 매칭 (stem)
}

여기서 시험 함정이 하나 있어요. Multi-field = 같은 데이터·다른 인덱스. 저장 공간 증가. 자주 검색·집계 둘 다 하면 가치 있음.

13 핵심 데이터 타입

문자열

타입 용도
text 검색 (분석·토큰화)
keyword 정확 매칭·집계

숫자

타입 범위
long 64-bit
integer 32-bit
short 16-bit
byte 8-bit
double 64-bit float
float 32-bit float
half_float 16-bit float
scaled_float scale 적용 (예: 가격 *100 = 정수)

날짜·시간

"created_at": { "type": "date" }

기본 형식 — yyyy-MM-dd'T'HH:mm:ssZ 또는 epoch_millis.

Boolean

"active": { "type": "boolean" }

Geo

"location": { "type": "geo_point" }
"area": { "type": "geo_shape" }

지리 검색·반경 등. 지도 API.

IP

"client_ip": { "type": "ip" }

IPv4·IPv6 자동 처리.

Object — 중첩

{
  "user": {
    "name": "Alice",
    "age": 30
  }
}

자동으로 user.name·user.age 평면화.

Nested — 객체 배열

{
  "comments": [
    { "author": "Alice", "text": "Hello" },
    { "author": "Bob", "text": "Hi" }
  ]
}

type: nested로 명시. 객체 단위 독립 인덱싱.

여기서 정말 중요한 시험 함정 — 객체 배열은 기본 평면화 = 잘못된 매칭. 예: comments.author = Alice AND comments.text = Hi도 매칭 (배열 평면화). nested 타입으로 객체 단위 보존.

Mapping 변경 — 한계

✓ 새 필드 추가
✓ 기존 필드 옵션 추가 (ignore_malformed 등)
✗ 기존 필드 타입 변경
✗ 필드 이름 변경
✗ 필드 삭제

여기서 정말 중요한 시험 함정 — 타입 변경 = Reindex 필요. 새 인덱스 생성 → _reindex API → 기존 인덱스 삭제·alias 전환. 운영 부담.

Reindex 패턴

# 1. 새 인덱스 생성 (정확한 mapping)
PUT /products-v2
{
  "mappings": { ... }
}

# 2. 데이터 복사
POST /_reindex
{
  "source": { "index": "products" },
  "dest": { "index": "products-v2" }
}

# 3. Alias 전환
POST /_aliases
{
  "actions": [
    { "remove": { "index": "products", "alias": "products-current" } },
    { "add": { "index": "products-v2", "alias": "products-current" } }
  ]
}

여기서 시험 함정이 하나 있어요. Alias = 인덱스 이름의 별칭. 클라이언트는 alias 사용 → 인덱스 교체 무중단. 운영 표준.

ignore_malformed

"price": {
  "type": "integer",
  "ignore_malformed": true
}

타입 안 맞아도 무시 (필드만 누락, 문서는 인덱싱). 견고성 ↑.

copy_to — 다중 필드 결합

"first_name": {
  "type": "text",
  "copy_to": "full_name"
},
"last_name": {
  "type": "text",
  "copy_to": "full_name"
},
"full_name": {
  "type": "text"
}

first_name·last_name 자동으로 full_name에도 복사. 통합 검색에 편리.

index — 인덱싱 비활성

"raw_data": {
  "type": "text",
  "index": false
}

저장만, 검색 X. 디스크 절감.

doc_values — 정렬·집계

"category": {
  "type": "keyword",
  "doc_values": false   # 정렬·집계 비활성
}

기본 활성. 사용 안 하면 비활성으로 디스크 절감.

Date Format — 다중 형식

"timestamp": {
  "type": "date",
  "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
}

||로 다중 형식 허용.

시험 직전 한 번 더 — 자주 헷갈리는 함정 모음

여기까지가 2편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.

  • Mapping = ES 스키마
  • Dynamic Mapping = 자동 추론 (운영 위험)
  • 운영 = 명시 Mapping
  • text vs keyword = 가장 중요
  • text = 분석·검색·정렬 X / keyword = 정확 매칭·집계 O
  • 검색 = text / 필터·집계·정렬 = keyword
  • Multi-field = 한 데이터·다중 인덱스 (저장 ↑·검색 유연)
  • 데이터 타입 — 문자열(text·keyword)·숫자·date·boolean·geo_point·ip
  • Object vs Nested — Object 자동 평면화·Nested = 객체 단위 보존
  • 객체 배열의 잘못된 매칭 방지 → nested
  • Mapping 변경 — 타입 변경·삭제 XReindex 필요
  • Reindex 패턴 — 새 인덱스 + _reindex API + Alias 전환
  • Alias = 인덱스 이름 별칭 (무중단 교체 표준)
  • ignore_malformed = 타입 안 맞아도 무시
  • copy_to = 다중 필드 결합
  • index: false = 저장만, 검색 X
  • doc_values: false = 정렬·집계 비활성
  • Date format = 다중 형식 (||)
  • 운영 — 명시 Mapping + Alias + Reindex 워크플로우

시리즈 다른 편

공식 문서: Mapping 에서 더 깊이.

다음 글(3편)에서는 Analyzer — text 필드의 토큰화 과정, Standard·Korean Analyzer, 커스텀 분석기 구성까지 풀어 갑니다.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!