| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- 자바
- HashMap
- junit5
- html
- vscode
- js
- 배열
- SpringBoot
- Java
- java테스트
- 정규식
- list
- CSS
- junit
- 인텔리제이
- 문자열
- 단위테스트
- Array
- math
- string
- IntelliJ
- Eclipse
- 자바스크립트
- Visual Studio Code
- javascript
- 테스트자동화
- 스프링부트
- ArrayList
- 자바문법
- input
- Today
- Total
어제 오늘 내일
[Spring Boot] "RDB에서 검색하면 느려터져요..." Elasticsearch로 고성능 검색 엔진 구축하기 본문
[Spring Boot] "RDB에서 검색하면 느려터져요..." Elasticsearch로 고성능 검색 엔진 구축하기
hi.anna 2026. 3. 29. 01:57
우리가 흔히 쓰는 관계형 데이터베이스(RDB)는 "저장"과 "정합성"에 특화되어 있지, "검색"에는 약합니다.
특히 본문 검색(Full-text Search)이나 오타 교정, 자동 완성 같은 기능은 RDB로 구현하기가 매우 까다롭고 느립니다.
이 문제를 해결하기 위해 Elasticsearch를 도입해 보겠습니다.
1. 왜 Elasticsearch 인가요? (역색인 구조)
RDB는 책의 본문을 처음부터 끝까지 다 읽으면서 찾는 방식(Scan)이라면, Elasticsearch는 책의 맨 뒤에 있는 "색인(Index)"을 보고 찾는 방식입니다.
- RDB: "강남"이라는 단어가 들어간 행을 다 뒤진다. -> 느림
- Elasticsearch: "강남" -> [1번 문서, 5번 문서, 100번 문서] -> 즉시 리턴
이 역색인(Inverted Index) 구조 덕분에 데이터가 아무리 많아도 검색 속도가 거의 일정하게 빠릅니다.
2. 설치하기 (Docker Compose)
엘라스틱서치(엔진)와 키바나(시각화 도구)를 함께 설치합니다.
docker-compose.yml
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.10
container_name: elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
kibana:
image: docker.elastic.co/kibana/kibana:7.17.10
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
docker-compose up -d 후 브라우저에서 http://localhost:5601 (Kibana)에 접속되면 성공입니다.
3. 스프링 부트 설정 (Spring Data Elasticsearch)
JPA를 써보셨다면 아주 익숙하실 겁니다. 스프링은 Spring Data Elasticsearch라는 강력한 라이브러리를 제공합니다.
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
}
application.yml
spring:
elasticsearch:
uris: http://localhost:9200
4. 도큐먼트(Document) 정의하기
RDB의 Entity와 비슷한 개념입니다.
@Getter
@Setter
@Document(indexName = "blog_post") // 인덱스 이름 (RDB의 테이블)
public class BlogPostDocument {
@Id
private String id;
@Field(type = FieldType.Text) // 형태소 분석이 필요한 필드
private String title;
@Field(type = FieldType.Text)
private String content;
@Field(type = FieldType.Keyword) // 정확히 일치해야 하는 필드 (태그 등)
private String category;
}
5. 리포지토리(Repository) 만들기
이게 하이라이트입니다. JPA처럼 인터페이스만 만들면 끝납니다.
public interface BlogPostRepository extends ElasticsearchRepository<BlogPostDocument, String> {
// 1. 제목에 검색어가 포함된 문서 찾기
List<BlogPostDocument> findByTitleContaining(String keyword);
// 2. 제목이나 내용에 검색어가 포함된 문서 찾기 (OR 검색)
List<BlogPostDocument> findByTitleContainingOrContentContaining(String title, String content);
}
6. 서비스에서 사용하기
이제 RDB 대신 Elasticsearch를 호출하면 됩니다.
@Service
@RequiredArgsConstructor
public class BlogSearchService {
private final BlogPostRepository blogPostRepository;
public List<BlogPostDocument> search(String keyword) {
// RDB였다면 3초 걸릴 쿼리가 0.05초 만에 나옵니다.
return blogPostRepository.findByTitleContaining(keyword);
}
// 데이터 저장 (색인)
public void save(BlogPostDocument post) {
blogPostRepository.save(post);
}
}
7. 주의사항: 데이터 동기화 (Sync)
가장 큰 고민거리는 "MySQL에 글이 써졌을 때, 어떻게 Elasticsearch에도 넣지?"입니다.
- Dual Write: 서비스 코드에서
rdbRepository.save()하고 바로esRepository.save()를 호출한다. (가장 쉽지만, 하나가 실패하면 데이터가 꼬임) - Logstash / Kafka Connect: MySQL의 바이너리 로그(Binlog)를 읽어서 비동기로 Elasticsearch에 밀어 넣는다. (구축이 복잡하지만 안정적)
초기에는 Dual Write나 Spring Event(@EventListener)를 활용해서 동기화하는 것을 추천합니다.
마치며
오늘의 결론입니다.
- RDB는 저장소, Elasticsearch는 검색 엔진으로 역할을 분리하자.
- 역색인 기술 덕분에 대용량 데이터에서도 검색 속도가 압도적으로 빠르다.
- Spring Data Elasticsearch를 쓰면 JPA처럼 쉽게 개발할 수 있다.
이제 여러분의 서비스는 구글처럼 빠른 검색 속도를 자랑하게 되었습니다.
다음 포스팅에서는 "서버가 1,000명까진 버티는데 10,000명이 오면 죽어요..." 내 서버의 한계점을 찾아내는 nGrinder 부하 테스트(Load Test)에 대해 알아보겠습니다.
도움이 되셨다면 좋아요와 댓글 부탁드립니다! 😊
