추천 시스템 설계: 협업 필터링·콘텐츠 기반·행렬 분해의 역할

협업 필터링, 콘텐츠 기반 추천, 행렬 분해를 후보 생성·재랭킹·서빙 흐름과 함께 정리한 추천 시스템 설계 가이드

2026-08-14 · 최초 발행 2025-10-14

추천은 모델 하나가 아니라 서빙까지 이어지는 구조다

추천 시스템은 사용자 행동과 아이템 속성에서 관심 후보를 만들고, 그 후보의 순서를 정하는 시스템이다. 학습은 오프라인에서 이뤄지고 요청은 온라인에서 처리되며, 대개 후보 생성(candidate generation)과 랭킹(ranking)을 분리한 흐름으로 구성된다.

협업 필터링, 콘텐츠 기반 추천, 행렬 분해는 이 흐름 안에서 각기 다른 역할을 맡는다. 데이터가 충분한 사용자에게는 상호작용 기반 신호가 강하고, 신규 사용자나 신규 아이템에는 콘텐츠 정보가 빈자리를 메운다.

상호작용과 콘텐츠에서 추천 신호를 찾는 방법

Collaborative Filtering

협업 필터링은 사용자-아이템 상호작용 행렬에서 유사한 사용자 또는 아이템을 찾아 추천한다. 메모리 기반 접근인 KNN과 ALS, BPR 같은 모델 기반 접근으로 나뉘며, 다른 사용자의 선택을 신호로 활용할 수 있다.

구현이 비교적 단순하고 도메인 제약이 적다는 장점이 있다. 반면 상호작용이 부족한 환경과 콜드 스타트에 민감하며, 희소한 데이터에서는 품질 관리가 어려워질 수 있다.

Content-Based Filtering

콘텐츠 기반 추천은 텍스트, 카테고리, 임베딩 같은 아이템·사용자 특성을 벡터로 만들고 유사도를 계산한다. 신규 아이템이나 사용자에 대응하기 좋고, 추천 이유를 설명하기도 비교적 쉽다.

다만 사용자가 이미 선호한 범위만 반복적으로 노출하는 과전문화(over-specialization)가 생길 수 있다. 결과 품질은 특징공학과 콘텐츠 품질에 크게 좌우된다.

Matrix Factorization

행렬 분해는 사용자와 아이템을 잠재 요인(latent factors) 공간으로 옮겨 내적 유사도를 학습하는 모델 기반 방식이다. SVD, ALS(implicit), BPR-MF가 대표적이며, 대규모 희소 행렬에서도 확장성을 확보할 수 있다.

성능과 확장성의 균형이 좋아 후보 생성에 적합하다. 설명가능성은 상대적으로 낮고 실시간 업데이트에는 제약이 있다.

데이터부터 재랭킹까지 연결하기

추천 품질은 모델 입력을 얼마나 일관되게 다루는지에 영향을 받는다. 입력에는 뷰, 클릭, 장바구니, 구매 같은 이벤트 로그와 텍스트·카테고리·가격을 포함한 아이템 메타데이터, 언어·지역 같은 사용자 속성이 들어간다.

스트리밍 수집에는 Kafka/Kinesis, 변환 계층에는 ETL/ELT(Lakehouse), 피처 관리에는 Feature Store를 사용할 수 있다. 이 과정에서 이벤트 중복과 지연 도착을 처리해야 한다.

상호작용 가중치는 implicit feedback의 뷰<클릭<구매 관계와 시간감쇠(time decay)를 고려해 설계한다. 콘텐츠는 TF-IDF/BM25, 카테고리 멀티핫, 이미지·텍스트 임베딩으로 표현할 수 있으며 정규화와 누락값 처리가 필요하다.

후보 생성에는 MF/ANN을 사용하고, 재랭킹에는 GBDT 또는 신경망과 비즈니스 규칙을 함께 적용한다. 탐색과 활용의 균형은 ε-greedy나 Thompson Sampling으로 다루며, 다양성과 신선도 제약도 랭킹 조건에 포함할 수 있다.

재랭킹/서빙모델/후보생성처리/피처입력 수집요청신규 사용자/아이템기존타임아웃/모델 실패이벤트 로그(뷰/클릭/구매)아이템 메타데이터(텍스트/카테고리)사용자 속성(언어/지역/디바이스)ETL/정제중복 제거·지연 처리특징 엔지니어링가중치·시간감쇠·TF-IDFFeature StoreCollaborative Filtering(KNN/ALS/BPR)Content-Based(cosine/임베딩)Matrix Factorization(implicit ALS)재랭킹 모델(GBDT/NN + 규칙)서빙 API캐시·A/B·모니터링콜드 스타트?Fallback인기·최근·규칙

이벤트 처리에는 idempotency와 세션 결합 규칙이 필요하다. Feature Store는 TTL과 버전을 관리하고, 모델 스냅샷은 원자적으로 배포하며 롤백 경로를 확보한다. 캐시와 모델 버전도 함께 맞춰야 한다.

후보 생성의 p95 목표는 50150ms, 전체 서빙의 p95 목표는 100300ms이며 도메인별로 다르다.

알고리즘 선택에 영향을 주는 운영 조건

항목 Collaborative Filtering Content-Based Matrix Factorization
성능(정확도) 충분한 상호작용 시 우수, 지역적 인기 반영 강점 콘텐츠 품질 의존, 초반 안정 대규모에서 우수, 후보생성에 강함
확장성 KNN은 비용↑, ANN로 완화 벡터 검색으로 선형 확장 분산 학습(ALS)로 수억 단위 확장
일관성(콜드 스타트) 신규에 취약 신규 아이템/사용자 대응 강점 사용자/아이템 양측 데이터 필요
안정성(노이즈) 클릭 스팸 민감, 정규화 필요 피처 품질 관리 시 안정 정규화·조기종료로 안정성 확보
운영 편의 구현 용이, 튜닝 다양 설명가능성 높음, 피처 관리 부담 파라미터/스케줄 관리 필요

전자상거래에서는 MF(implicit ALS)와 상위 카테고리 필터로 후보를 만들고, 가격·마진·재고·신선도를 GBDT 재랭킹 입력으로 사용할 수 있다. 신상품은 콘텐츠 기반 방식으로 노출하며, KPI는 CTR +1025%, CVR +515%, 재고 회전일수 감소로 제시된다.

미디어·스트리밍 환경에서는 최근 시청 임베딩과 CF를 섞고 시퀀스 기반 가중치와 장르 다양성 제약을 둔다. KPI는 시청시간 +8~20%, 세션당 콘텐츠 수 +10% 내외다.

뉴스·커뮤니티에는 신선도 가중치, 주제 다양성, 중복 방지 규칙과 민감 카테고리 안전장치가 필요하다. 재방문율 +512%, 구독 전환 +38%가 KPI로 제시된다.

마켓플레이스와 광고 슬롯에서는 MF 후보 생성 뒤 예산·빈도 제한, 실시간 클릭 확률 추정, 수익최적화(rCPM)를 이어 붙인다. KPI는 rCPM +5~15%, 광고주 이탈률 감소다.

콜드 스타트와 품질을 함께 다루는 하이브리드 구성

후보 생성은 MF(implicit)와 Content-Based를 함께 사용해 콜드 스타트에 대응하고, ANN(FAISS/ScaNN)으로 근사 최근접 탐색을 수행할 수 있다. 재랭킹에서는 클릭·구매 예측 모델에 diversity와 비즈니스 제약을 소프트 또는 하드 조건으로 적용한다.

가중 앙상블은 score = α·MF + β·CF + γ·CB처럼 구성하고, 사용자 세그먼트에 따라 α/β/γ를 조정한다. Meta-Model이 사용자 컨텍스트를 바탕으로 서브모델을 선택하는 게이팅 방식도 가능하다. ε-greedy, UCB, Thompson Sampling과 신상품·롱테일 가중치는 탐색 공간을 확보하는 데 쓴다.

캐시와 ANN을 도입할 때는 CTR +1030%, NDCG@10 +520%, CVR +312%, 객단가(AOV) +28%, 매출 +210%, 지표 p95 지연 2040% 절감이 정량 효과 범위로 제시된다. 사용자 만족도와 체류시간을 높이고 탐색 피로도를 줄이는 효과도 기대할 수 있다. 운영 자동화와 의사결정 신뢰도, 실험 문화에도 영향을 준다.

품질, 개인정보, 공정성을 운영 지표에 포함하기

이벤트 스키마를 표준화하고 봇·이상치를 필터링하며 지연을 보정해야 한다. 부정클릭 방지와 함께 샘플링·레이블 지연 편향은 딜레이 보정, IPS/DR 추정으로 완화하는 방안을 고려한다.

개인정보는 최소 수집을 원칙으로 하고 식별자 해싱·암호화, 접근 제어, 감사 로그를 적용한다. 지역 규제를 준수하며 모델·피처 저장소를 분리하고 비식별화 임베딩을 우선한다.

개인화가 과도해지면 편향과 버블이 생길 수 있다. 일일 카테고리 커버리지와 Gini 계수 같은 노출 다양성 메트릭을 관리하고 규칙 기반 캡핑을 적용한다.

배치 학습에 Click-Boost와 Real-time popularity 같은 스트리밍 보정을 더할 수 있다. 타임아웃에는 인기·최근·규칙 기반 Fallback을 두고 SLA에 따라 부하를 제어한다.

ALS와 콘텐츠 기반 Fallback 예시

전제조건: Python 3.10, pandas>=1.5, scipy>=1.10, scikit-learn>=1.2, implicit>=0.7

# pip install pandas scipy scikit-learn implicit
import numpy as np, pandas as pd
from scipy.sparse import coo_matrix
from implicit.als import AlternatingLeastSquares
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# 1) 입력 데이터(예시)
interactions = pd.DataFrame({
    "user_id": [1,1,2,2,3,3,3],
    "item_id": [10,11,10,12,11,12,13],
    "weight":  [1.0,2.0,1.0,1.0,1.0,2.0,1.0],  # implicit 가중치
})
items = pd.DataFrame({
    "item_id":[10,11,12,13],
    "title":["Running Shoes","Trail Shoes","Socks","Wind Jacket"],
    "desc":["lightweight running","mountain trail grip","cotton socks","windproof jacket for running"]
})

# 2) 행렬 구성
user_ids = interactions.user_id.astype("category")
item_ids = interactions.item_id.astype("category")
R = coo_matrix(
    (interactions.weight, (user_ids.cat.codes, item_ids.cat.codes)),
    shape=(user_ids.cat.categories.size, item_ids.cat.categories.size)
).tocsr()

# 3) MF(implicit ALS) 학습
als = AlternatingLeastSquares(factors=32, regularization=0.05, iterations=20, random_state=42)
als.fit(R.T)  # implicit 라이브러리는 아이템x사용자 선호

# 4) Content-Based 준비
tfidf = TfidfVectorizer(min_df=1, ngram_range=(1,2))
X = tfidf.fit_transform(items["desc"].fillna(""))

id2item = dict(enumerate(item_ids.cat.categories))
item2pos = {id_:pos for pos,id_ in id2item.items()}

def recommend(user_id:int, k:int=5):
    if user_id in user_ids.cat.categories:
        u = list(user_ids.cat.categories).index(user_id)
        recs = als.recommend(u, R, N=k, filter_already_liked_items=True)
        item_list = [id2item[i] for i,_ in recs]
        scores = [float(s) for _,s in recs]
        return pd.DataFrame({"item_id":item_list, "score":scores, "source":"ALS"})
    # Cold-start: 사용자 과거 없음 → 인기/콘텐츠 기반
    # 여기서는 콘텐츠 기반 유사 아이템 Top-N(인기 대체 가능)
    # 임의로 상위 TF-IDF 노름 큰 아이템 추천
    norms = np.asarray(X.power(2).sum(1)).ravel()
    topk = norms.argsort()[::-1][:k]
    item_list = [items.item_id.iloc[i] for i in topk]
    return pd.DataFrame({"item_id":item_list, "score":norms[topk], "source":"Content"})

print(recommend(1, k=3))   # 기존 사용자
print(recommend(999, k=3)) # 콜드 스타트 사용자

정기 재학습은 일 1~4회로 수행하고 실시간 보정 점수를 합산한다. ANN 인덱스는 주기적으로 리빌드하며, feature/view 버전을 고정하고 모델 배포의 원자성을 블루-그린 또는 카나리 방식으로 유지한다.

추천 시스템협업 필터링콘텐츠 기반 추천행렬 분해개인화