Elasticsearch의 형태소 분석기와 역인덱스: 검색 엔진의 핵심

2026. 2. 13. 17:23·개발 알쓸신잡

0. 들어가며

검색 기능을 공부하면서 MySQL의 LIKE 쿼리를 접하게 되었습니다. 단순 문자열 매칭을 통해 원하는 데이터를 찾을 수 있다는 점에서 직관적이고 이해하기 쉬운 방법이었습니다.

 

하지만 실제로 조금 더 깊게 살펴보면서 LIKE의 한계를 알게 되었습니다.

 

데이터가 많아질수록 성능이 급격히 저하될 수 있다는 점이 가장 큰 문제였습니다. 그 원인은 크게 세 가지로 나눌 수 있습니다.

 

첫째, 전체 테이블 스캔: LIKE '%검색어%'와 같이 앞쪽에 와일드카드가 붙으면 인덱스를 전혀 활용하지 못하고 모든 행을 검사하게 됩니다.
둘째, 조인과 집계 연산 부담: 검색과 함께 복잡한 조인이나 정렬, 집계를 수행하면 한 쿼리에서 처리해야 하는 데이터가 기하급수적으로 늘어나 속도가 떨어집니다.
셋째, 데이터 증가에 따른 I/O 병목: 리뷰나 게시글과 같이 텍스트가 누적되면서 디스크 I/O가 성능의 병목이 되고, 단순 캐싱만으로는 한계를 극복할 수 없습니다.

 

또한 LIKE는 단순 포함 여부만 판단할 뿐, 관련도를 계산할 수 없고, “사과”를 검색했을 때 “사과를”, “사과가”와 같은 형태 변화까지 처리하기 어렵다는 한계도 있습니다.

 

이처럼 검색은 단순 문자열 비교 이상의 문제임을 깨닫게 되었고, 자연스럽게 전문 검색 엔진인 Elasticsearch라는 기술을 알게 되었습니다.

 

이번 글에서는 Elasticsearch의 핵심 개념인 형태소 분석기와 역인덱스가 무엇인지, 그리고 각각이 어떤 역할을 하며 어떤 장단점을 가지는지 정리해보겠습니다.


1. Elasticsearch 란 무엇인가?

Elasticsearch는 오픈소스 분산형 검색 엔진입니다.

사실 이 한 문장 안에 Elasticsearch의 본질이 모두 담겨 있습니다.


1-1   Sharding(샤딩) - 분산형 검색 엔진이란 무엇인가? 

먼저 “분산형”이라는 말부터 이해해보겠습니다.

분산형 검색 엔진이란, 여러 대의 서버가 협력하여 데이터를 저장하고, 색인하고, 검색하는 구조를 의미합니다. 하나의 서버가 모든 데이터를 처리하는 것이 아니라 데이터를 나누어 여러 서버가 동시에 작업을 수행하는 방식입니다.

여기서 등장하는 핵심 개념이 바로 Sharding(샤딩) 입니다.

Sharding은 말 그대로 “조각내다”라는 뜻입니다. 데이터를 여러 서버에 나누어 저장하는 것을 의미합니다. Elasticsearch는 데이터뿐만 아니라 인덱스도 shard 단위로 분할하여 저장합니다.

 

 

예를 들어 1억 건의 데이터가 있다면 이를 하나의 서버가 처리하는 것이 아니라 여러 shard로 나누어 여러 서버에 분산 저장합니다.

이렇게 shard 단위로 분산되어 있기 때문에 검색 요청이 들어오면 여러 서버가 동시에 병렬로 처리하고, 그 결과를 모아 최종 응답을 반환합니다.

바로 이 구조 덕분에 대용량 환경에서도 텍스트 검색이 매우 빠르게 동작합니다.


1-2 왜 분산 구조가 강력한가?

이 구조의 가장 큰 장점은 확장성(Scalability) 입니다.

트래픽이 증가하거나 데이터가 늘어나면 서버를 추가하면 됩니다.

즉, 성능을 수직적으로 키우는(Scale-Up) 것이 아니라  서버를 추가해서 수평적으로 확장(Scale-Out)할 수 있습니다.  

 


1-3 복제본(Replica) - 고가용성

또한 엘라스틱 서치는 기본적으로 복제본을 만드는 습성이 있습니다.
때문에 일부 서버에 화재가 나거나, 미사일 공격을 받아서 서버가 박살이 나더라도  복제 본이 있으므로 서비스가 중단될 확률이 낮습니다. 이것을 높은 고가용성이라고 합니다. 쉽게 말하면 좀비처럼 잘 안죽는다는 뜻입니다.


정리하자면

분산형 검색 엔진이라서 텍스트 검색이 빠릅니다. 분산형이므로 스케일업이 가능하고, 복제본(Replica) 를 사용하면 높은 고가용성을 가지고 있습니다.


 

그런데 여기서 한 가지 의문이 생깁니다.

서버를 여러 대로 나누는 것과 “검색이 빠르다”는 것은 과연 같은 이야기일까요?

분산 처리를 한다고 해서 검색 알고리즘 자체가 빨라지는 것은 아닙니다.
여러 서버가 동시에 처리해 줄 뿐이지 각 서버 내부에서 검색이 어떻게 이루어지는지는 또 다른 문제입니다.

Elasticsearch가 진짜 빠른 이유는 단순히  분산 구조 때문이 아니라 검색을 위한 특별한 데이터 구조  역인덱스를 사용하기 때문입니다. 

 

이제 Elasticsearch의 속도를 결정하는 핵심 구조,
역인덱스가 무엇인지 살펴보겠습니다.

 


1. 역인덱스(Inverted Index)란 무엇인가?

역인덱스(Inverted Index)란, 전통적인 문서 저장 방식과 달리 단어를 기준으로 문서를 연결하는 자료구조를 말합니다.
일반적인 문서 저장 구조는 “문서 ID → 문서 내용”으로 이루어져 있어 특정 단어를 찾으려면 전체 문서를 하나씩 검사해야 합니다.
반면 역인덱스는 “단어 → 문서 목록” 구조로 저장되기 때문에 특정 단어를 검색할 때 전체 문서를 스캔하지 않고도 관련 문서를 바로 조회할 수 있습니다.

 

Elasticsearch는 내부적으로 Apache Lucene을 기반으로 동작하며 Lucene의 핵심 자료구조가 바로 역인덱스(Inverted Index) 입니다.

참고 : Apache Lucene란?
Apache Lucene은 자바(Java)로 작성된 오픈소스 정보 검색 라이브러리로, 텍스트 인덱싱과 검색 기능을 제공한다.
검색 엔진, 로그 분석 도구, 추천 시스템 등 다양한 소프트웨어의 핵심 검색 컴포넌트로 널리 사용된다.

 

 

우리가 일반적으로 생각하는 데이터 저장 구조는 이렇습니다.

문서 ID → 문서 내용

 

예를 들어

1 → 나는 사과를 좋아한다
2 → 나는 바나나를 좋아한다

 

형태소 분석기는 텍스트를 검색 가능한 토큰으로 분해하는 역할을 합니다. 한글의 경우 띄어쓰기만으로는 정확한 검색이 어렵기 때문에 형태소 분석이 필수적입니다. 이 상태에서 “사과”를 검색한다면 어떻게 될까요?

가장 단순한 방법은 모든 문서를 하나씩 읽으면서 “사과”가 포함되어 있는지 확인하는 것입니다.
데이터가 적다면 괜찮지만 수백만 건, 수천만 건이 된다면 이야기는 완전히 달라집니다.

 

역인덱스는 저장 구조를 뒤집습니다.

사과 → [1]
바나나 → [2]
좋아하다 → [1, 2]

 

문서 -> 단어가 아니라
단어 -> 문서

로 저장하는 방식입니다.

 

이렇게 되면 “사과”를 검색할 때 전체 문서를 검사할 필요없이  그냥 “사과”라는 단어가 연결된 문서 목록을 바로 가져오면 됩니다.


역인덱스의 내부 동작 원리

역인덱스는 단순히 “단어 → 문서ID”만 저장하지 않습니다. 조금 더 깊이 들어가 보면, 검색 품질을 결정하는 여러 정보들이 함께 저장됩니다.


 Posting List

Posting List는 특정 단어가 어떤 문서에 포함되어 있는지를 기록한 목록입니다.

 

예를 들어 사과라는 단어가 있다면

사과 → [Doc1, Doc5, Doc20]

처럼 해당 단어가 등장한 문서들의 ID가 저장됩니다.
이 덕분에 특정 단어를 검색하면 전체 문서를 스캔하지 않고도, 관련 문서만 빠르게 찾을 수 있습니다.


 Term Frequency (TF)

해당 문서에서 그 단어가 몇 번 등장했는지 저장합니다. 

어떤 문서에서 “사과”가 한 번 등장한 경우보다 열 번 등장한 경우가 해당 주제와 더 관련이 있을 가능성이 높습니다.
따라서 단어가 많이 등장할수록 더 높은 점수를 받을 수 있습니다.

 

갑자기 웬 점수?
검색 엔진은  찾은 문서들 중에서 어떤 문서를 위에 보여줄지 순서를 정해야 합니다.
이때 사용하는 것이 바로 관련도 점수(score) 입니다.
검색어와 더 관련 있다고 판단되는 문서 → 높은 점수 덜 관련 있다고 판단되는 문서 → 낮은 점수
이 점수를 기준으로 정렬해서 보여줍니다

 

Term Frequency는 검색 결과의 관련도 점수를 계산하기 위한 요소 중 하나입니다.
단어가 문서 내에서 많이 등장할수록 해당 주제와의 관련성이 높다고 판단되어,
최종 점수 계산에 긍정적인 영향을 미칩니다.

 Document Frequency (DF)

 

그 단어가 전체 문서 중 몇 개에 등장했는지 관리합니다.

 

예를 들어, “사과”라는 단어가 10만 개의 문서 중 100개에만 등장한다면 이는 비교적 희소한 단어입니다.
반대로 대부분의 문서에 등장하는 단어라면 검색어로서의 변별력이 낮습니다.

이러한 정보는 BM25와 같은 점수 계산 알고리즘에 활용됩니다.
BM25는 단어의 등장 빈도와 희소성을 함께 고려하여 검색 결과의 관련도를 계산합니다.

Elasticsearch는 단어의 빈도와 희소성을 계산하여 더 의미 있는 결과를 위로 정렬합니다.  

 


2. 형태소 분석기 (Analyzer)란 무엇인가?

형태소 분석기는 텍스트를 검색 가능한 토큰으로 분해하는 역할을 합니다. 한글의 경우 띄어쓰기만으로는 정확한 검색이 어렵기 때문에 형태소 분석이 필수적입니다.

 

예를 들어 다음 문장이 있다고 가정해보겠습니다.

나는 사과를 좋아한다

단순 공백 기준으로 자르면

나는 | 사과를 | 좋아한다

하지만 한국어에서는 “사과”가 핵심 단어입니다.“사과를” 그대로 저장하면 사용자가 “사과”만 검색했을 때 매칭이 되지 않을 수도 있습니다.

형태소 분석기는 이를 다음과 같이 처리할 수 있습니다.

나 | 사과 | 좋아하다

조사와 어미를 제거하고 의미 단위로 분해하는 것입니다.


형태소 분석기 구성요소 

형태소 분석기는 세가지 구성요소로 이루어져 있습니다

1. Character Filter

  • HTML 태그 제거, 특수문자 변환 등 전처리
  • 예: <p>안녕하세요</p> → 안녕하세요

2. Tokenizer

  • 텍스트를 토큰으로 분리
  • 예: 안녕하세요 반갑습니다 → [안녕하세요, 반갑습니다]

3. Token Filter

  • 토큰을 가공 (소문자 변환, 불용어 제거, 동의어 처리 등)
  • 예: [안녕하세요, 반갑습니다] → [안녕, 반갑]

한국어에서 형태소 분석이 중요한 이유

형태소 분석을 할 때 영어는 비교적 단순합니다.

apple / apples

하지만 한국어는 훨씬 복잡합니다.

사과
사과를
사과가
사과는
사과였다

형태소 분석 없이 단순 문자열 매칭을 한다면 이들은 모두 다른 단어로 취급됩니다.

그래서 한국어 검색에서는 형태소 분석이 사실상 필수입니다.


Elasticsearch Analyzer 설정 예시

아래는 Elasticsearch에서 한국어 형태소 분석기(Korean Analyzer)를 설정하는 예시입니다.

PUT /korean_example
{
  "settings": {
    "analysis": {
      "tokenizer": {
        "korean_tokenizer": {
          "type": "nori_tokenizer",
          "decompound_mode": "mixed"
        }
      },
      "analyzer": {
        "korean_analyzer": {
          "type": "custom",
          "tokenizer": "korean_tokenizer",
          "filter": ["lowercase", "trim"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "korean_analyzer"
      }
    }
  }
}
  • nori_tokenizer: 한국어 전용 형태소 분석기(Lucene 기반)
  • decompound_mode: mixed: 합성어를 분해하고 원형 단어도 함께 유지
  • filter: 소문자 변환, 불용어 제거 등 토큰 후처리

이렇게 설정하면 나는 사과를 좋아한다와 같은 문장도 "사과"라는 핵심 단어 중심으로 검색 가능하게 됩니다.
실제로 Elasticsearch에서는 Analyzer 설정을 통해 검색 정확도와 성능을 조절할 수 있으며 한국어 검색에서는 거의 필수적인 과정입니다.


마치며

앞으로 프로젝트를 진행하게 된다면 단순히 DB에서 LIKE로 처리하기보다는
검색이 중요한 도메인이라면 Elasticsearch를 한 번 직접 적용해보고 싶다는 생각이 들었습니다.

 

특히 상품 검색 게시글 검색 자동완성 기능 같은 영역에서는 직접 인덱스를 설계해보고 Analyzer를 튜닝해보는 경험이 굉장히 재미있을 것 같습니다.

 

이번 정리는 단순한 기술 정리가 아니라, “검색을 바라보는 시야가 조금 넓어진 경험”이었습니다.

 

저작자표시 비영리 (새창열림)

'개발 알쓸신잡' 카테고리의 다른 글

프로그래밍의 역사 속에서 프로그래밍 사고법을 배울 수 있는 5가지 교훈  (0) 2025.11.03
'개발 알쓸신잡' 카테고리의 다른 글
  • 프로그래밍의 역사 속에서 프로그래밍 사고법을 배울 수 있는 5가지 교훈
깊은바다속꼬북이
깊은바다속꼬북이
  • 깊은바다속꼬북이
    CodeBlossom
    깊은바다속꼬북이
  • 전체
    오늘
    어제
    • 분류 전체보기 (53) N
      • 라이징 캠프 (4)
      • 객채지향 개발론 (3)
      • 스프링 (10) N
      • 네트워크 (2)
      • 자바 (16)
      • 자료구조 (3)
      • 운영체제 (0)
      • 데이터베이스 (4)
      • 디자인패턴 (7)
      • JSP (1)
      • 개발 알쓸신잡 (2)
      • 일반 교양 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백엔드
    jit-compiler
    개발 교훈
    java
    java data area
    스프링
    어댑터 패턴(Adapter Pattern)
    java 버전별 특징
    JVM
    개발 철학
    디자인 패턴
    JUnnit5
    트랜잭션 전파레벨
    자바 Socket 클래스
    디자인패턴
    템플릿 메서드 패턴(Template Method Pattern)
    mockito라이브러리
    싱글턴 패턴(Singleton Pattern)
    MySQL 파서
    프로그램밍 언어
    객체지향
    한국어 검색
    전략 패턴(Strategy Pattern)
    MySQL 옵티마이저
    개발자 철학
    junnit5프레임워크
    GC
    자료구조
    jvm 클래스 로더
    spring
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
깊은바다속꼬북이
Elasticsearch의 형태소 분석기와 역인덱스: 검색 엔진의 핵심
상단으로

티스토리툴바