[개인] Steam 데이터 분석 서비스 - 5. AI 코멘트 (통계 검정 + Gemini)
1. AI 코멘트가 풀려는 문제
대시보드에 숫자만 보여주면 사용자가 직접 해석해야 합니다. “긍정률 87%”는 좋은 건가? 평소보다 떨어진 건가? 이 게임의 특정 코호트는 다른가? — 이런 질문에 답을 자동으로 짚어주는 게 AI 코멘트의 목표입니다.
2. 설계 원칙 — “분석은 코드, 표현은 AI”
핵심 결정: Gemini에게 분석을 시키지 않습니다. 분석은 모두 Python + BigQuery로 수행하고, Gemini는 결과 dict를 받아 자연어로 변환만 합니다.
이유:
- 환각 방지 — Gemini가 숫자를 만들거나 추세를 발명하면 신뢰 무너짐
- 일관성 — 같은 데이터면 항상 같은 분석 결과 (LLM 비결정성 회피)
- 비용 — 통계 계산은 BQ가 압도적으로 저렴
3. 3섹션 9항목 구조
설계 단계에서 인사이트를 (현황·구조·이벤트) 관점으로 분리했습니다.
섹션 ① 감성·키워드 요약 (현황)
현재 이 게임을 유저들이 어떻게 평가하나?
| 항목 | 무엇을 보나 |
|---|---|
| 1. Top 긍정/부정 카테고리 | 점유율 인용 (“부정 의견 중 80%가 반복성”) |
| 2. TF-IDF 차별화 키워드 | 전체 게임 + 같은 장르 두 코퍼스 대비 이 게임만의 키워드 |
| 3. 긍정률 4주 추세 | 최근주 vs 직전 3주 평균 |
섹션 ② 유저 세그먼트 시그널 (구조)
어떤 유저층이 만족/불만인가?
| 항목 | 무엇을 보나 |
|---|---|
| 4. 플레이타임 코호트 | 0-10h / 10-50h / 50h+ 추천률 — 진입장벽·이탈 |
| 5. 언어별 추천률 갭 | Top 5 언어, 최고-최저 차 — 현지화 시그널 |
섹션 ③ 변화 알림 (이벤트)
최근 무슨 일이 있었나?
| 항목 | 무엇을 보나 |
|---|---|
| 6. 패치 임팩트 | 최근 뉴스 ±7일 추천률·동접 변화 |
| 7. 세일 효과 | 최근 할인 윈도 동안 동접 변화 |
| 8. 부정 키워드 급증 | 최근 1주 vs 직전 4주 평균 |
| 9. 동접 z-score 이상치 | |z|≥2 일자에 뉴스/세일 cross-ref |
4. 통계적 유의성 — 단순 % 비교의 함정 회피
사례로 보는 차이
게임 A에서 60% 할인 진행. 직전 7일 대비 세일 기간 동접 +22.1% 증가.
- 단순 % 임계값 (예: ±15%): “효과 있음” 판정 → “ROI 검증된 가격 정책” 멘트 생성
- baseline z-score: 이 게임의 비-세일 일별 동접 변동성을 baseline으로 두면 σ=7,103. 세일 기간 평균 - baseline 평균을 σ로 나눈 z = +0.37 → 평소 변동 범위 안 → “세일 효과로 단정하긴 어려움”
같은 +22.1%지만 게임의 자체 변동성에 따라 의미가 완전히 다릅니다. 안정적인 게임의 +22%는 큰 신호고, 평소부터 들쭉날쭉한 게임의 +22%는 일상 변동.
적용한 통계 방법
| 항목 | 방법 |
|---|---|
| 추천률 변화 (패치 임팩트) | 두 비율 z-test (pooled SE) — 표본이 작으면 SE가 커져 자동으로 z 작아짐 |
| 동접 변화 (패치/세일) | Baseline z-score — 이벤트 ±7일 제외한 60일 일별 평균 분포 기준 |
| 동접 이상치 (단발) | Baseline z-score, |z|≥2 |
5단계 카테고리화로 일관된 라벨링:
뚜렷한_증가 (z≥2) / 약한_증가 (1≤z<2) / 평소_변동_범위 / 약한_감소 / 뚜렷한_감소
뚜렷한_상승 / 약한_상승 / 유의미한_변화_없음 / 약한_하락 / 뚜렷한_하락
def _two_proportion_z(pos1, n1, pos2, n2):
p1, p2 = pos1/n1, pos2/n2
p_pool = (pos1 + pos2) / (n1 + n2)
se = sqrt(p_pool * (1 - p_pool) * (1/n1 + 1/n2))
return (p2 - p1) / se if se else None
def _categorize_z(z, labels):
if z is None: return None
if z >= 2: return labels[0]
if z >= 1: return labels[1]
if z > -1: return labels[2]
if z > -2: return labels[3]
return labels[4]
5. TF-IDF 차별화 키워드 — 전체 vs 같은 장르
키워드 빈도 TOP 5만 보면 모든 게임이 비슷한 단어(“재미”, “그래픽”)만 나옵니다. 이 게임만 유난히 자주 나오는 단어를 찾으려면 다른 게임 대비 비교가 필요합니다.
TF = keyword_analysis.ratio (이 게임 리뷰에서의 비중)
IDF = ln(코퍼스 게임수 / 해당 키워드 등장 게임수)
TF-IDF = TF × IDF
추가로 같은 장르 코퍼스 비교를 따로 합니다. 정의: genres 필드의 첫 2 토큰 일치.
- 예: “Action, Adventure, Free To Play” 게임은 “Action, Adventure”로 시작하는 모든 게임과 비교
- 같은 장르 게임 < 10개면 장르 비교 생략 (IDF 안정성)
결과로 두 관점이 같이 나옵니다:
- 전체 게임 대비: 이 게임의 마케팅 메시지 후보
- 같은 장르 대비: 동종 경쟁 게임 대비 차별점
6. Gemini 프롬프트 설계 — 액션 시그널 패턴
3개 섹션 각각 전용 프롬프트(SECTION1_PROMPT / SECTION2_PROMPT / SECTION3_PROMPT).
출력 패턴 강제 — “구체 숫자 → 통계 결론 → 따라서 ~”
특히 섹션 ③(변화 알림)은 강한 가이드를 줘서 출력이 일관되도록 유도:
- 항상 절대 수치(delta_pp / delta_pct)를 먼저 짚고
- 그 다음 "통계적 검정 결과 유의미한/유의미하지 않은 변화로 나왔습니다" 식으로 결론 평이하게
- 마지막에 "따라서 ~" 형태로 액션 시그널 마무리
예) "추천률이 12.9%p 상승했고 통계적 검정 결과 유의미한 변화로 나왔습니다.
따라서 패치 방향성이 호평받았다고 볼 수 있습니다"
표현 화이트/블랙리스트
허용: "통계적 검정 결과", "통계적으로 의미있는/유의미한 변화",
"평소 변동 범위 안", "약한 증가/감소", "뚜렷한 증가/감소"
금지: "z-score", "표준편차", "p-value", "신뢰구간", "분산", "ROI"
일반인이 읽기 어려운 raw 통계 용어는 차단하되, “통계적으로 유의미한”처럼 일반화된 표현은 허용. 결과적으로 보고서스러운 톤은 빠지고 마케팅 코멘트 톤이 살아납니다.
결측 처리 — 메타 코멘트 금지
데이터 없는 항목은 통째로 언급 금지:
추세가 None이면 추세에 관한 어떤 문장도 쓰지 마.
"데이터가 부족하다" / "확인되지 않습니다" / "파악하기 어렵습니다" 같은
메타 코멘트도 절대 금지. 추세는 통째로 없는 항목으로 간주.
이 룰 없으면 Gemini가 “데이터가 부족해서 알기 어렵네요” 같은 잡음을 자주 끼워넣습니다.
7. 실제 출력 예시 — Dead by Daylight (app_id 381210)
최근 5월 5일 "9.6.1 | Bugfix Patch" 업데이트 이후 추천률은 3.6%p 하락했지만,
패치 직후라 표본이 적어 통계적으로 의미 있는 변화로 보긴 어렵습니다.
동접도 7.5% 줄었지만 이 역시 평소 변동 범위 안이라 패치 영향으로
단정하기는 어렵습니다.
한편, 4월 24일부터 5월 6일까지 진행된 60% 할인 세일 기간 동안
동접이 직전 7일 대비 22.1% 증가했지만, 통계적으로는 평소 변동 범위 안이라
세일 효과로 단정하기 어렵고 다른 요인도 함께 살펴봐야 할 것 같습니다.
5월 1일부터 3일까지는 동접이 평소보다 많이 높게 나타났는데, 이는
해당 기간 동안 진행된 60% 할인과 'Play While You Wait: Now available in 1v4!'
및 'What's new in 2V8?' 같은 뉴스 콘텐츠가 동반된 영향으로 보입니다.
3가지 모두 단순 % 비교라면 “효과 있음”으로 잡힐 수치(-3.6pp, -7.5%, +22.1%)이지만, 통계적으론 평소 변동 범위라 신중한 결론을 냅니다.
8. UX — 단일 트리거 + 병렬 실행
단일 트리거
🤖 popover 제목 옆에 “▶ 분석 실행” 버튼 하나. 클릭하면 3 섹션 모두 실행됩니다. 항목별 버튼 분리는 UX 복잡도만 늘림.
병렬 실행
from concurrent.futures import ThreadPoolExecutor
with st.spinner("3개 섹션 동시 분석 중..."):
with ThreadPoolExecutor(max_workers=3) as ex:
f1 = ex.submit(ai_comment_sentiment, app_id)
f2 = ex.submit(ai_comment_segment, app_id)
f3 = ex.submit(ai_comment_events, app_id)
# 3 Gemini 호출이 동시에 진행 (직렬 ~15초 → 병렬 ~5-8초)
이중 캐싱
@st.cache_data(ttl=3600)— 모든 사용자 공유, 같은 게임 재호출 시 0$st.session_state[f"ai_*_{app_id}"]— popover 닫았다 열어도 결과 유지
가독성 후처리
정규식으로 숫자(94.1%, 12.9%p, 53만 건)와 따옴표 안 한국어 키워드('밸런스', '중국어 간체')를 추출해 골드 색 굵은 글씨로 강조. 긴 문단도 핵심 숫자가 한눈에 들어옵니다.
9. 비용
게임당 분석 1회 = ~$0.001 (BQ 쿼리 ~10회 + Gemini 호출 3회).
| 항목 | 게임당 |
|---|---|
| BigQuery 스캔 | ~$0.0005 |
| Gemini Flash 입력 (~3k tokens) | ~$0.0002 |
| Gemini Flash 출력 (~900 tokens) | ~$0.0003 |
| 합계 | ~$0.001 |
자연 트래픽 기준 월 $1 미만. 최악 시나리오(누가 매시간 다른 게임 100개씩 누름) ~$0.10/시간.
비용 통제 안전망:
- GCP Billing Alert (월 $5 임계값) — 이메일 알림
- Vertex AI Quota cap — 일일 호출 제한
- 캐시 적중 시 0원
10. 협업 디테일 — Claude Code와 어떻게 만들었나
이 섹션이 본 시리즈의 핵심 차별화 포인트라 협업 흐름을 자세히 남깁니다.
본인이 결정한 것
- 9개 분석 항목 자체 — 어떤 인사이트가 의사결정에 필요한지 정의
- 3 섹션 분류 (현황/구조/이벤트) — 마케터가 받아볼 보고서 구조 관점
- 통계 방식 선택 — 단순 % 임계값에서 z-score baseline으로 전환 결정
- 임계값 — |z|=2 (95% CI), TF-IDF의 df≥2/count≥3, 코호트 n≥30 등
- 프롬프트 패턴 — “구체 숫자 → 통계 결론 → 따라서 ~”, 통계 용어 화이트/블랙리스트
- UX 흐름 — 단일 트리거, 병렬 실행, 이중 캐싱, 숫자/키워드 강조
- 트레이드오프 — Streamlit 공개 배포 시 비용 vs 접근성, AI hide vs 캐시 모드
Claude Code가 한 것
- 결정된 SQL/Python을 코드로 옮기는 페어 프로그래밍
- BQ 쿼리 디버깅 (PARSE_TIMESTAMP 타입 mismatch 같은 것)
- 정규식 작성 (한글 키워드 매칭)
- 코드 리뷰 (3개 에이전트 병렬 — 재사용성/품질/효율성)
- 문서 초안 (PROJECT_PLAN.md 보강, 이 블로그 포스트 시리즈)
그래서 얻은 것
혼자 했다면 한 달 이상 걸렸을 분량을 압축적으로 끝냈습니다. 그 시간을 방법론 자체를 다듬는 데 더 썼습니다. 예를 들어 단순 +15% 임계값으로 1주 운영해보고 “381210 데이터에서 +22%지만 사실 평소 수준”이라는 케이스를 발견 → z-score baseline으로 전환 → 같은 케이스가 z=+0.37로 정확히 잡히는지 검증 → 5단계 카테고리화 일관성 확인. 이런 반복적 개선이 가능했던 게 협업 효과입니다.
도구는 결정을 대신 내려주지 않습니다. 무엇을 만들지 정의하고 그 결과가 만족스러운지 판단하는 건 본인입니다. Claude Code는 그 정의를 빠르게 코드로 옮겨주는 파트너 역할을 했습니다.
11. 마무리 — 시리즈 정리
5개 포스트로 다룬 것을 한 줄씩:
- 개요 — 의사결정 보조 데이터 서비스 + Claude Code 협업 방식
- 데이터 수집 & GCP — Cloud Scheduler/Functions × 5 + BQ 직접 적재
- 데이터 구조 & 분석 — 8 BQ 테이블 + Kiwi 형태소 분석 + 24 카테고리
- 대시보드 — 3탭 + anchor 기능 + Streamlit Cloud 배포
- AI 코멘트 — 통계 검정 + Gemini 자연어 변환 (이 글)
결과물
다음 확장
- FastAPI + React로 풀스택 분리
- 인증 + 게임 개발자용 본인 게임 KPI 대시보드
- Pre-compute 모드로 AI 코멘트 비용 0화
읽어주셔서 감사합니다.