Spring Boot와 Elasticsearch 연동하기 — 대용량 검색 서비스의 핵심 구현

애플리케이션의 데이터가 커질수록 복잡한 검색 기능이 필요해집니다. 데이터베이스의 LIKE 검색만으로는 성능이 한계에 부딪히며, 특히 로그, 상품, 문서, 사용자 검색 같은 기능은 Elasticsearch를 통해 훨씬 빠르고 유연하게 구현할 수 있습니다. 

 

Elasticsearch란?

Elasticsearch는 JSON 기반의 분산형 검색·분석 엔진으로, 실시간으로 대량의 데이터를 색인(Indexing)하고 빠르게 검색할 수 있습니다. Lucene을 기반으로 하며, 역색인(Inverted Index) 구조를 사용하여 텍스트 검색에 특화되어 있습니다.

  • 대량의 데이터 검색에 탁월 (ms 단위 검색)
  • RESTful API 기반 — HTTP 요청으로 데이터 CRUD 가능
  • Spring Data Elasticsearch로 손쉽게 연동
  • Kibana, Logstash 등과 함께 ELK Stack을 구성 가능

 

1. Elasticsearch 환경 준비

Docker로 Elasticsearch 실행

docker run -d \
  --name elasticsearch \
  -p 9200:9200 \
  -e "discovery.type=single-node" \
  docker.elastic.co/elasticsearch/elasticsearch:8.12.2

기본 포트는 9200이며, 브라우저에서 http://localhost:9200으로 접속해 실행 여부를 확인할 수 있습니다. 출력에 "tagline" : "You Know, for Search"가 나타나면 정상적으로 실행된 것입니다.

 

2. Spring Boot 프로젝트 설정

Gradle 의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'

Spring Data Elasticsearch는 Elasticsearch 클라이언트를 추상화하여, JPA와 유사한 방식으로 문서를 다룰 수 있게 해줍니다.

 

application.yml 설정

spring:
  data:
    elasticsearch:
      cluster-nodes: localhost:9200
      repositories:
        enabled: true
  elasticsearch:
    uris: http://localhost:9200

버전에 따라 spring.data.elasticsearch.cluster-nodes 대신 spring.elasticsearch.uris를 사용하는 것이 일반적입니다. Elasticsearch 8 이상에서는 보안(SSL, 인증)이 활성화되어 있으므로, 필요 시 인증 비활성화 옵션을 설정하거나 API 키를 등록해야 합니다.

 

3. 도메인(Document) 정의

JPA의 Entity처럼 Elasticsearch에서도 데이터를 저장할 모델을 정의합니다.

@Document(indexName = "products")
public class Product {

    @Id
    private String id;

    private String name;
    private String description;
    private double price;

    @Field(type = FieldType.Keyword)
    private String category;

    // getters and setters
}

@Document 어노테이션은 Elasticsearch 인덱스 이름을 지정합니다. @Field를 이용하면 검색 필드 타입을 제어할 수 있습니다 (text, keyword, date 등).

 

4. Repository 정의

public interface ProductRepository extends ElasticsearchRepository<Product, String> {
    List<Product> findByNameContaining(String name);
    List<Product> findByCategory(String category);
}

Spring Data Elasticsearch는 메서드 네이밍 규칙을 기반으로 자동으로 쿼리를 생성합니다. 예를 들어, findByNameContaining("Phone")은 name 필드에 “Phone”이 포함된 문서를 검색합니다.

 

5. Controller 작성

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/products")
public class ProductController {

    private final ProductRepository productRepository;

    @PostMapping
    public Product save(@RequestBody Product product) {
        return productRepository.save(product);
    }

    @GetMapping("/search")
    public List<Product> search(@RequestParam String name) {
        return productRepository.findByNameContaining(name);
    }
}

이제 POST /api/products 요청으로 데이터를 색인하고, GET /api/products/search?name=키워드로 검색할 수 있습니다.

 

6. REST API로 직접 테스트

Elasticsearch는 자체 REST API를 제공하므로, 직접 호출로 테스트할 수도 있습니다.

# 문서 색인
curl -X POST "localhost:9200/products/_doc/1" -H 'Content-Type: application/json' -d'
{
  "name": "Galaxy S24",
  "description": "Samsung flagship smartphone",
  "price": 1200000,
  "category": "mobile"
}
'

# 검색
curl -X GET "localhost:9200/products/_search?q=name:Galaxy"

Spring Boot 내부에서도 같은 요청이 Repository를 통해 수행됩니다.

 

7. 고급 검색 기능 구현

Spring Data Elasticsearch에서는 NativeSearchQueryBuilder를 통해 복합 쿼리를 작성할 수 있습니다.

SearchHits<Product> search = elasticsearchOperations.search(
    new NativeSearchQueryBuilder()
        .withQuery(QueryBuilders.boolQuery()
            .must(QueryBuilders.matchQuery("name", "Galaxy"))
            .filter(QueryBuilders.rangeQuery("price").lte(1500000)))
        .build(),
    Product.class
);

이 코드는 “Galaxy”라는 키워드를 포함하면서 150만원 이하의 상품만 필터링하는 복합 검색을 수행합니다.

 

8. Elasticsearch 운영 시 고려사항

  • Index Mapping 관리: 인덱스 생성 시 스키마를 명확히 정의해야 불필요한 매핑 충돌 방지
  • Analyzer 설정: 한국어 검색 시 NoriAnalyzer, 형태소 분석기 활용
  • Cluster 모니터링: Kibana, ElasticHQ, X-Pack으로 노드 상태 점검
  • 성능 최적화: shard 개수, replica 수 조정 / refresh interval 관리
  • 보안: Elastic 8.x 이상에서는 기본적으로 TLS와 인증이 활성화되어 있으므로 API Key 또는 SSL 설정 필요

 

9. Kibana를 통한 시각화

Kibana를 설치하면 Elasticsearch 데이터를 대시보드로 시각화할 수 있습니다.

docker run -d \
  --name kibana \
  -p 5601:5601 \
  --link elasticsearch:elasticsearch \
  docker.elastic.co/kibana/kibana:8.12.2

Kibana의 Dev Tools에서 인덱스, 매핑, 쿼리를 직접 실행하면서 결과를 시각적으로 확인할 수 있습니다.

 

주의할 점

  • Elasticsearch 버전과 Spring Data Elasticsearch 버전을 반드시 호환시켜야 함
  • 대용량 색인 시 Bulk API 사용으로 성능 향상
  • 운영 환경에서는 Replica = 1 이상으로 설정해 데이터 안정성 확보
  • 필드 타입 자동 매핑(“dynamic mapping”)은 의도치 않은 매핑 오류를 유발할 수 있으므로 주의

 


 

Spring Boot와 Elasticsearch를 연동하면 단순한 CRUD를 넘어 고성능 검색 서비스를 구현할 수 있습니다. 특히 JPA와 병행하여 사용하면 데이터 저장은 DB, 검색은 Elasticsearch로 분리해 성능과 확장성을 모두 확보할 수 있습니다.