Skip to content

feat: Firebase Analytics 이벤트 계측 확장 (Phase 1/2/3, 64개)#377

Merged
unam98 merged 4 commits intodevelopfrom
feature/firebase-event-tracking
Apr 3, 2026
Merged

feat: Firebase Analytics 이벤트 계측 확장 (Phase 1/2/3, 64개)#377
unam98 merged 4 commits intodevelopfrom
feature/firebase-event-tracking

Conversation

@unam98
Copy link
Copy Markdown
Collaborator

@unam98 unam98 commented Apr 3, 2026

작업 배경

  • 코스→러닝 전환율 20% 병목의 원인을 데이터로 파악할 수 없는 상태 (12개 화면 블랙박스)
  • 데이터 드리븐 의사결정을 위해 Firebase 이벤트를 35개 → 64개로 확장

변경 사항

구분 파일 수 내용
인프라 2 Analytics.kt 파라미터 지원 logEvent 추가, EventName.kt 신규 상수 29개 + Param 객체
Phase 1 (P0) 6 RunActivity(러닝 전구간), CountDownActivity, DrawActivity, EndRunActivity, CourseDetailActivity, StorageMyDrawFragment
Phase 2 (P1) 8 LoginActivity, GiveNicknameActivity, DiscoverSearchActivity, DiscoverPickActivity, DiscoverUploadActivity, MyDrawDetailActivity, MyHistoryDetailActivity, SchemeActivity
Phase 3 (P2) 2 MyRewardActivity, MyPageEditNameActivity
합계 19파일 +378줄, -29줄

핵심 추가 이벤트

  • action_run_start/complete/abandon — 러닝 블랙박스 해소
  • view_countdown, click_cancel_countdown — 러닝 직전 이탈 측정
  • view_course_complete_result — 코스 완성 후 "바로 뛰기" vs "저장" 분기 측정
  • click_run_from_detail — 코스 발견→러닝 전환 측정
  • action_login_success/fail — 로그인 전환율, 장애 감지

영향 범위

  • 기존 이벤트 로깅 코드 변경 없음 (신규 추가만)
  • 런타임 영향: Firebase logEvent 호출 추가 (경량, 비동기 처리)
  • Android/iOS 동일 이벤트명 사용 (iOS 별도 작업 예정)

Test Plan

  • Debug 빌드 성공 확인
  • Firebase DebugView에서 이벤트 발화 확인
  • BigQuery 연동 후 퍼널 쿼리 검증
  • iOS 동일 이벤트 구현 후 크로스 플랫폼 데이터 일관성 확인

🤖 Generated with Claude Code

Summary by CodeRabbit

Telemetry

  • Enhanced tracking of user interactions across key app features including authentication flows, course discovery and creation, running sessions, drawing tools, profile management, and content storage to provide better insights into user engagement patterns and app usage.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

Warning

Rate limit exceeded

@unam98 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 2 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 13 minutes and 2 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45712a87-dc29-4d44-b300-dafaddb609ab

📥 Commits

Reviewing files that changed from the base of the PR and between 569702e and 6d2aebe.

📒 Files selected for processing (19)
  • app/src/main/java/com/runnect/runnect/presentation/countdown/CountDownActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/detail/CourseDetailActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/discover/pick/DiscoverPickActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/draw/DrawActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/endrun/EndRunActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/mypage/editname/MyPageEditNameActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/mypage/history/detail/MyHistoryDetailActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/mypage/reward/MyRewardActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/run/RunActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/scheme/SchemeActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/storage/StorageMyDrawFragment.kt
  • app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt
  • app/src/main/java/com/runnect/runnect/presentation/storage/mydrawdetail/MyDrawDetailActivity.kt
  • app/src/main/java/com/runnect/runnect/util/analytics/Analytics.kt
  • app/src/main/java/com/runnect/runnect/util/analytics/EventName.kt
📝 Walkthrough

Walkthrough

This PR introduces comprehensive analytics event logging across 13 activities and 2 fragments in the presentation layer, tracking user interactions including login, course discovery, drawing, running, and profile management. It also adds new event name constants, parameter definitions, and flexible logEvent overload methods to the analytics utility layer.

Changes

Cohort / File(s) Summary
Login & Onboarding Analytics
presentation/login/LoginActivity.kt, presentation/login/GiveNicknameActivity.kt
Added analytics events for login success/failure (with method and error code), signup success, and nickname completion, including new parameter tracking for user authentication method and new user status.
Course Discovery & Detail Analytics
presentation/detail/CourseDetailActivity.kt, presentation/discover/pick/DiscoverPickActivity.kt, presentation/discover/search/DiscoverSearchActivity.kt, presentation/discover/upload/DiscoverUploadActivity.kt, presentation/scheme/SchemeActivity.kt
Added analytics logging for course detail interactions (start run, unscrap), discover pick view, search execution (with keyword and result count), upload completion (with course ID and distance), and deep-link navigation (with target screen derivation).
Course Drawing & Running Flow Analytics
presentation/draw/DrawActivity.kt, presentation/countdown/CountDownActivity.kt, presentation/run/RunActivity.kt, presentation/endrun/EndRunActivity.kt
Added analytics events across the full running lifecycle: drawing start/completion (with point count and distance), countdown view/cancellation, run start/complete/abandon (with computed time and distance metrics), and end-run save operations.
User Profile & History Analytics
presentation/mypage/editname/MyPageEditNameActivity.kt, presentation/mypage/history/detail/MyHistoryDetailActivity.kt, presentation/mypage/reward/MyRewardActivity.kt, presentation/storage/mydrawdetail/MyDrawDetailActivity.kt
Added analytics for profile editing, run history detail viewing, reward page views, and my-draw detail viewing, each tracking relevant identifiers (record ID, course ID, etc.).
Storage Fragment Analytics
presentation/storage/StorageMyDrawFragment.kt, presentation/storage/StorageScrapFragment.kt
Added view analytics for storage lists (my-draw and scrap courses) with course count, plus course selection tracking for my-draw list.
Analytics Infrastructure
util/analytics/Analytics.kt, util/analytics/EventName.kt
Extended Analytics with two new logEvent overloads: one accepting a Bundle and one accepting variable-length key-value pairs (mapped to Bundle). Expanded EventName with 40+ new event constants organized by feature area and introduced a new nested EventName.Param object containing 30+ standardized parameter key constants for consistent event logging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Analytics dance through every screen,
Events logged where users have been,
From clicks to draws, from runs to rest,
Data flows to pass the test! 📊✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: Firebase Analytics instrumentation expansion across 64 events in Phases 1/2/3, directly corresponding to the comprehensive analytics integration changes across 19 files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/firebase-event-tracking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@unam98 unam98 self-assigned this Apr 3, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt (1)

76-83: ⚠️ Potential issue | 🟡 Minor

Capture the login method when the request starts.

These events derive method from the mutable socialLogin field when the callback arrives. If no provider was selected yet, or another tap changes socialLogin before success/failure comes back, the event gets recorded as "kakao". Persist the requested method at click time and reuse that value for the result event.

Also applies to: 111-115, 123-127, 136-140

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt`
around lines 76 - 83, At each click handler (cvGoogleLogin, cvKakaoLogin and the
other similar handlers at the other occurrences), capture the chosen provider
method into an immutable/local variable (e.g., requestedMethod) at click time
instead of relying on the mutable socialLogin field; set socialLogin as before,
then record requestedMethod = googleLogin.method / kakaoLogin.method (or set a
separate lastRequestedMethod property) and pass or reuse that stored immutable
value inside the signIn result callbacks (success/failure event recording) so
the recorded method reflects the request that started rather than the current
value of socialLogin when the callback runs.
app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt (1)

106-109: ⚠️ Potential issue | 🟠 Major

Don't use data-refresh success as the source of truth for a VIEW_* event.

VIEW_STORAGE_SCRAP fires every time this list reloads successfully — initial load, pull-to-refresh, and ScreenRefreshEvent.RefreshStorageScrap — and it doesn't fire at all when the user opens the tab but the fetch fails. That will skew this screen's view count and funnel denominator. Move the view event to the fragment's visibility entry point, and keep COURSE_COUNT on a separate load/result event if you still need it.

Also applies to: 119-124, 183-186

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt`
around lines 106 - 109, The VIEW_STORAGE_SCRAP event is currently emitted inside
the refresh success path (e.g., from binding.refreshLayout.setOnRefreshListener
-> getMyScrapCourses()), causing it to fire on refreshes and miss failed initial
loads; move the VIEW_STORAGE_SCRAP emission out of the data-load success
handlers and into the fragment visibility entry point (e.g., onResume or the
fragment's entry lifecycle method) so it records a screen view when the user
actually sees the fragment; leave COURSE_COUNT emission tied to the load/result
event produced by getMyScrapCourses() (and any handlers for
ScreenRefreshEvent.RefreshStorageScrap) so counts reflect successful
fetches—apply the same change to the other occurrences referenced around the
getMyScrapCourses() calls (lines noted 119–124 and 183–186).
app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt (1)

151-154: ⚠️ Potential issue | 🟡 Minor

Log the keyword that produced this response, not the current EditText value.

The request is started with a local keyword, but the success handler rereads binding.etDiscoverSearchTitle.text. If the user edits the field while the request is in flight, you'll emit a mismatched KEYWORD/RESULT_COUNT pair. Carry the submitted keyword with the request or in the success state.

Also applies to: 178-181

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt`
around lines 151 - 154, The success handler currently rereads
binding.etDiscoverSearchTitle instead of using the submitted local keyword,
causing KEYWORD/RESULT_COUNT mismatches if the user edits the field while the
request is inflight; change the flow to carry the submitted keyword with the
request (e.g., pass the local keyword into viewModel.getCourseSearch or include
it in the success state/emitted result) and update the success handler to
log/use that submitted keyword rather than re-reading
binding.etDiscoverSearchTitle; update both occurrences (the call at
getCourseSearch(...) and the corresponding success handling code) so the logged
KEYWORD is the exact keyword that started the request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.kt`:
- Around line 118-122: The event is logging Param.DISTANCE_M as a String
(uploadCourse?.distance) which breaks numeric analytics; update the
Analytics.logEvent call in DiscoverUploadActivity
(EventName.ACTION_COURSE_UPLOAD_COMPLETE) to convert uploadCourse?.distance
(from DiscoverUploadCourse.distance) to a numeric type before sending — e.g.,
safely parse to Double or Int (using a safe parse that returns null on failure),
optionally round/format as needed, and pass that numeric value (or null) for
Param.DISTANCE_M so parsing errors don’t crash and numeric aggregations work
correctly.

In `@app/src/main/java/com/runnect/runnect/presentation/draw/DrawActivity.kt`:
- Around line 497-503: The Analytics.logEvent calls in DrawActivity are sending
viewModel.distanceSum (which is in kilometers) as Param.DISTANCE_M; update both
places where Analytics.logEvent uses Param.DISTANCE_M to convert kilometers to
meters (multiply distanceSum by 1000) before passing it, taking care to handle
the distanceSum value/type/nullability (e.g., use distanceSum.value or safe-call
and convert to an Int/Double as expected by Analytics). Locate the calls to
Analytics.logEvent in DrawActivity (the blocks that include Param.DISTANCE_M to
viewModel.distanceSum.value and the similar block later) and replace the
argument with the converted meter value.

In `@app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt`:
- Around line 111-116: The current logging in LoginActivity sends the
user-facing error string (it) as Param.ERROR_CODE to Analytics.logEvent; replace
this with a stable, bounded code or category by mapping the raw message to an
enum/constant (e.g., LoginErrorCode or a function
mapLoginErrorToCode(errorMessage): String). Update the block that computes
method and calls Analytics.logEvent (the code referencing socialLogin,
GoogleLogin, EventName.ACTION_LOGIN_FAIL and Param.ERROR_CODE) to call your
mapper and pass the mapped code/category instead of the raw `it`; if no mapping
is found, emit a generic fallback code like "UNKNOWN_LOGIN_ERROR". Ensure the
mapper is deterministic and does not forward user-visible text to analytics.

In `@app/src/main/java/com/runnect/runnect/presentation/run/RunActivity.kt`:
- Around line 101-105: The analytics calls pass km-valued fields directly into
meter-named params; update each callsite (e.g., the Analytics.logEvent call
using EventName.ACTION_RUN_START with Param.TARGET_DISTANCE_M and similar calls
around lines referenced) to normalize distance to meters before logging by
converting the km value (runCourseData?.distance or distanceSum) to meters
(distanceInMeters = (value ?: 0.0) * 1000) and pass that normalized value to
Param.TARGET_DISTANCE_M / Param.COURSE_DISTANCE_M; do this conversion once per
callsite and ensure the type matches the analytics API (Int/Long/Double) when
calling Analytics.logEvent.

In `@app/src/main/java/com/runnect/runnect/util/analytics/Analytics.kt`:
- Around line 42-58: The logEvent implementation accepts unsupported Bundle
types (Int, Float, Boolean) that Firebase Analytics may not recognize; update
the loop in Analytics.logEvent to normalize params to Firebase-supported types
by converting Int -> Long (use putLong), Float -> Double (use putDouble), and
Boolean -> Long (1L/0L) or String, while keeping String, Long, and Double as-is
before calling firebaseAnalytics?.logEvent; ensure keys like COURSE_ID,
RESULT_COUNT, IS_NEW_USER are therefore stored as Long/Double/String to be
reliably processed by Firebase.

---

Outside diff comments:
In
`@app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt`:
- Around line 151-154: The success handler currently rereads
binding.etDiscoverSearchTitle instead of using the submitted local keyword,
causing KEYWORD/RESULT_COUNT mismatches if the user edits the field while the
request is inflight; change the flow to carry the submitted keyword with the
request (e.g., pass the local keyword into viewModel.getCourseSearch or include
it in the success state/emitted result) and update the success handler to
log/use that submitted keyword rather than re-reading
binding.etDiscoverSearchTitle; update both occurrences (the call at
getCourseSearch(...) and the corresponding success handling code) so the logged
KEYWORD is the exact keyword that started the request.

In `@app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt`:
- Around line 76-83: At each click handler (cvGoogleLogin, cvKakaoLogin and the
other similar handlers at the other occurrences), capture the chosen provider
method into an immutable/local variable (e.g., requestedMethod) at click time
instead of relying on the mutable socialLogin field; set socialLogin as before,
then record requestedMethod = googleLogin.method / kakaoLogin.method (or set a
separate lastRequestedMethod property) and pass or reuse that stored immutable
value inside the signIn result callbacks (success/failure event recording) so
the recorded method reflects the request that started rather than the current
value of socialLogin when the callback runs.

In
`@app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt`:
- Around line 106-109: The VIEW_STORAGE_SCRAP event is currently emitted inside
the refresh success path (e.g., from binding.refreshLayout.setOnRefreshListener
-> getMyScrapCourses()), causing it to fire on refreshes and miss failed initial
loads; move the VIEW_STORAGE_SCRAP emission out of the data-load success
handlers and into the fragment visibility entry point (e.g., onResume or the
fragment's entry lifecycle method) so it records a screen view when the user
actually sees the fragment; leave COURSE_COUNT emission tied to the load/result
event produced by getMyScrapCourses() (and any handlers for
ScreenRefreshEvent.RefreshStorageScrap) so counts reflect successful
fetches—apply the same change to the other occurrences referenced around the
getMyScrapCourses() calls (lines noted 119–124 and 183–186).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 67ffab0c-d38a-4825-89d3-33ed81203834

📥 Commits

Reviewing files that changed from the base of the PR and between eeccb1c and 569702e.

📒 Files selected for processing (19)
  • app/src/main/java/com/runnect/runnect/presentation/countdown/CountDownActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/detail/CourseDetailActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/discover/pick/DiscoverPickActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/draw/DrawActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/endrun/EndRunActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/mypage/editname/MyPageEditNameActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/mypage/history/detail/MyHistoryDetailActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/mypage/reward/MyRewardActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/run/RunActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/scheme/SchemeActivity.kt
  • app/src/main/java/com/runnect/runnect/presentation/storage/StorageMyDrawFragment.kt
  • app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt
  • app/src/main/java/com/runnect/runnect/presentation/storage/mydrawdetail/MyDrawDetailActivity.kt
  • app/src/main/java/com/runnect/runnect/util/analytics/Analytics.kt
  • app/src/main/java/com/runnect/runnect/util/analytics/EventName.kt

@unam98 unam98 force-pushed the feature/firebase-event-tracking branch 2 times, most recently from 12ebff9 to 409305f Compare April 3, 2026 08:07
Comment on lines +5 to +8
// ========================================
// App Start / Onboarding
// ========================================

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일 주석이 과하게 달렸어. 필수가 아니라면 제거해줘

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영 완료 (193a29d) — 과도한 구분선 주석 제거

unam98 added 4 commits April 3, 2026 17:38
코스→러닝 전환율 20% 병목 분석을 위해 Firebase 이벤트를 35개에서 64개로 확장.
12개 블랙박스 화면(RunActivity, CountDown 등)에 계측 코드 삽입.

- Phase 1 (P0): 러닝 전구간, 카운트다운, 코스 그리기 완성 플로우
- Phase 2 (P1): 로그인 성공/실패, 온보딩, 검색, 업로드, 보관함, 딥링크
- Phase 3 (P2): 리워드, 프로필 수정
- DISTANCE_M 파라미터 km→m 변환 (DrawActivity, RunActivity)
- Analytics.logEvent에서 Int→Long, Float→Double, Boolean→Long 변환
- LoginActivity ERROR_CODE를 안정적 상수로 변경
- DiscoverUploadActivity distance를 Double로 파싱
- EventName.kt 과도한 구분선 주석 제거
- StorageMyDrawFragment: VIEW_STORAGE_MY_DRAW 추가
- StorageScrapFragment: VIEW_STORAGE_SCRAP 추가
- click_nav_course_drawing → click_course_drawing_tab_bar
- click_nav_course_discovery → click_course_discovery_tab_bar
- click_nav_storage → click_storage_tab_bar
- click_nav_my_page → click_my_page_tab_bar
@unam98 unam98 force-pushed the feature/firebase-event-tracking branch from ab04d13 to 6d2aebe Compare April 3, 2026 08:38
@unam98 unam98 merged commit 9cde977 into develop Apr 3, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant