📌 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 다운로드 후 실행
- Elasticsearch 홈페이지에서 설치 파일 다운로드.
- 압축을 해제하고 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를 호출하면 검색 기능을 사용할 수 있음!
- Elasticsearch 서버가 실행 중인지 확인(http://localhost:9200에 접속).
- Spring Boot 애플리케이션을 실행.
- 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
- 요청의 Authorization 탭을 선택.
- Type을 Basic Auth로 설정.
- 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 |