AI 캐릭터 서비스에 사용자 관계 기억 기능 통합
목차
API 여러 개가 서로 다른 엔드포인트에서 사용자와 캐릭터 간의 상호작용을 기록하고 있는데, 그 정보들이 제각각 흩어져 있었다. 채팅 맥락은 chat 라우트에, 선물 기록은 gift, 사진 공유는 photo—각각 따로따로였고, 캐릭터 프로필도 단편적이었다. 이번 작업은 그 흩어진 정보들을 하나의 글로벌 사용자 프로필에 누적하고, 관계의 맥락을 요약해서 다시 활용할 수 있게 한 것이다.
왜 '기억'이 필요했나
AI 캐릭터와의 대화는 맥락이 깊을수록 자연스러워진다. 같은 사람이 여러 번 상호작용할 때:
- "지난번에 선물 줬던 그것" → 기억 없으면 맥락 파악 불가
- 사용자의 선호도, 관심사의 변화 → 누적되지 않으면 매번 처음부터
- 관계의 깊이 → 단순 방문 횟수가 아니라 상호작용의 다양성(채팅, 선물, 사진 등)에서 드러남
한 두 문장의 시스템 메시지로는 이런 풍부함을 전달할 수 없다. 실제 상호작용 데이터가 데이터베이스에 축적되어야 한다. 그래야 프롬프트에 "지난 3개월간 선물을 5번 받았고, 특히 시각적 대화를 선호한다" 같은 정보를 넘길 수 있다.
구조: 마이그레이션과 API 조율
| 영역 | 역할 | 담당 파일 |
|---|---|---|
| DB 스키마 변경 | 글로벌 프로필 테이블 추가, 관계 요약 저장소 | sql/migrate-memory.sql |
| 대화 기록 연계 | chat API에서 상호작용 기록 | src/app/api/chat/route.ts |
| 선물 기록 연계 | gift API에서 선물 사건 누적 | src/app/api/gift/route.ts |
| 시각 정보 연계 | photo API에서 공유 이력 | src/app/api/photo/route.ts |
| 프로필 생성/갱신 로직 | 누적 데이터에서 요약 생성 | src/lib/characters.ts |
| 프롬프트 주입 | 요약된 기억을 대화에 포함 | src/lib/chat.ts |
삼개 API(chat, gift, photo)가 각각 자신의 엔드포인트에서 이벤트를 기록하되, 모두 같은 글로벌 프로필 테이블로 쓴다. 여기서 중요한 건 일관성이다. 어떤 API든 사용자-캐릭터 쌍에 대해 같은 프로필을 본다는 보장이 필요했다.
마이그레이션 파일은 기존 데이터를 손실 없이 옮기는 동시에 새로운 스키마를 설정하는 진입점이다. 이전에는 각 테이블이 독립적이었다면, 이제는 이들이 하나의 통합 뷰를 제공한다.
회고: 좋았던 점, 어려웠던 점
좋았던 점
분산된 이벤트 수집의 명확성
각 API가 자신의 역할만 충실하게 하면, characters.ts의 프로필 생성 로직이 모든 데이터를 수집해서 요약한다. API는 "사건을 기록하라"만 신경 쓰고, 누적과 해석은 lib 레이어에 맡길 수 있다. 이렇게 책임을 분리하니 각 엔드포인트의 테스트도 단순해졌다.
재사용성
chat.ts에서 요약된 프로필을 한 번 받아오면, 여러 위치에서 활용 가능하다. 프롬프트에 집어넣을 수도, 필터링에 사용할 수도, 나중에 추가 분석을 할 때도 이미 구조화된 데이터를 쓸 수 있다.
어려웠던 점
마이그레이션 시 기존 관계 재구성
이미 존재하는 상호작용 데이터들(기존 chat_history, gift_log 등)을 글로벌 프로필로 역산하는 과정이 까다로웠다. 예를 들어 "이 사용자는 A 캐릭터와 몇 개월간 상호작용했는데, 현재까지의 누적 통계는?" 같은 쿼리를 한 번에 맞춰야 했다. 부분 마이그레이션이나 낮은 데이터 품질이 있으면 요약이 왜곡될 수 있다.
요약 알고리즘의 선택
"관계의 깊이를 어떤 지표로 정의할 것인가"는 단순한 기술 문제가 아니었다. 메시지 수? 시간? 상호작용 다양성의 가중치? 팀과 논의 끝에 다중 차원(recency, frequency, interaction type variety)으로 정의했는데, 이게 맞는지는 실제 모델의 응답 품질로만 검증할 수 있다.
// 예: 관계 요약 생성 시 고려하는 차원들
const relationshipSummary = {
interactionCount: totalCount,
recencyDays: daysSinceLastInteraction,
preferredChannels: ['chat', 'gift'], // 어떤 방식으로 주로 상호작용하는가
emotionalTone: aggregatedSentiment, // 대화의 톤 추이
topicPreferences: [...], // 자주 논의되는 주제
};
배운 점
여러 엔드포인트에서 같은 대상(사용자-캐릭터 관계)을 다루는 시스템을 설계할 때는 단일 진실 공급처(single source of truth)를 먼저 정의하는 게 중요하다는 걸 다시금 느꼈다. 마이그레이션을 깔끔하게 하려면 기존 구조부터 명확히 해석해야 하고, 새 스키마는 "이전 정보를 모두 손실 없이 담되 더 나은 구조로" 설계해야 한다.
또한 팀 차원에서, 이런 변경을 배포할 때는 DB 마이그레이션 — API 롤아웃 — 모니터링의 순서와 타이밍을 맞춰야 한다. 마이그레이션이 먼저 되고 나서 API가 새 스키마를 읽고 쓸 준비가 되어야 사용자에게 영향을 주지 않는다.
다음은 이 기억 체계가 정말 대화 품질을 높이는지, 어떤 요약이 가장 효과적인지 모니터링하고 반복할 차례다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.