Skip to content

Fix/#111: MixPanel 슈퍼 속성 누락 및 FeedExited lastVisibleItemIndex 오류 수정#112

Merged
DongChyeon merged 3 commits into
developfrom
bugfix/#111-fix-analytics-super-properties-and-last-visible-index
May 11, 2026
Merged

Fix/#111: MixPanel 슈퍼 속성 누락 및 FeedExited lastVisibleItemIndex 오류 수정#112
DongChyeon merged 3 commits into
developfrom
bugfix/#111-fix-analytics-super-properties-and-last-visible-index

Conversation

@DongChyeon
Copy link
Copy Markdown
Member

@DongChyeon DongChyeon commented May 11, 2026

🛠 Related issue

closed #111

어떤 변경사항이 있었나요?

  • 🐞 BugFix Something isn't working

✅ CheckPoint

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • PR 컨벤션에 맞게 작성했습니다. (필수)
  • merge할 브랜치의 위치를 확인해 주세요(main❌/develop⭕) (필수)
  • Approve된 PR은 assigner가 머지하고, 수정 요청이 온 경우 수정 후 다시 push를 합니다. (필수)
  • BugFix의 경우, 버그의 원인을 파악하였습니다. (선택)

✏️ Work Description

  • MixpanelAnalytics 초기화 시 platform("android"), app_version 슈퍼 속성 등록
  • Analytics.identify(userId) 인터페이스 추가 — 로그인/로그아웃 시 user_id 슈퍼 속성 갱신 (비로그인 시 null)
  • UserPreferencesDataStoreuserId 저장 추가 — 로그인 후 users/me 응답의 userId를 DataStore에 영속화
  • BuyOrNotViewModel에서 userId Flow 구독 → analytics.identify() 호출로 앱 전역에서 사용자 식별 유지
  • HomeScreen.kt lastVisibleItemIndexlistState.firstVisibleItemIndex 대신 listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index로 수정
  • DisposableEffect(Unit)LifecycleEventEffect(ON_STOP) 교체 — saveState 네비게이션 환경에서 onDispose가 호출되지 않아 feed_exited 이벤트가 누락되던 문제 수정

😅 Uncompleted Tasks

N/A

📢 To Reviewers

  • user_id 슈퍼 속성은 String?으로, 비로그인 상태에서는 JSONObject.NULL로 Mixpanel에 등록되어 속성 자체는 유지되지만 null 값으로 전송됩니다.
  • app_versionAnalyticsModule에서 PackageManager로 런타임에 가져오며 DI로 주입합니다.
  • feed_exited 누락 원인: Jetpack Navigation의 saveState = true 설정 시 홈 화면이 Composition에서 제거되지 않아 DisposableEffect.onDispose가 호출되지 않았습니다. LifecycleEventEffect(ON_STOP) 으로 변경하여 다른 탭 이동 및 백그라운드 전환 시 모두 정상 호출됩니다.

- Analytics 인터페이스에 identify(userId: String?) 추가
- MixpanelAnalytics 초기화 시 platform, app_version, user_id(null) 슈퍼 속성 등록
- DataStore에 userId(Long) 저장 레이어 추가 (datastore → repository)
- 로그인 후 fetchAndStoreUserProfile()에서 userId DataStore 저장
- BuyOrNotViewModel에서 userId 변화 감지 → analytics.identify() 호출
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Warning

Rate limit exceeded

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

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 @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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 03a2da39-0648-4c25-9f88-064786f01f05

📥 Commits

Reviewing files that changed from the base of the PR and between 776760d and 30e0cad.

📒 Files selected for processing (1)
  • feature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeScreen.kt

Walkthrough

사용자 ID 관리 계층을 DataStore부터 도메인까지 추가하고, 로그인 시 저장된 ID를 Mixpanel 분석에 전달하여 사용자 식별을 수행합니다. Mixpanel 슈퍼 속성(platform, app_version, user_id)을 등록하고, 앱 초기화 시 BuyOrNotViewModel에서 userId 변경을 모니터링하여 analytics.identify를 호출합니다. 또한 HomeScreen의 피드 종료 이벤트에서 마지막 보이는 아이템 인덱스를 올바르게 기록합니다.

Changes

사용자 ID 식별 및 분석 통합

Layer / File(s) Summary
도메인 계약 확정
domain/src/main/java/com/sseotdabwa/buyornot/domain/repository/UserPreferencesRepository.kt
userId: Flow<Long> 프로퍼티와 suspend fun updateUserId(userId: Long) 메서드를 인터페이스에 추가합니다.
DataStore 모델 확장
core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferences.kt
UserPreferences 데이터 클래스에 userId: Long = 0L 프로퍼티를 추가합니다.
DataStore 인터페이스 정의
core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferencesDataSource.kt
UserPreferencesDataSource 인터페이스에 userId: Flow<Long> 프로퍼티와 updateUserId 메서드를 선언합니다.
DataStore 구현
core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferencesDataSourceImpl.kt
DataStore 선호도 키 USER_ID를 추가하고, userId 흐름을 구현하며, updateUserId 메서드로 값을 저장하고, clearUserInfo에서도 제거합니다.
저장소 구현
core/data/src/main/java/com/sseotdabwa/buyornot/core/data/repository/UserPreferencesRepositoryImpl.kt
UserPreferencesDataSource.userId를 노출하는 userId 프로퍼티와 updateUserId 메서드를 추가합니다.
분석 인터페이스 확장
core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/Analytics.kt
identify(userId: String?) 메서드를 Analytics 인터페이스에 추가합니다.
디버그 분석 구현
core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/DebugAnalytics.kt
DebugAnalytics에서 identify 메서드를 구현하여 userId를 로깅합니다.
Mixpanel 분석 구현 및 슈퍼 속성
core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/MixpanelAnalytics.kt
생성자에서 appVersion을 받아 초기화 시 Mixpanel 슈퍼 속성(platform, app_version, user_id)을 등록하고, identify 메서드를 구현하여 사용자를 식별합니다.
분석 의존성 주입 설정
core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.kt
AnalyticsModule에서 패키지 매니저로부터 앱 버전을 추출하고 MixpanelAnalytics 생성 시 전달합니다.
로그인 시 사용자 ID 저장
feature/auth/src/main/java/com/sseotdabwa/buyornot/feature/auth/ui/LoginViewModel.kt
fetchAndStoreUserProfile 메서드에서 프로필 저장 시 userPreferencesRepository.updateUserId(profile.id)를 호출합니다.
ViewModel 사용자 ID 식별 연결
app/src/main/java/com/sseotdabwa/buyornot/ui/BuyOrNotViewModel.kt
BuyOrNotViewModelUserPreferencesRepository를 주입받아 init 블록에서 userId 변경을 관찰하고, 0이 아닌 경우 문자열로 변환하여 analytics.identify를 호출합니다.
홈 화면 피드 종료 인덱스 수정
feature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeScreen.kt
HomeFeedListDisposableEffect에서 lastVisibleItemIndexlistState.layoutInfo.visibleItemsInfo.lastOrNull()?.index로 정확하게 계산합니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Nexters/BuyOrNot-Android#43: UserPreferencesRepository 및 그 구현체의 변경으로 사용자 선호도 저장소 계층이 이전 PR과 일관성 있게 확장됩니다.
  • Nexters/BuyOrNot-Android#106: Analytics 인터페이스와 구현체(DebugAnalytics, MixpanelAnalytics)를 직접 수정하여 사용자 식별 기능이 추가됩니다.
  • Nexters/BuyOrNot-Android#58: HomeScreen의 피드 리스트 아이템 인덱스 처리 방식이 수정되어 이전 관련 PR과 함께 작동합니다.

Suggested labels

🛠️ FIX, 💪 동현동현동현

Suggested reviewers

  • Imagine-Choi
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용을 명확하게 요약합니다: MixPanel 슈퍼 속성 누락 및 FeedExited lastVisibleItemIndex 오류 수정.
Linked Issues check ✅ Passed 코드 변경사항이 #111 이슈의 모든 요구사항을 충족합니다: MixpanelAnalytics에 슈퍼 속성 등록, Analytics.identify() 인터페이스 추가, userId 저장 및 관찰, lastVisibleItemIndex 수정.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #111 이슈의 명시된 요구사항과 직접적으로 관련되어 있으며, 범위를 벗어난 변경사항이 없습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 bugfix/#111-fix-analytics-super-properties-and-last-visible-index

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.

@DongChyeon DongChyeon changed the title fix/#111: 애널리틱스 슈퍼 속성 누락 및 FeedExited lastVisibleItemIndex 오류 수정 Fix/#111: MixPanel 슈퍼 속성 누락 및 FeedExited lastVisibleItemIndex 오류 수정 May 11, 2026
@DongChyeon DongChyeon self-assigned this May 11, 2026
@DongChyeon DongChyeon added ✨ FEAT 기능 개발 (애매하면 기능 개발로 두도록 하자) 💪 동현동현동현 labels May 11, 2026
@DongChyeon DongChyeon requested a review from Imagine-Choi May 11, 2026 13:22
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: 1

🧹 Nitpick comments (2)
core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.kt (1)

33-38: ⚡ Quick win

앱 버전 추출이 대체로 안전하지만 방어적 처리를 고려하세요.

getPackageInfo(context.packageName, 0)는 자신의 패키지에 대해서는 예외가 발생하지 않아야 하지만, PackageManager.NameNotFoundException이 이론적으로 가능합니다. Singleton 생성 시점에 예외가 발생하면 앱이 시작되지 않으므로, 방어적으로 try-catch를 추가하는 것이 좋습니다.

🛡️ 제안: 예외 처리 추가
-            val appVersion =
-                context.packageManager
-                    .getPackageInfo(context.packageName, 0)
-                    .versionName
-                    ?: "unknown"
+            val appVersion = try {
+                context.packageManager
+                    .getPackageInfo(context.packageName, 0)
+                    .versionName
+                    ?: "unknown"
+            } catch (e: Exception) {
+                "unknown"
+            }
             MixpanelAnalytics(mixpanel, appVersion)
🤖 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 33 - 38, The code that reads appVersion via
context.packageManager.getPackageInfo(...) should be made defensive: wrap the
call in a try-catch for PackageManager.NameNotFoundException (and optionally
Exception) when constructing appVersion so a failure returns a safe fallback
like "unknown" instead of throwing during singleton initialization; update the
block that computes appVersion (the code that assigns appVersion and then
constructs MixpanelAnalytics) to catch the exception, log the error (using your
project's logger) and set appVersion = "unknown" before passing it into
MixpanelAnalytics.
core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferencesDataSourceImpl.kt (1)

53-54: 💤 Low value

0L을 "미로그인" 센티널 값으로 사용 중입니다.

현재 구현은 userId가 없을 때 0L을 반환하며, 이는 ViewModel에서 null로 변환되어야 합니다. 이는 암묵적인 결합을 만들지만, 일반적으로 백엔드에서 0을 유효한 사용자 ID로 사용하지 않으므로 실용적인 접근입니다.

더 명시적인 대안으로는 Flow<Long?>을 사용할 수 있지만, 현재 구현도 충분히 동작합니다.

🤖 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/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferencesDataSourceImpl.kt`
around lines 53 - 54, The current userId Flow in UserPreferencesDataSourceImpl
returns 0L as a sentinel for "not logged in" (override val userId: Flow<Long> =
context.userPreferencesDataStore.data.map { it[Keys.USER_ID] ?: 0L }), which
obscures semantics; change the API to return a nullable Flow<Long?> by updating
the userId declaration to Flow<Long?> and map missing values to null (map {
it[Keys.USER_ID] }) so callers (e.g., ViewModel) can explicitly handle
logged-out state; update any call sites expecting Flow<Long> to accept
Flow<Long?> accordingly.
🤖 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/MixpanelAnalytics.kt`:
- Around line 26-33: The identify(userId: String?) implementation sets a user_id
super property but doesn't clear Mixpanel's internal distinct_id on logout;
update identify to call mixpanel.reset() when userId is null (or add and call a
new Analytics.reset() method that invokes mixpanel.reset()) and when a non-null
userId is provided still call mixpanel.identify(userId) and set the super
property via mixpanel.registerSuperProperties; ensure both identify and the
new/reset method use mixpanel.reset()/mixpanel.identify and
mixpanel.registerSuperProperties consistently so events after logout get a fresh
anonymous distinct_id.

---

Nitpick comments:
In
`@core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.kt`:
- Around line 33-38: The code that reads appVersion via
context.packageManager.getPackageInfo(...) should be made defensive: wrap the
call in a try-catch for PackageManager.NameNotFoundException (and optionally
Exception) when constructing appVersion so a failure returns a safe fallback
like "unknown" instead of throwing during singleton initialization; update the
block that computes appVersion (the code that assigns appVersion and then
constructs MixpanelAnalytics) to catch the exception, log the error (using your
project's logger) and set appVersion = "unknown" before passing it into
MixpanelAnalytics.

In
`@core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferencesDataSourceImpl.kt`:
- Around line 53-54: The current userId Flow in UserPreferencesDataSourceImpl
returns 0L as a sentinel for "not logged in" (override val userId: Flow<Long> =
context.userPreferencesDataStore.data.map { it[Keys.USER_ID] ?: 0L }), which
obscures semantics; change the API to return a nullable Flow<Long?> by updating
the userId declaration to Flow<Long?> and map missing values to null (map {
it[Keys.USER_ID] }) so callers (e.g., ViewModel) can explicitly handle
logged-out state; update any call sites expecting Flow<Long> to accept
Flow<Long?> accordingly.
🪄 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: 853eb55c-f228-4645-9b0c-0331f58c4936

📥 Commits

Reviewing files that changed from the base of the PR and between bdcc1e2 and 776760d.

📒 Files selected for processing (12)
  • app/src/main/java/com/sseotdabwa/buyornot/ui/BuyOrNotViewModel.kt
  • core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/Analytics.kt
  • core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/DebugAnalytics.kt
  • core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/MixpanelAnalytics.kt
  • core/analytics/src/main/java/com/sseotdabwa/buyornot/core/analytics/di/AnalyticsModule.kt
  • core/data/src/main/java/com/sseotdabwa/buyornot/core/data/repository/UserPreferencesRepositoryImpl.kt
  • core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferences.kt
  • core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferencesDataSource.kt
  • core/datastore/src/main/java/com/sseotdabwa/buyornot/core/datastore/UserPreferencesDataSourceImpl.kt
  • domain/src/main/java/com/sseotdabwa/buyornot/domain/repository/UserPreferencesRepository.kt
  • feature/auth/src/main/java/com/sseotdabwa/buyornot/feature/auth/ui/LoginViewModel.kt
  • feature/home/src/main/java/com/sseotdabwa/buyornot/feature/home/ui/HomeScreen.kt

saveState/restoreState 네비게이션에서 DisposableEffect(Unit)의 onDispose가
호출되지 않는 문제를 ON_STOP 라이프사이클 이벤트로 대체하여 수정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
saveState 네비게이션 환경에서 HomeFeedList가 Composition에 유지되므로
재방문마다 enterTimeMs를 갱신하지 않으면 누적 시간이 전송되는 문제 수정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@DongChyeon DongChyeon merged commit dd80ed5 into develop May 11, 2026
1 check passed
@DongChyeon DongChyeon deleted the bugfix/#111-fix-analytics-super-properties-and-last-visible-index branch May 11, 2026 13:45
@coderabbitai coderabbitai Bot mentioned this pull request May 11, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ FEAT 기능 개발 (애매하면 기능 개발로 두도록 하자) 💪 동현동현동현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐞 BugFix - 애널리틱스 슈퍼 속성 누락 및 FeedExited lastVisibleItemIndex 오류

1 participant