이 글은 누구를 위한 것인가
- 패션 상품 반품의 40-60%가 사이즈 불만족인 팀
- 브랜드마다 사이즈가 달라 고객 불만이 쌓이는 쇼핑몰
- 고객 신체 사이즈 데이터를 어떻게 활용할지 모르는 팀
들어가며
패션 이커머스 반품의 절반은 사이즈 때문이다. "보통 M인데 이 브랜드는 작아요" — 이 한 마디가 반품과 부정 리뷰로 이어진다. 정확한 사이즈 추천은 반품률을 30% 이상 낮출 수 있다.
이 글은 bluefoxdev.kr의 패션 이커머스 사이즈 시스템 를 참고하여 작성했습니다.
1. 사이즈 데이터 수집 전략
[신체 데이터 수집 방법]
직접 입력 (설정 페이지):
키, 몸무게, 가슴둘레, 허리둘레, 엉덩이둘레
평소 신는 신발 사이즈
선호 핏 (타이트/레귤러/루즈)
구매 이력 기반 추론:
구매 상품의 사이즈 + 반품 여부
"작음/큼" 리뷰 키워드 분석
교환 요청 패턴 (S→M: 작았음)
퀴즈/온보딩:
"평소 어떤 핏을 선호하세요?"
"이 상품과 비슷한 [기존 구매 상품]은 어떠셨나요?"
[브랜드 사이즈 정규화]
각 브랜드의 사이즈 스펙 수집
표준 신체 치수로 변환
"이 브랜드 M = 일반 S-M"으로 표시
2. 사이즈 추천 엔진
from dataclasses import dataclass
@dataclass
class UserBodyProfile:
user_id: str
height_cm: float
weight_kg: float
chest_cm: float | None
waist_cm: float | None
hip_cm: float | None
shoe_size_kr: int | None
fit_preference: str # 'tight', 'regular', 'loose'
@dataclass
class SizeRecommendation:
recommended_size: str
confidence: float
reason: str
size_guide: dict # 실측 사이즈 정보
fit_note: str # "이 브랜드는 작게 나옵니다"
async def recommend_size(
user_id: str,
product_id: str,
) -> SizeRecommendation:
profile = await get_user_body_profile(user_id)
product = await get_product_with_size_chart(product_id)
purchase_history = await get_size_purchase_history(user_id)
# 1. 신체 사이즈 기반 추천
body_recommendation = match_size_by_measurements(
profile=profile,
size_chart=product["size_chart"],
category=product["category"],
)
# 2. 구매 이력 기반 보정
history_adjustment = calculate_history_adjustment(
purchase_history=purchase_history,
brand=product["brand"],
category=product["category"],
)
# 3. 브랜드 사이즈 편차 적용
brand_offset = await get_brand_size_offset(product["brand"], product["category"])
final_size = apply_adjustments(
base_size=body_recommendation["size"],
history_adj=history_adjustment,
brand_offset=brand_offset,
fit_preference=profile.fit_preference,
)
confidence = calculate_confidence(
has_measurements=bool(profile.chest_cm),
history_count=len(purchase_history),
brand_data_quality=brand_offset["data_quality"],
)
return SizeRecommendation(
recommended_size=final_size,
confidence=confidence,
reason=build_reason_text(body_recommendation, history_adjustment, brand_offset),
size_guide=product["size_chart"][final_size],
fit_note=brand_offset.get("fit_note", ""),
)
def calculate_history_adjustment(
purchase_history: list[dict],
brand: str,
category: str,
) -> int:
"""과거 구매 데이터로 사이즈 조정값 계산"""
relevant = [
h for h in purchase_history
if h["category"] == category and not h["returned"]
]
if not relevant:
return 0
# 반품된 경우의 사이즈 방향 분석
returns_with_reason = [
h for h in purchase_history
if h["returned"] and h["return_reason"] in ("too_small", "too_large")
]
adjustments = []
for r in returns_with_reason:
if r["return_reason"] == "too_small":
adjustments.append(+1) # 한 사이즈 업
else:
adjustments.append(-1) # 한 사이즈 다운
if not adjustments:
return 0
return round(sum(adjustments) / len(adjustments))
3. 반품 데이터 피드백 루프
async def process_return_feedback(return_request: dict):
"""반품 사유를 사이즈 모델에 피드백"""
if return_request["reason"] not in ("too_small", "too_large", "size_issue"):
return
# 사이즈 이력 업데이트
await db.execute("""
UPDATE user_purchase_size_history
SET returned = true,
return_reason = $1,
return_size_feedback = $2
WHERE order_item_id = $3
""",
return_request["reason"],
return_request.get("comment"),
return_request["order_item_id"]
)
# 브랜드 사이즈 편차 통계 업데이트
await update_brand_size_stats(
brand=return_request["brand"],
category=return_request["category"],
purchased_size=return_request["purchased_size"],
return_reason=return_request["reason"],
)
# 사이즈 추천 정확도 메트릭 기록
was_recommended = await check_if_size_was_recommended(
user_id=return_request["user_id"],
order_item_id=return_request["order_item_id"],
)
if was_recommended:
await record_recommendation_miss(
user_id=return_request["user_id"],
product_id=return_request["product_id"],
)
마무리
사이즈 추천의 시작은 신체 데이터 수집이다. 퀴즈형 온보딩으로 키·몸무게만 받아도 첫 추천이 가능하다. 브랜드 사이즈 편차 데이터는 반품 데이터에서 자동으로 학습하면 된다. "이 브랜드는 작게 나옵니다" 문구 하나가 반품을 막는 가장 간단한 방법이다.