feat/#105: Mixpanel 기반 이벤트 로깅 연동 (피드/투표 생성 흐름)#106
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…submitted) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d, abandoned) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
워크스루Mixpanel SDK를 통한 애널리틱스 기반 사용자 이벤트 추적 기능을 구현합니다. 새로운 core/analytics 모듈은 debug와 release 빌드별 구현을 제공하며, Home과 Upload 기능에서 피드 조회, 투표 제출, 투표 생성 라이프사이클을 추적합니다. 변경 사항Mixpanel 애널리틱스 통합
예상 코드 리뷰 노력🎯 3 (중간) | ⏱️ ~25 분 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.kt (1)
27-33: ⚡ Quick win
trackAutomaticEvents = true로 인해 의도치 않은 자동 이벤트가 수집됩니다
getInstance(Context, String, boolean)의 세 번째 파라미터trackAutomaticEvents는 "앱 세션, 첫 앱 실행, 앱 업데이트 등 일반적인 모바일 이벤트를 수집할지 여부"를 제어합니다.true로 설정 시 PR 명세에 없는$app_open,App Session,First App Open등의 이벤트가 자동으로 Mixpanel에 전송됩니다. 공식 README 예제는trackAutomaticEvents = false를 사용합니다.커스텀 이벤트만 추적하려면
false로 변경하세요.💡 제안 수정
- val mixpanel = - MixpanelAPI.getInstance( - context, - BuildConfig.MIXPANEL_TOKEN, - true, - ) + val mixpanel = + MixpanelAPI.getInstance( + context, + BuildConfig.MIXPANEL_TOKEN, + false, + )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.kt` around lines 27 - 33, The Mixpanel client is being created with trackAutomaticEvents=true which causes automatic events like $app_open and App Session to be sent; update the MixpanelAPI.getInstance call in AnalyticsModule (the invocation that constructs MixpanelAPI.getInstance(context, BuildConfig.MIXPANEL_TOKEN, true) and the subsequent MixpanelAnalytics instantiation) to pass false for the trackAutomaticEvents parameter so only custom events are tracked.feature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeScreen.kt (1)
368-379: ⚡ Quick win
onIntent에rememberUpdatedState사용 권장
DisposableEffect(Unit)은 최초 컴포지션 시 한 번만 실행되므로,onDispose람다 내부의onIntent는 첫 컴포지션 시점의 참조를 캡처합니다. 부모에서onIntent가 새 람다 인스턴스로 교체될 경우onDispose에서 오래된 참조를 사용하게 됩니다.rememberUpdatedState로 항상 최신 값을 참조하도록 하는 것이 Compose 관용적 패턴입니다.♻️ 제안 수정
+ val currentOnIntent by rememberUpdatedState(onIntent) val enterTimeMs = remember { System.currentTimeMillis() } DisposableEffect(Unit) { - onIntent(HomeIntent.OnFeedScreenEntered(firstVisibleItemIndex = listState.firstVisibleItemIndex)) + currentOnIntent(HomeIntent.OnFeedScreenEntered(firstVisibleItemIndex = listState.firstVisibleItemIndex)) onDispose { - onIntent( + currentOnIntent( HomeIntent.OnFeedScreenExited( lastVisibleItemIndex = listState.firstVisibleItemIndex, timeSpentSeconds = (System.currentTimeMillis() - enterTimeMs) / 1000f, ), ) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeScreen.kt` around lines 368 - 379, The DisposableEffect currently captures the original onIntent reference causing stale callbacks on dispose; wrap onIntent with rememberUpdatedState (e.g., val currentOnIntent = rememberUpdatedState(onIntent)) and call currentOnIntent.value(...) inside the DisposableEffect/onDispose (for the OnFeedScreenEntered and OnFeedScreenExited invocations) so the latest lambda is always used while keeping enterTimeMs and listState usage the same.gradle/libs.versions.toml (1)
43-44: 💤 Low valueMixpanel Android 버전 업데이트 검토 권장
현재 사용 중인 7.5.3은 최신 7.x 버전(7.5.4)보다 하나의 패치 버전이 뒤쳐져 있습니다. 또한 최신 주요 버전은 8.6.0이며, 8.x 시리즈로의 업그레이드를 고려할 수 있습니다. 다만 보안 취약점은 발견되지 않았으므로, 필요에 따라 선택적으로 진행하면 됩니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@gradle/libs.versions.toml` around lines 43 - 44, The libs.versions.toml entry for mixpanel currently pins "mixpanel" to "7.5.3"; update this value to "7.5.4" to pick up the latest 7.x patch (or to "8.6.0" if you decide to move to the 8.x major), then run the Android build and unit/integration tests to verify compatibility; also review Mixpanel release notes/changelog for any breaking changes or migration steps before choosing 8.6.0 and update any initialization code if required (search for occurrences of the mixpanel dependency or initialization code in the project to adjust usage).feature/upload/src/main/java/com/sseotdabwa/buyornot/feature/upload/ui/UploadViewModel.kt (1)
49-72: ⚡ Quick win
lastTouchedField값은 상수나 enum으로 묶어두는 편이 좋겠습니다.지금처럼 문자열이 여러 군데 흩어져 있으면 오타 하나로
vote_create_abandoned.last_step스키마가 조용히 깨집니다. 이 값들은 상태값이면서 동시에 분석 계약이라서, 컴파일 타임에 묶어두는 쪽이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/upload/src/main/java/com/sseotdabwa/buyornot/feature/upload/ui/UploadViewModel.kt` around lines 49 - 72, Several places set lastTouchedField using raw string literals (e.g., "category", "price", "link", "title", "content", "images") which is brittle; replace these literals with a single source of truth by introducing a typed enum or sealed class (e.g., LastTouchedField) and update the UploadViewState.lastTouchedField type accordingly, then replace all assignments inside the UploadViewModel intent handlers (the updateState calls in branches like UploadIntent.UpdatePrice, UpdateTitle, UpdateContent, AddImages, etc.) to use LastTouchedField.CATEGORY, .PRICE, .LINK, .TITLE, .CONTENT, .IMAGES (or equivalent constants) so the value is checked at compile time and cannot drift via typos.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/AnalyticsEvent.kt`:
- Around line 24-28: The VoteCreateCompleted analytics event currently includes
the raw user input field voteTitle which risks leaking sensitive data; change
the data class definition (data class VoteCreateCompleted) to stop sending the
raw title and instead include privacy-preserving attributes such as titleLength:
Int and hasTitle: Boolean, or a predefined titleCategory: String (or a one-way
hash if you must correlate without exposing content) so external analytics never
receive the original voteTitle text.
In
`@core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/MixpanelAnalytics.kt`:
- Around line 39-42: Don't send the raw free-text voteTitle to external
analytics: in the AnalyticsEvent.VoteCreateCompleted handling (where
props.put("vote_title", voteTitle) is used) remove or stop populating
"vote_title" with the original string and instead add derived, non-identifying
features such as vote_title_length (integer), vote_title_is_empty (boolean) and
vote_title_length_bucket (e.g., "0","1-20","21-100", etc.); update the props.put
calls around AnalyticsEvent.VoteCreateCompleted to use these derived fields
(refer to props and voteTitle usage in MixpanelAnalytics.kt /
AnalyticsEvent.VoteCreateCompleted) so no raw user input is sent.
In
`@feature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeViewModel.kt`:
- Around line 269-279: The analytics.track(AnalyticsEvent.VoteSubmitted(...))
call is executed before the network vote is confirmed, causing
failed/rolled-back votes to be logged; move this tracking call into the API
success handler so only confirmed votes are sent. Locate the optimistic-vote
block in HomeViewModel where analytics.track is called (use symbols
AnalyticsEvent.VoteSubmitted, targetFeed, optionIndex, FeedCategory.entries) and
remove it from the pre-request path, then invoke the same analytics.track(...)
inside the onSuccess/response callback that currently handles the successful
vote (the code that performs rollback on failure around the existing API
response handling). Ensure the feedId, voteChoice and feedCategory values are
computed the same way when called from the success callback.
In
`@feature/upload/src/main/java/com/sseotdabwa/buyornot/feature/upload/ui/UploadViewModel.kt`:
- Around line 163-170: The analytics.track call inside the onSuccess lambda
(tracking AnalyticsEvent.VoteCreateCompleted) can throw and abort the success
flow; wrap the tracking call in a safe catcher (e.g., runCatching {
analytics.track(...) } or try/catch) so any exception is swallowed or logged but
does not prevent the remaining success handling (clearing isLoading and
navigating home) in the onSuccess block; ensure you reference the onSuccess
lambda, analytics.track, and AnalyticsEvent.VoteCreateCompleted when making the
change.
---
Nitpick comments:
In
`@core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.kt`:
- Around line 27-33: The Mixpanel client is being created with
trackAutomaticEvents=true which causes automatic events like $app_open and App
Session to be sent; update the MixpanelAPI.getInstance call in AnalyticsModule
(the invocation that constructs MixpanelAPI.getInstance(context,
BuildConfig.MIXPANEL_TOKEN, true) and the subsequent MixpanelAnalytics
instantiation) to pass false for the trackAutomaticEvents parameter so only
custom events are tracked.
In
`@feature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeScreen.kt`:
- Around line 368-379: The DisposableEffect currently captures the original
onIntent reference causing stale callbacks on dispose; wrap onIntent with
rememberUpdatedState (e.g., val currentOnIntent =
rememberUpdatedState(onIntent)) and call currentOnIntent.value(...) inside the
DisposableEffect/onDispose (for the OnFeedScreenEntered and OnFeedScreenExited
invocations) so the latest lambda is always used while keeping enterTimeMs and
listState usage the same.
In
`@feature/upload/src/main/java/com/sseotdabwa/buyornot/feature/upload/ui/UploadViewModel.kt`:
- Around line 49-72: Several places set lastTouchedField using raw string
literals (e.g., "category", "price", "link", "title", "content", "images") which
is brittle; replace these literals with a single source of truth by introducing
a typed enum or sealed class (e.g., LastTouchedField) and update the
UploadViewState.lastTouchedField type accordingly, then replace all assignments
inside the UploadViewModel intent handlers (the updateState calls in branches
like UploadIntent.UpdatePrice, UpdateTitle, UpdateContent, AddImages, etc.) to
use LastTouchedField.CATEGORY, .PRICE, .LINK, .TITLE, .CONTENT, .IMAGES (or
equivalent constants) so the value is checked at compile time and cannot drift
via typos.
In `@gradle/libs.versions.toml`:
- Around line 43-44: The libs.versions.toml entry for mixpanel currently pins
"mixpanel" to "7.5.3"; update this value to "7.5.4" to pick up the latest 7.x
patch (or to "8.6.0" if you decide to move to the 8.x major), then run the
Android build and unit/integration tests to verify compatibility; also review
Mixpanel release notes/changelog for any breaking changes or migration steps
before choosing 8.6.0 and update any initialization code if required (search for
occurrences of the mixpanel dependency or initialization code in the project to
adjust usage).
🪄 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: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 6b8c13c7-ba19-4f97-a518-a0f12b217c3b
📒 Files selected for processing (18)
.github/workflows/android-ci.yml.github/workflows/distribute.ymlapp/build.gradle.ktscore/analytics/build.gradle.ktscore/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/Analytics.ktcore/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/AnalyticsEvent.ktcore/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/DebugAnalytics.ktcore/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/MixpanelAnalytics.ktcore/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.ktfeature/home/build.gradle.ktsfeature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeContract.ktfeature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeScreen.ktfeature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeViewModel.ktfeature/upload/build.gradle.ktsfeature/upload/src/main/java/com/sseotdabwa/buyornot/feature/upload/ui/UploadContract.ktfeature/upload/src/main/java/com/sseotdabwa/buyornot/feature/upload/ui/UploadViewModel.ktgradle/libs.versions.tomlsettings.gradle.kts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🛠 Related issue
closed #105
어떤 변경사항이 있었나요?
✅ CheckPoint
✏️ Work Description
Mixpanel 기반 이벤트 로깅 시스템을 구축하고, 피드/투표 생성 흐름에 대한 이벤트를 연동합니다.
아키텍처
core/analytics신규 모듈 추가Analytics인터페이스 +AnalyticsEventsealed classDebugAnalytics(Logcat 출력)MixpanelAnalytics(실제 전송)SingletonComponent로 DI,local.properties에서mixpanel.token읽음로깅 이벤트
feed_viewedfirst_visible_item_indexfeed_exitedtime_spent_seconds,last_visible_item_indexvote_submittedfeed_id,vote_choice,feed_categoryvote_create_startedentry_source,is_logged_invote_create_completeditem_id,vote_title,option_countvote_create_abandonedfilled_fields,last_step😅 Uncompleted Tasks
📢 To Reviewers
MIXPANEL_TOKENsecret을 GitHub Actions에 등록해야 CI/CD 빌드가 정상 동작합니다 (Settings → Secrets and variables → Actions)local.properties에mixpanel.token=<실제토큰>추가 필요 (로컬 빌드 시)tag: Analytics)에만 출력됩니다📃 RCA 룰
Summary by CodeRabbit
새로운 기능