Elasticsearch 보강 — 검색 문서 모델링

2026-05-26Elasticsearch 입문에서 운영까지

Elasticsearch 보강편. 매핑 메커닉이 아니라 '요구사항을 보고 어떤 타입을 고를지' 설계 사고법 — text vs keyword 결정, copy_to 통합 검색, 명시 매핑 원칙.

📚 Elasticsearch 입문에서 운영까지 · 40편 — 검색 문서 모델링

이 글은 Elasticsearch 입문에서 운영까지 시리즈의 보강편이에요. 8편 매핑·9편 필드 타입에서 매핑이 어떻게 동작하는지 메커닉을 다뤘죠. 그런데 막상 검색 서비스를 설계하려고 하면 다른 벽에 부딪혀요 — "이 필드는 어떤 타입으로 잡지?" 메커닉을 안다고 좋은 매핑이 나오는 건 아니거든요. 이 글은 요구사항에서 출발해 매핑을 결정하는 검색 문서 모델링 사고법을 풀어요.

문서 모델링은 메커닉을 안다고 되는 게 아니에요

textkeyword의 차이는 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 = 원본 저장(원문 반환용) — 안 쓰는 큰 필드만 신중히 제외
  • 모델링 습관 = 각 필드가 "검색용인가 표시용인가" 구분

공식 문서: Mappingcopy_to에서 타입과 옵션의 자세한 사양을 확인할 수 있어요.

시리즈 다른 편

같이 읽으면 좋은 글:

답글 남기기

error: Content is protected !!