최종 프로젝트

경매 검색 기능 강화를 위해 AWS OpenSearch를 EC2와 연동하여 실행시키기

phonebee 2025. 4. 27. 20:29

저번에는 퍼블릭 도메인을 생성하여 AWS OpenSearch를 진행했다면 이번에는 EC2와 연동하여 진행하는 OpenSearch를 해보기로 했다.

 

1. EC2 제작

먼저, 연동하기 위한 EC2 인스턴스를 제작하기로 했다.

▶ 주의 사항

OpenSearch 도메인과 연결하기 위한 EC2 인스턴스를 제작할 때에는 주의해야할 것이 있었다.

① 사용할 VPC가 연동할 OpenSearch 도메인과 같아야한다.

 

그리고 기본적인 외부 접속용(SSH, 8080)인 보안 그룹을 추가하여 인스턴스 제작을 완료했다.

 

2. OpenSearch 도메인 설정

현 OpenSearch에는 EC2에서만 접근을 해야하기 때문에 보안 그룹을 수정하여 EC2의 보안 그룹 ID만 허용하도록 제작해야한다고 한다,

그래서 OpenSearch용 보안그룹에 EC2의 보안 그룹 ID를 소스로 넣어서 보안그룹을 재설정했다.

 

3. EC2에서 Spring Boot 실행하기

이제 EC2에 접속하여 실행시켜보도록 하자

 

ssh -i "your-key.pem" ec2-ubuntu@<EC2 Public IP>

 

우선 EC2에 접속하기 위해서 EC2를 제작하면서 만든 pem키가 저장된 파일 경로와 EC2의 퍼블륵 IP를 위의 코드에 적용시켜서 실행한다.

그러면 EC2에 접속하게 된다.

그 후에는
● 시스템 업데이트
sudo apt update && sudo apt upgrade -y

 

● Java 설치

sudo apt install openjdk-17-jdk -y

 

를 하여 환경을 구성한다.

4. .jar파일 빌드

이제 Spring Boot 애플리케이션 실행 준비를 위해 .jar파일을 EC2에 복사해야한다.

그래서 해당 프로젝트의 jar파일을 찾고 있었는데
막상 찾다 보니 jar파일이 만들어지지 않았다.

그래서 인텔리제이에 있는 build시스템에서 boojar를 사용하여 jar파일을 생성했다.

그 후
scp -i "C:\Users\user\Downloads\auctionSearchTest.pem" path\to\your\file.jar ubuntu@3.35.25.159:~

를 통해서 jar파일을 EC2에 복사해서 넣는다.

5. EC2에서 Spring boot 앱 실행

이제 마지막으로 EC2에서 Spring boot 앱을 실행한다.

 

DB_URL="jdbc:mysql://auctions.c9ak2ykyg8pv.ap-northeast-2.rds.amazo
naws.com:3306/auctionMarket" DB_USERNAME="EC2의 마스터 이름" DB_PASSWORD="마스터 비밀번호" JWT_SECRET_KEY="7
Iqk7YyM66W07YOA7L2U65Sp7YG065+9U3ByaW5n6rCV7J2Y7Yqc7YSw7LWc7JuQ67mI7J6F64uI64ukLg==" java -jar AuctionMarket-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod

해당 코드를 EC2에 작성하여 실행하게 되었다.
위의 실행 코드는 DB_URL, USERNAME, PASSWORD, JWT_SECRET_KEY를 직접 입력해주었는데 전의 로컬에서는 환경 변수로 해당 값들을 전부 주었지만 EC2에서는 로컬에 준 환경변수는 사용이 불가하므로 따로 줘야했다.
그리고 EC2에서 Spring Boot를 실행하는 것이기 때문에 USERNAME, PASSWORD는 해당 EC2의 마스터 이름과 패스워드를 줘야 실행을 시킬 수 있다.

그래서 해당 코드를 작성 후 실행하게 되면
EC2에서 Spring Boot를 실행하게 되었다.

EC2에서 실행한 Spring Boot에서 경매 검색 기능 실행

 

▶ 진행하면서 발생한 문제 트러블 슈팅

1. 로컬로 진행한 elastic search와의 충돌
● 배경

EC2에 jar파일을 저장한 후에 실행을 진행했더니 에러가 발생하면서 실패하게 되었다.

 

● 원인

현재 OpenSearch는 VPC 전용으로 만들어졌지만 Spring Data Elasticsearch 기본 설정은 Public IP로 통신하려고 하기 때문에 실패를 하게 된다.

● 해결 과정

찾은 해결 과정으로는 RestHighLevelClient를 직접 사용하여 OpenSearch 쿼리에 던지는 것이였다.

그래서 기존에 있었던 AuctionSearchService를 수정하고

import com.example.auctionmarket.domain.auction.document.AuctionDocument;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opensearch.action.index.IndexRequest;
import org.opensearch.action.search.SearchRequest;
import org.opensearch.action.search.SearchResponse;
import org.opensearch.client.RequestOptions;
import org.opensearch.client.RestHighLevelClient;

import org.opensearch.common.unit.Fuzziness;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.search.SearchHit;
import org.opensearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@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;
    }
}

 

다음으로는 RestHighLevelClient를 쓰고 있기에 auctionSearchRepository를 주석처리를 했다.

 

이렇게 하고도 같은 에러가 나타나기에 좀 더 찾아 봤더니
@EnableElasticsearchRepositories 때문에 주석처리를 하지 않더라도 파일은 계속해서 남아 있기 때문에 Spring은 컴파일 타임에 그걸 신경 안 쓰고 무조건 탐색한다고 한다.

 

그래서 해당 어노테이션과 auctionSearchRepository를 삭제한 후에 진행하게 되었는데...

다시 똑같은 에러가 반복되었다.

계속 같은 에러가 반복되기에 더 이상 코드의 문제가 아닌 것 같아서 다른 문제점을 찾아 봤더니

 

빌드를 다시 해야 지금까지 코드 수정한 내용이 적용이 된다고 한다.

 

그래서 EC2에 저장된 기존의 jar 파일을 삭제하고 새로 build를 한 후에 수정한 jar 파일을 다시 EC2서버에 복제한 후에 해당 jar파일을 실행했더니 성공적으로 프로그램이 가동되었다.

그리고 postman으로 진행했더니 정상적으로 경매 검색까지 진행할 수 있었다.