최종 프로젝트

AWS OpenSearch를 활용한 검색 기능 구축기

phonebee 2025. 4. 25. 20:55

1. OpenSearch란?

OpenSearch는 AWS에서 제공하는 Elasticsearch 기반의 오픈소스 검색 엔진으로, 대규모 텍스트 기반 검색과 로그 분석에 적합한 도구

이번 프로젝트에서 OpenSearch를 활용하여 경매 검색 기능을 구축

 

2. AWS OpenSearch 도메인 생성하기
우선, OpenSearch를 사용하기 위해서는 AWS OpenSearch 도메인을 생성해야한다.

 

● 도메인이란?

OpenSearch에서 domain은 하나의 클러스터(서버 집합)를 의미하며, 여기에 인덱스를 저장하고 쿼리할 수 있다.

 

도메인을 생성하기 위해서 OpenSearch 서비스에 있는 도메인 생성을 통해서 생성하게 되었다.

 

○ 설정

비용 절감을 위해서 많은 부분을 줄이게 되었다.
▷ 버전 : OpenSearch 2.17

▷ 데이터 노드 수
- 가용 영역 : 1-대기 상태가 아닌 AZ

- 인스턴스 유형 : t3.small.search

- 데이터 노드 수 : 1

- 스토리지 유형 : EBS

- 볼륨 크기 : 10

 

여기서 퍼블릭 엑세스를 허용해서 제작했다.

 

이제 인텔리제이로 넘어와서 코드를 작성했다.

3. 인덱스 구조 정의(AuctionDocument)

@Document(indexName = "auctions")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuctionDocument {

    @Id
    private Long id;

    private String productName;
    private String category;
    private Long minPrice;
    private String startTime;
    private String endTime;
}

● @Document(indexName = "auctions")로 인덱스를 지정

● 검색 대상 필드를 문자열로 관리

 

4. Spring Boot에서 OpenSearch 연동

implementation 'org.opensearch.client:opensearch-rest-high-level-client:2.17.0'

build.gradle에 OpenSearch를 연동하기 위해서 의존성을 추가

 

● OpenSearchConfig 설정

@Configuration
public class OpenSearchConfig {

    @Value("${opensearch.url}")
    private String openSearchUrl;

    @Value("${opensearch.username}")
    private String username;

    @Value("${opensearch.password}")
    private String password;

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

        RestClientBuilder builder = RestClient.builder(HttpHost.create(openSearchUrl))
                .setHttpClientConfigCallback(httpClientBuilder ->
                        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));

        return new RestHighLevelClient(builder);
    }
}

● OpenSearch에 연결할 RestHighLevelClient를 설정하는 Spring @Configuration

● application.properties에서 opensearch.url, username, password를 읽어와서 연결에 사용한다.

● Basic 인증을 적용하여 AWS OpenSearch 퍼블릭 도메인에 접속할 수 있도록 구성했다.

 

 

5. 검색 기능 구현(keyword + category 기반)

@Service
@RequiredArgsConstructor
public class AuctionOpenSearchService {
    private final RestHighLevelClient restHighLevelClient;
    private final ObjectMapper objectMapper;

    private static final String INDEX_NAME = "auctions";

    public void save(AuctionDocument auction) throws IOException {
        IndexRequest request = new IndexRequest(INDEX_NAME)
                .id(String.valueOf(auction.getId()))
                .source(objectMapper.writeValueAsString(auction), XContentType.JSON);

        restHighLevelClient.index(request, RequestOptions.DEFAULT);
    }

    public List<AuctionDocument> search(String keyword, String category, int page, int size) throws IOException {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        if(keyword != null && !keyword.isBlank()){
            boolQuery.should(QueryBuilders.matchPhrasePrefixQuery("productName", keyword))
                    .should(QueryBuilders.fuzzyQuery("productName", keyword).fuzziness(Fuzziness.AUTO))
                    .minimumShouldMatch(1);
        }

        if(category != null && !category.isBlank()){
            boolQuery.filter(QueryBuilders.termQuery("category", category));
        }

        int from = (page - 1) * size;

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
                .query(boolQuery)
                .from(from)
                .size(size);

        SearchRequest searchRequest = new SearchRequest(INDEX_NAME).source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<AuctionDocument> result = new ArrayList<>();
        for(SearchHit hit : searchResponse.getHits().getHits()) {
            AuctionDocument auction = objectMapper.readValue(hit.getSourceAsString(), AuctionDocument.class);
            result.add(auction);
        }

        return result;
    }
}

● matchPhrasePrefixQuery를 통해 keyword 접두어 일치 검색을 수행한다.

● fuzzyQuery를 추가하여 오타가 있는 검색어도 허용한다.

● category의 경우 termQuery를 사용해 category를 정확히 필터링한다.

● 검색 결과는 AuctionDocument 리스트로 변환되어 반환된다.

 

현재는 AWS OpenSearch를 퍼블릭 도메인으로 제작을 했지만, EC2를 사용해서 다시 구현할 생각이다.