관리 메뉴

피터의 개발이야기

[kotlin] Spring Data Elasticsearch 샘플코드 본문

Programming/Kotlin

[kotlin] Spring Data Elasticsearch 샘플코드

기록하는 백앤드개발자 2024. 8. 11. 10:10
반응형

ㅁ 들어가며

ㅇ Spring Data Elasticsearch를 Kotlin으로 예제 코드를 작성해보겠다. 

ㅇ 이 글은 Kotlin과 Spring Boot를 사용하여 Elasticsearch에 데이터를 저장하고, JPA로 등록 조회 방법을 정리하였다.

 

ㅁ 관련 글

 [kotlin] Spring Data Elasticsearch 샘플코드
 [Elasticsearch] Elasticsearch + Kibana 설치하기 with Docker

 [Elasticsearch] Docker로 Elasticsearch 설치 및 테스트하기

 [Elasticsearch] Kibana Query Language 사용법 정리

 

ㅁ Elasticsearch API-KEY 생성

ㅇ Elasticsearch와 통신하기 위해서 api-key가 필요하다.

ㅇ kibana, Dev Tools에서 실행하였다. 

POST /_security/api_key
{
  "name": "spring_command",
  "expiration": "999d",
  "role_descriptors": { 
    "role-team-index-command": {
      "cluster": ["all"],
      "index": [
        {
          "names": ["*"],
          "privileges": ["all"]
        }
      ]
    }
  }
}

결과: 
{
  "id": "P26mDJEBPA0rStSZL1pD",
  "name": "spring_command",
  "expiration": 1808807703364,
  "api_key": "Z_9Rv5N9SlelmGvFK7T8rw",
  "encoded": "UDI2bURKRUJQQTByU3RTWkwxcEQ6Wl85UnY1TjlTbGVsbUd2Rks3VDhydw=="
}

ㅇ 실행한 스크립트

 

ㅇ 생성된 키는 Management에서 확인할 수 있다.

 

ㅁ 프로젝트 생성

ㅇ MySQL Driver, Spring Data JPA, Spring Data Elasticsearch, Spring Web을 추가하여 프로젝트 구성

 

ㅁ 의존성 추가

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")

    runtimeOnly("com.mysql:mysql-connector-j")

    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

 

ㅁ Elasticsearch 연결 설정

spring:
  application:
    name: kotlin-elasticsearch
  elasticsearch:
    rest:
      url: localhost:9200
      api_key: Z_9Rv5N9SlelmGvFK7T8rw
      username: elastic
      password: Bcw_HDE8Nr2*RhmO6Cko
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/playground?characterEncoding=UTF-8
    username: root
    password: strong-password

ㅇ application.yml 파일에 Elasticsearch 연결 정보를 추가한다.

ㅇ username과 password는 Elasticsearch를 설치하였을 때에 자동생성되었던 것이다.

  ㄴ이곳을 참조: [Elasticsearch] Elasticsearch + Kibana 설치하기 with Docker

 

@Configuration
@EnableElasticsearchRepositories
class ESconfig(
    @Value("\${spring.elasticsearch.rest.url}") val uri: String,
    @Value("\${spring.elasticsearch.rest.api_key}") val apiKey: String,
    @Value("\${spring.elasticsearch.rest.username}") val username: String,
    @Value("\${spring.elasticsearch.rest.password}") val password: String
): ElasticsearchConfiguration() {

    override fun clientConfiguration(): ClientConfiguration {
        val httpHeaders = HttpHeaders()
        httpHeaders.add("Authorization", "Bearer $apiKey")
        return ClientConfiguration.builder()
            .connectedTo(uri)
            .usingSsl()
            .withDefaultHeaders(httpHeaders)
            .withBasicAuth(username, password )
            .build()
    }
}

ㅇ ES의 연결을 설정한다.

 

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

ㅇ 위와 같은 SSL 인증서 문제가 발생하면, 이곳을 [Spring] SSL 접속시, PKIX path building failed 오류 해결방법 참조하면 된다.

 

ㅁ 엔티티 클래스 정의

import org.springframework.data.annotation.Id
import org.springframework.data.elasticsearch.annotations.Document
import org.springframework.data.elasticsearch.annotations.Field
import org.springframework.data.elasticsearch.annotations.FieldType

@Document(indexName = "products")
data class Product(
    @Id
    val id: String,

    @Field(type = FieldType.Text, name = "name")
    val name: String,

    @Field(type = FieldType.Double, name = "price")
    val price: Double
)

ㅇ Elasticsearch에 저장할 데이터 모델을 정의한다.

 

ㅁ Repository 인터페이스 생성

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository

interface ProductRepository : ElasticsearchRepository<Product, String> {
    fun findByNameContaining(name: String): List<Product>
}

ㅇ Spring Data Elasticsearch를 사용하여 Repository 인터페이스를 생성한다.

 

ㅁ 서비스 클래스 구현

import org.springframework.stereotype.Service

@Service
class ProductService(private val productRepository: ProductRepository) {

    fun saveProduct(product: Product): Product {
        return productRepository.save(product)
    }

    fun findProductById(id: String): Product? {
        return productRepository.findById(id).orElse(null)
    }

    fun searchProducts(name: String): List<Product> {
        return productRepository.findByNameContaining(name)
    }
}

ㅇ 데이터를 저장하고 조회하는 서비스 클래스를 구현한다.

 

ㅁ 컨트롤러 구현

import com.peterica.kotlinelasticsearch.Product
import com.peterica.kotlinelasticsearch.service.ProductService
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/products")
class ProductController(private val productService: ProductService) {

    @GetMapping("/{id}")
    fun getProduct(@PathVariable id: String): Product? {
        return productService.findProductById(id)
    }

    @GetMapping("/search")
    fun searchProducts(@RequestParam name: String): List<Product> {
        return productService.searchProducts(name)
    }
    
    @PostMapping
    fun createProduct(@RequestBody product: Product): Product {
        return productService.saveProduct(product)
    }
}

ㅇ REST API를 통해 데이터를 저장하고 조회할 수 있는 컨트롤러를 구현한다.

 

ㅁ Spring Data Elasticsearch Versions

Spring Data Release Train Spring Data Elasticsearch Elasticsearch
Spring Framework
2024 5.3.1 8.13.4 6.1.x
2023.1 (Vaughan) 5.2.x 8.11.1 6.1.x
2023.0 (Ullmann) 5.1.x 8.7.1 6.0.x
2022.0 (Turing) 5.0.x[1] 8.5.3 6.0.x
2021.2 (Raj) 4.4.x[1] 7.17.3 5.3.x
2021.1 (Q) 4.3.x[1] 7.15.2 5.3.x
2021.0 (Pascal) 4.2.x[1] 7.12.0 5.3.x
2020.0 (Ockham) 4.1.x[1] 7.9.3 5.3.2
Neumann 4.0.x[1] 7.6.2 5.2.12
Moore 3.2.x[1] 6.8.12 5.2.12
Lovelace 3.1.x[1] 6.2.2 5.1.19
Kay 3.0.x[1] 5.5.0 5.0.13
Ingalls 2.1.x[1] 2.4.0 4.3.25

Spring Data 릴리스 트레인에서 사용되는 Elasticsearch 및 Spring 버전과 그에 포함된 Spring Data Elasticsearch 버전을 보여준다.

 

ㅁ BootRun 테스트

ㅇ 정상 빌드 및 SpringBoot 정상 작동을 확인하였다.

ㅇ 이제부터는 테스트를 진행해 보자.

 

ㅁ TDD 코드 작성

package com.peterica.kotlinelasticsearch

import com.peterica.kotlinelasticsearch.repository.ProductRepository
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
class KotlinElasticsearchApplicationTests {

    @Autowired
    private lateinit var productRepository: ProductRepository
    @BeforeEach
    fun setUp() {
        productRepository.deleteAll()
    }

    @Test
    fun `test save and find product`() {
        val product = Product(id = "1", name = "Test Product", price = 10.0)
        productRepository.save(product)

        val foundProduct = productRepository.findById("1").orElse(null)
        assertNotNull(foundProduct)
        assertEquals("Test Product", foundProduct?.name)
        assertEquals(10.0, foundProduct?.price)
    }
    @Test
    fun `test find all products`() {
        val product1 = Product(id = "1", name = "Product 1", price = 10.0)
        val product2 = Product(id = "2", name = "Product 2", price = 20.0)
        productRepository.saveAll(listOf(product1, product2))

        val products = productRepository.findAll()
        assertEquals(2, products.count())
    }
}

 

ㅁ Kibana 데이터 생성

# 인덱스생성
PUT /products

출력:
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "products"
}

# 인덱스 삭제
DELETE /products

출력:
{
  "acknowledged": true
}

ㅇ 인덱스를 생성 삭제할 수 있다.

 

# 생성
POST /products/_doc
{
    "id": "1",
    "name": "Laptop",
    "price": 999.99
}

출력:
{
  "_index": "products",
  "_id": "VL24CpEBcfvwz7Sg77M4",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

 

ㅇ Kibana를 통한 조회

 

소스는 여기에 있음

 

ㅁ 함께 보면 좋은 사이트

Elasticsearch에 Java Low Level REST Client를 통해 요청 보내기

Spring Data Elasticsearch

Spring Data Elasticsearch Versions

 

반응형
Comments