Elasticsearch와 Spring Boot의 조합은 대용량 로그 분석이나 실시간 검색 기능을 구현할 때 매우 강력한 도구입니다. 하지만 초보 개발자들이 처음 부딪히는 문제 중 하나는 “필요한 필드만 어떻게 골라서 가져올 수 있을까?”입니다. 이 글에서는 Spring Boot 환경에서 Elasticsearch의 특정 인덱스에 대해 _source
필드를 활용하여 필요한 데이터만 선택적으로 조회하는 방법을 실전 예제와 함께 상세히 설명합니다.

목차
- Elasticsearch 쿼리, 왜 필드 선택이 중요한가?
- Spring Boot와 Elasticsearch 연동 기본 설정
- Elasticsearch 인덱스에 특정 필드만 조회하기 – 개념 이해
- Spring Data Elasticsearch에서 필드 선택 쿼리 작성 방법
- 예제 코드: @timestamp, message, agent.id 필드만 조회하기
- 검색 성능과 응답 최적화를 위한 팁
- 자주 발생하는 문제와 해결 방법
- 결론: 명확한 목적의 쿼리가 애플리케이션 성능을 좌우한다
Elasticsearch 쿼리, 왜 필드 선택이 중요한가?
Elasticsearch는 기본적으로 문서 전체를 반환합니다. 하지만 실제 애플리케이션에서는 모든 필드가 필요하지 않을 때가 많습니다. 예를 들어, 로그 데이터를 조회할 때 우리는 @timestamp
, message
, agent.id
만 필요할 수 있습니다. 이처럼 불필요한 필드를 제외하는 것은 다음과 같은 이점을 제공합니다.
- 네트워크 비용 절감: 필드가 줄어들면 Elasticsearch에서 클라이언트로 전달되는 데이터 양이 줄어듭니다.
- 응답 속도 향상: 데이터 양이 줄면 Elasticsearch가 더 빠르게 응답합니다.
- 메모리 사용 최적화: 클라이언트 애플리케이션에서 필요 없는 데이터를 처리하지 않아도 되므로 메모리 사용량이 줄어듭니다.
이를 가능하게 하는 것이 바로 _source
필드입니다. Elasticsearch는 문서 저장 시 원본 데이터를 _source
라는 메타 필드에 저장하고, 이 _source
에서 필요한 필드만 추출하는 방식으로 응답을 구성할 수 있습니다.
다음 장에서는 Spring Boot에서 Elasticsearch와의 기본적인 연동을 설정하는 방법을 먼저 알아보겠습니다.

Spring Boot와 Elasticsearch 연동 기본 설정
Spring Boot에서 Elasticsearch를 사용하려면 몇 가지 설정이 필요합니다. Spring Data Elasticsearch를 이용하면 자바 코드 내에서 간단하게 쿼리를 작성하고 실행할 수 있습니다. 이 장에서는 필요한 의존성과 설정 방법을 소개합니다.

1. 의존성 추가
Maven 또는 Gradle 프로젝트에 다음과 같은 의존성을 추가합니다.
org.springframework.boot
spring-boot-starter-data-elasticsearch
버전에 따라 `spring-data-elasticsearch`에서 사용하는 Elasticsearch 클라이언트 라이브러리가 달라지므로 Spring Boot 버전에 호환되는 의존성을 선택하는 것이 중요합니다.
2. 애플리케이션 설정
application.yml
혹은 application.properties
에서 Elasticsearch 연결 정보를 지정합니다.
spring:
data:
elasticsearch:
client:
reactive:
endpoints: localhost:9200
Spring Boot 3.x 이후부터는 RestHighLevelClient
대신 ElasticsearchOperations
또는 ReactiveElasticsearchClient
사용이 권장됩니다. 일반적으로는 `ElasticsearchOperations`가 널리 사용됩니다.
3. 연결 테스트
설정이 완료되면, 애플리케이션을 실행하여 연결 여부를 테스트할 수 있습니다. 테스트용으로 다음과 같은 간단한 명령을 실행할 수 있습니다.
@Autowired
private ElasticsearchOperations elasticsearchOperations;
@PostConstruct
public void testConnection() {
boolean indexExists = elasticsearchOperations.indexOps(IndexCoordinates.of("my-index")).exists();
System.out.println("연결된 인덱스 존재 여부: " + indexExists);
}
이제 Elasticsearch와의 연동이 준비되었습니다. 다음 장에서는 본격적으로 Elasticsearch 쿼리에서 _source
필드를 활용하여 원하는 필드만 조회하는 방법을 이론적으로 설명하겠습니다.
Elasticsearch 인덱스에 특정 필드만 조회하기 – 개념 이해
Elasticsearch는 유연한 스키마와 강력한 쿼리 언어를 제공하는 반면, 처음 사용하는 개발자에게는 그 유연함이 오히려 복잡함으로 다가올 수 있습니다. 특히 “_source” 필드를 활용한 부분 필드 조회는 성능 최적화의 핵심이지만, 잘못 사용하면 결과가 누락되거나 비효율적인 쿼리가 될 수 있습니다.
1. _source 필드란?
_source
필드는 Elasticsearch 문서의 원본 데이터를 저장하고 있는 필드입니다. 쿼리 응답 시 전체 문서가 아닌 특정 필드만 응답받고자 할 때, 이 _source
를 필터링하여 필요한 데이터만 전달받을 수 있습니다.
예를 들어, 다음과 같이 쿼리를 구성하면 @timestamp
, message
, agent.id
세 필드만 반환됩니다.
GET /log-index/_search
{
"_source": ["@timestamp", "message", "agent.id"],
"query": {
"match_all": {}
}
}
이러한 필드 선택은 특히 다음과 같은 상황에서 유용합니다:
- 문서 크기가 매우 큰 경우
- 리스트 화면 등에서 요약 정보만 필요한 경우
- 네트워크 트래픽이나 렌더링 속도를 줄이고 싶은 경우
2. include / exclude 옵션
_source
필드에서 단순히 포함할 필드를 지정할 수도 있고, 특정 필드를 제외시킬 수도 있습니다. 이때 사용하는 옵션이 includes
와 excludes
입니다.
GET /log-index/_search
{
"_source": {
"includes": ["@timestamp", "message", "agent.id"],
"excludes": ["host.name"]
},
"query": {
"match_all": {}
}
}
단, includes
와 excludes
는 동시에 사용할 수 있으나, 과도하게 조합하면 오히려 유지 보수가 어려워질 수 있습니다. 일반적으로는 포함할 필드를 명시하는 방식이 더 명확합니다.
3. nested 필드 구조에서의 주의
Elasticsearch의 문서 구조는 계층적으로 구성될 수 있으며, agent.id
와 같은 필드는 nested 혹은 object 타입으로 정의되어 있을 수 있습니다. 이 경우 정확한 경로를 명시하지 않으면 필드가 누락될 수 있습니다.
예를 들어 다음과 같은 구조에서는:
{
"@timestamp": "2025-04-01T12:00:00Z",
"message": "User login detected",
"agent": {
"id": "abc123",
"name": "filebeat"
}
}
agent.id
처럼 하위 필드명을 정확히 명시해야 원하는 데이터를 받을 수 있습니다. 이제 이러한 원리를 기반으로 Spring Boot 코드에서 실제로 원하는 필드를 필터링하는 방법을 살펴보겠습니다.
Spring Data Elasticsearch에서 필드 선택 쿼리 작성 방법
Spring Boot에서 Elasticsearch의 _source
필터링 기능을 사용하려면 NativeSearchQueryBuilder
를 통해 직접 쿼리를 구성해야 합니다. ElasticsearchRepository
와 같은 기본 Repository 방식에서는 특정 필드만 선택하는 기능이 제한적이므로, 보다 세밀한 제어가 필요한 경우에는 ElasticsearchOperations
또는 ElasticsearchRestTemplate
을 사용해야 합니다.
1. NativeSearchQueryBuilder 활용
NativeSearchQueryBuilder
는 Elasticsearch의 원시 쿼리 구성을 자바 객체로 표현할 수 있게 도와주는 도구입니다. 여기에서 withSourceFilter
메서드를 사용하여 포함할 필드를 설정할 수 있습니다.
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withSourceFilter(new FetchSourceFilter(
new String[] { "@timestamp", "message", "agent.id" },
null))
.build();
이렇게 작성한 쿼리는 Elasticsearch에 전달될 때 다음과 같은 JSON 형식으로 변환됩니다.
{
"_source": ["@timestamp", "message", "agent.id"],
"query": {
"match_all": {}
}
}
2. ElasticsearchOperations을 이용한 실행
생성된 NativeSearchQuery
객체를 ElasticsearchOperations
를 통해 실행할 수 있습니다.
@Autowired
private ElasticsearchOperations elasticsearchOperations;
List<LogDocument> results = elasticsearchOperations.search(
searchQuery,
LogDocument.class,
IndexCoordinates.of("log-index")
).stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
위 예제에서는 log-index
라는 인덱스에서 @timestamp
, message
, agent.id
필드만 포함하여 문서를 검색하고, 결과를 LogDocument
DTO에 매핑하고 있습니다.
3. DTO를 활용한 결과 매핑
응답 데이터를 받기 위한 DTO는 해당 필드만 정의하여 구성하는 것이 좋습니다. 이렇게 하면 불필요한 필드가 자동으로 무시되고, 직렬화 성능도 향상됩니다.
@Data
public class LogDocument {
@JsonProperty("@timestamp")
private String timestamp;
private String message;
@JsonProperty("agent.id")
private String agentId;
}
여기서 주의할 점은 @JsonProperty
를 활용해 JSON 키와 Java 필드명을 정확히 매핑해야 한다는 것입니다. 특히 dot(.)을 포함한 필드명(agent.id
)은 자바 필드명으로 사용할 수 없으므로, 이와 같은 어노테이션 처리가 필요합니다.
다음 장에서는 위 내용을 종합하여 하나의 예제 프로젝트 코드로 통합하고, 실제 동작 과정을 설명해 드리겠습니다.
예제 코드: @timestamp, message, agent.id 필드만 조회하기
이제 앞에서 배운 개념과 코드를 종합하여, 실무에서 활용 가능한 하나의 예제 코드를 구성해보겠습니다. 목표는 Elasticsearch 인덱스 log-index
에서 @timestamp
, message
, agent.id
필드만 선택하여 조회하고 이를 Java 객체에 매핑하는 것입니다.
1. DTO 클래스 정의
먼저 응답을 받을 DTO를 정의합니다. Elasticsearch 문서 구조에 따라 @JsonProperty
어노테이션을 통해 필드명을 매핑합니다.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LogDocument {
@JsonProperty("@timestamp")
private String timestamp;
private String message;
@JsonProperty("agent.id")
private String agentId;
}
2. 서비스 메서드 구현
ElasticsearchOperations
를 주입받아 NativeSearchQuery
를 구성하고 실행합니다. 응답은 SearchHit
를 통해 DTO로 변환됩니다.
@Service
public class LogSearchService {
@Autowired
private ElasticsearchOperations elasticsearchOperations;
public List<LogDocument> fetchLogFields() {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withSourceFilter(new FetchSourceFilter(
new String[] { "@timestamp", "message", "agent.id" },
null
))
.build();
SearchHits<LogDocument> searchHits = elasticsearchOperations.search(
query,
LogDocument.class,
IndexCoordinates.of("log-index")
);
return searchHits.stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
}
}
3. 컨트롤러에서 결과 확인
간단한 REST API를 만들어 클라이언트가 필터링된 결과를 요청할 수 있도록 합니다.
@RestController
@RequestMapping("/logs")
public class LogController {
@Autowired
private LogSearchService logSearchService;
@GetMapping("/fields")
public ResponseEntity<List<LogDocument>> getFilteredLogs() {
List<LogDocument> logs = logSearchService.fetchLogFields();
return ResponseEntity.ok(logs);
}
}
위와 같은 구조로 구성하면, 클라이언트는 /logs/fields
엔드포인트를 통해 Elasticsearch로부터 필요한 필드만 응답받을 수 있습니다. 이는 프론트엔드 애플리케이션에서 로그 리스트를 출력하거나, 대시보드에서 특정 필드만 시각화할 때 매우 유용한 패턴입니다.
다음 장에서는 이처럼 필드를 제한하는 방식이 검색 성능과 응답 속도에 어떤 긍정적인 영향을 미치는지 실무적인 팁과 함께 살펴보겠습니다.
검색 성능과 응답 최적화를 위한 팁
Elasticsearch에서 필요한 필드만 골라서 조회하는 것은 단순한 기능 그 이상입니다. 이는 시스템 전반의 성능에 직접적인 영향을 줄 수 있는 중요한 전략입니다. 이 장에서는 응답 속도, 네트워크 비용, 메모리 사용량 등을 최적화하는 팁을 소개합니다.
1. 필드 제한은 곧 데이터 전송량 최소화
Elasticsearch는 JSON 기반의 데이터를 반환합니다. 문서 하나당 수십에서 수백 개의 필드를 포함하는 경우, 모든 필드를 반환하게 되면 전송량이 급증합니다. 예를 들어, 다음과 같은 두 요청의 응답 차이는 명확합니다.
요청 방식 | 응답 크기 | 설명 |
---|---|---|
전체 문서 조회 | ~150KB | 모든 필드 포함, 대부분 불필요 |
필드 제한 조회 | ~12KB | 3개 필드만 응답, 90% 이상 절감 |
특히 모바일 네트워크 환경이나 내부 API 연동처럼 트래픽이 민감한 환경에서는 이러한 차이가 실질적인 시스템 성능으로 이어집니다.
2. DTO를 통한 직렬화 최적화
필드가 많은 Entity를 그대로 반환하게 되면 Jackson 등 직렬화 프레임워크가 모든 필드를 스캔하고 처리해야 합니다. 이는 CPU 자원을 낭비하게 되고, 경우에 따라 메모리 누수로 이어질 수 있습니다. DTO를 별도로 정의하여 필요한 필드만 직렬화하면 이러한 리스크를 최소화할 수 있습니다.
또한, Entity 클래스에는 종종 JPA 관련 어노테이션이 포함되어 있어 Elasticsearch 전용 조회에는 부적절할 수 있습니다. 가급적 Elasticsearch 전용 DTO를 구성하여 사용하는 것이 바람직합니다.
3. 필드 타입에 따라 성능이 좌우된다
Elasticsearch에서 숫자, 키워드, 날짜 필드는 비교적 빠르게 처리됩니다. 반면, 텍스트 필드의 분석기(analyzer)는 비용이 크므로 조회 시 필드 선택을 잘못하면 성능 병목이 발생할 수 있습니다. 따라서 검색용으로 사용되는 필드와 단순 출력용 필드를 구분하여 처리하는 것이 좋습니다.
예를 들어, message
필드는 text
타입이지만, message.keyword
필드를 활용하면 분석기 없이 검색하거나 정렬할 수 있어 성능이 향상됩니다.
이처럼 단순한 “_source 필터링”이지만, 실제로는 시스템 효율성을 극대화하는 중요한 기술적 선택입니다. 다음 장에서는 이 과정에서 자주 마주치는 문제와 그 해결책에 대해 정리해 보겠습니다.
자주 발생하는 문제와 해결 방법
Spring Boot와 Elasticsearch를 연동하고 특정 필드만 조회하는 과정에서 몇 가지 흔한 이슈에 부딪힐 수 있습니다. 이 장에서는 실무에서 자주 발생하는 문제와 그 해결 방법을 사례 중심으로 정리합니다.
1. “No mapping found for field” 오류
이 오류는 보통 쿼리에서 지정한 필드명이 Elasticsearch 인덱스 매핑(mapping)에 존재하지 않을 때 발생합니다. 대표적인 원인은 다음과 같습니다:
- 필드명을 잘못 입력했거나 대소문자가 다른 경우
- 필드가 존재하지만 nested 타입이라 경로가 올바르지 않은 경우
해결 방법:
- Kibana 또는
GET /index/_mapping
API를 통해 실제 필드명 확인 - nested 필드일 경우 경로를 정확히 명시하거나
nested query
사용
2. includeFields가 적용되지 않을 때
Spring Data Elasticsearch에서 FetchSourceFilter
로 필드를 지정했음에도 전체 문서가 반환되는 경우가 있습니다. 주요 원인은 다음과 같습니다:
- 버전에 따라
includeFields
기능이 일부 무시되는 경우 ElasticsearchRepository
사용 시 제한적인 쿼리만 허용됨
해결 방법:
ElasticsearchOperations
또는ElasticsearchRestTemplate
으로 전환- Elasticsearch 버전과 Spring Data 버전의 호환성 확인
3. 필드명이 점(dot)을 포함한 경우 매핑 실패
예를 들어 agent.id
와 같은 필드는 JSON 상에서는 객체 안의 속성을 의미하지만, Java 필드명으로는 사용할 수 없습니다. 이 경우 직렬화/역직렬화 과정에서 문제가 발생할 수 있습니다.
해결 방법:
@JsonProperty("agent.id")
어노테이션을 필드에 명시- 객체 구조를 그대로 표현하고 하위 클래스로 분리
@Data
public class Agent {
private String id;
}
@Data
public class LogDocument {
@JsonProperty("@timestamp")
private String timestamp;
private String message;
private Agent agent;
}
4. nested 필드에서 필터링이 안 될 때
Elasticsearch는 기본적으로 object 타입과 nested 타입을 다르게 취급합니다. nested 타입은 별도의 쿼리 문법을 사용해야 하며, 단순한 includeFields 지정만으로는 하위 필드를 정확히 추출하지 못하는 경우가 많습니다.
해결 방법:
- 인덱스 매핑 시 object와 nested 타입을 명확히 구분
- nested query를 사용하고 include 필드도 명확히 지정
이처럼 오류의 대부분은 쿼리 문법이나 매핑 설정, 도구 간의 호환성 문제에서 비롯됩니다. 사전에 필드 구조를 잘 파악하고, DTO와 쿼리를 일관성 있게 구성하면 대부분의 문제를 사전에 방지할 수 있습니다.
마지막으로, 이 모든 내용을 종합하여 글의 핵심 메시지를 정리하고, 실무에 적용 가능한 방향성을 제시하며 마무리해보겠습니다.
결론: 명확한 목적의 쿼리가 애플리케이션 성능을 좌우한다
Elasticsearch는 유연성과 확장성이 뛰어난 검색 엔진이지만, 이러한 강점을 제대로 활용하기 위해서는 쿼리의 목적을 분명히 해야 합니다. 특히 필드 조회에 있어서 불필요한 데이터를 배제하고 필요한 필드만 골라내는 전략은 단순한 코드 최적화를 넘어, 전체 시스템 성능과 사용자 경험에 지대한 영향을 끼칩니다.
이번 글에서는 Spring Boot 애플리케이션에서 Elasticsearch에 연결하고, _source
필드를 활용하여 특정 필드만 조회하는 방법을 상세하게 다뤘습니다. 단순히 쿼리를 작성하는 것을 넘어서, DTO 구성, 네트워크 최적화, 오류 대응 전략까지 실무에서 꼭 알아야 할 내용을 체계적으로 설명했습니다.
마지막으로 강조하고 싶은 메시지는 다음과 같습니다:
“Elasticsearch 쿼리는 단지 데이터를 검색하는 것이 아니라, 애플리케이션의 의도를 가장 효과적으로 전달하는 언어이다.”
필드를 선택하는 그 작은 선택 하나가, 곧 응답 시간, 시스템 부하, 사용자 만족도에 직결될 수 있다는 점을 기억하세요. 그리고 이러한 최적화는 개발 초기 단계에서부터 설계에 반영되어야 합니다.
이 글을 통해 Elasticsearch와 Spring Boot의 연동에 대한 자신감이 생겼기를 바랍니다. 이제 다음 단계로는 조건 검색, 정렬, 페이징 등과 필드 필터링을 조합하여 더욱 강력한 검색 시스템을 구현해보세요.