DB/데이터 관리

Elasticsearch란?

dddzr 2025. 4. 10. 21:20

📌 1. Elasticsearch란?

Elasticsearch는 분산 검색 및 분석 엔진으로, 대규모 데이터를 빠르게 검색하고 분석하는 데 최적화된 시스템!!

검색, 로그 수집, 데이터 분석 등의 용도로 사용된다.

 

1-1. Elasticsearch의 주요 개념

  • 인덱스(Index)
    데이터를 검색하기 위해 구성된 기본적인 저장 단위. 각 인덱스는 문서(document)의 집합으로 구성, 이 문서들은 JSON 형식으로 저장된다.

  • 샤드(Shard)와 레플리카(Replica)
    Elasticsearch는 데이터를 샤드(shard)로 나누어 여러 서버에 분산 저장, 이를 레플리카(replica)로 복제하여 고가용성을 보장한다.

  • 검색 및 분석
    사용자는 검색 쿼리를 통해 데이터를 빠르게 찾고, 분석할 수 있다.  전체 텍스트 검색 및 고급 필터링 기능을 제공한다.

  • Mapping과 Analyzer
    Mapping을 통해 문서의 구조를 정의하고, Analyzer를 통해 텍스트 데이터를 어떻게 분석할지 결정한다. 이때 텍스트 필드가 인덱싱될 때 토큰화와 불용어 제거 같은 처리가 이루어지고, 압축이 적용되기도 한다.

 

1-2. Elasticsearch의 검색 성능 최적화하는 방법

Elasticsearch는 대규모 데이터를 빠르게 검색하고 분석하기 위해 다양한 최적화 기법을 사용하는데, 주요 기법은 아래와 같다.

몰라도 사용하는데 지장은 없다!! 읽고 넘어가자.😊

 

1️⃣ 샤딩 및 분산 처리 최적화

  • 데이터를 여러 노드에 분산 저장하고 샤드/레플리카를 활용해 부하 분산 및 고가용성 확보.
  • 자동 샤드 리밸런싱과 로드 밸런싱을 통해 클러스터 성능 유지.

 

2️⃣ 인덱싱 효율화

  • 필요한 필드만 인덱싱(서브셋 인덱싱)하거나 다중 인덱스를 활용해 저장 공간과 검색 속도 최적화.
  • 대량 데이터는 배치 인덱싱으로 처리해 리소스 절약.

 

3️⃣ 데이터 압축 및 저장 최적화

  • LZ4 등의 압축 알고리즘을 활용해 저장 공간 절약 및 빠른 검색 지원.

 

4️⃣ 검색 쿼리 및 구조 최적화

  • 불용어 제거, 어근 분석 등 큐레이션과 토큰화 적용.
  • Bool Query 등으로 복합 쿼리 구조 최적화.
  • 필터/쿼리 분리 및 캐싱 활용으로 응답 시간 단축.

 

5️⃣ 인덱스 구조 및 리프레시 제어

  • 역 인덱스를 활용해 텍스트 검색 최적화.
  • 리프레시 간격 조절로 성능과 데이터 반영 속도 균형 조절.

 

⭐참고 자료

https://goodbyeanma.tistory.com/160



📌 2. 데이터 삽입

2-1. 인덱스 없이 데이터 삽입

예제: products라는 인덱스 없이 데이터 삽입

PUT http://localhost:9200/products/_doc/1
{
  "name": "노트북",
  "price": 1500000,
  "category": "전자제품"
}

➡️ products라는 인덱스가 자동 생성됨
➡️ _doc/1 → 문서 ID 1로 저장됨

2-2. 명시적으로 인덱스 정의

실무에서는 명시적으로 인덱스 설계를 먼저 함!
ElasticSearch도 데이터 구조를 정의하는 "매핑(Mapping)"을 미리 설정하는 게 좋음.
안 그러면 필드 타입이 자동으로 지정되는데, 나중에 바꾸기 어려움움!

명시적으로 인덱스를 정의하는 방법

PUT http://localhost:9200/products
{
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "price": { "type": "integer" },
      "category": { "type": "keyword" }
    }
  }
}

➡️ name, price, category 필드가 정의된 products 인덱스가 만들어짐!

 

📌3. Spring Boot Elasticsearch 사용법

✅ 요약

1️⃣ Elasticsearch 서버 실행 → Gradle에 의존성 추가.

2️⃣ Spring Boot에서 설정 추가 (application.yml 또는 application.properties).

3️⃣ Elasticsearch Repository와 연동하여 데이터를 관리.

4️⃣ 필요한 경우 Elasticsearch RestTemplate을 사용하여 커스텀 쿼리 작성.

3-1. Elasticsearch 설치 및 실행 

두 가지 방법으로 설치 및 실행할 수 있다.

🔹 Docker로 Elasticsearch 실행

docker run -d --name elasticsearch \

  -p 9200:9200 -p 9300:9300 \

  -e "discovery.type=single-node" \

  docker.elastic.co/elasticsearch/elasticsearch:8.11.2

 

🔹 Elasticsearch 다운로드 후 실행

  1. Elasticsearch 홈페이지에서 설치 파일 다운로드.
  2. 압축을 해제하고 bin/elasticsearch를 실행. (window는 .bat) (관리자 권한으로)

 

3-2. 의존성 추가

Elasticsearch와 통신하기 위해 Spring Data Elasticsearch gradle 의존성을 추가한다.

dependencies {    
    //spring-boot-starter-data-elasticsearch (추천)
    implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'

    // Spring Data Elasticsearch (추천x)
    implementation 'org.springframework.data:spring-data-elasticsearch'

    // Elasticsearch-java (고급 설정 필요시)
    implementation 'co.elastic.clients:elasticsearch-java'
}

 

💡어떤 걸 써야 할까?

spring-data-elasticsearch
→ Spring 애플리케이션에서 기본적인 검색 및 CRUD (@Document + ElasticsearchRepository, ElasticsearchTemplate )

 

Spring-boot-starter-data-elasticsearch

→  Spring Boot 프로젝트에서 Elasticsearch를 쉽게 연동(자동 설정 지원)

→ spring-data-elasticsearch 포함 + Elasticsearch RestClient + 자동 설정 지원

Elasticsearch-java

→ 기능을 세밀하게 제어 (고급 쿼리 가능)

→ Elasticsearch 서버와의 HTTP 요청을 통해 직접 통신 (저수준 클라이언트)

→ spring-data-elasticsearch와 서로 독립적이므로, 함께 사용할 수도 있다. 

 

Spring Boot에선 대부분 spring-boot-starter-data-elasticsearch만  필요! 😊

 

3-3. Spring Boot 설정

application.yml 또는 application.properties에 Elasticsearch 서버 정보를 추가.

spring.elasticsearch.rest.uris=http://localhost:9200
spring.elasticsearch.username=elastic
spring.elasticsearch.password=changeme
# 비밀번호 초기화: bin/elasticsearch-reset-password -u elastic
콘솔에 새로운 비밀번호가 출력된다.

 

3-4. Elasticsearch Repository 생성

Elasticsearch 데이터를 CRUD 방식으로 다룰 수 있도록 Spring Data Elasticsearch Repository를 사용합니다.

 

✅ 3-4-1. 엔티티 클래스 정의

Elasticsearch에 저장할 데이터를 정의한다.

참고로 JPA(@Entity) + Elasticsearch(@Document)는 같이 사용 가능.

그러나 RDB의 모든 데이터를 ElasticSearch에 저장할 필요 없어서 따로 관리한다!! (검색 필드만 저장하면 됨!!)


import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;

@Document(indexName = "products") //인덱스 이름 지정
public class Product {
    @Id
    private String id;
    private String name;
    private double price;

    // Getters and Setters
}

 

✅ 3-4-1. Repository

1️⃣기본 코드
Spring Data Elasticsearch에서 ElasticsearchRepository를 상속하면 기본적인 CRUD 제공!

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductSearchRepository extends ElasticsearchRepository<ProductSearch, Long> {
}

✔️서비스에서 ProductSearchRepository 함수 호출

  • save(ProductSearch entity): ES에 데이터 저장
  • findById(Long id): 특정 ID로 조회
  • deleteById(Long id): 데이터 삭제

 

2️⃣ 커스텀 검색 메서드 추가 (검색 조건 적용)
검색 조건을 추가하고 싶다면 메서드를 직접 정의!

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface ProductSearchRepository extends ElasticsearchRepository<ProductSearch, Long> {

    // ✅ 이름으로 검색 (대소문자 구분 없이 검색)
    List<ProductSearch> findByNameContainingIgnoreCase(String name);

    // ✅ 카테고리로 검색 (완전 일치)
    List<ProductSearch> findByCategory(String category);

    // ✅ 이름 + 설명에서 검색 (OR 조건)
    List<ProductSearch> findByNameContainingOrDescriptionContaining(String name, String description);
}

✔️ findByNameContainingIgnoreCase("노트북") 같은 방식으로 검색 가능!
✔️ JPA처럼 findBy~ 형태로 검색 쿼리를 만들 수 있음!

 

3️⃣ 직접 검색 쿼리 실행 (Native Query 사용)
복잡한 검색 조건을 적용하고 싶다면, @Query를 활용!

import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface ProductSearchRepository extends ElasticsearchRepository<ProductSearch, Long> {

    // ✅ Multi-field 검색 (name + description에서 검색)
    @Query("{\"multi_match\": {\"query\": \"?0\", \"fields\": [\"name\", \"description\"]}}")
    List<ProductSearch> searchByNameOrDescription(String keyword);
}

✔️ 이제 searchByNameOrDescription("게이밍")을 호출하면, 이름(name) 또는 설명(description)에 "게이밍"이 포함된 제품을 검색할 수 있음!

 

4️⃣ QueryBuilder

✔️ @Query로 표현하기 어려운 복잡한 검색 조건을 적용할 때

✔️ 검색 조건을 동적으로 만들고 싶을 때

✔️ Elasticsearch의 matchQuery, boolQuery, multi_matchQuery, rangeQuery 등을 직접 이용하고 싶을 때

import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.stereotype.Service;

@Service
public class CustomSearchService {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchTemplate;

    public SearchHits<Product> searchByCustomQuery(String keyword) {
        return elasticsearchTemplate.search(
            org.springframework.data.elasticsearch.core.query.NativeSearchQuery.builder()
                .withQuery(QueryBuilders.matchQuery("description", keyword))
                .build(),
            Product.class
        );
    }
}

 

✅ 3-5. Service와 Controller 작성

📖 데이터 추가 서비스

⭐ RDB와 ElasticSearch 동기화 방법

1️⃣ 서비스 로직에서 직접 동기화 -> 예제는 이거!!

데이터를 저장할 때 RDB와 ES에 모두 저장

2️⃣ 비동기 동기화 (Kafka 활용)

RDB 데이터 변경 감지 → ElasticSearch에 반영

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;  // JPA 저장소

    @Autowired
    private ProductSearchRepository productSearchRepository; // ES 저장소

    @Transactional
    public void saveProduct(Products product) {
        productRepository.save(product); // RDB 저장

        // 검색용 엔티티로 변환 후 ES 저장
        ProductSearch productSearch = new ProductSearch();
        productSearch.setProductId(product.getProductId());
        productSearch.setName(product.getName());
        productSearch.setDescription(product.getDescription());
        productSearch.setCategory(product.getCategory());

        productSearchRepository.save(productSearch); // ES 저장
    }
}

 

📖 데이터 조회 서비스(검색)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class ProductSearchService {

    @Autowired
    private ProductSearchRepository productSearchRepository;

    // ✅ 이름으로 검색
    public List<ProductSearch> searchByName(String name) {
        return productSearchRepository.findByNameContainingIgnoreCase(name);
    }

    // ✅ 카테고리로 검색
    public List<ProductSearch> searchByCategory(String category) {
        return productSearchRepository.findByCategory(category);
    }

    // ✅ 복합 검색 (이름 + 설명)
    public List<ProductSearch> searchByKeyword(String keyword) {
        return productSearchRepository.searchByNameOrDescription(keyword);
    }
}

 

📖 컨트롤러

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/search")
public class ProductSearchController {

    @Autowired
    private ProductSearchService productSearchService;
Private ProductService productService;

// ✅ 상품 추가 API
    // ✅ 검색 API (ex: /search/name?query=노트북)
    @GetMapping("/name")
    public List<ProductSearch> searchByName(@RequestParam String query) {
        return productSearchService.searchByName(query);
    }

    // ✅ 검색 API (ex: /search/category?query=전자제품)
    @GetMapping("/category")
    public List<ProductSearch> searchByCategory(@RequestParam String query) {
        return productSearchService.searchByCategory(query);
    }

    // ✅ 복합 검색 API (ex: /search/keyword?query=게이밍)
    @GetMapping("/keyword")
    public List<ProductSearch> searchByKeyword(@RequestParam String query) {
        return productSearchService.searchByKeyword(query);
    }

}

 

3-6. Elasticsearch 시작 및 테스트

 ✅ 이제 API를 호출하면 검색 기능을 사용할 수 있음!

  1. Elasticsearch 서버가 실행 중인지 확인(http://localhost:9200에 접속).
  2. Spring Boot 애플리케이션을 실행.
  3. Postman, cURL, 또는 브라우저에서 API를 테스트.

ProductService에서 사용 예제

GET http://localhost:8080/search/name?query=노트북
GET http://localhost:8080/search/category?query=전자제품
GET http://localhost:8080/search/keyword?query=게이밍

 

🚨 4.Error

🚨http 접속 오류

ProductServiceApplicationTests > contextLoads() FAILED java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180 Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:804 Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:804 Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1808 Caused by: org.springframework.beans.BeanInstantiationException at BeanUtils.java:222 Caused by: org.springframework.dao.DataAccessResourceFailureException at ElasticsearchExceptionTranslator.java:107 Caused by: java.lang.RuntimeException at ElasticsearchExceptionTranslator.java:62 Caused by: javax.net.ssl.SSLHandshakeException at RestClient.java:929 Caused by: javax.net.ssl.SSLHandshakeException at Alert.java:131 Caused by: sun.security.validator.ValidatorException at PKIXValidator.java:439 sun.security.provider.certpath.SunCertPathBuilderException at SunCertPathBuilder.java:148 1 test completed, 1 failed

 

💡 원인 분석

  • javax.net.ssl.SSLHandshakeException: SSL 핸드셰이크 중에 문제가 발생.
  • sun.security.validator.ValidatorException: PKIXValidator와 SunCertPathBuilderException: 인증서의 유효성을 검증하는 과정에서 문제가 발생했음을 의미.

🛠 해결 방법

Elasticsearch 서버에서 사용하는 SSL 인증서를 클라이언트 JVM의 신뢰할 수 있는 저장소(truststore)에 추가.

 

1️⃣ Elasticsearch 인증서 다운로드

Elasticsearch 서버가 사용하는 SSL 인증서를 브라우저에서 다운로드하거나, openssl 명령어를 사용하여 인증서를 다운로드할 수 있다.

 

📖 openssl 명령어를 사용

openssl s_client -connect localhost:9200 -showcerts

 

📖 브라우저에서 다운로드



 

2️⃣Java Truststore에 인증서 추가

인증서를 다운로드한 후, keytool을 사용하여 Java truststore에 추가한다.

keytool -importcert -file elasticsearch.crt -keystore cacerts -alias elasticsearch

C:\Windows\System32>keytool -importcert -file C:\{my-service}\src\main\resources\WIN-325LDHGUD53.crt -keystore "C:\Program Files\Java\jdk-17\lib\security\cacerts" -alias elasticsearch

키 저장소 비밀번호 입력:
// …
이 인증서를 신뢰합니까? [아니오]:  예
인증서가 키 저장소에 추가되었습니다.

 

3️⃣Spring Boot에서 Truststore 사용 설정

Spring Boot 애플리케이션에서 이 인증서를 신뢰하려면, application.properties 또는 application.yml 파일에서 truststore 경로를 설정해야 한다.

xpack.security.http.ssl.truststore.path: classpath:WIN-325LDHGUD53.crt
xpack.security.http.ssl.truststore.password: password

 

4️⃣Elasticsearch에 대한 HTTPS 및 인증서 설정 점검

Elasticsearch의 elasticsearch.yml 파일에서 SSL 관련 설정을 점검.

xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: classpath:keystore.p12
xpack.security.http.ssl.keystore.password: your_keystore_password

# https 요청
xpack.security.enabled: true
xpack.security.http.ssl.enabled: true

# Keystore for server-side SSL/TLS
xpack.security.http.ssl.keystore.path: classpath:keystore.p12
xpack.security.http.ssl.keystore.password: sumin

# Truststore for client authentication (if needed)
xpack.security.http.ssl.truststore.path: classpath:WIN-325LDHGUD53.crt
xpack.security.http.ssl.truststore.password: password

 

5️⃣ 결과

 

🚨 Could not rename log file 'logs/gc.log' to 'logs/gc.log.08' (Permission denied).

logs/gc.log 또는 다른 로그 파일에 대한 쓰기 권한이 없어서 발생.

관리자 권한 실행

📌 5. 스키마 확인 방법

Postman에서 확인

Get https://localhost:9200/_cat/indices?v

 

Authorization 

  1. 요청의 Authorization 탭을 선택.
  2. Type을 Basic Auth로 설정.
  3. username과 password 필드에 인증 정보를 입력.

⭐인증 방법

✅ 인증을 아예 비활성화하려면? (개발 환경에서만)

elasticsearch.yml 설정 변경 (보안 비활성화)

xpack.security.enabled: false

 

✅ 비밀번호를 모른다면? (재설정 방법)

elasticsearch-reset-password 명령어로 재설정

bin/elasticsearch-reset-password -u elastic

✔️ 실행하면 새로운 비밀번호가 생성됨

✔️ Elasticsearch를 재시작해야 적용됨!

systemctl restart elasticsearch  # Linux
.\bin\elasticsearch.bat  # Windows (CMD에서 실행)

 

🚀 elasticSearch 인덱스 관리

작업 방법 설명
현재 인덱스 확인 GET _cat/indices?v 존재하는 모든 인덱스 조회
인덱스 구조 확인 GET my_index/_mapping 특정 인덱스의 필드 구조 확인
데이터 조회 GET my_index/_search?pretty 저장된 데이터 일부 확인
인덱스 삭제 DELETE my_index 특정 인덱스 초기화
새로운 인덱스 생성 PUT my_index 새로운 인덱스 만들기
Spring Boot에서 자동 관리 @Document(indexName = "products") 애플리케이션 시작 시 자동 생성

 

📌 6. RDB를 ElasticSearch로 마이그레이션

RDB를 이용하고 있는 도중에 elasitcSearch를 도입할 경우, 데이터를 변환 후 색인(Indexing)하는 과정이 필요!

✅ 6-1. 마이그레이션 방식

1️⃣ Batch 방식 (배치 동기화, 일괄 전환) → 처음 데이터 옮길 때 추천

  • 일정 주기마다 RDB 데이터를 읽어서 ES로 전송
  • ETL 도구 또는 스크립트를 사용
  • 적합한 경우: 초기 마이그레이션, 검색 데이터가 자주 변경되지 않는 경우

2️⃣ CDC 방식 (Change Data Capture) → 실시간 동기화

  • RDB의 변경사항(INSERT, UPDATE, DELETE)을 감지하여 ES로 동기화
    Debezium, Kafka Connect 등을 활용
  • 적합한 경우: 실시간 검색 데이터 동기화가 필요한 경우

3️⃣ 애플리케이션 직접 연동 → 코드에서 직접 동기화

  • Spring Boot 등에서 DB 저장 시 ES에도 동시에 저장
  • 서비스 로직에 직접 반영
  • 적합한 경우: 동기화 로직을 직접 관리할 수 있는 경우

✅ 6-2.  방법별 상세 설명 & 구현 예제

1️⃣ Batch 방식 (배치 스크립트 이용) → Python, Logstash 활용

  • 데이터를 한 번에 마이그레이션할 때 사용
  • Python, Logstash, JDBC 등을 활용
  • 예제: Python + Elasticsearch Bulk API
import mysql.connector
from elasticsearch import Elasticsearch, helpers

# MySQL 연결
db = mysql.connector.connect(
    host="localhost",
    user="root",
    password="password",
    database="ecommerce"
)
cursor = db.cursor(dictionary=True)

# Elasticsearch 연결
es = Elasticsearch(["http://localhost:9200"])

# MySQL에서 데이터 가져오기
cursor.execute("SELECT id, name, description FROM products")
products = cursor.fetchall()

# Elasticsearch로 데이터 넣기
actions = [
    {
        "_index": "products",
        "_id": product["id"],
        "_source": product
    }
    for product in products
]
helpers.bulk(es, actions)

print("데이터 마이그레이션 완료!")

 

2️⃣ CDC 방식 (Debezium + Kafka 활용)

RDB 테이블 변경을 자동으로 감지하여 ElasticSearch에 반영

 

🔹 구성 요소

  • Debezium → RDB 변경 감지
  • Kafka Connect → 변경 데이터 전달
  • Elasticsearch Sink Connector → ES에 저장

 

🔹예시

MySQL ( MySQL binlog 설정 )

→ Kafka ( Kafka + Debezium + ElasticSearch 연동 )

→ ElasticSearch ( 변경 감지 후 자동 반영 )

3️⃣ 애플리케이션에서 직접 연동 (Spring Boot)

  • 서비스 코드에서 DB 저장과 동시에 ES에도 저장
  • 예제: Spring Boot + Spring Data Elasticsearch
@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository; // JPA용

    @Autowired
    private ElasticsearchProductRepository esRepository; // ElasticSearch용

    @Transactional
    public void saveProduct(Product product) {
        productRepository.save(product); // RDB 저장
        esRepository.save(product); // ElasticSearch 저장
    }
}

 

🔥6-3. 어떤 방식이 가장 좋을까?

방식 장점 단점 적합한 경우
Batch 방식 초기 마이그레이션 간단, 빠름 실시간 반영 어려움 초기 마이그레이션
CDC 방식 실시간 동기화, 변경 사항 자동 반영 Kafka 등 추가 구성 필요 검색 데이터가 자주 변경되는 경우
애플리케이션 직접 연동 동기화 로직을 직접 관리 가능 서비스 코드 복잡해짐 검색 데이터가 적고, 트래픽이 적은 경우

 

🚀 결론

1️⃣ 한 번에 데이터를 옮길 땐 → Batch 방식
2️⃣ 실시간 동기화 필요하면 → CDC 방식(Debezium, Kafka)
3️⃣ 애플리케이션에서 직접 관리 가능하면 → 직접 동기화

👉실무에서는 보통 1️⃣+2️⃣ 조합



'DB > 데이터 관리' 카테고리의 다른 글

Elasticsearch > analyzer  (0) 2026.01.25
개인 식별 정보 암호화 방법  (0) 2026.01.25
CTE(Common Table Expression)란?  (0) 2025.11.29
JDBC / SQL Mapper / ORM (JPA, Hibernate, MyBatis)  (0) 2024.09.25