Skip to content

도메인: Analytics (Firebase Analytics 이벤트)

항목
SDKfirebase_analytics (Flutter)
Firebase 프로젝트dartbrief (dev/prod 단일 — 프로젝트 분리 X)
Dev 격리setAnalyticsCollectionEnabled(flavor.isProd) — dev 빌드는 수집 OFF
사용자 식별자setUserId(user.userId.toString())auth/me 성공 시

왜 단일 Firebase 프로젝트: Google 로그인 / FCM / App Check 가 모두 같은 GoogleService-Info.plist 에 묶여 있어 dev/prod 프로젝트 분리는 마찰만 큼. dev 메트릭 오염은 collection 토글로 처리.

도입 배경 (2026-05-07 5명 패널 토론)

lazy auth (비로그인 reveal 게이트) + 웹 부활 검토 중, DEVIL 의 핵심 지적:

"전환 측정 없이 lazy 풀면 게이트 통과율 / 중도 이탈을 못 봄. 출시 후 회복 불가능한 데이터 공백."

→ 5-PR 계획 1번으로 analytics 사전 셋업 (PR #54 머지 완료, 2026-05-07).

단일 진입점

mobile/lib/shared/analytics/analytics_service.dartAnalyticsService 만 사용. 호출 측은 typed 메서드만 — 이벤트명/파라미터 키 문자열이 코드 전반에 흩어지지 않도록.

dart
final analytics = ref.read(analyticsServiceProvider);
await analytics.logRevealAttempt(rceptNo: rceptNo);

모든 메서드는 try/catch 로 감싸 native 채널 실패 시에도 앱 흐름을 막지 않음 (분석 누락 < 사용자 액션 차단).

이벤트 taxonomy

인증 (Firebase 표준)

이벤트메서드파라미터송출 시점
loginlogLoginloginMethod: 'kakao'|'google'|'apple'AuthController.handleAuthToken 안에서 refetch() 후 (login_page 의 mounted 가드 외)
sign_uplogSignUpsignUpMethod현재 미사용 — 백엔드가 신규/기존 구분 응답 안 함. 시그니처만 준비
logoutlogLogoutAuthController.logout() 안에서 setUserId(null) 보다 먼저

setUserId 직렬화 — login 흐름은 AuthController.handleAuthTokenrefetch() 안에서 setUserId(user.userId.toString())await 한 뒤 같은 컨트롤러에서 logLogin 직접 송출 (page-level 호출 X — LoginPage dispose/race 회피, 단일 책임 지점). 401 콜백 vs 명시적 logout race 는 _isLoggingOut 플래그로 우회 (자세히는 flutter-gotchas §7).

공시 / 분석 funnel

이벤트파라미터송출 시점
disclosure_viewrcept_no, corp_code?공시 상세 페이지 진입
analyze_inspectrcept_no, status, is_short?, already_revealed?Inspect 메타 도착 — 폴링 dedup (아래)
reveal_attemptrcept_no"AI 분석 보기" 버튼 클릭 직전
reveal_resultrcept_no, outcome, newly_revealed?, can_watch_ad?reveal API 응답 / 예외 / 화면 이탈

status (analyze_inspect): 'completed' | 'pending' | 'unavailable' — Flutter AnalysisMetaState 타입 기준. 백엔드 QUOTA_EXHAUSTED 는 HTTP 200 으로 내려와 AnalysisMetaCompleted'completed' 로 기록됨. 'unavailable' 은 202+UNAVAILABLE 응답 시 송출 (현재 백엔드 미생성, reserved).

폴링 dedupdisclosure_detail_controller._lastInspectStatus 가 직전 status 를 기억해 같은 status 가 폴링으로 5회 들어와도 1번만 송출. status 가 바뀌면 다시 송출.

RevealOutcome enum

reveal_result.outcome 차원. 모든 종결 분기가 단일 이벤트 — funnel 분기 분석 용이.

의미백엔드 매핑
successreveal 성공 (PaidReveal 또는 FreeReveal)Outcome.PaidReveal / FreeReveal
quota_exhausted주 quota 소진429 QUOTA_EXHAUSTED
not_ready분석 아직 안 됨Outcome.Pending
unavailable영구 불가 (FAILED + retry≥3)reserved — 현재 Flutter reveal 경로 미송출 (별도 PR scope)
error클라이언트 측 예외 (네트워크/서버 장애)
cancelledreveal 응답 도착 전 controller dispose

error / cancelledreveal_attempt 대비 reveal_result구조적 누락 방지 — 모든 attempt 가 어떤 outcome 으로든 종결되어야 funnel 깨짐을 알 수 있음.

Engagement

이벤트파라미터송출 시점
watchlist_addcorp_code종목 추가 성공 (unawaited 로 화면 흐름 차단 X)

광고 (현재 미송출)

이벤트메서드상태
ad_reward_earnedlogAdRewardEarned시그니처만 유지, 클라이언트 송출 X

왜 클라이언트 송출 안 하나 — prod 는 AdMob SSV 가 백엔드를 직접 호출하므로 클라이언트는 검증 통과 여부를 모름. 클라이언트에서 송출하면 SSV 실패해도 보상 funnel 이 +1 → false-positive. 정확한 측정은 백엔드 SSV 핸들러에서 Firebase Measurement Protocol / BigQuery 로 별도 기록 예정 (별도 PR scope).

lazy auth

이벤트파라미터의도
auth_gate_shownaction: 'reveal'|'watchlist'|'feedback'|'sign_in'게이트 노출 — 비로그인 사용자가 어떤 액션으로 게이트에 닿는지. sign_in 은 명시적 로그인 진입점 (설정 → 로그인 메뉴) 으로 후속 액션 없이 로그인 자체가 목적
auth_gate_dismissedaction, reason: 'close'|'swipe'게이트 닫힘 (취소) — 게이트 통과율 분모. reason 으로 X 명시 닫기 vs swipe down/system back 구분 → 풀스크린 vs 모달 A/B 결정 차원
  • AuthGateAction enum 으로 액션 차원 고정.
  • 게이트 통과 (로그인 성공) 경로는 별도 이벤트 X — 기존 login(provider) 이벤트가 게이트 직후 송출돼 timestamp correlation 으로 측정.
  • reason 분리는 모달 vs 풀스크린 결정 입력값. swipe 비율이 높으면 풀스크린이 dismiss 마찰 추가 가치, close (X) 비율이 높으면 추가 가치 낮음.

Edge case

  • dev 빌드 → prod 메트릭 오염: bootstrap.dartsetAnalyticsCollectionEnabled(flavor.isProd) 로 dev 는 수집 OFF. flavor 기준 (kDebugMode 아님 — make run-dev--releasekDebugMode=false).
  • 사용자 식별자 racesetUserId(null)logLogout 보다 먼저 발생하면 logout 이 익명 귀속되어 이탈 funnel 깨짐. AuthController.logout()_isLoggingOut 플래그 + 순차 await 로 보장.
  • 401 콜백 vs logout() — 토큰 만료 시 onUnauthorized 콜백이 setUserId(null) 호출. 그러나 명시적 logout 흐름에서는 logout 이벤트가 익명 귀속될 위험 → _isLoggingOut=true 면 콜백이 setUserId(null) 우회.
  • Controller dispose 중 reveal: disclosure_detail_controller.reveal()analytics 를 함수 진입 시 캡처 (ref.read 를 await 후 호출하면 dispose 후 crash). dispose 후엔 RevealOutcome.cancelled 송출.
  • 폴링 중 같은 이벤트 폭주 — Inspect 메타가 pending 인 동안 폴링 (5→10→15→20s backoff, 이후 60s 고정) → _lastInspectStatus dedup 으로 1회만 송출.

관련 코드

법적 페이지 영향

Firebase Analytics 는 패키지 추가만으로 App Instance ID + 자동 수집 이벤트(session, screen_view, app_open 등) + 기기/OS/언어/국가 가 Google 로 송신된다. 따라서 다음 페이지에 Google LLC (Firebase Analytics) 위탁/국외이전 명시 필수:

  • frontend/src/app/privacy/page.tsx — 제2조 자동 수집 정보, 제5조 위탁, 제6조 국외 이전
  • frontend/src/app/terms/page.tsx — 제9조·제12조 외부 서비스 목록

이벤트 taxonomy 추가 (logEvent 신규) 자체는 법적 페이지 영향 없음 (이미 "앱 사용 이벤트" 로 포괄). 단, 새로운 자동 수집 식별자 / 디바이스 정보 수집 SDK 추가 시 동시 갱신.

관련 PR

  • PR #54 (2026-05-07) — firebase_analytics 도입 + 핵심 funnel 이벤트 wiring
  • PR #71 (2026-05-11) — 법적 페이지에 Firebase Analytics 위탁 명시 (PR #54 누락분 정정)