Backend/spring cloud (MSA)

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๏ธโƒฃ ์กฐํ•ฉ