Elasticsearch 입문 32편 — Spring Data Elasticsearch (Repository·Template·@Document·Reactive)

2026-05-19Elasticsearch 입문에서 운영까지

Elasticsearch 입문 32편 Spring Data ES. @Document·Template·Repository·Reactive·Criteria·Auditing.

📚 Elasticsearch 입문에서 운영까지 · 32편 — Spring Data Elasticsearch (Repository·Template·@Document·Reactive)

이 글은 Elasticsearch 입문에서 운영까지 시리즈 38편 중 32편이에요. 26~31편까지 cluster·shard·snapshot·security·monitoring·performance 운영 6편을 끝내고, 이제 통합 파트의 첫 글로 자바 백엔드에서 Elasticsearch 를 어떻게 깔끔하게 쓰는지 살핍니다.

📚 학습 노트

이 글은 Spring Data Elasticsearch 6.0 공식 docs 와 Elasticsearch 8.x 공식 client docs 를 참고해 한국어 학습 노트로 풀어쓴 자료예요.

자바 백엔드 입문 44~50편에서 다룬 JPA 와 같은 *Repository · Template · POJO 매핑* 패턴이라 한 호흡에 따라옵니다. Spring Boot 3.5+ 환경에서 따라 치면 본문이 머리에 잘 박혀요.

왜 32편이 Spring Data Elasticsearch 인가

자바 백엔드 입문 44~50편에서 다룬 JPA"RDBMS 를 자바 객체로 다룬다" 였다면, 이 글은 "Elasticsearch 를 자바 객체로 다룬다" 자리예요. Spring Data 프로젝트 의 같은 패밀리라서 — Repository · Template · POJO 매핑 · Auditing — 네 가지 추상화가 거의 똑같이 따라와요. JPA 를 한 번 써본 사람은 형태 익숙 단계에서 30%는 그냥 잡혀요.

7편(Search API) 부터 22편(Vector Search) 까지 우리는 cURL 로 JSON DSL 던지기 만 해왔어요. 운영 코드에서 그걸 그대로 쓰면 문자열 조립·타입 안 맞음·NPE 가 줄줄이 터져요. Spring Data Elasticsearch 가 이 자리를 어노테이션 + 메서드 호출 로 깔끔하게 잡아 줍니다.

이 글이 끝나면 — @Document 로 POJO 매핑 짜고, Repository 로 메서드 이름만으로 쿼리 만들고, ElasticsearchTemplate 으로 복잡한 집계도 한 메서드로 호출하고, Reactive 환경까지 함께 잡힙니다.

의존성 — Spring Data Elasticsearch 6.0.0

작성 시점(2026-05-19) 기준 최신 안정 버전은 Spring Data Elasticsearch 6.0.0 이고, Spring Boot 3.5+ 와 함께 쓰는 게 표준 조합이에요. Boot 의 dependency management 가 버전을 잡아 주니까 사용자는 starter 한 줄만 박으면 됩니다.

Gradle (Kotlin DSL) 기준:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")
    // Reactive 환경이면 추가
    implementation("org.springframework.boot:spring-boot-starter-webflux")
}

Maven 이면 똑같이 spring-boot-starter-data-elasticsearch 한 줄. 이 starter 가 내부적으로 Elasticsearch Java Client (공식 client, 8.x 부터 low-level + high-level 통합) 와 Spring Data Commons 를 끌고 와요.

연결은 application.yml 에 호스트만 박으면 끝.

spring:
  elasticsearch:
    uris: http://localhost:9200
    username: elastic
    password: ${ELASTIC_PASSWORD}
    socket-timeout: 5s
    connection-timeout: 1s

Spring Boot 가 알아서 ElasticsearchTemplate · RestClient · Repository 자동 설정 을 다 끼워 줘요. 별도 @Configuration 클래스 없이 호스트 한 줄 만으로 동작 시작.

운영 환경에선 TLS·CA 인증서·API Key 를 함께 박아요. AWS OpenSearch Service 면 SigV4 서명 이 추가로 들어가는데, 35편(AWS OpenSearch) 에서 깊이.

환경 권장 client 인증 연결 코드
로컬 Docker RestClient (기본) 없음 또는 elastic/비번 URI 한 줄
자체 운영 (TLS) RestClient + SSLContext API Key @Configuration 으로 SSLBundle
Elastic Cloud RestClient API Key cloud-id + api-key
AWS OpenSearch OpenSearch Java Client SigV4 AWS SDK 의존성

여기서부터는 로컬 ES 8.x 단일 노드 기준으로 따라가요.

@Document·@Field·@Id — POJO 어노테이션 매핑

JPA 의 @Entity · @Column · @Id 와 같은 자리에 ES 는 @Document · @Field · @Id 가 있어요. 자바 클래스 한 개가 ES 인덱스 한 개 에 대응되고, 필드는 ES 필드 로 매핑.

가장 단순한 Product 예시:

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

    @Id
    private String id;

    @Field(type = FieldType.Text, analyzer = "nori_analyzer")
    private String name;

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

    @Field(type = FieldType.Double)
    private Double price;

    @Field(type = FieldType.Date, format = DateFormat.date_optional_time)
    private Instant createdAt;

    @Field(type = FieldType.Nested)
    private List<Tag> tags;

    // getters/setters
}

@Document클래스 → 인덱스 매핑이에요. 핵심 속성을 정리하면:

속성 의미 기본값·예시
indexName 대상 인덱스 이름 필수 — "products" 또는 "products-#{T(java.time.LocalDate).now()}" SpEL
createIndex 앱 시작 시 인덱스 자동 생성 true (단, 권장 X — 운영은 명시적으로)
versionType 낙관적 락 타입 EXTERNAL
writeTypeHint _class 자동 박기 6.0 부터 기본 false (구버전 호환 시 켜기)
shards Primary Shard 수 1 (운영은 명시적으로)
replicas Replica 수 1

@Field필드 → ES 필드 매핑이에요. type 으로 ES 필드 타입을 지정하고, 분석기·multi-field·null 처리 까지 한 어노테이션 안에서 다 잡혀요. 9편(Field Types) 에서 다룬 모든 ES 타입이 FieldType enum 에 그대로 들어 있어요.

@IdES 의 _id 자리예요. JPA 와 달리 @GeneratedValue 는 없고, 값을 안 박으면 ES 가 자동으로 UUID 생성. 직접 박는 경우엔 외부 시스템의 PK (예: PG 의 product_id) 를 그대로 넣는 게 일반.

@MultiField한 필드를 여러 매핑으로 색인할 때 써요. 한국어 풀텍스트 검색과 정확 매칭을 둘 다 잡고 싶을 때 자주 등장.

@MultiField(
    mainField = @Field(type = FieldType.Text, analyzer = "nori_analyzer"),
    otherFields = {
        @InnerField(suffix = "keyword", type = FieldType.Keyword),
        @InnerField(suffix = "english", type = FieldType.Text, analyzer = "english")
    }
)
private String name;

→ 색인되는 필드는 name · name.keyword · name.english 셋. 검색에선 name 으로 풀텍스트, name.keyword 로 정확 매칭, name.english 로 영어 분석을 동시 호출 가능.

@Setting · @Mapping 은 인덱스 settings·mapping 을 JSON 파일로 박을 때 써요.

@Document(indexName = "products")
@Setting(settingPath = "settings/products-settings.json")
@Mapping(mappingPath = "mappings/products-mapping.json")
public class Product { ... }

→ 어노테이션만으로 못 잡는 복잡한 custom analyzer · synonyms · normalizer 는 JSON 파일에 박고 클래스패스에서 읽어요. 11편(Korean Analyzer) 의 Nori 사용자 사전이 이 패턴.

ElasticsearchOperations·ElasticsearchTemplate — sync API

JPA 의 EntityManager · JdbcTemplate 와 같은 자리예요. ElasticsearchOperations 가 인터페이스고, ElasticsearchTemplate 이 표준 구현이에요. 의존성 주입은 인터페이스로 받는 게 표준.

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ElasticsearchOperations operations;

    public Product save(Product product) {
        return operations.save(product);
    }

    public Product findById(String id) {
        return operations.get(id, Product.class);
    }

    public SearchHits<Product> searchByName(String keyword) {
        Query query = NativeQuery.builder()
            .withQuery(q -> q.match(m -> m.field("name").query(keyword)))
            .withPageable(PageRequest.of(0, 20))
            .build();
        return operations.search(query, Product.class);
    }

    public void delete(String id) {
        operations.delete(id, Product.class);
    }
}

핵심 메서드를 정리하면:

메서드 자리 비고
save(T) / save(Iterable<T>) upsert (있으면 update, 없으면 insert) 내부 bulk 사용
get(id, Class<T>) id 로 단건 조회 null 가능
search(Query, Class<T>) 풀텍스트·집계 검색 SearchHits<T> 반환
searchForStream(...) 큰 결과셋 스트림 scroll 자동
delete(id, Class<T>) / delete(Query, Class<T>) 단건·조건 삭제 by query 도 가능
update(UpdateQuery, IndexCoordinates) partial update doc / script 둘 다
bulk(...) 다건 묶음 처리 23편(Bulk API) 와 동등
indexOps(Class<T>) 인덱스 관리 핸들 mapping·alias·delete

반환 타입 SearchHits결과 리스트 + total · maxScore · aggregations · suggests 를 한 묶음으로 들고 있어요. highlight · sort value · inner hits 까지 다 들어가니까, ES 의 raw response 를 거의 한 객체로 받는다고 보면 됩니다.

SearchHits<Product> hits = operations.search(query, Product.class);
long total = hits.getTotalHits();
float maxScore = hits.getMaxScore();

for (SearchHit<Product> hit : hits) {
    Product product = hit.getContent();
    Map<String, List<String>> highlight = hit.getHighlightFields();
    // ...
}

집계 결과는 hits.getAggregations() 로 받아요. 15~17편의 metric · bucket · pipeline 집계가 그대로 자바 코드로 호출됩니다.

ElasticsearchRepository — 메서드 이름 기반 쿼리

JPA Repository 와 완전히 같은 패턴 이에요. 인터페이스만 만들고, 메서드 이름을 find · By · And · Or · Between · OrderBy 같은 키워드로 짜면 런타임에 쿼리 자동 생성.

public interface ProductRepository extends ElasticsearchRepository<Product, String> {

    List<Product> findByBrand(String brand);

    Page<Product> findByNameContaining(String keyword, Pageable pageable);

    List<Product> findByPriceBetween(Double min, Double max);

    Slice<Product> findByBrandAndPriceLessThan(String brand, Double max, Pageable pageable);

    long countByBrand(String brand);

    void deleteByCreatedAtBefore(Instant cutoff);
}

→ 위 코드에서 XML · JSON · DSL 한 줄 없이 ES 가 호출돼요. 메서드 이름이 그대로 match·range·term 쿼리 로 자동 변환됩니다.

JPA Repository 키워드와 거의 같지만, 몇 가지 ES 고유 차이 가 있어요.

키워드 JPA Spring Data ES
findBy<X> WHERE x = ? term 쿼리
findBy<X>Containing LIKE %?% match 쿼리 (분석기 적용)
findBy<X>StartingWith LIKE ?% prefix 쿼리
findBy<X>Between BETWEEN ? AND ? range 쿼리
findBy<X>In IN (?, ?, ?) terms 쿼리
OrderBy<X>Desc ORDER BY x DESC sort by x desc

복잡한 쿼리는 @Query 어노테이션으로 JSON DSL 직접 박기 도 가능.

public interface ProductRepository extends ElasticsearchRepository<Product, String> {

    @Query("""
        {
          "bool": {
            "must": [
              { "match": { "name": "?0" } }
            ],
            "filter": [
              { "range": { "price": { "lte": ?1 } } }
            ]
          }
        }
        """)
    List<Product> findByNameAndMaxPrice(String name, Double maxPrice);
}

→ JPA 의 @Query("SELECT p FROM ...")완전히 같은 형태 예요. ES DSL JSON 안에서 ?0 · ?1 같은 위치 파라미터를 그대로 쓰면 됩니다.

페이지네이션은 Pageable 그대로 — PageRequest.of(0, 20, Sort.by("price").descending()) 같은 표준 Spring Data 패턴.

ReactiveElasticsearchTemplate — 반응형 API

Spring WebFlux 환경에선 sync API 대신 ReactiveElasticsearchOperations (인터페이스) / ReactiveElasticsearchTemplate (구현) 을 써요. 모든 메서드가 Mono · Flux 를 반환하는 것 빼면 이름·시그니처가 sync 와 거의 같아요.

@Service
@RequiredArgsConstructor
public class ReactiveProductService {

    private final ReactiveElasticsearchOperations operations;

    public Mono<Product> save(Product product) {
        return operations.save(product);
    }

    public Mono<Product> findById(String id) {
        return operations.get(id, Product.class);
    }

    public Flux<SearchHit<Product>> searchByName(String keyword) {
        Query query = NativeQuery.builder()
            .withQuery(q -> q.match(m -> m.field("name").query(keyword)))
            .build();
        return operations.search(query, Product.class);
    }
}

Reactive Repository 도 그대로 제공돼요 — ReactiveElasticsearchRepository<T, ID> 를 상속하면 메서드 이름 기반 쿼리가 Mono · Flux 로 반환됩니다.

public interface ReactiveProductRepository extends ReactiveElasticsearchRepository<Product, String> {

    Flux<Product> findByBrand(String brand);

    Mono<Long> countByBrand(String brand);
}

운영에서 헷갈리는 자리는 언제 Reactive 를 쓸 것이냐 인데 — 전 구간 비동기 (Netty · Reactor · R2DBC) 가 이미 구축된 곳에선 sync ES 호출이 blocking 이라 worst case 가 됩니다. 그런 환경에서만 Reactive ES 가 의미 있고, 보통의 Tomcat + JPA 환경에선 sync 가 단순·디버깅 모두 압도적으로 편해요.

작성 시점(2026-05-19) 기준 Reactive ES 의 가장 큰 함정은 context propagation@Auditing · SecurityContext · MDC 같은 ThreadLocal 기반 컨텍스트가 Reactor chain 을 따라가지 않아요. 해결은 Reactor Context · @ContextPropagation 어노테이션 또는 micrometer-context-propagation 라이브러리. 함정 섹션에서 다시.

Criteria API & NativeQuery — 복잡한 쿼리 빌더

메서드 이름이나 @Query 로 못 잡는 동적 쿼리 는 두 가지 빌더가 있어요.

Criteria API — 도메인-친화 빌더

Criteria criteria = new Criteria("name").contains("나이키")
    .and(new Criteria("brand").is("Nike"))
    .and(new Criteria("price").lessThanEqual(100_000));

CriteriaQuery query = new CriteriaQuery(criteria, PageRequest.of(0, 20));
SearchHits<Product> hits = operations.search(query, Product.class);

JPA Criteria API 와 같은 체이닝 형태예요. and · or · not · is · contains · between · greaterThan 같은 메서드로 bool 쿼리 가 자동 조립됩니다. 동적 필터 (사용자가 체크한 옵션만 OR 로 묶기) 자리에 가장 어울려요.

NativeQuery — ES DSL 그대로 빌더

import co.elastic.clients.elasticsearch._types.query_dsl.*;

NativeQuery query = NativeQuery.builder()
    .withQuery(q -> q
        .bool(b -> b
            .must(m -> m.match(mm -> mm.field("name").query("나이키")))
            .filter(f -> f.range(r -> r.field("price").lte(JsonData.of(100_000))))
        )
    )
    .withAggregation("by_brand", a -> a
        .terms(t -> t.field("brand").size(10))
    )
    .withSort(s -> s.field(f -> f.field("price").order(SortOrder.Desc)))
    .withPageable(PageRequest.of(0, 20))
    .build();

SearchHits<Product> hits = operations.search(query, Product.class);

ES Java Client 의 DSL 그대로 자바 람다 형태로 호출하는 빌더예요. Criteria API 로는 표현 불가한 function_score · nested · has_child · pipeline aggregation 모두 여기서 가능. 작성 시점(2026-05-19) 기준 NativeQuery 가 권장 표준 이에요. (구버전 NativeSearchQuery 는 deprecated.)

빌더 어울리는 자리 가독성 표현력
Method name 단순 1~2 조건 ★★★★★ ★★
@Query (JSON) 정적 복잡 쿼리 ★★★ ★★★★★
Criteria API 동적 필터 조합 ★★★★ ★★★
NativeQuery 모든 쿼리·집계 ★★★ ★★★★★

Index 관리 — IndexOperations 와 Auditing

운영 코드에선 인덱스 자체 를 다루는 자리도 자주 등장해요. operations.indexOps(Class<T>) 또는 operations.indexOps(IndexCoordinates.of("products")) 로 핸들을 받아요.

IndexOperations indexOps = operations.indexOps(Product.class);

// 인덱스 생성
indexOps.createWithMapping();

// 매핑 확인
Map<String, Object> mapping = indexOps.getMapping();

// alias 추가 — 5편(Index 관리) 의 alias 운영 패턴
AliasActions aliasActions = new AliasActions(
    new AliasAction.Add(AliasActionParameters.builder()
        .withIndices("products-2026-05")
        .withAliases("products")
        .build())
);
indexOps.alias(aliasActions);

// refresh — 색인 직후 강제 검색 가능 상태로
indexOps.refresh();

// 삭제
indexOps.delete();

운영 인덱스는 보통 @Document(createIndex = false) 로 잠그고, Flyway-같은 별도 마이그레이션 도구 로 인덱스를 명시적으로 만들어 둡니다. 앱이 자동 생성하면 PR 미통과 매핑 변경 이 운영에 들어가는 사고가 잦아요.

Auditing 은 JPA Auditing 과 같은 패턴이에요. @CreatedDate · @LastModifiedDate · @CreatedBy · @LastModifiedBy 가 그대로 사용 가능.

@Configuration
@EnableElasticsearchAuditing(auditorAwareRef = "auditorAware")
public class ElasticsearchConfig {

    @Bean
    public AuditorAware<String> auditorAware() {
        return () -> Optional.of(SecurityContextHolder.getContext().getAuthentication())
            .map(Authentication::getName);
    }
}

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

    @Id
    private String id;

    @CreatedDate
    private Instant createdAt;

    @LastModifiedDate
    private Instant updatedAt;

    @CreatedBy
    private String createdBy;

    @LastModifiedBy
    private String updatedBy;
}

save() 호출 시 ES Template 이 현재 시간 · AuditorAware 의 사용자 를 자동으로 박아 줘요. 운영 추적·감사 로그 자리에 표준.

함정은 @EnableElasticsearchAuditing 누락@EnableJpaAuditing 만 켜고 ES 쪽은 안 켜면 조용히 무시 됩니다. 함정 섹션에서 다시.

자주 만나는 사고 6가지

사고 1 — Client version mismatch

원인 — Spring Data Elasticsearch 6.0.0 은 Elasticsearch 8.x 서버 에 맞춰져 있어요. 서버가 7.x 면 client 가 no compatible API 오류를 뱉으며 연결 자체가 거부됩니다.

해결 — Spring Data ES 버전과 ES 서버 버전을 공식 호환 표 로 맞춰요. 6.0.x ↔ 8.13+, 5.3.x ↔ 8.11+, 5.2.x ↔ 8.7+. ES 9.x 미리보기는 작성 시점(2026-05-19) 기준 Spring Data ES 6.1.x 미리보기와만 호환.

사고 2 — Dynamic Mapping 으로 필드 타입 자동 추론 망함

원인@Field(type = ...) 를 안 박으면 ES 가 첫 색인되는 값 으로 타입을 자동 추론해요. "123" 같은 숫자 문자열을 text 로 잡았다가 나중에 range 쿼리가 안 도는 사고가 자주 등장.

해결 — 모든 필드에 @Field(type = ...) 명시. 인덱스도 dynamic: strict 로 잠그는 게 표준. 8편(Mapping Deep) 의 Mapping Explosion 사고와 같은 자리.

사고 3 — Date format mismatch

원인 — 자바 Instant 또는 LocalDateTime 을 그대로 박으면 직렬화 형식이 ES 매핑의 format 과 안 맞아 색인 거부 또는 문자열로 색인 되는 사고.

해결@Field(type = FieldType.Date, format = DateFormat.date_optional_time) 를 명시하고, Jackson 의 JSR-310 모듈 (jackson-datatype-jsr310) 이 의존성에 들어가 있는지 확인. Spring Boot 3.x 면 기본 포함이지만, 커스텀 ObjectMapper 를 쓰면 빠지기 쉬워요.

사고 4 — Reactive context propagation 끊김

원인 — Reactive ES Template 을 쓰는 환경에서 SecurityContext · MDC · @CreatedByReactor chain 의 다른 스레드 로 가면 사라져요. AuditorAware 가 항상 null 을 반환하는 증상으로 드러나요.

해결Hooks.enableAutomaticContextPropagation() 호출 또는 micrometer-context-propagation 의존성 추가. Reactor Context 로 명시적으로 값을 흘려보내는 게 가장 안전.

사고 5 — @EnableElasticsearchAuditing 누락

원인JPA Auditing 만 켜고 ES Auditing 을 안 켜는 실수가 가장 흔해요. @CreatedDate · @LastModifiedDate조용히 동작 안 함 — 예외도 안 뜨고 그냥 null 이 들어가요.

해결@EnableElasticsearchAuditing@SpringBootApplication 클래스나 별도 @Configuration 에 명시. JPA 와 ES 가 같은 앱에 있으면 둘 다 켜야 합니다.

사고 6 — Repository 의 save() 가 매번 refresh

원인 — Spring Data ES 의 save() 가 기본적으로 RefreshPolicy.NONE 인데, 테스트 환경에서 IMMEDIATE 로 잡아 둔 채 운영으로 그대로 가면 — 매 색인마다 refresh 가 도는 사고. 처리량이 1/10 로 떨어져요.

해결 — 운영에선 RefreshPolicy.NONE 또는 WAIT_UNTIL 만 사용. IMMEDIATE 는 통합 테스트 전용 으로 명확히 분리. application.ymlspring.data.elasticsearch.client.reactive.refresh-policy 로 전역 설정 가능.

사고 7 — writeTypeHint_class 필드가 매핑을 깨먹음

원인 — Spring Data ES 5.x 까지 기본값이 WriteTypeHint.TRUE모든 문서에 _class 필드 가 자동 박혔어요. 6.0 부터 기본 FALSE 로 바뀌었는데, 5.x 인덱스를 6.0 코드로 읽으면 역직렬화 실패 사고가 등장.

해결 — 기존 5.x 인덱스를 6.0 으로 읽을 땐 @Document(writeTypeHint = WriteTypeHint.TRUE) 로 명시. 신규 인덱스는 기본 FALSE 유지.

운영 권장 패턴 5가지

운영 인덱스는 항상 alias 로 노출 합니다. @Document(indexName = "products") 에서 "products"alias 고, 실제 인덱스는 "products-2026-05" 같이 날짜 suffix 가 붙은 별도 인덱스예요. 5편(Index 관리) 의 alias 운영 패턴과 그대로 연결.

@Document(createIndex = false)앱 자동 생성 차단 하고, 인덱스·매핑·alias 는 별도 마이그레이션 으로 명시적으로 만들어요. Flyway-같은 도구 또는 CI/CD 스크립트 에 두는 게 표준. 37편(IaC) 에서 깊이.

Repository 와 ElasticsearchTemplate 을 역할로 분리 합니다. 단순 CRUD · 메서드 이름 쿼리 는 Repository 로, 복잡한 동적 검색 · 집계 · 함수 점수 는 Template + NativeQuery 로 — 섞어 쓰면 디버깅이 어려워요.

RefreshPolicy 는 운영 코드에서 NONE 또는 WAIT_UNTIL 만. 통합 테스트 전용으로 IMMEDIATE 를 박을 땐 @TestConfiguration 로 분리해서 프로덕션 빌드에 절대 안 새는 구조로.

Mapping 변경은 코드 배포 전에 ES 에 먼저 적용 합니다. Rolling deploy 중간에 매핑 불일치 인스턴스가 한 번이라도 트래픽을 받으면 색인 거부 + 사용자 응답 오류 가 됩니다. 매핑 → 새 인덱스 → reindex → alias 갈아끼우기 → 코드 배포 순서가 표준.

시험 직전 한 번 더 — 압축 노트

  • Spring Data Elasticsearch 6.0.0 = Spring Boot 3.5+ 와 함께. JPA 와 같은 Repository · Template · POJO 패턴.
  • @Document(indexName, createIndex, writeTypeHint) = 클래스 → 인덱스 매핑.
  • @Field(type, analyzer, format) = 필드 → ES 필드 매핑. @MultiField 로 한 필드 → 여러 매핑.
  • @Id = ES _id. 값 안 박으면 ES 가 UUID 자동 생성.
  • @Setting · @Mapping = JSON 파일로 settings · mapping 박기.
  • ElasticsearchOperations = 인터페이스, ElasticsearchTemplate = 구현. save · get · search · delete · update · bulk · indexOps.
  • SearchHits = 결과 + total · maxScore · aggregations · highlight 한 묶음.
  • ElasticsearchRepository = 메서드 이름 기반 자동 쿼리 (JPA Repository 와 같은 패턴).
  • @Query("""json""") = JSON DSL 직접 박기. ?0 · ?1 위치 파라미터.
  • ReactiveElasticsearchOperations = WebFlux 환경. Mono · Flux 반환. context propagation 함정 주의.
  • Criteria API = 도메인 친화 빌더 (동적 필터). NativeQuery = ES DSL 그대로 빌더 (권장 표준).
  • IndexOperations = 인덱스 자체 관리 (create · mapping · alias · refresh · delete).
  • @EnableElasticsearchAuditing + AuditorAware = @CreatedDate · @LastModifiedDate · @CreatedBy · @LastModifiedBy.
  • 7대 사고: client/server 버전 mismatch · 동적 매핑 타입 망함 · Date format mismatch · Reactive context 끊김 · @EnableElasticsearchAuditing 누락 · RefreshPolicy IMMEDIATE 누수 · writeTypeHint 의 _class 매핑 깨짐.
  • 권장 패턴: alias 노출 · createIndex=false · Repository/Template 역할 분리 · RefreshPolicy 운영 NONE · 매핑 선배포 후 코드 배포.

시리즈 다른 편

  • 이전 글 = 31편 Performance Tuning — JVM·OS·인덱스·쿼리 최적화
  • 다음 글 = 33편 Kibana·ELK 스택 — Discover·Dashboard·Lens·Logstash·Beats
  • 1편 = Elasticsearch 입문 종합 — 검색·로그·AI 한 통에 다 들어가는 분산 엔진
  • 5편 = Index 관리 — Create·Settings·Alias·Reindex
  • 8편 = Mapping Deep — Static·Dynamic·Multi-field·Runtime
  • 11편 = Korean Analyzer — Nori·mecab-ko·사용자 사전
  • 19편 = Search Features — search_after·scroll·highlight·rescore
  • 23편 = Bulk API — 처리량 10배 늘리는 패턴
  • 34편 = Observability — APM·Tracing·Profiling
  • 35편 = AWS OpenSearch Service — Domain·Serverless·Provisioned
  • 37편 = IaC — Terraform·CloudFormation·Helm

한 줄 정리 — Spring Data Elasticsearch = @Document POJO 매핑 · ElasticsearchTemplate sync API · Repository 메서드 쿼리 · Reactive 변형 · NativeQuery DSL 빌더 · Auditing 까지 JPA 와 같은 패턴 으로 ES 를 자바에서 깔끔하게 쓰는 표준. 6.0.0 + Spring Boot 3.5+ 이 작성 시점 표준 조합.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!