기술 이야기
home
Programming
home

실시간 유저 액션 데이터와 VectorDB를 활용한 개인화 추천 시스템

Upload date
2025/05/12
Tag
추천
AI
개인화
VectorDB
실시간
데이터
Editor
추천팀_이성규
4 more properties

0. 들어가며

안녕하세요. 추천팀 AI Engineer 이성규입니다.
저희 추천팀은 유저분들이 게임에 더욱 몰입하고 지속적으로 플레이할 수 있도록, 개인 맞춤형 상품 및 게임 콘텐츠를 추천하는 시스템을 개발하고 운영하고 있습니다.
최근 추천 시스템은 단순한 룰 기반이나 협업 필터링 방식의 한계를 넘어서, 유저의 실시간 행동 데이터와 콘텐츠 특성을 정밀하게 반영한 방향으로 진화하고 있습니다.
이러한 흐름 속에서 Vector Database(이하 VectorDB)는 유사도 기반 검색과 콘텐츠 기반 필터링 추천의 핵심 인프라로 주목받고 있습니다.
추천팀 역시 인게임에서의 기존 추천 시스템의 한계를 극복하고자 VectorDB를 도입했으며, 이를 실시간 유저 액션 데이터와 결합하여 보다 정교한 개인화 추천 시스템을 구현했습니다.
그 결과, 실시간 유저 액션 데이터와 VectorDB를 활용한 개인화 추천 시스템이 지난 3월 오픈한 퍼스트 디센던트의 상품 추천에 적용되었습니다. (2025.03.13 오픈)
그림 1: 퍼스트 디센던트 상점 내 상품 추천 화면

1. 인게임에서 기존 협업 필터링&모델 기반 추천 시스템의 한계

추천 시스템은 크게 세 가지 방식으로 나뉩니다. 아이템 자체의 속성을 바탕으로 유사 아이템을 추천하는 콘텐츠 기반 필터링(Content-based Filtering), 유저와 아이템 간의 상호작용 데이터를 활용하는 협업 필터링(Collaborative Filtering), 그리고 학습된 모델을 통해 추천을 수행하는 모델 기반 방식입니다.
이러한 방식들은 유저의 취향이나 콘텐츠 특성을 반영해 개인화된 추천을 제공할 수 있다는 강점을 갖고 있으며, 일반적인 플랫폼 환경에서는 높은 효율을 보입니다.
하지만 게임 인게임 환경에서는 이 방식들이 구조적인 제약에 부딪히는 경우가 많습니다. 특히 협업 필터링과 모델 기반 방식은 실시간 변화가 잦고 유저 풀이 유동적인 게임 특성상, 적용과 유지에 있어 뚜렷한 한계를 드러냅니다.

Cold Start: 데이터가 부족할 때 생기는 추천의 한계

기존에 운영 중인 게임이라면 어느 정도의 상호작용 데이터가 존재하지만, 최근에는 출시를 앞둔 신규 게임에서도 추천 시스템 도입에 대한 수요가 빠르게 증가하고 있습니다.
하지만 신규 게임은 유저나 아이템에 대한 데이터가 거의 없거나 부족한 상태이기 때문에, 기대하는 수준의 추천이 제대로 이루어지기 어렵습니다. 이를 Cold Start 문제라고 부릅니다.
이 문제는 운영 중인 게임에서도 신규 유저 유입이나 신규 아이템 추가와 같은 이벤트가 발생할 때 다시 등장할 수 있습니다.
신규 유저 문제(User Cold Start): 새로 유입된 유저에 대한 행동 데이터가 부족해, 기존 데이터를 활용한 정밀한 개인화 추천이 어렵습니다.
신규 아이템 문제(Item Cold Start): 새롭게 추가된 콘텐츠나 아이템에 대한 클릭, 검색, 구매 등의 피드백 데이터가 없어, 시스템이 이를 제대로 추천하지 못합니다.
Cold Start란?
추천 시스템이 적절한 추천을 수행하기 위해 필요한 과거 데이터(클릭, 검색, 구매 등)가 부족하거나 존재하지 않아, 추천 품질이 현저히 저하되는 상황을 말합니다. 즉, 시스템이 아직 "차가운 상태"로 작동한다는 의미입니다.

지속적인 인게임 업데이트로 인한 대응의 어려움

게임은 콘텐츠가 자주 추가되고 변경되는 빠르게 변화하는 환경입니다. 그러나 기존의 협업 필터링 방식과 모델 기반 방식은 배치 처리 기반으로 설계되어 있어, 이런 변화에 민첩하게 대응하기 어렵습니다.
이들 방식은 보통 주기적으로 데이터를 수집하고, 일정 기간마다 모델을 학습시키는 구조로 운영됩니다. 이 때문에 다음과 같은 문제가 발생합니다.
업데이트 직후, 추천 모델의 정확도가 일시적으로 낮아짐
새로 추가된 아이템이 추천에 반영되기까지 시간이 지연됨
반복적인 수작업 대응으로 인해 운영 리소스 증가
즉, 실시간성이 중요한 게임 환경과는 구조적으로 맞지 않는 부분이 존재합니다.

게임 간 범용 적용의 어려움

협업 필터링 및 모델 기반 추천 시스템은 각 게임의 콘텐츠 구성, 상호작용 방식, 비즈니스 구조에 맞춰 개별 최적화가 필요합니다. 이로 인해 다음과 같은 확장성 문제도 발생합니다.
게임이 새로 추가될 때마다 별도의 모델 설계와 개발 리소스가 필요
추천 알고리즘의 공통 모듈화 및 재사용이 어려움
추천 품질이나 운영 성과를 통일된 기준으로 관리하기 어려움
결과적으로, 기존 추천 시스템은 확장성과 유지 효율성 측면에서 비효율적이며, 조직 차원의 서비스 스케일링에 큰 제약을 줍니다.
정리하자면 인게임 환경에서 기존 추천 시스템(협업 필터링, 모델 기반 방식)은
Cold Start
잦은 콘텐츠 업데이트에 대한 민감도
게임 간 범용 적용의 한계
와 같은 구조적 제약을 갖고 있습니다.
이러한 한계를 극복하기 위해, VectorDB를 기반으로 실시간 유저 액션 데이터와 메타데이터를 벡터화하여, 벡터 유사도에 기반한 개인화 추천 시스템이 도입되었습니다.

2. 실시간 유저 액션 데이터와 VectorDB 기반의 개인화 추천 시스템

이 방식을 추천 시스템에 적용하면, 앞서 언급한 기존 방식의 주요 한계를 상당 부분 해소할 수 있습니다.

Cold-Start 해결

VectorDB는 아이템이나 유저 데이터를 의미 기반의 벡터로 변환해 저장함으로써, 기존에 상호작용 이력이 없는 상황에서도 의미적으로 유사한 콘텐츠를 추천할 수 있는 기반을 제공합니다.
표현 방식이 다르더라도 유사한 의미를 가진 콘텐츠 간의 연결이 가능하며, 유저와 아이템 간 데이터가 부족한 신규 게임, 신규 유저, 신규 아이템 상황에서도 추천이 가능합니다. 이를 통해 추천 시스템의 가장 대표적인 문제 중 하나인 Cold Start를 유연하게 극복할 수 있습니다.

빠른 업데이트 대응력

콘텐츠 메타데이터를 임베딩한 후 벡터 기반으로 유사도를 계산하는 구조를 채택하고 있어, 별도의 학습 과정 없이도 새롭게 추가된 아이템을 실시간으로 추천에 반영할 수 있습니다.
즉, 업데이트가 자주 발생하는 게임 환경에서도 즉시 추천 품질을 유지할 수 있으며, 동시에 추천 이유에 대한 설명 가능성까지 확보할 수 있습니다.
최근에 구매한 상품과 유사한 상품입니다.

추천 시나리오 확장성 확보

실시간 유저 행동 데이터를 기반으로 다양한 추천 시나리오를 구성할 수 있어, 게임별 특성에 맞춘 커스터마이징은 물론, 다양한 장르와 서비스 환경에 빠르게 적용할 수 있는 확장성이 강점입니다.
최근 {클릭|검색|구매|장착|…}한 {아이템|카테고리|…}과 유사한 아이템 추천

실시간 유저 반응을 반영한 개인화 추천

실시간으로 수집되는 유저의 액션 데이터(클릭, 검색, 구매, 장착 등)를 기반으로, 유저의 최신 선호와 맥락에 맞는 아이템을 동적으로 추천합니다. 정적인 추천이 아닌, 유저의 현재 상태에 최적화된 개인화 경험을 제공함으로써 추천의 품질과 몰입감을 동시에 높입니다.

3. Architecture

그림 2: 실시간 유저 액션 데이터와 VectorDB를 활용한 개인화 추천 시스템의 Architecture
퍼스트 디센던트에서 상점 추천이 이루어지는 전체적인 아키텍처 흐름은 아래와 같이 구성됩니다.
유저 요청 발생 (Request)
유저가 인게임 상점의 추천 배너에 접근하면, 추천 API로 요청이 발생합니다. 이 시점에서 추천 API는 유저의 최근 행동 데이터를 기반으로 추천할 상품을 결정하기 위한 준비를 시작합니다.
유저 액션 데이터 조회
추천 API는 MySQL에 저장된 유저의 액션 데이터를 조회합니다.
이 데이터는 Flink Cluster에서 실시간으로 수집된 유저의 액션 로그(클릭, 검색, 구매, 장착 등)를 기반으로 합니다.
최근에 어떤 상품을 클릭하거나 구매했는지와 같은 행동 데이터가 조회되어 추천의 출발점이 됩니다.
벡터 유사도 검색
추천 API는 조회한 유저 행동 데이터의 벡터 데이터를 VectorDB에서 검색합니다.
VectorDB는 벡터 공간에서 상품 간의 유사도를 계산하여, 유사한 상품들을 유사도 순서대로 정렬해 제공합니다.
이때 벡터 데이터는 상점 메타 데이터가 변경될 때마다 Lambda에서 임베딩 모델을 통해 업데이트되며, 새로운 상품도 동일한 프로세스로 벡터화됩니다.
유사도 검색은 유저가 최근에 클릭하거나 구매한 상품을 기준으로 이루어집니다.
Re-ranking 및 응답(Response)
VectorDB에서 가져온 유사한 상품 리스트는 그대로 유저에게 노출되지 않습니다.
추천 API는 유저가 이미 보유한 상품, 구매 제한이 걸린 상품 등을 필터링하는 비즈니스 로직 및 유효성 검사를 수행합니다.
최종적으로 필터링된 리스트는 유저의 맥락에 맞게 재정렬(Re-ranking) 되며, 추천 결과가 응답(Response)으로 전달됩니다.
서비스 관점에서 아키텍처는 OfflineOnline 두 가지 흐름으로 나뉩니다. 각 구성은 서로 보완적인 역할을 하며, 정적인 콘텐츠 변화와 동적인 유저 행동 변화에 유연하게 대응하도록 설계되었습니다.
Offline: 정기적이거나 이벤트성으로 발생하는 게임 콘텐츠(메타 데이터)의 변경에 대응
Online: 서비스 환경에서 유저의 실시간 행동을 반영하여 개인화된 추천을 제공
이러한 Offline-Online 이중 구조를 통해, 추천 시스템은 콘텐츠 변화와 유저 행동 변화에 유연하게 대응할 수 있으며, 실시간성과 최신성을 갖춘 추천 서비스를 제공합니다.
이제 아키텍처를 각 구성 요소별로 나누어 자세히 살펴보겠습니다.
※ 각 아키텍처 단계에 포함된 예시 코드와 데이터는 실제 서비스의 구현과 다를 수 있으며, 이해를 돕기 위한 참고용 예시임을 미리 안내드립니다.

3-1. Offline: 메타데이터 임베딩 & 벡터 테이블 적재

그림 3: 메타데이터 임베딩 & 벡터 테이블 적재 Part
게임의 패치나 신규 콘텐츠 업데이트로 메타데이터가 변경되면, 해당 데이터는 임베딩 모델(Embedding Model)을 통해 벡터로 변환됩니다. 이렇게 생성된 벡터는 VectorDB에 테이블 형태로 적재되며, 이후 유사도 검색의 기준 데이터로 활용됩니다.
정기 업데이트뿐만 아니라, Hotfix와 같은 비정기 업데이트에도 빠르게 대응하기 위해 Lambda를 통해 이벤트 트리거 기반의 메타데이터 임베딩 및 벡터 테이블 적재 파이프라인을 구축하였습니다. 이를 통해 메타 데이터 업데이트가 즉각적으로 반영될 수 있으며, 최신 콘텐츠가 실시간에 가깝게 추천 시스템에 반영됩니다.

임베딩 모델

임베딩 모델은 이미지의 색상이나 텍스트의 의미 등 다양한 데이터를 수치화하여 벡터 형태로 변환합니다. 벡터는 고차원 데이터를 압축하면서도 주요 특징을 보존할 수 있어, 데이터 처리 효율성과 검색 속도를 크게 향상시킵니다.
벡터는 사전 학습된 임베딩 모델 또는 도메인 특화 모델을 통해 생성됩니다. 이를 통해 이미지, 텍스트, 유저 행동 로그 등 다양한 데이터 유형에서 벡터를 추출할 수 있습니다.
예를 들어, 텍스트 데이터를 벡터화할 때는 Huggingface의 intfloat/multilingual-e5-small과 같은 사전 학습된 텍스트 임베딩 모델을 활용할 수 있습니다. 이 모델은 다양한 언어의 텍스트를 벡터로 변환하는 데 최적화되어 있으며, 유사도 검색에서 활용하기 적합한 벡터를 생성합니다.
import torch.nn.functional as F from torch import Tensor from transformers import AutoTokenizer, AutoModel # Average Pooling def average_pool(last_hidden_states: Tensor, attention_mask: Tensor) -> Tensor: last_hidden = last_hidden_states.masked_fill(~attention_mask[..., None].bool(), 0.0) return last_hidden.sum(dim=1) / attention_mask.sum(dim=1)[..., None] # Embedding Model class TextEmbedder: def __init__(self, model_name='intfloat/multilingual-e5-small'): self.model = AutoModel.from_pretrained(model_name) self.tokenizer = AutoTokenizer.from_pretrained(model_name) def get_embedding(self, text): input_text = "query: " + text inputs = self.tokenizer(input_text, max_length=512, padding=True, truncation=True, return_tensors="pt") output = self.model(**inputs) embeddings = average_pool(output.last_hidden_state, inputs['attention_mask']) normalized_embeddings = F.normalize(embeddings, p=2, dim=1) return normalized_embeddings # Define Model model = TextEmbedder() # Text Embedding example_text = "추천팀 화이팅" vector = model.get_embedding(example_text) print(vector)
Python
복사

3-2. Online: 실시간 유저 액션 데이터 수집

그림 4: 실시간 유저 액션 데이터 수집 Part
서비스에서 정의된 클릭, 구매, 장착 등의 유저 액션 데이터는 실시간으로 스트리밍되어 DB에 유저 단위로 저장됩니다.
이렇게 적재된 유저 액션 데이터는 추천 API에서 조회한 후, VectorDB의 벡터 유사도 기반 검색을 통해 유사한 콘텐츠를 찾아 추천에 활용됩니다.
예를 들어, 특정 유저가 가장 최근에 클릭한 아이템을 조회하려면 아래와 같은 쿼리를 작성할 수 있습니다.
SELECT item_id FROM user_click_table WHERE user_id=1 ORDER BY updated_at DESC LIMIT 1;
SQL
복사

3-3. Online: 벡터 유사도 검색

그림 5: 벡터 유사도 검색 Part
유저 액션 데이터의 벡터는 VectorDB에서 조회되며, 벡터 유사도 검색을 통해 유사한 순서대로 상품이 정렬되어 반환됩니다.
VectorDB는 벡터 데이터를 별도의 변환 과정 없이 그대로 저장하고, 벡터 공간에서의 거리 연산(코사인 거리, 유클리디안 거리 등)을 통해 데이터 간의 관계를 계산합니다. 이를 기반으로 빠르고 정확한 검색 결과를 제공하는 데 최적화된 인덱싱 방식을 사용하며, 이러한 구조는 전통적인 관계형 데이터베이스(RDB)와 달리 대규모 비정형 데이터의 벡터 연산에 특화되어 있습니다.
VectorDB로 활용할 수 있는 서비스에는 Milvus, Weaviate, Pinecone, PostgreSQL, Opensearch 등이 있으며, 각 서비스는 운영 편의성, 러닝 커브, 사용 기능 범위, 데이터 크기, 비용 측면에서 차별화된 특징을 가집니다.
저희 팀은 운영의 편의성, 기존 인프라와의 연계성, 벡터 데이터의 크기, 비용 등을 종합적으로 고려하여 PostgreSQL을 VectorDB로 선정했습니다.
PostgreSQL
PostgreSQL은 기본적으로 관계형 데이터베이스이지만, 확장 기능인 pgvector라는 확장 기능을 통해 벡터 데이터를 효율적으로 저장하고 유사도 검색까지 수행할 수 있습니다.
추천팀은 다음과 같은 실질적인 강점에 기반하여 PostgreSQL을 VectorDB로 채택하였습니다.
첫째, 표준 SQL을 그대로 활용할 수 있다는 점입니다. 벡터 유사도 연산과 메타데이터 필터링을 결합한 복합 쿼리를 작성할 수 있어, 쿼리 작성과 운영이 직관적입니다.
둘째, ORM(Object Relational Mapping)과의 연동이 용이하다는 점입니다. SQLAlchemy 같은 ORM과 쉽게 통합할 수 있어 기존 애플리케이션 환경에 손쉽게 연결할 수 있으며, 전용 VectorDB처럼 별도의 API 학습이 필요하지 않아 러닝 커브가 낮습니다.
셋째, AWS RDS를 통해 안정적인 운영 환경을 구축할 수 있다는 점입니다. RDS는 자동 백업, 장애 복구, 모니터링 등 관리 기능을 기본으로 제공하므로, 별도의 인프라 구성 없이도 안정적인 벡터 연산 환경을 구현할 수 있습니다.
이러한 요소들을 바탕으로, 추천팀은 PostgreSQL(RDS)을 활용해 벡터 유사도 검색을 직접 수행함으로써, 고비용의 벡터 전용 솔루션 없이도 실시간 추천 서비스의 품질과 운영 효율성을 모두 확보할 수 있었습니다.

pgvector

pgvector는 PostgreSQL에서 벡터 데이터를 저장하고 유사도 검색을 수행할 수 있게 해주는 확장 기능입니다.
이 확장 기능을 통해 PostgreSQL은 Vector라는 새로운 데이터 타입을 사용할 수 있게 되며, 이를 통해 벡터 간의 유사도 연산을 SQL 쿼리로 직접 수행할 수 있습니다. 대표적인 유사도 연산으로는 Cosine Distance, Euclidean Distance, Inner Product가 있으며, 이러한 연산은 벡터 간의 거리를 계산해 유사성을 측정하는 데 사용됩니다.
또한, pgvector는 HNSW, IVFFlat 같은 인덱스를 제공하여 대규모 벡터 데이터에서도 빠른 유사도 검색이 가능하도록 설계되었습니다.
이러한 인덱스는 벡터 간의 거리 계산을 최적화하여 검색 속도를 크게 향상시킵니다.
-- 384차원 벡터 컬럼을 포함한 테이블 생성 예시 CREATE TABLE item_vector_table( item_id INT PRIMARY KEY, item_vector VECTOR(384), ... )
SQL
복사
더 상세한 내용은 아래 사이트를 참고하시기 바랍니다.

벡터 유사도 검색(Vector Similarity Search)

벡터 유사도 검색은 다차원 공간에 표현된 벡터들 간의 거리를 계산해 데이터 간의 유사성을 측정하는 방식입니다.
pgvector는 이러한 벡터 유사도 검색을 SQL 연산자 형태로 지원합니다. 이를 통해 사용자는 SQL 쿼리에서 벡터 유사도 연산을 직접 수행할 수 있으며, 다양한 유사도 계산 방식을 선택할 수 있습니다.
연산자
거리 계산 방식
설명
적용 대상
예시
<->
L2 Distance (Euclidean)
두 벡터 간의 유클리디안 거리 (직선 거리)
Vector (Float)
ORDER BY embedding <-> '[1, 2, 3]'
<#>
(Negative) Inner Product
두 벡터의 내적을 음수화한 값
Vector (Float)
ORDER BY embedding <#> '[1, 2, 3]'
<=>
Cosine Distance
두 벡터의 방향 차이를 기반으로 한 거리
Vector (Float)
ORDER BY embedding <=> '[1, 2, 3]'
<+>
L1 Distance (Manhattan)
각 차원의 절댓값 차이의 합 (경로 기반 거리)
Vector (Float)
ORDER BY embedding <+> '[1, 2, 3]'
<~>
Hamming Distance
이진 벡터 간 비트 차이의 수 (1과 0의 불일치 수)
Binary Vector
ORDER BY binary_vec <~> B'101010'
<%>
Jaccard Distance
이진 벡터의 교집합 대비 합집합의 보완 값
Binary Vector
ORDER BY binary_vec <%> B'110101'
pgvector에서 Cosine Distance 연산자를 활용해 특정 아이템과 유사한 아이템을 찾아내는 쿼리는 아래와 같은 형태로 작성할 수 있습니다. 이 쿼리는 Vector Index Scan을 고려하지 않은 순차 검색(Seq Scan) 방식으로, 특정 아이템(target_item_id)과의 Cosine Similarity 상위 5개 아이템을 조회하는 예시입니다.
-- 1) 조회 대상이 되는 아이템이 DB에 존재하지 않아, 실시간으로 임베딩 모델을 통해 추출한 벡터로 조회할 경우 SELECT item_id , 1 - (item_vector <=> {target_vector}) AS cosine_similarity FROM item_vector_table ORDER BY cosine_similarity DESC LIMIT 5 -- 2) 조회 대상이 되는 아이템과 벡터가 DB에 존재할 경우 WITH target AS ( SELECT item_vector AS target_vector FROM item_vector_table WHERE item_id = {target_item_id} ) SELECT item_id , 1 - (item_vector <=> (SELECT target_vector FROM target)) AS cosine_similarity FROM item_vector_table WHERE item_id <> {target_item_id} ORDER BY cosine_similarity DESC LIMIT 5
SQL
복사
pgvector를 사용해 벡터 유사도 검색을 수행할 때 유의해야 할 점은, pgvector의 연산자들이 유사도가 아닌 거리(Distance)를 반환한다는 점입니다.
<=> 연산자는 Cosine Distance를 계산하는데, 이 값이 0에 가까울수록 유사도가 높다는 것을 의미합니다. 하지만 일반적인 유사도 지표는 값이 클수록 더 유사한 것으로 해석되기 때문에, 쿼리에서는 1 - (item_vector <=> target_vector)와 같은 방식으로 거리를 유사도로 변환하여 정렬합니다.
벡터 유사도 검색 쿼리는 메타데이터 상황에 따라 두 가지 방식으로 나뉩니다:
실시간 벡터 추출 방식
조회 대상 아이템이 DB에 존재하지 않는 경우, 실시간으로 임베딩 모델을 통해 벡터를 추출하고, 해당 벡터를 직접 비교 대상으로 삼아 유사 아이템을 조회하는 방식입니다.
이러한 방식은 이커머스 플랫폼처럼 상품이 자주 등록되거나, 업데이트 빈도가 높은 환경에서 유효합니다.
DB 내 벡터 기반 조회 방식
조회 대상 아이템이 DB에 존재하는 경우, WITH 절을 사용해 해당 아이템의 벡터를 추출한 후, 나머지 벡터와의 유사도를 계산해 유사 아이템을 조회하는 방식입니다.
인게임 상점처럼 상품 구성이 사전에 정의되고, 일괄 업데이트되는 구조에서 이 방식이 주로 활용됩니다.
이미 저장된 벡터를 기반으로 검색을 수행하기 때문에, 서버 리소스 소모가 적고 Latency도 낮다는 장점이 있습니다.
이처럼 pgvector의 벡터 유사도 검색은 플랫폼의 운영 구조에 따라 실시간 벡터 추출 방식과 DB 내 벡터 기반 조회 방식 중 하나를 선택하는 전략을 취할 수 있습니다. 이를 통해 시스템의 Latency와 서버 리소스 소모를 최적화할 수 있습니다.
예를 들어 일반적인 이커머스 플랫폼의 경우, 다수의 판매자가 자율적으로 상품을 등록하고 그 수가 지속적으로 변동되는 구조를 갖고 있습니다. 이처럼 상품이 수시로 추가되거나 변경되는 환경에서는 추천 시스템이 사용하는 벡터 테이블에 신규 상품이 즉시 반영되지 않을 가능성이 큽니다. 따라서 이러한 경우에는 실시간으로 임베딩을 생성하고 이를 기반으로 유사 아이템을 조회하는 방식이 더 효과적입니다.
반면, 인게임 상점게임 기획자가 사전에 상품 구성을 정의하고 업데이트 단위로 상품을 일괄 등록 및 노출하는 구조를 가지고 있습니다. 이와 같은 환경에서는 모든 상품의 메타데이터와 벡터가 출시 전에 미리 준비되기 때문에, 추천 시스템이 조회하는 시점에는 이미 DB에 벡터가 저장되어 있는 경우가 일반적입니다.
따라서 인게임 상점의 경우, 서버에 별도의 임베딩 모델을 올려 실시간 임베딩을 수행할 필요 없이 이미 저장된 벡터를 기반으로 유사도 계산을 수행하는 쿼리 방식이 더 적합합니다. 이 방식은 실시간 임베딩을 수행하는 것에 비해 서버 리소스 소모가 적고, 응답 시간(Latency)도 최소화할 수 있는 장점이 있습니다.
구분
이커머스 플랫폼
인게임 상점
상품 등록 주체
판매자별로 개별 상품을 실시간으로 등록
게임 기획자/개발자에 의해 사전 등록
상품 등록 주기
실시간 등록 및 변경
시즌/업데이트 단위의 기획 중심으로 갱신
임베딩 보장 여부
실시간 생성 필요
사전 생성 및 적재
추천 쿼리 방식
실시간 생성된 벡터로 검색
DB에 있는 상품과 벡터 기준 검색
유사도 계산 흐름
{target_vector} 직접 사용
테이블내 벡터 조회 후 비교

Backend API

Backend API에서는 유저의 요청을 수신하면, 우선 유저의 실시간 액션 데이터를 조회하고 이를 기반으로 VectorDB에서 벡터 유사도를 기준으로 유사한 콘텐츠를 검색합니다. 그러나 이렇게 도출된 결과가 그대로 유저에게 전달되지는 않습니다. 추천 결과에는 이미 유저가 보유 중인 아이템이거나 구매 제한 조건에 걸린 상품, 혹은 현재 게임 진행 단계에서 획득할 수 없는 콘텐츠가 포함될 수 있기 때문입니다.
이러한 문제를 방지하기 위해, 추천팀은 검색된 아이템 리스트에 대해 비즈니스 로직과 유효성 검사에 따라 적합성을 판단하는 필터링 과정을 거칩니다. 이 과정에서는 게임 운영팀과 협의하여 정의한 추천 정책과 로직이 적용됩니다.
또한, 단순히 부적합한 상품을 제외하는 것에서 그치지 않고, 최종 추천 리스트는 유저의 현재 상황과 맥락에 맞춰 재조정(Re-ranking) 과정을 통해 다시 한번 정렬됩니다. 이 과정을 통해 유저에게 더욱 적합한 추천 결과를 제공할 수 있습니다.

4. 인게임 동작 예시

2025년 3월 13일, 퍼스트 디센던트는 인게임 상점에 신규 계승자 ‘세레나’와 관련된 다양한 상품을 출시했습니다. 이를 기반으로, 인게임 내에서 어떻게 동작하는지 예시를 통해 설명드리겠습니다.
그림 6: ‘세레나’ 출시와 함께 인게임 상점에 추가된 신규 상품
먼저 신규 상품이 추가되면서 메타데이터 변경 이벤트가 발생하고, 이로 인해 Lambda가 호출됩니다. Lambda는 해당 상품들의 정보를 임베딩 모델을 통해 벡터로 변환하고, 변환된 벡터를 VectorDB에 적재합니다.
신규 상품들이 인게임 상점에 출시됨에 따라 유저들이 상품을 구매할 수 있는 상태가 됩니다.
상품 ID
상품명
상품 카테고리
임베딩
1001
세레나
계승자
[0.07036118, -0.004898336, ⋯, 0.09182782, -0.036802858]
1002
세레나의 엑소 시스터
프리미엄 스킨
[0.02184711, 0.05481943, ⋯, 0.04791184, -0.02721572]
1003
애쉬 베이지 롱 헤어
헤드 스킨
[0.046864092, -0.017678011, ⋯, -0.041629758, -0.08442732]
1004
다크 웨이브 브라운
헤드 스킨
[0.03872639, 0.06218843, ⋯, -0.05283276, 0.01973361]
1005
아르케 헤일로
헤드 스킨
[-0.01394487, 0.08023298, ⋯, 0.03471182, -0.06742215]
1006
위스테리아
화장
[0.02718467, -0.05173892, ⋯, -0.04415782, 0.08273392]
1007
작약
화장
[-0.04526819, 0.03287215, ⋯, 0.05802147, 0.02498301]
1008
눈송이
화장
[0.06641194, 0.01918422, ⋯, -0.03788156, -0.07013092]
1009
발굴 특화 번들
조력자
[-0.01234567, 0.08432178, ⋯, 0.01010101, -0.04567891]
1010
비행 적응 훈련 번들
조력자
[0.09321764, -0.07112235, ⋯, -0.05938191, 0.03748376]
1025
글레이의 엘리아스 박사
프리미엄 스킨
[0.05738291, -0.00481273, ⋯, 0.03321119, -0.06638472]
1026
헤일리의 사일런트 킬
프리미엄 스킨
[0.04182934, -0.08274615, ⋯, 0.02357191, -0.05821938]
이제 인게임 상점에서 유저들이 신규 상품을 구매할 수 있는 상태가 되는데요. 예를 들어, 유저 A가 신규 상품 ‘세레나’를 구매했다고 가정해보겠습니다. 이 구매 액션은 실시간 스트리밍을 통해 유저 액션 DB에 기록됩니다.
유저 ID
상품 ID
액션 형태
액션 발생 일시
A
1001 (세레나)
구매
2025-03-13 22:00:00
이후 유저 A가 상점 내 추천 배너에 접근하게 되면, 추천 API가 호출됩니다. 추천 API는 먼저 DB에서 유저 A의 최근 구매 이력을 조회합니다. 이때 가장 최근에 구매한 ‘세레나’를 기준으로 VectorDB에서 벡터 유사도 검색이 시작됩니다.
이 과정에서 VectorDB는 ‘세레나’와 유사한 상품들을 벡터 유사도 순으로 정렬해 반환합니다.
순위
상품 ID
상품명
상품 카테고리
유사도
1
1002
세레나의 엑소 시스터
프리미엄 스킨
0.94385
2
1007
작약
화장
0.91468
3
1006
위스테리아
화장
0.90638
4
1008
눈송이
화장
0.90507
5
1003
애쉬 베이지 롱 헤어
헤드 스킨
0.89411
6
1005
아르케 헤일로
헤드 스킨
0.89056
7
1004
다크 웨이브 브라운
헤드 스킨
0.88496
이후에는 비즈니스 로직과 유효성 검사 과정을 통해 Re-ranking이 수행됩니다. 이를 통해 유저 A에게 가장 적합한 추천 상품들이 최종적으로 노출됩니다.
그림 7: 유저 A가 구매한 ‘세레나’ 상품과 유사한 상품 추천
특히 이번 사례에서 주목할 점은, 유저 A와 신규 상품 간 상호작용 데이터가 존재하지 않는 Cold Start 상황에서도, 실시간 유저 액션 데이터와 사전에 임베딩된 상품 벡터를 기반으로 개인화 추천이 이루어졌다는 점입니다. 또한, 상품 출시와 동시에 실시간 데이터 흐름(메타데이터 이벤트 Lambda 호출 벡터화 VectorDB 적재 실시간 추천)이 자연스럽게 이어지면서, 신규 상품 데이터가 즉각적으로 추천 시스템에 반영되었습니다.
결과적으로, 신규 콘텐츠가 출시될 때마다 별도의 추가 작업 없이도 추천 시스템에 빠르게 반영되어, 유저에게 실시간 개인화 추천 경험을 제공할 수 있는 인프라가 구축된 것입니다.

5. 나가며

지금까지 실시간 유저 액션 데이터와 VectorDB를 활용한 개인화 추천 시스템의 구조와 동작 방식을 살펴보았습니다.
이 시스템은 단순한 모델 예측을 넘어 실시간성, 유저 맥락 인식, 콘텐츠 유사도 기반 검색 등 다양한 요소가 결합된 복합적인 기술 구조로 구성되어 있습니다. 추천(Recommendation) 분야는 전통적인 AI 모델과는 달리, 단일 모델이 아닌 여러 기술이 유기적으로 결합된 시스템 단위의 문제로 다뤄집니다. 이는 추천이라는 행위가 하나의 모델이나 로직만으로 해결되지 않고, 다양한 기술과 역할이 통합되어야 완성되기 때문입니다.
실제 서비스 환경에서 추천 시스템은 단순히 유사한 아이템을 찾아주는 것을 넘어, 유저의 현재 상황, 콘텐츠 속성, 시스템 상태 등 다양한 맥락적 요소를 종합적으로 고려한 전략적인 추천을 요구합니다. 따라서 추천 시스템의 설계와 운영에는 AI, 백엔드, 데이터 엔지니어, 분석가 등 다양한 분야의 전문가들이 각자의 전문성을 바탕으로 긴밀하게 협력해야 합니다.
결국, 추천 시스템을 잘 설계하고 운영한다는 것은 곧 유저를 깊이 있게 이해하는 시스템을 구축하는 것과 같습니다.
앞으로도 유저의 맥락을 더 정교하게 반영하고, 게임 경험을 더욱 풍부하게 만드는 추천 시스템을 함께 고민해 나가길 기대하며, 이번 포스팅을 마치겠습니다.
지금까지 추천팀 이성규였습니다. 감사합니다.

Reference

함께 읽으면 좋은 콘텐츠
Related Sites
 넥슨 게임 포탈
회사 소개
플랫폼본부 소개
인재 영입
플랫폼본부 테크블로그 운영 정책
 테크블로그 문의 gs_site_contents@nexon.co.kr