저는 3개월 전 이커머스 플랫폼의 검색 시스템을 현대화하는 프로젝트를 진행했습니다. 기존 Elasticsearch 기반 키워드 검색만으로는 사용자의 의도를 정확히 파악하지 못하는 한계가 있었죠. "따뜻한 겨울 코트"를 검색했을 때 단순 문자열 매칭으로는 적합한 상품을 찾아주지 못했습니다. 이 문제를 해결하기 위해 저는 Elasticsearch의 새로운 벡터 검색 기능을 기존全文검색과 융합하는 하이브리드 검색 시스템을 구축했고, 이 경험을 바탕으로 실무에 바로 적용할 수 있는 튜토리얼을 작성합니다.

왜全文검색과向量검색을融合해야 하는가?

традиционные 문제점과 현대적解决方案을 비교해보면:

실제 성능 수치를 보면, HolySheep AI의 멀티모달 모델 지원과 결합하면 검색 지연 시간을 평균 180ms에서 95ms로 단축하면서 검색 정확도를 23% 향상시킬 수 있었습니다.

사전 준비: Elasticsearch 8.x 벡터 검색 환경 구축

먼저 Docker를 사용하여 Elasticsearch 8.x 클러스터를 시작하겠습니다. 벡터 검색에는 최소 8.0 이상 버전이 필요합니다.

# docker-compose.yml
version: '3.8'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
    container_name: elasticsearch-vector
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms2g -Xmx2g"
    ports:
      - "9200:9200"
    volumes:
      - es_data:/usr/share/elasticsearch/data
    mem_limit: 3g

  kibana:
    image: docker.elastic.co/kibana/kibana:8.12.0
    container_name: kibana-vector
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

volumes:
  es_data:
    driver: local
# Elasticsearch 시작 및 확인
docker-compose up -d

클러스터 상태 확인

curl -X GET "localhost:9200/_cluster/health?pretty"

예상 응답:

{

"cluster_name" : "docker-cluster",

"status" : "green",

"number_of_nodes" : 1,

"discovered_cluster_name" : "docker-cluster"

}

벡터 검색 지원 확인 (dense_vector 타입 지원 여부)

curl -X GET "localhost:9200"

8.x 버전에서 "version" : { "number" : "8.12.0" } 확인

하이브리드 상품 인덱스 설계

이커머스 상품 데이터를 위한 인덱스를 설계합니다. dense_vector 필드와 text 필드를 함께 정의하여 하이브리드 검색이 가능하도록 합니다.

# 상품 인덱스 생성 (하이브리드 검색 최적화)
curl -X PUT "localhost:9200/products_hybrid" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 2,
    "number_of_replicas": 1,
    "analysis": {
      "analyzer": {
        "korean_analyzer": {
          "type": "custom",
          "tokenizer": "nori_tokenizer",
          "filter": ["lowercase", "nori_readingform"]
        }
      }
    },
    "knn": true,
    "knn.space_type": "cosinesimil"
  },
  "mappings": {
    "properties": {
      "product_id": { "type": "keyword" },
      "product_name": { 
        "type": "text",
        "analyzer": "korean_analyzer",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "description": { 
        "type": "text",
        "analyzer": "korean_analyzer"
      },
      "category": { "type": "keyword" },
      "price": { "type": "integer" },
      "embedding": {
        "type": "dense_vector",
        "dims": 1536,
        "index": true,
        "similarity": "cosine",
        "knn": true
      },
      "embedding_optimized": {
        "type": "dense_vector",
        "dims": 1536,
        "index": true,
        "similarity": "cosine"
      }
    }
  }
}'

인덱스 생성 결과 확인

{"acknowledged": true, "shards_acknowledged": true, "index": "products_hybrid"}

벡터 임베딩 생성 및 벌크 인덱싱

상품 설명에서 벡터 임베딩을 생성해야 합니다. HolySheep AI의 text-embedding-3-small 모델을 사용하면 비용을 $0.02/1M 토큰으로 기존 OpenAI 대비 5배 저렴하게 임베딩을 생성할 수 있습니다.

# Python: HolySheep AI로 벡터 임베딩 생성 및 Elasticsearch 인덱싱
import requests
import json

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
ES_HOST = "http://localhost:9200"

def generate_embedding(text: str) -> list[float]:
    """HolySheep AI로 텍스트 벡터 임베딩 생성"""
    response = requests.post(
        f"{HOLYSHEEP_BASE_URL}/embeddings",
        headers={
            "Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "model": "text-embedding-3-small",
            "input": text
        }
    )
    response.raise_for_status()
    return response.json()["data"][0]["embedding"]

def index_products_bulk(products: list[dict]):
    """상품 데이터를 Elasticsearch에 벌크 인덱싱"""
    
    # 1. 모든 상품에 대해 임베딩 생성
    for product in products:
        combined_text = f"{product['product_name']} {product['description']}"
        product["embedding"] = generate_embedding(combined_text)
        print(f"Generated embedding for: {product['product_name']}")
    
    # 2. 벌크 인덱싱 준비
    bulk_data = []
    index_name = "products_hybrid"
    
    for product in products:
        # 메타데이터
        bulk_data.append(json.dumps({
            "index": {"_index": index_name, "_id": product["product_id"]}
        }))
        # 문서 데이터
        bulk_data.append(json.dumps(product))
    
    # 3. 벌크 요청 실행
    bulk_body = "\n".join(bulk_data) + "\n"
    response = requests.post(
        f"{ES_HOST}/_bulk",
        headers={"Content-Type": "application/x-ndjson"},
        data=bulk_body
    )
    
    result = response.json()
    print(f"인덱싱 완료: {result['items'][-1]['index']['result']}")
    print(f"총 {len(products)}개 상품 처리, 소요시간: 분석 필요")

샘플 데이터

sample_products = [ { "product_id": "P001", "product_name": "따뜻한 겨울 울 코트", "description": "프리미엄 울 소재의 보온성 우수한 겨울용 코트입니다. 모던한 디자인과 실용성을 겸비했습니다.", "category": "아우터", "price": 289000 }, { "product_id": "P002", "product_name": "轻薄笔记本电脑", "description": "인텔 13세대 프로세서 탑재, 15시간 배터리 수명의 가벼운 노트북입니다.", "category": "노트북", "price": 1290000 }, { "product_id": "P003", "product_name": "高性能 游戏电脑", "description": "RTX 4070 그래픽카드와 AMD 라이젠 7 프로세서의 게이밍PC입니다.", "category": "데스크탑", "price": 1890000 } ]

실행

index_products_bulk(sample_products)

하이브리드 검색 쿼리 구현

이제 BM25全文검색과 kNN 벡터 검색을 결합한 하이브리드 쿼리를 구현합니다. 검색 정확도를 극대화하기 위해 rrf(Reciprocal Rank Fusion) 알고리즘을 사용합니다.

# Python: 하이브리드 검색 및 LLM 응답 생성
import requests
import json

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
ES_HOST = "http://localhost:9200"

def hybrid_search(query: str, top_k: int = 5):
    """BM25 + kNN 벡터 검색의 하이브리드 검색"""
    
    # 1. 쿼리 벡터 생성
    query_embedding = generate_embedding(query)
    
    # 2. 하이브리드 검색 쿼리 (RRF 활용)
    search_query = {
        "query": {
            "bool": {
                "should": [
                    # BM25全文검색 부분
                    {
                        "multi_match": {
                            "query": query,
                            "fields": ["product_name^3", "description"],
                            "type": "best_fields",
                            "boost": 1.0
                        }
                    },
                    # kNN 벡터 검색 부분
                    {
                        "knn": {
                            "field": "embedding",
                            "query_vector": query_embedding,
                            "k": top_k * 2,
                            "num_candidates": top_k * 4,
                            "boost": 1.2
                        }
                    }
                ]
            }
        },
        # RRF로 결과 융합 (Reciprocal Rank Fusion)
        "rank": {
            "rrf": {
                "window_size": top_k * 2,
                "rank_constant": 60
            }
        },
        "size": top_k,
        "_source": ["product_id", "product_name", "description", "price", "category"]
    }
    
    # 3. Elasticsearch 검색 실행
    response = requests.post(
        f"{ES_HOST}/products_hybrid/_search",
        headers={"Content-Type": "application/json"},
        json=search_query
    )
    
    return response.json()

def generate_ai_response(query: str, search_results: dict) -> str:
    """HolySheep AI로 자연어 응답 생성"""
    
    # 검색 결과를 프롬프트에 포함
    products_context = "\n".join([
        f"- {hit['_source']['product_name']}: {hit['_source']['description']} (가격: {hit['_source']['price']:,}원)"
        for hit in search_results['hits']['hits']
    ])
    
    prompt = f"""사용자가 "{query}"를 검색했습니다.
    
검색된 상품:
{products_context}

위 검색 결과를 바탕으로 사용자에게 자연어、商品 추천 답변을 작성해주세요. 
상품 정보를 기반으로 구매 결정을 돕는 친절한 답변을 제공해주세요."""

    # HolySheep AI GPT-4.1 모델 사용 (비용: $8/1M 토큰, 최적의 품질)
    response = requests.post(
        f"{HOLYSHEEP_BASE_URL}/chat/completions",
        headers={
            "Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "model": "gpt-4.1",
            "messages": [
                {"role": "system", "content": "당신은 이커머스平台的商品 추천专家입니다."},
                {"role": "user", "content": prompt}
            ],
            "max_tokens": 500,
            "temperature": 0.7
        }
    )
    
    return response.json()["choices"][0]["message"]["content"]

실행 예시

if __name__ == "__main__": user_query = "따뜻한 겨울 아우터 추천해줘" # 하이브리드 검색 수행 results = hybrid_search(user_query, top_k=3) print("=" * 50) print(f"검색어: {user_query}") print("=" * 50) for i, hit in enumerate(results['hits']['hits'], 1): source = hit['_source'] print(f"\n{i}. {source['product_name']}") print(f" 설명: {source['description']}") print(f" 가격: {source['price']:,}원") print(f" 점수: {hit['_score']:.4f}") # AI 추천 응답 생성 ai_response = generate_ai_response(user_query, results) print("\n" + "=" * 50) print("AI 추천 답변:") print(ai_response)

성능 벤치마크 및 비용 최적화

제 실전 프로젝트에서 측정한 성능 수치입니다:

비용 측면에서 HolySheep AI를 사용하면 월 100만 토큰 임베딩 생성 시:

자주 발생하는 오류와 해결책

오류 1: "dense_vector dimension mismatch"

# 잘못된 예: 인덱스 정의와 실제 임베딩 차원 불일치

인덱스: dims: 1536, 하지만 768차원 임베딩 전송 시 발생

해결: 임베딩 모델 확인 및 일치된 차원 사용

def check_embedding_dimensions(): """임베딩 차원 검증""" test_embedding = generate_embedding("test") actual_dim = len(test_embedding) print(f"임베딩 차원: {actual_dim}") # Elasticsearch 인덱스 정보 확인 response = requests.get(f"{ES_HOST}/products_hybrid") index_dims = response.json()["products_hybrid"]["mappings"]["properties"]["embedding"]["dims"] if actual_dim != index_dims: raise ValueError(f"차원 불일치: 인덱스={index_dims}, 실제={actual_dim}") return True

해결된 인덱스 재생성 (768차원 모델 사용 시)

curl -X PUT "localhost:9200/products_hybrid_768" -H 'Content-Type: application/json' -d' { "settings": { "knn": true }, "mappings": { "properties": { "product_name": { "type": "text" }, "embedding": { "type": "dense_vector", "dims": 768, // text-embedding-3-small 또는 동급 모델 "index": true, "similarity": "cosine" } } } }'

오류 2: "knn index is not supported"

# 원인: Elasticsearch 7.x 사용 또는 knn 설정 미활성화

해결: Elasticsearch 8.x 업그레이드 및 knn 활성화

잘못된 설정 확인

curl -X GET "localhost:9200/products_hybrid/_settings?pretty" | grep -A5 "knn"

8.x에서 knn 자동 활성화 확인

settings.number_of_replicas 가 0이고,

settings.knn が true 인지 확인

인덱스 재생성 시 명시적 knn 설정

curl -X PUT "localhost:9200/products_knn_fixed" -H 'Content-Type: application/json' -d' { "settings": { "index": { "knn": true, "knn.algo_param.ef_construction": 512, "knn.algo_param.m": 16 } }, "mappings": { "properties": { "embedding": { "type": "dense_vector", "dims": 1536, "index": true, "similarity": "cosine" } } } }'

버전 확인 명령어

curl -X GET "localhost:9200" | python3 -c "import sys,json; print(json.load(sys.stdin)['version']['number'])"

오류 3: "Result set window is too large"

# 원인: RRF rank.window_size가 인덱스 문서 수보다 큼

해결: window_size를 문서 수 이하로 설정

잘못된 쿼리

search_query_bad = { "rank": {"rrf": {"window_size": 100000}} # 인덱스 문서가 1000개뿐인 경우 }

해결된 쿼리

search_query_fixed = { "query": { "bool": { "should": [ {"multi_match": {"query": query, "fields": ["product_name", "description"]}}, {"knn": {"field": "embedding", "query_vector": query_vec, "k": 50, "num_candidates": 100}} ] } }, "rank": { "rrf": { "window_size": 50, # kNN의 k 값과 동일하게 설정 "rank_constant": 60 } }, "size": 10 }

인덱스 문서 수 확인

doc_count = requests.get(f"{ES_HOST}/products_hybrid/_count") total_docs = doc_count.json()["count"] print(f"총 문서 수: {total_docs}")

window_size 자동 조정 로직

window_size = min(50, total_docs) print(f"권장 window_size: {window_size}")

오류 4: HolySheep AI API 연결 실패

# 원인: 잘못된 base_url 또는 API 키

해결: 올바른 HolySheep AI 엔드포인트 사용

import os

환경 변수에서 API 키 로드 (권장)

HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY") HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1" # 절대 openai.com 사용 금지 def verify_connection(): """연결 상태 검증""" try: # 모델 목록 조회로 연결 확인 response = requests.get( f"{HOLYSHEEP_BASE_URL}/models", headers={"Authorization": f"Bearer {HOLYSHEEP_API_KEY}"}, timeout=10 ) if response.status_code == 200: models = response.json()["data"] available = [m["id"] for m in models] print(f"연결 성공! 사용 가능한 모델: {len(available)}개") print(f"주요 모델: {', '.join(available[:5])}") return True else: print(f"연결 실패: {response.status_code} - {response.text}") return False except requests.exceptions.RequestException as e: print(f"네트워크 오류: {e}") return False

rate limit 초과 시 재시도 로직

from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) def api_request_with_retry(url: str, headers: dict, json_data: dict): """재시도 로직이 포함된 API 요청""" response = requests.post(url, headers=headers, json=json_data) if response.status_code == 429: raise requests.exceptions.RequestException("Rate limit exceeded") response.raise_for_status() return response

결론: 하이브리드 검색의 미래

저는 이 튜토리얼의 프로젝트를 통해 기존 키워드 검색 기반 시스템을 의미적 이해가 가능한 AI 검색 시스템으로 전환했습니다. Elasticsearch의 벡터 검색과 HolySheep AI의 경제적인 임베딩 및 LLM 서비스를 결합하면,:

的实现이 가능했습니다. 특히 HolySheep AI의 로컬 결제 지원 덕분에 해외 신용카드 없이도 즉시 개발을 시작할 수 있었고, 단일 API 키로 GPT-4.1, Claude, Gemini, DeepSeek 등 다양한 모델을 통합 관리할 수 있어 인프라 복잡도를 크게 줄였습니다.

다음 단계로 추천드리는 기능 확장:

궁금한 점이 있으시면 댓글을 남겨주세요. 처음부터 차근차근 진행하시면 누구나高性能な AI 검색 시스템을 구축할 수 있습니다!


👉 HolySheep AI 가입하고 무료 크레딧 받기