이 글은 누구를 위한 것인가
- 검색 결과 품질이 낮아 전환율이 떨어지는 커머스 플랫폼의 엔지니어
- "베스트셀러 순" 외에 더 스마트한 랭킹 로직을 도입하려는 팀
- 검색 개인화를 어디서부터 시작해야 할지 모르는 개발자
들어가며
커머스 검색은 두 가지 문제를 동시에 풀어야 한다: 관련성(relevance) 과 비즈니스 목표(conversion). 가장 관련성 높은 결과가 항상 구매 전환으로 이어지지 않고, 재고가 없는 상품을 1위에 노출해도 의미가 없다.
좋은 커머스 검색 랭킹은 텍스트 매칭 점수, 상품 품질 신호, 비즈니스 규칙, 개인화 요소를 가중합산해 결정된다.
1. 텍스트 랭킹: BM25 기반
Elasticsearch를 쓴다면 기본 랭킹 알고리즘은 BM25다. 단순 키워드 매칭보다 훨씬 정교하다.
필드별 가중치 설계
모든 필드가 동등하게 중요하지 않다. 상품명에서의 매칭이 설명 텍스트에서의 매칭보다 관련성이 높다.
{
"query": {
"multi_match": {
"query": "나이키 러닝화",
"fields": [
"name^10", // 상품명: 가중치 10배
"brand^8", // 브랜드명: 8배
"category^5", // 카테고리: 5배
"tags^3", // 태그: 3배
"description^1" // 설명: 기본
],
"type": "best_fields",
"fuzziness": "AUTO" // 오타 허용
}
}
}
한국어 형태소 분석
한국어 검색에서 형태소 분석기는 필수다. "러닝화"를 "러닝" + "화"로 분리하고, "나이키"를 브랜드 사전으로 인식해야 한다.
- Nori 분석기: Elasticsearch 공식 한국어 분석기, 형태소 분리 + 품사 필터링
- 커스텀 사전: 브랜드명, 상품 고유명사 추가 등록 필수
- 동의어 사전: "스니커즈 = 운동화 = 스니커", "노트북 = 랩탑" 매핑
2. 비즈니스 신호 통합: Function Score
텍스트 랭킹만으로는 부족하다. 판매 실적, 재고, 가격 경쟁력 등 비즈니스 신호를 점수에 반영해야 한다.
{
"query": {
"function_score": {
"query": { "multi_match": { ... } },
"functions": [
{
"filter": { "term": { "in_stock": true } },
"weight": 1.5
},
{
"field_value_factor": {
"field": "sales_30d",
"modifier": "log1p", // 로그 스케일로 극단값 완화
"factor": 0.1
}
},
{
"field_value_factor": {
"field": "review_score",
"factor": 0.5
}
},
{
"gauss": {
"price": {
"origin": 50000, // 사용자 예상 가격대 중심
"scale": 30000,
"decay": 0.5
}
}
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
신호별 역할
| 신호 | 효과 | 주의사항 |
|---|---|---|
| 재고 여부 | 품절 상품 하단 배치 | 재고 실시간성 중요 |
| 최근 30일 판매량 | 인기 상품 부스팅 | 신상품 불이익 주의 |
| 리뷰 점수 | 품질 신호 | Bayesian 평균 사용 |
| 가격 적합도 | 예산 맞는 상품 우선 | 개인화 연계 가능 |
| 판매자 신뢰도 | 우수 판매자 부스팅 | 오픈마켓 전용 |
3. 검색 의도 분류
같은 키워드라도 사용자 의도가 다를 수 있다. "나이키"는 브랜드 탐색일 수도 있고, "나이키 에어맥스"는 특정 제품 검색일 수 있다.
의도 유형
| 의도 | 예시 쿼리 | 최적 결과 유형 |
|---|---|---|
| 탐색 (Navigational) | "나이키", "ABC마트" | 브랜드/카테고리 페이지 |
| 정보 탐색 (Informational) | "러닝화 추천" | 큐레이션 리스트, 가이드 |
| 거래 (Transactional) | "나이키 에어맥스 270 260" | 정확한 상품 매칭 |
의도 분류는 쿼리 패턴 분석(숫자 포함 여부, 브랜드명 포함 여부, 형용사 포함 여부)으로 규칙 기반으로 시작할 수 있고, 데이터가 쌓이면 분류 모델로 발전시킨다.
4. 개인화: 행동 신호 수집과 활용
수집해야 할 행동 신호
| 신호 | 가중치 | 설명 |
|---|---|---|
| 구매 | 높음 | 가장 강력한 선호 신호 |
| 장바구니 담기 | 중상 | 구매 의향 |
| 상품 상세 조회 | 중 | 관심 표현 |
| 검색 후 클릭 | 중 | 검색 결과 관련성 |
| 카테고리 탐색 | 낮음 | 관심 카테고리 |
| 위시리스트 | 중 | 잠재 구매 의향 |
사용자 프로필 구성
interface UserProfile {
userId: string;
preferredCategories: Record<string, number>; // category_id -> weight
preferredBrands: Record<string, number>;
priceRange: { min: number; max: number };
recentlyViewed: string[]; // product_ids
purchaseHistory: string[]; // product_ids
}
개인화 적용 방법
1단계: 쿼리 시점 부스팅
{
"query": {
"function_score": {
"query": { "..." },
"functions": [
{
// 사용자가 선호하는 카테고리 상품 부스팅
"filter": {
"terms": { "category_id": ["user_preferred_categories"] }
},
"weight": 1.3
},
{
// 이미 구매한 상품은 하단 배치
"filter": {
"terms": { "id": ["user_purchased_product_ids"] }
},
"weight": 0.3
}
]
}
}
}
2단계: 협업 필터링
"나와 비슷한 사용자가 본/산 상품"을 결과에 반영한다. 콜드 스타트(신규 사용자) 문제는 인기 상품이나 카테고리 기본 랭킹으로 대체한다.
5. 쿼리 이해도 개선
자동완성과 오타 교정
사용자 입력: "나이클"
→ 오타 교정 제안: "나이키"를 말씀하시는 건가요?
→ 동시에 "나이클" 결과도 표시 (zero result 방지)
Zero Result 처리
검색 결과가 없는 경우 빈 화면보다 대안을 제시한다.
- 쿼리 완화: AND → OR 검색으로 전환
- 동의어 확장: 등록된 동의어로 재검색
- 카테고리 대체: "이 카테고리의 인기 상품"
- 관련 검색어 제안
6. 검색 품질 측정
오프라인 지표
| 지표 | 의미 |
|---|---|
| NDCG@10 | 상위 10개 결과의 관련성 순서 품질 |
| MRR (Mean Reciprocal Rank) | 첫 번째 관련 결과의 평균 위치 |
| Precision@K | 상위 K개 중 관련 결과 비율 |
온라인 지표 (A/B 테스트)
| 지표 | 의미 |
|---|---|
| CTR (Click-Through Rate) | 검색 결과 클릭률 |
| CVR (Conversion Rate) | 검색 → 구매 전환율 |
| Zero-click rate | 아무것도 클릭하지 않고 이탈 |
| SERP Abandonment | 검색 결과 페이지에서 이탈 |
맺으며
커머스 검색 랭킹 개선은 큰 폭의 전환율 향상으로 직결된다. BM25 + 한국어 형태소 분석기 세팅부터 시작하고, 판매량·재고·리뷰 점수를 Function Score로 통합하면 즉각적인 품질 개선을 볼 수 있다.
개인화는 행동 로그 수집 파이프라인을 먼저 구축하고, 데이터가 쌓인 후 단계적으로 도입하는 것이 현실적이다. 모든 변경은 A/B 테스트로 검증해야 한다 — 직관적으로 좋아 보이는 랭킹 변경이 실제 전환율을 낮추는 경우가 적지 않다.