Skip to content

Conversation

@kez-lab
Copy link
Member

@kez-lab kez-lab commented May 4, 2025

개요

  • 서버 로그인/회원가입 API 의존성 제거
  • Room DB를 활용한 로컬 로그인 구조 구현
  • Result 타입을 활용한 예외 처리 개선

작업 사항

  • 서버 로그인 관련 코드 제거 (AuthService, LoginRequest/Response, SignUpRequest/Response)
  • Room DB에 UserAuth, UserProfile 엔티티 추가
  • AuthRepository 인터페이스 개선 (Result 패턴 적용)
  • 로그인 상태와 사용자 정보를 Flow로 제공하는 구조로 변경
  • LocalAuthRepository 구현체 추가 (Room DB 연동)
  • SocialAuthDataStore 인터페이스와 KakaoSocialAuthDataStoreImpl 구현체 추가
  • AuthUseCase 및 관련 ViewModel 구조 개선

변경 사항

  • 서버 인증 로직이 완전히 제거되고 카카오 API와 Room DB를 사용하는 로컬 로그인 구조로 대체됨
  • Room DB 버전 1 → 2로 업데이트 (마이그레이션 추가)

Summary by CodeRabbit

  • New Features

    • Improved splash screen animation speed and streamlined transition to the main activity after animation completion.
  • Bug Fixes

    • Updated permission checks for usage stats to improve reliability across Android versions.
  • Refactor

    • Simplified onboarding and profile flows by removing authentication and point management features.
    • Removed all login and point-related screens, logic, and dependencies, including third-party login integration.
    • Streamlined user profile and challenge features by eliminating point tracking and related UI elements.
  • Chores

    • Cleaned up unused dependencies, build scripts, and resource files.
    • Removed Kakao SDK integration and associated configuration from the project.
    • Updated theme and resource files to reflect the removal of point and login features.
  • Documentation

    • Updated string resources and XML layouts to match new app flows and remove references to deprecated features.

@kez-lab kez-lab requested a review from a team as a code owner May 4, 2025 12:34
@kez-lab kez-lab self-assigned this May 4, 2025
@kez-lab kez-lab added 🐱의진 의진이 작업 🔎refact 내부 로직은 변경하지 않고 기존의 코드를 개선하는 리팩토링 ✂️delete 불필요한 코드 삭제 labels May 4, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 3, 2025

Walkthrough

This change removes all Kakao login, authentication, and point-related features from the codebase. It deletes associated modules, classes, repositories, network services, UI elements, and dependencies. The build configuration, navigation, and resource files are updated to reflect these removals. Some onboarding and profile flows are simplified, and related data models and interfaces are eliminated.

Changes

Cohort / File(s) Change Summary
Kakao Login & Auth Removal
feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt, feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt, feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewPagerAdapter.kt, feature/login/src/main/AndroidManifest.xml, feature/login/src/main/res/layout/activity_login.xml, feature/login/src/main/res/values/strings.xml, feature/login/build.gradle.kts, app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt, app/src/main/AndroidManifest.xml, app/build.gradle.kts, app/proguard-rules.pro, build-logic/convention/src/main/kotlin/com/hmh/hamyeonham/plugin/CommonConfigs.kt, gradle/libs.versions.toml, settings.gradle.kts
Removed Kakao login UI, logic, resources, SDK initialization, dependencies, and build configuration. Deleted login activities, view models, adapters, and related manifest entries.
Point Feature Removal
feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointActivity.kt, feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointAdapter.kt, feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointViewModel.kt, feature/challenge/src/main/res/layout/activity_point.xml, feature/challenge/src/main/res/layout/fragment_challenge.xml, feature/challenge/src/main/AndroidManifest.xml, feature/challenge/build.gradle.kts, feature/main/src/main/java/com/hmh/hamyeonham/feature/main/MainActivity.kt, feature/main/src/main/res/values/strings.xml, core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/viewmodel/MainEffect.kt, core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/viewmodel/MainViewModel.kt, core/viewmodel/main/build.gradle.kts, feature/mypage/build.gradle.kts, feature/mypage/src/main/res/layout/fragment_my_page.xml, feature/mypage/src/main/res/values/strings.xml, data/point/*, domain/point/*, core/network/src/main/java/com/hmh/hamyeonham/core/network/point/*, core/network/src/main/java/com/hmh/hamyeonham/core/network/di/PointModule.kt
Removed point management UI, logic, adapters, view models, network services, repositories, domain models, and all associated resources and dependencies.
Login Domain & Data Layer Removal
domain/login/*, data/login/*, core/network/src/main/java/com/hmh/hamyeonham/core/network/login/*, core/network/src/main/java/com/hmh/hamyeonham/core/network/di/AuthModule.kt
Deleted domain and data modules for login, including repositories, models, mappers, and network services. Removed related build scripts and manifests.
Network Auth/DataStore Refactor
core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/AuthenticatorUtil.kt, core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt, core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt, core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/HeaderInterceptor.kt, core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/DefaultUserPreference.kt, core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/UserPreference.kt, core/network/src/main/java/com/hmh/hamyeonham/core/network/di/DataStoreModule.kt, core/network/build.gradle.kts
Removed AuthenticatorUtil and refactored network authentication to use UserPreference instead of HMHNetworkPreference. Updated interceptors and DI modules accordingly.
Navigation & Common Interface Changes
app/src/main/java/com/hmh/hamyeonham/navigation/DefaultNavigationProvider.kt, core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt
Removed navigation methods for login and point features from navigation provider interfaces and implementations.
Onboarding/Profile Simplification
feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt, feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt, feature/onboarding/build.gradle.kts, data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/mapper/MypageMapper.kt, data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/DefaultUserInfoRepository.kt, domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt
Removed access token and sign-up logic from onboarding. Simplified user profile mapping and data models by removing point-related fields.
Database Migration
core/database/src/main/java/com/hmh/hamyeonham/core/database/HMHRoomDatabase.kt, core/database/src/main/java/com/hmh/hamyeonham/core/database/di/DatabaseModule.kt
Added migration for new user-related tables in Room database and updated DI to include migration.
Permission & Qualifier Cleanup
core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionActivity.kt, core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionExt.kt, core/common/src/main/java/com/hmh/hamyeonham/common/qualifier/OAuth.kt
Updated permission checks and removed unused Kakao qualifier annotation.
Design System/Theme
core/designsystem/src/main/res/values/themes.xml
Added edge-to-edge enforcement attribute in theme XML.
Workflow & CI
.github/workflows/develop_PR_builder.yml, .github/workflows/release.yml
Removed Kakao API key from workflow environment variables.
Splash Animation
app/src/main/res/layout/activity_sample.xml
Increased Lottie animation playback speed for splash screen.

Sequence Diagram(s)

Omitted due to the large-scale and multi-feature nature of the changes.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related PRs

Suggested reviewers

  • kangyuri1114

Poem

In fields where logins hopped and points would sprout,
The bunnies have cleared the old code out!
Kakao’s gone, and points have flown,
The garden’s now simpler, freshly sown.
With lighter paws and cleaner views,
We leap ahead—no more login queues!
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/remove_server_login

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 12

🔭 Outside diff range comments (1)
core/database/src/main/java/com/hmh/hamyeonham/core/database/HMHRoomDatabase.kt (1)

51-62: Avoid using GlobalScope for database operations

Using GlobalScope is discouraged as it can lead to memory leaks and uncontrolled coroutine lifecycles. Consider using a more controlled scope or making this a suspend function.

Replace GlobalScope with a suspend function:

-    @OptIn(DelicateCoroutinesApi::class)
-    fun deleteAll() {
-        GlobalScope.launch {
-            usageGoalsDao().deleteAll()
-            usageTotalGoalDao().deleteAll()
-            challengeDao().deleteAll()
-            deletedGoalsDao().deleteAll()
-            lockDao().deleteAll()
-            userAuthDao().clearUserAuth()
-            userProfileDao().clearUserProfile()
-        }
+    suspend fun deleteAll() {
+        usageGoalsDao().deleteAll()
+        usageTotalGoalDao().deleteAll()
+        challengeDao().deleteAll()
+        deletedGoalsDao().deleteAll()
+        lockDao().deleteAll()
+        userAuthDao().clearUserAuth()
+        userProfileDao().clearUserProfile()
     }
♻️ Duplicate comments (1)
domain/login/src/main/java/com/hmh/hamyeonham/login/model/SignUpUser.kt (1)

3-7: Mirror the Login change – same migration caveat applies.

Same comment as for Login.userId; ensure the DB & serialization layers can ingest / emit Long seamlessly.

🧹 Nitpick comments (24)
core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt (2)

4-4: Unused import android.app.Activity

Activity is not referenced anywhere in this file. Keeping unused imports will trigger IDE / lint warnings and clutters the diff.
Remove it (or add the intended extension/utility that requires it).


7-7: Unused import android.content.ContextWrapper

Same as above – the symbol is currently unused. Delete the import or introduce the code that depends on it.

app/src/main/res/layout/activity_sample.xml (2)

36-36: Animation speed increase looks good but consider synchronization.

The logo animation speed increase to 1.5 aligns well with the new animation-driven navigation flow in StartActivity.kt. However, consider whether the background animation (currently at speed 1.0) should also be adjusted to maintain visual harmony between the two animations.


36-36: Consider the user experience impact of faster animation speed.

Increasing the logo animation speed from 1.0 to 1.5 will make the splash screen complete 50% faster. While this improves app startup time, consider:

  1. Visual quality: Ensure the animation still looks smooth at 1.5x speed
  2. Accessibility: Some users may need more time to process the splash screen visually
  3. Synchronization: The background animation (line 21) remains at speed 1.0, which may create visual inconsistency

Consider either:

  • Synchronizing both animations to the same speed for visual consistency
  • Testing the animation at 1.5x speed to ensure it maintains quality and readability
    </review_comment_end>
app/src/main/java/com/hmh/hamyeonham/StartActivity.kt (6)

29-33: Consider more robust animation completion detection.

The animation completion listener implementation is clean and effective. However, consider using a more robust completion check to handle potential floating-point precision issues.

Apply this diff for more reliable completion detection:

 binding.splashLottieAppLogo.addAnimatorUpdateListener {
-    if (it.animatedFraction == 1.0f) {
+    if (it.animatedFraction >= 1.0f) {
         navigateToLogin()
     }
 }

17-17: Remove unused CoroutineScope.

The coroutineScope property is declared but never used in the current implementation.

Apply this diff to clean up unused code:

-private val coroutineScope = CoroutineScope(Dispatchers.Main)

Also remove the unused import:

-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers

17-17: Remove unused CoroutineScope.

The coroutineScope property is declared but not used in the current implementation since the modal dialog logic was removed.

-private val coroutineScope = CoroutineScope(Dispatchers.Main)

Also remove the unused import:

-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers

27-34: Consider adding animation failure safety mechanism.

While the animation-driven navigation is cleaner than the previous modal dialog approach, consider adding a safety mechanism in case the animation fails to complete (e.g., due to device performance issues or animation file corruption).

You could add a timeout mechanism:

private fun initLottieSplash() {
    binding.splashLottieAppLogo.playAnimation()
    
    var hasNavigated = false
    
    binding.splashLottieAppLogo.addAnimatorUpdateListener { animator ->
        if (animator.animatedFraction >= 0.99f && !hasNavigated) {
            hasNavigated = true
            binding.splashLottieAppLogo.removeAllUpdateListeners()
            navigateToLogin()
        }
    }
    
    // Safety timeout after 3 seconds
    binding.splashLottieAppLogo.postDelayed({
        if (!hasNavigated) {
            hasNavigated = true
            navigateToLogin()
        }
    }, 3000)
}

29-33: Consider more robust animation completion detection.

The animation completion logic is clean, but using exact floating-point comparison (== 1.0f) can be unreliable due to precision issues. Consider using a threshold-based approach or the animation's end callback.

-        binding.splashLottieAppLogo.addAnimatorUpdateListener {
-            if (it.animatedFraction == 1.0f) {
-                navigateToLogin()
-            }
-        }
+        binding.splashLottieAppLogo.addAnimatorListener(object : android.animation.Animator.AnimatorListener {
+            override fun onAnimationEnd(animation: android.animation.Animator) {
+                navigateToLogin()
+            }
+            override fun onAnimationStart(animation: android.animation.Animator) {}
+            override fun onAnimationCancel(animation: android.animation.Animator) {}
+            override fun onAnimationRepeat(animation: android.animation.Animator) {}
+        })

Alternatively, if you prefer the update listener approach:

-            if (it.animatedFraction == 1.0f) {
+            if (it.animatedFraction >= 0.99f) {

17-17: Remove unused coroutineScope.

The coroutineScope field appears to be unused after removing the dialog logic. Consider removing it to clean up the code.

-    private val coroutineScope = CoroutineScope(Dispatchers.Main)

Also remove the unused import:

-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
domain/login/src/main/java/com/hmh/hamyeonham/login/model/User.kt (1)

8-18: Resolve the design uncertainty and finalize the User model structure.

The TODO comment indicates uncertainty about domain-level user data requirements. The commented code suggests a comprehensive user model with social login integration, synchronization status, and various identifiers. Consider finalizing this design decision to avoid technical debt.

The commented code shows a well-structured user model that includes:

  • Social login properties (Kakao integration)
  • Synchronization tracking (connectedAt, synchedAt)
  • User lifecycle flags (hasSignedUp)
  • Multiple identifier types (id, uuid)

Would you like me to help analyze the authentication flow requirements and suggest which properties should be included in the final User model?

data/login/build.gradle.kts (1)

10-18: Review network dependency usage in data/login

Our automated searches across all .kt and .java files under data/login did not find any references to the core.network module. Please manually verify there are no indirect usages (for example, DI bindings or extension functions) before dropping this dependency.

• No occurrences of core.network in source files
• No import …core.network statements detected

If you confirm it’s truly unused, remove the line from data/login/build.gradle.kts:

-    implementation(projects.core.network)
domain/login/src/main/java/com/hmh/hamyeonham/login/di/AuthProvider.kt (1)

1-7: Enum location: consider moving out of di package.

AuthProvider is a pure domain concept but it lives under a di (dependency-injection) package, which usually only hosts Dagger/Hilt annotations & modules.
Placing the enum under …login.model (or …login.type) keeps package semantics clean and avoids accidental coupling with DI infrastructure.

No functional impact, just package hygiene.

-package com.hmh.hamyeonham.login.di
+package com.hmh.hamyeonham.login.model
core/database/src/main/java/com/hmh/hamyeonham/core/database/di/DatabaseModule.kt (2)

5-5: Verify BuildConfig usage.

The BuildConfig import was added but doesn't appear to be used in the visible code. Consider removing it if not needed.


5-5: Potentially unused import.

The BuildConfig import appears to be unused in the current code. Consider removing it unless it's needed for conditional database configuration.

domain/login/src/main/java/com/hmh/hamyeonham/login/repository/SocialAuthDataStore.kt (1)

9-33: Well-designed abstraction for social authentication providers.

The interface provides a clean contract for social login implementations with appropriate async support. The separation of concerns between authentication state and user data is good architectural design.

Consider using a Result pattern instead of nullable return types for better error handling and to distinguish between "no user found" vs "network error" scenarios.

interface SocialAuthDataStore {
-    suspend fun login(): UserInfo?
+    suspend fun login(): Result<UserInfo>
    
-    suspend fun refreshUserInfo(): UserInfo?
+    suspend fun refreshUserInfo(): Result<UserInfo>
}
domain/login/src/main/java/com/hmh/hamyeonham/login/model/UserInfo.kt (1)

6-21: Consider the relationship between UserInfo.id and Profile.userId.

The model has both UserInfo.id and Profile.userId which could be confusing. Clarify the relationship - are these always the same value, or do they serve different purposes?

Also consider if Long type for IDs is appropriate for all social login providers, as some might use string-based identifiers.

data class UserInfo(
    val id: Long,
    val profile: Profile? = null
) {
    data class Profile(
-        val userId: Long,
+        // Remove userId if it's always the same as UserInfo.id
         val nickname: String? = null,
         val profileImageUrl: String? = null,
         val email: String? = null,
         val ageRange: String? = null,
         val gender: String? = null
     )
}
core/database/src/main/java/com/hmh/hamyeonham/core/database/dao/UserProfileDao.kt (1)

21-22: Consider renaming clearUserProfile() for clarity.

The method clearUserProfile() deletes all user profiles from the table, not just a single user's profile. Consider renaming to clearAllUserProfiles() to make the scope clearer.

-    @Query("DELETE FROM user_profile")
-    suspend fun clearUserProfile()
+    @Query("DELETE FROM user_profile")
+    suspend fun clearAllUserProfiles()
core/database/src/main/java/com/hmh/hamyeonham/core/database/dao/UserAuthDao.kt (1)

12-26: Consider adding @transaction annotation for data consistency

The DAO operations that modify user authentication state should be wrapped in a transaction to ensure data consistency, especially when called in sequence.

Add the @Transaction annotation to methods that might be called together:

 @Dao
 interface UserAuthDao {
     @Query("SELECT * FROM user_auth WHERE id = 1")
     suspend fun getUserAuth(): UserAuth?
     
     @Query("SELECT * FROM user_auth WHERE id = 1")
     fun observeUserAuth(): Flow<UserAuth?>
     
+    @Transaction
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     suspend fun insertOrUpdateUserAuth(userAuth: UserAuth)
     
+    @Transaction
     @Query("UPDATE user_auth SET isLoggedIn = :isLoggedIn WHERE id = 1")
     suspend fun updateLoginStatus(isLoggedIn: Boolean)
     
+    @Transaction
     @Query("DELETE FROM user_auth")
     suspend fun clearUserAuth()
 }
domain/login/src/main/java/com/hmh/hamyeonham/login/usecase/AuthUseCase.kt (1)

40-44: Consider implementing proper withdrawal logic

The withdrawal method currently only performs logout. Consider implementing proper user data deletion or at least documenting the expected behavior more clearly.

Would you like me to help implement a proper withdrawal method that clears user data from the database? I can open an issue to track this task.

data/login/src/main/java/com/hmh/hamyeonham/login/repository/LocalAuthRepository.kt (1)

102-108: Consider optimizing observeUserProfile with JOIN query

The current implementation triggers a separate profile query for each auth state change, which could be optimized.

Consider adding a method in UserAuthDao that returns a combined Flow of auth and profile data using a JOIN query. This would reduce the number of database queries and improve performance.

Would you like me to help create an optimized DAO method that combines user auth and profile data in a single query?

feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt (1)

175-182: Remove unnecessary object creation.

The getRequestDomain(state) call on line 178 creates an object that isn't used, which is wasteful.

 fun signUp() {
     viewModelScope.launch {
-        val state = onBoardingState.value
-        val request = getRequestDomain(state) // TODO 나중에 온보딩 데이터를 쓰고 싶은 경우
+        // TODO 나중에 온보딩 데이터를 쓰고 싶은 경우
         _onboardEffect.emit(OnboardEffect.OnboardSuccess)
         AmplitudeUtils.trackEventWithProperties("complete_onboarding_finish")
     }
 }
data/login/src/main/java/com/hmh/hamyeonham/login/datasource/kakao/KakaoSocialAuthDataStoreImpl.kt (1)

52-58: Add logging for debugging failed user info refresh.

Similar to the login method, exceptions are swallowed without logging.

 override suspend fun refreshUserInfo(): UserInfo? {
     return try {
         fetchUserInfo()
     } catch (e: Exception) {
+        android.util.Log.e("KakaoAuth", "Failed to refresh user info", e)
         null
     }
 }
domain/login/src/main/java/com/hmh/hamyeonham/login/model/LoginInfo.kt (1)

3-3: Consider relocating AuthProvider enum.

The AuthProvider enum is imported from the di package, which typically contains dependency injection modules rather than domain models. Consider moving AuthProvider to a more appropriate package like model or common.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45a2f4a and 327e78a.

📒 Files selected for processing (50)
  • app/src/main/java/com/hmh/hamyeonham/StartActivity.kt (1 hunks)
  • app/src/main/res/layout/activity_sample.xml (1 hunks)
  • core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt (1 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/HMHRoomDatabase.kt (4 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/dao/UserAuthDao.kt (1 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/dao/UserProfileDao.kt (1 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/di/DaoModule.kt (1 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/di/DatabaseModule.kt (2 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/entity/UserAuth.kt (1 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/entity/UserProfile.kt (1 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/AuthenticatorUtil.kt (2 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt (2 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/DefaultUserPreference.kt (2 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/UserPreference.kt (1 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/HeaderInterceptor.kt (1 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/di/AuthModule.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/di/DataStoreModule.kt (2 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/login/AuthService.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/login/model/LoginRequest.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/login/model/LoginResponse.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/signup/model/SignUpRequest.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/signup/model/SignUpResponse.kt (0 hunks)
  • data/login/build.gradle.kts (1 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/DefaultAuthRepository.kt (0 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/datasource/kakao/KakaoAuthCodeClient.kt (1 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/datasource/kakao/KakaoSocialAuthDataStoreImpl.kt (1 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/di/AuthBinder.kt (1 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/di/AuthDataSourceKey.kt (1 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/di/AuthModule.kt (0 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/mapper/LoginMapper.kt (0 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/mapper/UserProfileMapper.kt (1 hunks)
  • data/login/src/main/java/com/hmh/hamyeonham/login/repository/LocalAuthRepository.kt (1 hunks)
  • domain/login/build.gradle.kts (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/di/AuthProvider.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/Login.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/LoginInfo.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/SignUpUser.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/User.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/UserInfo.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/UserProfile.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/repository/AuthRepository.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/repository/SocialAuthDataStore.kt (1 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/usecase/AuthUseCase.kt (1 hunks)
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt (1 hunks)
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt (1 hunks)
  • feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/MyPageFragment.kt (2 hunks)
  • feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/viewmodel/MyPageViewModel.kt (2 hunks)
  • feature/mypage/src/main/res/values/strings.xml (1 hunks)
  • feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt (1 hunks)
  • feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt (2 hunks)
💤 Files with no reviewable changes (9)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/login/model/LoginResponse.kt
  • data/login/src/main/java/com/hmh/hamyeonham/login/mapper/LoginMapper.kt
  • data/login/src/main/java/com/hmh/hamyeonham/login/di/AuthModule.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/login/model/LoginRequest.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/di/AuthModule.kt
  • data/login/src/main/java/com/hmh/hamyeonham/login/DefaultAuthRepository.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/signup/model/SignUpResponse.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/login/AuthService.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/signup/model/SignUpRequest.kt
👮 Files not reviewed due to content moderation or server errors (2)
  • app/src/main/res/layout/activity_sample.xml
  • app/src/main/java/com/hmh/hamyeonham/StartActivity.kt
🧰 Additional context used
🧬 Code Graph Analysis (2)
core/database/src/main/java/com/hmh/hamyeonham/core/database/HMHRoomDatabase.kt (1)
data/login/src/main/java/com/hmh/hamyeonham/login/repository/LocalAuthRepository.kt (1)
  • userAuthDao (16-109)
feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt (1)
core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt (1)
  • toast (28-30)
🪛 detekt (1.23.8)
data/login/src/main/java/com/hmh/hamyeonham/login/datasource/kakao/KakaoSocialAuthDataStoreImpl.kt

[warning] 31-31: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 55-55: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR Checker
🔇 Additional comments (50)
domain/login/src/main/java/com/hmh/hamyeonham/login/model/User.kt (1)

3-5: Unreferenced User model—validate its necessity or intended use

The new User data class in
domain/login/src/main/java/com/hmh/hamyeonham/login/model/User.kt
is currently not imported or referenced anywhere in the codebase. Before expanding its properties:

• Confirm whether this User class is meant to drive any authentication or user-related flows (e.g., replace or map to UserProfile/UserInfo).
• Remove the file if it’s a placeholder or obsolete.
• If it should serve as the domain model, integrate it into the use cases and repository layers, and clarify which fields are essential.

Please verify its intended role and update or remove accordingly.

app/src/main/res/layout/activity_sample.xml (1)

36-36: Animation speed increase aligns with new navigation logic.

The increased speed from 1.0 to 1.5 for the logo animation is appropriate given that StartActivity.kt now uses animation completion as the trigger for navigation. This will provide a more responsive user experience.

app/src/main/java/com/hmh/hamyeonham/StartActivity.kt (1)

36-39: LGTM! Clean navigation implementation.

The navigation logic properly transitions to the login activity and finishes the splash screen, preventing unwanted back navigation.

domain/login/build.gradle.kts (1)

7-10: Confirm the variant of the coroutine artifact being pulled in.

domain/login is a plain JVM/Kotlin module (java-library + kotlin("jvm")).
Double-check that libs.kotlin.coroutines resolves to org.jetbrains.kotlinx:kotlinx-coroutines-core (not the Android classifier) to avoid leaking Android-specific transitive deps into a pure JVM module.

If the version catalog entry currently points to …:kotlinx-coroutines-android, add an explicit core binary for this module:

-implementation(libs.kotlin.coroutines)
+implementation(libs.kotlin.coroutines.core)
domain/login/src/main/java/com/hmh/hamyeonham/login/model/Login.kt (1)

3-7: DB migration risk after switching userId to Long.

Changing the type from IntLong is fine at the domain layer, but it requires:

  1. A Room schema migration for every table/column that persisted the old Int type.
  2. Safe casting in mapping layers that still receive an Int from legacy sources (cached JSON, Preference values, etc.).

Please verify that Migration(1, 2) (or newer) alters column types correctly and that no old serialization code forces the value back to Int.

core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/UserPreference.kt (2)

3-3: LGTM! Interface rename improves clarity.

The renaming from HMHNetworkPreference to UserPreference is more descriptive and aligns well with the new user-centric authentication architecture.


7-7: Type update verification passed

All references to userId have been confirmed to use Long consistently—no remaining Int assumptions were found.

• LocalAuthRepository (data/login/.../LocalAuthRepository.kt) passes userAuth.userId (Long) to DAO
• UserProfileDao signature (core/database/.../UserProfileDao.kt) defines getUserProfile(userId: Long)
• UserProfileMapper (data/login/.../UserProfileMapper.kt) maps userId fields as Long

No further changes required.

core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt (1)

4-4: LGTM! Consistent interface update.

The import and constructor parameter changes from HMHNetworkPreference to UserPreference are correct and maintain the same functionality while aligning with the new authentication architecture.

Also applies to: 19-19

core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/AuthenticatorUtil.kt (1)

6-6: LGTM! Interface update maintains functionality.

The changes from HMHNetworkPreference to UserPreference are correctly applied. The logout logic remains intact and properly handles clearing authentication data, database cleanup, and Kakao logout.

Also applies to: 21-21

data/login/src/main/java/com/hmh/hamyeonham/login/di/AuthDataSourceKey.kt (1)

1-7: AuthProvider enum verified; AuthDataSourceKey is correctly implemented
The AuthProvider enum is defined in domain/login/src/main/java/com/hmh/hamyeonham/login/di/AuthProvider.kt with KAKAO, GOOGLE, and EMAIL. Your @MapKey annotation with @Retention(AnnotationRetention.BINARY) is correctly set up for Dagger multibinding. No further changes needed.

feature/mypage/src/main/res/values/strings.xml (1)

29-36: LGTM! Comprehensive string resources for profile features.

The new string resources properly support the enhanced profile and authentication features. The naming convention is consistent, and the Korean text covers all necessary UI states including login status, profile fields, and user messaging.

core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/HeaderInterceptor.kt (2)

3-3: LGTM! Clean interface refactoring.

The update from HMHNetworkPreference to UserPreference is consistent with the broader authentication refactoring. The functionality remains intact while aligning with the new user preference abstraction.

Also applies to: 10-10


3-3: LGTM! Clean interface rename.

The import and constructor parameter updates from HMHNetworkPreference to UserPreference are consistent with the broader authentication refactoring. The core functionality remains unchanged.

Also applies to: 10-10

domain/login/src/main/java/com/hmh/hamyeonham/login/model/LoginInfo.kt (2)

8-13: LGTM! Well-structured login state model.

The LoginInfo data class provides a comprehensive representation of user authentication state with appropriate properties for userId, provider, login status, and timestamp tracking.


5-13: Well-structured login information model.

The LoginInfo data class effectively encapsulates user authentication state with appropriate property types. The userId as Long aligns with the broader type changes mentioned in the refactoring.

feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/MyPageFragment.kt (4)

79-79: LGTM! Simplified ViewModel API.

The method names have been simplified from handleLogout()/handleWithdrawal() to logout()/withdrawal(), which provides a cleaner and more direct API.

Also applies to: 92-92


99-106: LGTM! Improved effect handling structure.

The consolidation from viewModel.userEffect to viewModel.effect and the use of effect.message for error handling provides better consistency and error messaging capabilities.


79-79: LGTM! Simplified method names.

The method calls have been appropriately updated to use the simplified names logout() and withdrawal() instead of handleLogout() and handleWithdrawal(), aligning with the ViewModel refactoring.

Also applies to: 92-92


99-105: Improved effect handling with dynamic error messages.

The effect flow has been properly renamed to effect, and the error handling now uses dynamic message properties from the effect objects, providing better flexibility for error reporting.

core/database/src/main/java/com/hmh/hamyeonham/core/database/di/DatabaseModule.kt (2)

21-29: LGTM! Proper database migration setup.

The addition of MIGRATION_1_2 is essential for handling the schema changes when upgrading from version 1 to 2. The migration ensures existing users can upgrade without data loss.


22-29: Essential migration handling for schema changes.

The addition of MIGRATION_1_2 is necessary to handle the database schema update that introduces the new UserAuth and UserProfile entities. This ensures existing user data is preserved during the upgrade.

core/database/src/main/java/com/hmh/hamyeonham/core/database/di/DaoModule.kt (2)

43-53: LGTM! Consistent DAO provider implementation.

The new DAO providers for UserAuthDao and UserProfileDao follow the established pattern and are properly configured for dependency injection. They support the new authentication entities introduced in the database migration.


43-53: Well-structured DAO providers for new authentication entities.

The new UserAuthDao and UserProfileDao providers follow the established pattern and correctly integrate with the dependency injection system. These are essential for the local authentication functionality.

domain/login/src/main/java/com/hmh/hamyeonham/login/model/UserProfile.kt (1)

1-13: LGTM! Clean domain model design.

The UserProfile domain model is well-structured with appropriate nullable fields and clear documentation. The design aligns well with the local authentication architecture changes described in the PR objectives.

core/network/src/main/java/com/hmh/hamyeonham/core/network/di/DataStoreModule.kt (2)

8-9: LGTM! Consistent import updates.

The import statements have been correctly updated to use the new DefaultUserPreference and UserPreference classes, aligning with the broader refactoring of authentication preferences.


74-74: LGTM! Dependency injection binding updated correctly.

The binding has been properly updated to bind DefaultUserPreference to UserPreference, maintaining consistency with the import changes and the overall authentication refactoring.

core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/DefaultUserPreference.kt (1)

9-11: LGTM! Class rename aligns with architectural changes.

The class has been appropriately renamed to DefaultUserPreference and implements the updated UserPreference interface, consistent with the authentication system refactoring.

data/login/src/main/java/com/hmh/hamyeonham/login/di/AuthBinder.kt (1)

14-29: Excellent DI module design for extensible social authentication.

The use of multibinding with @AuthDataSourceKey and AuthProvider enum creates a clean, extensible pattern for adding multiple social login providers. The singleton scoping is appropriate for authentication components.

core/database/src/main/java/com/hmh/hamyeonham/core/database/dao/UserProfileDao.kt (1)

10-19: Well-designed DAO with both reactive and non-reactive access patterns.

The DAO provides good coverage of CRUD operations with appropriate Room annotations. The combination of suspend functions and Flow-based observation supports different usage patterns well.

data/login/src/main/java/com/hmh/hamyeonham/login/mapper/UserProfileMapper.kt (2)

3-6: Good use of type alias to distinguish between similar UserProfile types.

The type alias import com.hmh.hamyeonham.core.database.entity.UserProfile as DbUserProfile effectively prevents confusion between the database entity and domain model with the same name.


10-47: Clean and straightforward mapping functions.

The extension functions provide clear conversion paths between different UserProfile representations. The 1:1 field mappings are appropriate for these data transformation operations.

feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt (1)

117-146: Formatting improvements enhance readability

The JSON object construction for Amplitude tracking is now more readable with better line breaks and consistent formatting.

core/database/src/main/java/com/hmh/hamyeonham/core/database/HMHRoomDatabase.kt (1)

66-98: Migration SQL is correct and handles schema upgrade properly

The migration correctly creates the new tables with appropriate column types and primary keys. The SQL ensures backward compatibility.

domain/login/src/main/java/com/hmh/hamyeonham/login/usecase/AuthUseCase.kt (1)

11-73: Well-structured use case following clean architecture principles

The AuthUseCase properly encapsulates authentication operations and provides a clean interface for the presentation layer. The use of Result type for error handling and Flow for reactive streams is appropriate.

data/login/src/main/java/com/hmh/hamyeonham/login/repository/LocalAuthRepository.kt (1)

16-109: Solid implementation of local authentication repository

The repository properly implements the authentication interface with good error handling, reactive streams support, and clean separation of concerns. The integration with social auth data stores is well-designed.

feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt (2)

37-37: LGTM! Simplified login method call.

The removal of context parameter aligns with the new authentication architecture where the ViewModel handles the login flow internally.


40-41: Well-structured reactive login handling.

The new approach using separate methods for state observation (checkLoginState()) and effect handling (handleLoginEffect()) provides clear separation of concerns. The error handling covers both failure scenarios with appropriate user feedback via toast messages.

Also applies to: 44-60

feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt (1)

71-71: LGTM! Simplified constructor aligns with new architecture.

Removing the AuthRepository and HMHNetworkPreference dependencies is consistent with the shift to local authentication management.

feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/viewmodel/MyPageViewModel.kt (2)

5-5: Excellent architectural improvement.

Replacing multiple dependencies with a single AuthUseCase provides better abstraction and follows the clean architecture principle of depending on use cases rather than repositories directly.

Also applies to: 21-21


27-49: Well-implemented Result pattern with proper error handling.

Both logout() and withdrawal() methods demonstrate consistent error handling with user-friendly fallback messages. The use of the Result pattern makes the success/failure flows explicit and easier to handle.

data/login/src/main/java/com/hmh/hamyeonham/login/datasource/kakao/KakaoSocialAuthDataStoreImpl.kt (1)

60-85: Verify login success/failure handling.

Both loginWithKakaoTalk() and loginWithAccount() always resume with Unit regardless of success or failure. This means the login() method cannot distinguish between successful and failed login attempts when deciding whether to fetch user info.

The current flow assumes login always succeeds and attempts to fetch user info. Consider modifying these methods to return boolean success indicators or throw exceptions on failure.

domain/login/src/main/java/com/hmh/hamyeonham/login/repository/AuthRepository.kt (1)

8-51: Excellent interface design with comprehensive documentation.

This refactored AuthRepository interface demonstrates several architectural best practices:

  • Consistent error handling: All suspend functions return Result<T> for predictable error handling
  • Reactive programming: Flow-based observation methods enable reactive UI updates
  • Provider abstraction: AuthProvider parameter allows supporting multiple authentication providers
  • Comprehensive documentation: Each method has clear KDoc explaining purpose, parameters, and return values
  • Clean separation: Distinguishes between one-time operations and continuous state observation

The interface provides a solid foundation for the new authentication architecture.

feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt (3)

6-7: Good architectural improvement with AuthUseCase abstraction

The refactoring to use AuthUseCase instead of direct repository dependencies improves the separation of concerns and makes the ViewModel more testable.

Also applies to: 31-31


26-26: Proper state management with loading flag and effect flow

Good addition of the isLoading flag for better UI state management and proper encapsulation of the effect flow.

Also applies to: 36-37


50-56: Clean provider-specific login methods

Good implementation of provider-specific public methods that delegate to the common private implementation.

data/login/src/main/java/com/hmh/hamyeonham/login/datasource/kakao/KakaoAuthCodeClient.kt (5)

28-36: Verify the source and licensing of this implementation

The comment indicates this is based on Kakao SDK's internal AuthCodeClient. Please ensure:

  1. This implementation complies with Kakao SDK's license terms
  2. Consider using the official SDK methods if available instead of reimplementing internal classes

40-103: Well-implemented KakaoTalk authorization method

The method properly handles the case when KakaoTalk is not installed and includes comprehensive OAuth parameters with PKCE support.


157-179: Robust OAuth callback handling

Good implementation of the result receiver with proper error parsing and appropriate use of HTTP 302 status for OAuth redirect errors.


187-199: Correct PKCE implementation following OAuth 2.0 standards

The code verifier and challenge generation properly implements PKCE with S256 method, using appropriate Base64 encoding flags for URL safety.


189-190: Verify Kakao SDK algorithm constants

We weren’t able to locate the definitions for Constants.CODE_VERIFIER_ALGORITHM or Constants.CODE_CHALLENGE_ALGORITHM in the local codebase (they’re provided by the Kakao SDK). Please confirm in the SDK’s Constants class (e.g., in its source or JAR):

  • CODE_VERIFIER_ALGORITHM is defined as "SHA-256"
  • CODE_CHALLENGE_ALGORITHM matches the required PKCE algorithm (typically "S256" or the same "SHA-256" identifier)

No code changes are needed here if those values are correct. If they differ, consider overriding or defining the expected constants locally to ensure the PKCE flow uses the proper hash algorithm.

Comment on lines +29 to +33
binding.splashLottieAppLogo.addAnimatorUpdateListener {
if (it.animatedFraction == 1.0f) {
navigateToLogin()
}
.showAllowingStateLoss(supportFragmentManager)

}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve animation completion detection and cleanup.

The current implementation has a few potential issues:

  1. Floating point precision: Using exact equality (== 1.0f) for floating point comparison can be unreliable. Animation fraction might not reach exactly 1.0f due to precision issues.

  2. Missing cleanup: The animator listener is not removed, which could potentially cause memory leaks.

Apply this diff to improve the implementation:

 private fun initLottieSplash() {
     binding.splashLottieAppLogo.playAnimation()
-    binding.splashLottieAppLogo.addAnimatorUpdateListener {
-        if (it.animatedFraction == 1.0f) {
+    binding.splashLottieAppLogo.addAnimatorUpdateListener { animator ->
+        if (animator.animatedFraction >= 0.99f) {
+            binding.splashLottieAppLogo.removeAllUpdateListeners()
             navigateToLogin()
         }
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
binding.splashLottieAppLogo.addAnimatorUpdateListener {
if (it.animatedFraction == 1.0f) {
navigateToLogin()
}
.showAllowingStateLoss(supportFragmentManager)
}
private fun initLottieSplash() {
binding.splashLottieAppLogo.playAnimation()
binding.splashLottieAppLogo.addAnimatorUpdateListener { animator ->
if (animator.animatedFraction >= 0.99f) {
binding.splashLottieAppLogo.removeAllUpdateListeners()
navigateToLogin()
}
}
}
🤖 Prompt for AI Agents
In app/src/main/java/com/hmh/hamyeonham/StartActivity.kt around lines 29 to 33,
replace the exact floating point equality check on animatedFraction with a
threshold comparison (e.g., check if animatedFraction is greater than or equal
to 0.99f) to handle precision issues. Additionally, after navigating to login,
remove the animator update listener to prevent potential memory leaks by
unregistering the listener from the animation.

Comment on lines 10 to 18
@Entity(tableName = "user_auth")
data class UserAuth(
@PrimaryKey
val id: Int = 1, // 항상 같은 ID 사용 (싱글 인스턴스)
val userId: Long = -1,
val providerType: String = "", // "KAKAO", "GOOGLE" 등 AuthProvider의 name
val isLoggedIn: Boolean = false,
val lastLoginTimestamp: Long = 0
) No newline at end of file
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider alternatives to the singleton pattern and improve type safety.

The entity design has a few areas for improvement:

  1. Singleton with fixed ID: Using id: Int = 1 to enforce a single instance is unconventional for Room. Consider using a repository-level singleton pattern instead.

  2. String-based provider type: The providerType field would benefit from an enum for better type safety:

+enum class AuthProvider {
+    KAKAO, GOOGLE, NONE;
+    
+    companion object {
+        fun fromString(value: String): AuthProvider = 
+            values().find { it.name == value } ?: NONE
+    }
+}

 data class UserAuth(
     @PrimaryKey
     val id: Int = 1,
     val userId: Long = -1,
-    val providerType: String = "", // "KAKAO", "GOOGLE" 등 AuthProvider의 name
+    val providerType: String = AuthProvider.NONE.name,
     val isLoggedIn: Boolean = false,
     val lastLoginTimestamp: Long = 0
 )
🤖 Prompt for AI Agents
In
core/database/src/main/java/com/hmh/hamyeonham/core/database/entity/UserAuth.kt
around lines 10 to 18, avoid using a fixed ID value to enforce a singleton in
the Room entity; instead, remove the default fixed ID and handle singleton
behavior at the repository or service layer. Also, replace the String type of
the providerType field with an enum class representing the authentication
providers to improve type safety and prevent invalid values.

Comment on lines 9 to 19
@Entity(tableName = "user_profile")
data class UserProfile(
@PrimaryKey
val userId: Long,
val nickname: String? = null,
val profileImageUrl: String? = null,
val email: String? = null,
val ageRange: String? = null,
val gender: String? = null,
val updatedAt: Long = System.currentTimeMillis()
) No newline at end of file
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider moving timestamp generation to repository layer.

The entity design is mostly well-structured, but there's one concern with the default timestamp:

 data class UserProfile(
     @PrimaryKey
     val userId: Long,
     val nickname: String? = null,
     val profileImageUrl: String? = null,
     val email: String? = null,
     val ageRange: String? = null,
     val gender: String? = null,
-    val updatedAt: Long = System.currentTimeMillis()
+    val updatedAt: Long
 )

Rationale: Using System.currentTimeMillis() as a default value in the entity can cause issues with:

  • Unit testing (timestamps become unpredictable)
  • Data consistency (multiple instances created at different times)
  • Database operations (updates vs inserts might get different timestamps)

Consider generating the timestamp in the repository or DAO layer where you have better control over when it's set.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Entity(tableName = "user_profile")
data class UserProfile(
@PrimaryKey
val userId: Long,
val nickname: String? = null,
val profileImageUrl: String? = null,
val email: String? = null,
val ageRange: String? = null,
val gender: String? = null,
val updatedAt: Long = System.currentTimeMillis()
)
@Entity(tableName = "user_profile")
data class UserProfile(
@PrimaryKey
val userId: Long,
val nickname: String? = null,
val profileImageUrl: String? = null,
val email: String? = null,
val ageRange: String? = null,
val gender: String? = null,
val updatedAt: Long
)
🤖 Prompt for AI Agents
In
core/database/src/main/java/com/hmh/hamyeonham/core/database/entity/UserProfile.kt
lines 9 to 19, the default timestamp for updatedAt is set using
System.currentTimeMillis() in the entity, which can cause issues with testing
and data consistency. Remove the default value from the entity and instead set
the updatedAt timestamp explicitly in the repository or DAO layer when creating
or updating UserProfile instances to ensure better control and predictability.

Comment on lines +33 to 39
override var userId: Long
get() = preferences.getLong("user_id", -1)
set(value) {
preferences.edit(commit = true) {
putInt("user_id", value)
putLong("user_id", value)
}
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify migration impact for existing users.

The userId type change from Int to Long is significant and requires careful consideration for existing users who may have Int values stored in SharedPreferences.


🏁 Script executed:

#!/bin/bash
# Description: Search for any migration or compatibility handling for the userId type change
# Expected: Find migration logic or compatibility handling for existing Int userId values

echo "Searching for userId migration or compatibility handling..."
rg -A 5 -B 5 "userId.*migration|migration.*userId|user_id.*int|int.*user_id" --type kt

echo "Searching for any SharedPreferences migration patterns..."
rg -A 10 "SharedPreferences.*migration|migration.*SharedPreferences" --type kt

echo "Checking for userId type usage patterns..."
rg "userId.*Int|Int.*userId" --type kt

Length of output: 640


🏁 Script executed:

#!/bin/bash
# Verify if any legacy Int-based API for “user_id” still exists or if migration code is present

echo "🔍 Searching for legacy getInt usage for 'user_id'..."
rg -n 'getInt\("user_id"' -g '*.kt'

echo "🔍 Searching for legacy putInt usage for 'user_id'..."
rg -n 'putInt\("user_id"' -g '*.kt'

echo "🔍 Searching for migration/convert patterns in Kotlin sources..."
rg -n -E 'migrate|migration|convert|upgrade' -g '*.kt'

Length of output: 914


Implement migration for legacy Int-based userId in SharedPreferences

No compatibility or migration logic was found for existing users who have user_id stored as an Int. Calling getLong("user_id") on an Int-backed preference will throw a ClassCastException at runtime. Please add fallback handling to read the old Int value and re-save it as a Long.

• File: core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/DefaultUserPreference.kt, lines 33–39
• Add a try/catch around getLong (or migrate on initialization) to preserve existing data

Suggested diff:

 override var userId: Long
-    get() = preferences.getLong("user_id", -1)
+    get() = try {
+        preferences.getLong("user_id", -1)
+    } catch (e: ClassCastException) {
+        // Migrate legacy Int value to Long
+        val intValue = preferences.getInt("user_id", -1)
+        preferences.edit(commit = true) {
+            putLong("user_id", intValue.toLong())
+        }
+        intValue.toLong()
+    }
     set(value) {
         preferences.edit(commit = true) {
             putLong("user_id", value)
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override var userId: Long
get() = preferences.getLong("user_id", -1)
set(value) {
preferences.edit(commit = true) {
putInt("user_id", value)
putLong("user_id", value)
}
}
override var userId: Long
get() = try {
preferences.getLong("user_id", -1)
} catch (e: ClassCastException) {
// Migrate legacy Int value to Long
val intValue = preferences.getInt("user_id", -1)
preferences.edit(commit = true) {
putLong("user_id", intValue.toLong())
}
intValue.toLong()
}
set(value) {
preferences.edit(commit = true) {
putLong("user_id", value)
}
}
🤖 Prompt for AI Agents
In
core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/network/DefaultUserPreference.kt
lines 33-39, the userId getter directly calls getLong on preferences which can
throw ClassCastException if the stored user_id is an Int. To fix this, wrap the
getLong call in a try/catch block; in the catch, read the user_id as an Int,
convert it to Long, save it back as Long, and return the Long value. This
migration ensures backward compatibility with legacy Int-based user_id values.

Comment on lines 20 to 34
override suspend fun login(): UserInfo? {
return try {
// 카카오톡이 설치되어 있으면 카카오톡으로 로그인, 아니면 계정으로 로그인
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
loginWithKakaoTalk()
} else {
loginWithAccount()
}

// 로그인 성공 후 사용자 정보 조회
fetchUserInfo()
} catch (e: Exception) {
null
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling and logging.

The current implementation swallows exceptions without logging, making debugging difficult. The login flow should also handle and propagate errors properly.

 override suspend fun login(): UserInfo? {
     return try {
         // 카카오톡이 설치되어 있으면 카카오톡으로 로그인, 아니면 계정으로 로그인
         if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
             loginWithKakaoTalk()
         } else {
             loginWithAccount()
         }

         // 로그인 성공 후 사용자 정보 조회
         fetchUserInfo()
     } catch (e: Exception) {
+        // Log the exception for debugging
+        android.util.Log.e("KakaoAuth", "Login failed", e)
         null
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override suspend fun login(): UserInfo? {
return try {
// 카카오톡이 설치되어 있으면 카카오톡으로 로그인, 아니면 계정으로 로그인
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
loginWithKakaoTalk()
} else {
loginWithAccount()
}
// 로그인 성공 후 사용자 정보 조회
fetchUserInfo()
} catch (e: Exception) {
null
}
}
override suspend fun login(): UserInfo? {
return try {
// 카카오톡이 설치되어 있으면 카카오톡으로 로그인, 아니면 계정으로 로그인
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
loginWithKakaoTalk()
} else {
loginWithAccount()
}
// 로그인 성공 후 사용자 정보 조회
fetchUserInfo()
} catch (e: Exception) {
// Log the exception for debugging
android.util.Log.e("KakaoAuth", "Login failed", e)
null
}
}
🧰 Tools
🪛 detekt (1.23.8)

[warning] 31-31: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🤖 Prompt for AI Agents
In
data/login/src/main/java/com/hmh/hamyeonham/login/datasource/kakao/KakaoSocialAuthDataStoreImpl.kt
around lines 20 to 34, the login function catches exceptions but does not log
them, which hinders debugging and error tracking. Modify the catch block to log
the exception details using an appropriate logger before returning null, and
consider rethrowing or propagating the exception if needed to allow higher-level
handling of login errors.

Comment on lines 68 to 81
override suspend fun logout(): Result<Unit> = runCatching {
val userAuth = userAuthDao.getUserAuth() ?: return@runCatching Unit // 이미 로그아웃 상태

// 현재 로그인된 제공자 확인
val provider = AuthProvider.valueOf(userAuth.providerType)
val dataStore = socialAuthDataStores[provider]
?: throw IllegalArgumentException("지원하지 않는 소셜 로그인 제공자입니다: $provider")

// SDK를 사용한 소셜 로그아웃 처리
dataStore.logout()

// DB에서 로그인 상태 업데이트
userAuthDao.updateLoginStatus(false)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle invalid provider type in logout

Similar to the getCurrentAuth method, the AuthProvider.valueOf() call here can also throw an exception.

Add error handling for invalid provider types:

-        val provider = AuthProvider.valueOf(userAuth.providerType)
+        val provider = try {
+            AuthProvider.valueOf(userAuth.providerType)
+        } catch (e: IllegalArgumentException) {
+            // Log the error and clear the invalid auth data
+            userAuthDao.clearUserAuth()
+            return@runCatching Unit
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override suspend fun logout(): Result<Unit> = runCatching {
val userAuth = userAuthDao.getUserAuth() ?: return@runCatching Unit // 이미 로그아웃 상태
// 현재 로그인된 제공자 확인
val provider = AuthProvider.valueOf(userAuth.providerType)
val dataStore = socialAuthDataStores[provider]
?: throw IllegalArgumentException("지원하지 않는 소셜 로그인 제공자입니다: $provider")
// SDK를 사용한 소셜 로그아웃 처리
dataStore.logout()
// DB에서 로그인 상태 업데이트
userAuthDao.updateLoginStatus(false)
}
override suspend fun logout(): Result<Unit> = runCatching {
val userAuth = userAuthDao.getUserAuth() ?: return@runCatching Unit // 이미 로그아웃 상태
// 현재 로그인된 제공자 확인
val provider = try {
AuthProvider.valueOf(userAuth.providerType)
} catch (e: IllegalArgumentException) {
// Log the error and clear the invalid auth data
userAuthDao.clearUserAuth()
return@runCatching Unit
}
val dataStore = socialAuthDataStores[provider]
?: throw IllegalArgumentException("지원하지 않는 소셜 로그인 제공자입니다: $provider")
// SDK를 사용한 소셜 로그아웃 처리
dataStore.logout()
// DB에서 로그인 상태 업데이트
userAuthDao.updateLoginStatus(false)
}
🤖 Prompt for AI Agents
In
data/login/src/main/java/com/hmh/hamyeonham/login/repository/LocalAuthRepository.kt
around lines 68 to 81, the logout method uses AuthProvider.valueOf() without
handling invalid provider types, which can throw an exception. Wrap the valueOf
call in a try-catch block or use a safe method to handle invalid provider
strings gracefully, and return a failure Result or handle the error
appropriately to prevent crashes.

@@ -0,0 +1,13 @@
package com.hmh.hamyeonham.login.model

import com.hmh.hamyeonham.login.di.AuthProvider
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify AuthProvider import location.

The AuthProvider enum is imported from the DI package (com.hmh.hamyeonham.login.di.AuthProvider). Consider whether this enum should be in a domain/model package instead of the DI package for better separation of concerns.


🏁 Script executed:

#!/bin/bash
# Description: Check where AuthProvider is defined and how it's used across the codebase
# Expected: AuthProvider should ideally be in a domain/model package, not DI

# Find the AuthProvider definition
fd "AuthProvider.kt" --exec cat {}

# Check other usages of AuthProvider to see if they also import from DI
rg "import.*AuthProvider" -A 2

Length of output: 1991


Refactor AuthProvider into the domain model package

The AuthProvider enum currently lives in the DI package (com.hmh.hamyeonham.login.di), but it represents a domain concept used by multiple layers. To enforce clear separation of concerns, move it to the domain/model package and update all imports accordingly.

Files requiring import updates:

  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt
  • data/login/src/main/java/com/hmh/hamyeonham/login/repository/LocalAuthRepository.kt
  • domain/login/src/main/java/com/hmh/hamyeonham/login/usecase/AuthUseCase.kt
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/LoginInfo.kt
  • domain/login/src/main/java/com/hmh/hamyeonham/login/repository/AuthRepository.kt

Example diff in AuthProvider.kt:

- package com.hmh.hamyeonham.login.di
+ package com.hmh.hamyeonham.login.model

 enum class AuthProvider {
     KAKAO,
     GOOGLE,
     EMAIL,
 }

And in each consumer:

- import com.hmh.hamyeonham.login.di.AuthProvider
+ import com.hmh.hamyeonham.login.model.AuthProvider
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import com.hmh.hamyeonham.login.di.AuthProvider
-import com.hmh.hamyeonham.login.di.AuthProvider
+import com.hmh.hamyeonham.login.model.AuthProvider
🤖 Prompt for AI Agents
In domain/login/src/main/java/com/hmh/hamyeonham/login/model/LoginInfo.kt at
line 3 and similarly in all listed files, move the AuthProvider enum from the di
package to the domain/model package to better represent its domain nature.
Update all import statements referencing AuthProvider to point to the new
location in the domain/model package. Ensure that all files consuming
AuthProvider reflect this change to maintain consistency and proper separation
of concerns.

Comment on lines 40 to 47
viewModelScope.launch {
authUseCase.isLoggedIn().onSuccess { isLoggedIn ->
_loginState.value = loginState.value.copy(
autoLogin = isLoggedIn,
isLoading = false
)
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle potential failure case in login status check

The init block only handles the success case. Consider handling potential failures and clarify the isLoading state management:

  1. The isLoading flag starts as false and is set to false again - consider if initial loading state should be true
  2. Add error handling for the failure case
 init {
     viewModelScope.launch {
+        _loginState.value = loginState.value.copy(isLoading = true)
         authUseCase.isLoggedIn()
             .onSuccess { isLoggedIn ->
                 _loginState.value = loginState.value.copy(
                     autoLogin = isLoggedIn,
                     isLoading = false
                 )
             }
+            .onFailure {
+                _loginState.value = loginState.value.copy(
+                    autoLogin = false,
+                    isLoading = false
+                )
+            }
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
viewModelScope.launch {
authUseCase.isLoggedIn().onSuccess { isLoggedIn ->
_loginState.value = loginState.value.copy(
autoLogin = isLoggedIn,
isLoading = false
)
}
}
init {
viewModelScope.launch {
// show loading while we check login status
_loginState.value = loginState.value.copy(isLoading = true)
authUseCase.isLoggedIn()
.onSuccess { isLoggedIn ->
_loginState.value = loginState.value.copy(
autoLogin = isLoggedIn,
isLoading = false
)
}
.onFailure {
_loginState.value = loginState.value.copy(
autoLogin = false,
isLoading = false
)
}
}
}
🤖 Prompt for AI Agents
In
feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt
around lines 40 to 47, the coroutine handling login status only processes the
success case and sets isLoading to false without considering the initial loading
state or failure scenarios. Update the code to set isLoading to true before
starting the check, handle the failure case by updating the login state
accordingly (e.g., setting an error flag or message), and ensure isLoading is
set to false after either success or failure to properly reflect the loading
state.

Comment on lines 58 to 69
private fun loginWith(provider: AuthProvider) {
viewModelScope.launch {
authUseCase.login(provider)
.onSuccess {
AmplitudeUtils.trackEventWithProperties("click_onboarding_kakao")
_effect.emit(LoginEffect.LoginSuccess)
}
.onFailure { exception ->
_effect.emit(LoginEffect.LoginError(exception.message ?: "로그인에 실패했습니다"))
}
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect Amplitude event tracking for different providers

The Amplitude event is hardcoded to "click_onboarding_kakao" but this method handles both Kakao and Google logins. The event should be provider-specific.

 private fun loginWith(provider: AuthProvider) {
     viewModelScope.launch {
         authUseCase.login(provider)
             .onSuccess {
-                AmplitudeUtils.trackEventWithProperties("click_onboarding_kakao")
+                val eventName = when (provider) {
+                    AuthProvider.KAKAO -> "click_onboarding_kakao"
+                    AuthProvider.GOOGLE -> "click_onboarding_google"
+                }
+                AmplitudeUtils.trackEventWithProperties(eventName)
                 _effect.emit(LoginEffect.LoginSuccess)
             }
             .onFailure { exception ->
                 _effect.emit(LoginEffect.LoginError(exception.message ?: "로그인에 실패했습니다"))
             }
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private fun loginWith(provider: AuthProvider) {
viewModelScope.launch {
authUseCase.login(provider)
.onSuccess {
AmplitudeUtils.trackEventWithProperties("click_onboarding_kakao")
_effect.emit(LoginEffect.LoginSuccess)
}
.onFailure { exception ->
_effect.emit(LoginEffect.LoginError(exception.message ?: "로그인에 실패했습니다"))
}
}
}
}
private fun loginWith(provider: AuthProvider) {
viewModelScope.launch {
authUseCase.login(provider)
.onSuccess {
val eventName = when (provider) {
AuthProvider.KAKAO -> "click_onboarding_kakao"
AuthProvider.GOOGLE -> "click_onboarding_google"
}
AmplitudeUtils.trackEventWithProperties(eventName)
_effect.emit(LoginEffect.LoginSuccess)
}
.onFailure { exception ->
_effect.emit(LoginEffect.LoginError(exception.message ?: "로그인에 실패했습니다"))
}
}
}
🤖 Prompt for AI Agents
In
feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt
between lines 58 and 69, the Amplitude event tracking is hardcoded to
"click_onboarding_kakao" regardless of the login provider. Modify the code to
dynamically set the event name based on the provider argument, for example using
a conditional or mapping to emit "click_onboarding_kakao" for Kakao and a
corresponding event for Google, ensuring the event accurately reflects the login
provider used.

Copy link
Member

@kangyuri1114 kangyuri1114 left a comment

Choose a reason for hiding this comment

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

👍

kez-lab added 2 commits August 5, 2025 17:21
Feat/remove_kakao_login: 카카오 로그인 삭제
Copy link

@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

🔭 Outside diff range comments (4)
core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionActivity.kt (2)

33-33: Missing permission-check methods will break compilation

The calls to hasUsageStatsPermission() and hasOverlayPermission() in PermissionActivity.kt have no corresponding definitions in the codebase—this will cause build failures. You need to either restore/implement these methods (e.g. as class members or Context extensions) or remove/update their usages.

Affected locations in core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionActivity.kt:

  • Line 33: if (hasOverlayPermission())
  • Line 42: subsequent call
  • Line 72
  • Line 80
  • Lines 102–103

Please add the missing method implementations or refactor these calls accordingly.


33-33: Missing Permission Check Methods in PermissionActivity

It looks like hasOverlayPermission() and hasUsageStatsPermission() are called in PermissionActivity.kt but aren’t defined there (nor as extensions). Without these methods in scope, the class won’t compile.

Please add or import implementations for these two helpers. For example, you can:

  • Define them in PermissionActivity (mirroring the logic from RequestPermissionFragment)
  • Or move them into a shared utility/extension file and import that

References in PermissionActivity.kt to update:

  • Line 33: if (hasOverlayPermission())
  • Line 42: similar overlay check
  • Line 72: usage-stats check
  • Line 80: another usage-stats call
  • Lines 102–103: final permission checks

Ensure both methods return the correct Boolean based on Android’s permission APIs.

core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionExt.kt (2)

127-137: Inconsistency: Fragment version still uses old approach.

The Fragment.hasUsageStatsPermission() method still uses the old UsageStatsManager.queryUsageStats() approach while AppCompatActivity.hasUsageStatsPermission() was updated to use AppOpsManager. This creates inconsistent behavior between Activity and Fragment contexts.

Apply this diff to align the Fragment version with the improved AppCompatActivity implementation:

 fun Fragment.hasUsageStatsPermission(): Boolean {
-    val usageStatsManager =
-        requireContext().getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
-    val time = System.currentTimeMillis()
-    val stats = usageStatsManager.queryUsageStats(
-        UsageStatsManager.INTERVAL_DAILY,
-        time - 1000 * 60,
-        time,
-    )
-    return stats != null && stats.isNotEmpty()
+    val ops = requireContext().getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
+    val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+        ops.unsafeCheckOpNoThrow(
+            AppOpsManager.OPSTR_GET_USAGE_STATS,
+            android.os.Process.myUid(),
+            requireContext().packageName
+        )
+    } else {
+        ops.checkOpNoThrow(
+            AppOpsManager.OPSTR_GET_USAGE_STATS,
+            android.os.Process.myUid(),
+            requireContext().packageName
+        )
+    }
+
+    return when (mode) {
+        AppOpsManager.MODE_ALLOWED -> true
+        AppOpsManager.MODE_DEFAULT,
+        AppOpsManager.MODE_IGNORED,
+        AppOpsManager.MODE_ERRORED -> false
+        else -> false
+    }
 }

127-137: Inconsistent implementation between Activity and Fragment extensions.

The Fragment.hasUsageStatsPermission() method still uses the old UsageStatsManager.queryUsageStats() approach, while AppCompatActivity.hasUsageStatsPermission() uses the new AppOpsManager approach. This inconsistency could lead to different permission check results.

Consider updating the Fragment implementation to match the Activity implementation:

 fun Fragment.hasUsageStatsPermission(): Boolean {
-    val usageStatsManager =
-        requireContext().getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
-    val time = System.currentTimeMillis()
-    val stats = usageStatsManager.queryUsageStats(
-        UsageStatsManager.INTERVAL_DAILY,
-        time - 1000 * 60,
-        time,
-    )
-    return stats != null && stats.isNotEmpty()
+    val ops = requireContext().getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
+    val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+        ops.unsafeCheckOpNoThrow(
+            AppOpsManager.OPSTR_GET_USAGE_STATS,
+            android.os.Process.myUid(),
+            requireContext().packageName
+        )
+    } else {
+        ops.checkOpNoThrow(
+            AppOpsManager.OPSTR_GET_USAGE_STATS,
+            android.os.Process.myUid(),
+            requireContext().packageName
+        )
+    }
+    
+    return when (mode) {
+        AppOpsManager.MODE_ALLOWED -> true
+        AppOpsManager.MODE_DEFAULT,
+        AppOpsManager.MODE_IGNORED,
+        AppOpsManager.MODE_ERRORED -> false
+        else -> false
+    }
 }
🧹 Nitpick comments (3)
feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt (1)

169-174: Consider renaming method for clarity.

The simplified signUp() method correctly removes server dependencies and immediately emits success. However, since it no longer performs actual user registration, consider renaming it to something more descriptive like completeOnboarding() or finishOnboarding() to better reflect its current functionality.

-    fun signUp() {
+    fun completeOnboarding() {
         viewModelScope.launch {
             _onboardEffect.emit(OnboardEffect.OnboardSuccess)
             AmplitudeUtils.trackEventWithProperties("complete_onboarding_finish")
         }
     }
feature/mypage/src/main/res/layout/fragment_my_page.xml (1)

2-5: Consider keeping the tools namespace for better design-time previews

Removing the xmlns:tools declaration eliminates the ability to use design-time attributes (e.g., tools:context, tools:text).
If the project still relies on Android Studio’s layout preview, re-add the namespace even if no tools: attributes are currently present—future UI tweaks often benefit from it.

-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt (1)

8-8: Address the TODO: Remove the AuthInterceptor entirely.

The TODO comment indicates this class should be deleted, and given that it no longer performs any authentication logic, it has become dead code.

Since this interceptor no longer serves any purpose in the local authentication architecture, would you like me to help identify all the places where this class is still being injected or referenced so it can be completely removed?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 327e78a and 7028ad9.

📒 Files selected for processing (84)
  • .github/workflows/develop_PR_builder.yml (0 hunks)
  • .github/workflows/release.yml (0 hunks)
  • app/build.gradle.kts (0 hunks)
  • app/proguard-rules.pro (0 hunks)
  • app/src/main/AndroidManifest.xml (0 hunks)
  • app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt (0 hunks)
  • app/src/main/java/com/hmh/hamyeonham/StartActivity.kt (2 hunks)
  • app/src/main/java/com/hmh/hamyeonham/navigation/DefaultNavigationProvider.kt (0 hunks)
  • build-logic/convention/src/main/kotlin/com/hmh/hamyeonham/plugin/CommonConfigs.kt (0 hunks)
  • core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt (0 hunks)
  • core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionActivity.kt (2 hunks)
  • core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionExt.kt (2 hunks)
  • core/common/src/main/java/com/hmh/hamyeonham/common/qualifier/OAuth.kt (0 hunks)
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/HMHRoomDatabase.kt (3 hunks)
  • core/designsystem/src/main/res/values/themes.xml (2 hunks)
  • core/network/build.gradle.kts (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/AuthenticatorUtil.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt (2 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt (1 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/di/NetModule.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/di/PointModule.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/PointService.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/ChallengeDateRequest.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/EarnPointRequest.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/EarnPointResponse.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/PointListResponse.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/UsablePointResponse.kt (0 hunks)
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/UsePointResponse.kt (0 hunks)
  • core/viewmodel/main/build.gradle.kts (0 hunks)
  • core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/viewmodel/MainEffect.kt (0 hunks)
  • core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/viewmodel/MainViewModel.kt (1 hunks)
  • data/login/.gitignore (0 hunks)
  • data/login/build.gradle.kts (0 hunks)
  • data/login/src/main/AndroidManifest.xml (0 hunks)
  • data/point/.gitignore (0 hunks)
  • data/point/build.gradle.kts (0 hunks)
  • data/point/src/main/AndroidManifest.xml (0 hunks)
  • data/point/src/main/java/com/hmh/hamyeonham/data/point/PointMapper.kt (0 hunks)
  • data/point/src/main/java/com/hmh/hamyeonham/data/point/di/PointBinder.kt (0 hunks)
  • data/point/src/main/java/com/hmh/hamyeonham/data/point/repository/DefaultPointRepository.kt (0 hunks)
  • data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/mapper/MypageMapper.kt (1 hunks)
  • data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/DefaultUserInfoRepository.kt (1 hunks)
  • domain/login/.gitignore (0 hunks)
  • domain/login/build.gradle.kts (0 hunks)
  • domain/login/src/main/AndroidManifest.xml (0 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/Login.kt (0 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/SignRequestDomain.kt (0 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/SignUpUser.kt (0 hunks)
  • domain/login/src/main/java/com/hmh/hamyeonham/login/repository/AuthRepository.kt (0 hunks)
  • domain/point/.gitignore (0 hunks)
  • domain/point/build.gradle.kts (0 hunks)
  • domain/point/src/main/AndroidManifest.xml (0 hunks)
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/EarnPoint.kt (0 hunks)
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/PointInfo.kt (0 hunks)
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/UsablePoint.kt (0 hunks)
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/UsePoint.kt (0 hunks)
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/repository/PointRepository.kt (0 hunks)
  • domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt (0 hunks)
  • feature/challenge/build.gradle.kts (0 hunks)
  • feature/challenge/src/main/AndroidManifest.xml (0 hunks)
  • feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeFragment.kt (1 hunks)
  • feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointActivity.kt (0 hunks)
  • feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointAdapter.kt (0 hunks)
  • feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointViewModel.kt (0 hunks)
  • feature/challenge/src/main/res/layout/activity_point.xml (0 hunks)
  • feature/challenge/src/main/res/layout/fragment_challenge.xml (0 hunks)
  • feature/login/build.gradle.kts (0 hunks)
  • feature/login/src/main/AndroidManifest.xml (0 hunks)
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt (0 hunks)
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt (0 hunks)
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewPagerAdapter.kt (0 hunks)
  • feature/login/src/main/res/layout/activity_login.xml (0 hunks)
  • feature/login/src/main/res/values/strings.xml (0 hunks)
  • feature/main/src/main/java/com/hmh/hamyeonham/feature/main/MainActivity.kt (1 hunks)
  • feature/main/src/main/res/values/strings.xml (0 hunks)
  • feature/mypage/build.gradle.kts (0 hunks)
  • feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/MyPageFragment.kt (3 hunks)
  • feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/viewmodel/MyPageViewModel.kt (1 hunks)
  • feature/mypage/src/main/res/layout/fragment_my_page.xml (2 hunks)
  • feature/mypage/src/main/res/values/strings.xml (1 hunks)
  • feature/onboarding/build.gradle.kts (0 hunks)
  • feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt (2 hunks)
  • gradle/libs.versions.toml (0 hunks)
  • settings.gradle.kts (0 hunks)
💤 Files with no reviewable changes (67)
  • feature/login/src/main/AndroidManifest.xml
  • core/network/build.gradle.kts
  • data/login/build.gradle.kts
  • feature/challenge/build.gradle.kts
  • feature/mypage/build.gradle.kts
  • feature/onboarding/build.gradle.kts
  • domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt
  • domain/point/src/main/AndroidManifest.xml
  • data/login/.gitignore
  • data/point/src/main/AndroidManifest.xml
  • core/viewmodel/main/build.gradle.kts
  • app/proguard-rules.pro
  • domain/login/build.gradle.kts
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/EarnPointRequest.kt
  • app/src/main/java/com/hmh/hamyeonham/navigation/DefaultNavigationProvider.kt
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/Login.kt
  • domain/login/.gitignore
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/UsePointResponse.kt
  • app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/EarnPointResponse.kt
  • core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt
  • data/login/src/main/AndroidManifest.xml
  • data/point/.gitignore
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/ChallengeDateRequest.kt
  • domain/point/.gitignore
  • feature/login/build.gradle.kts
  • domain/login/src/main/AndroidManifest.xml
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/UsePoint.kt
  • feature/challenge/src/main/AndroidManifest.xml
  • feature/login/src/main/res/layout/activity_login.xml
  • data/point/src/main/java/com/hmh/hamyeonham/data/point/PointMapper.kt
  • core/common/src/main/java/com/hmh/hamyeonham/common/qualifier/OAuth.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/AuthenticatorUtil.kt
  • app/src/main/AndroidManifest.xml
  • domain/point/build.gradle.kts
  • .github/workflows/release.yml
  • build-logic/convention/src/main/kotlin/com/hmh/hamyeonham/plugin/CommonConfigs.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/di/NetModule.kt
  • data/point/build.gradle.kts
  • settings.gradle.kts
  • app/build.gradle.kts
  • core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/viewmodel/MainEffect.kt
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/EarnPoint.kt
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/UsablePoint.kt
  • .github/workflows/develop_PR_builder.yml
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/UsablePointResponse.kt
  • feature/challenge/src/main/res/layout/fragment_challenge.xml
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/di/PointModule.kt
  • feature/login/src/main/res/values/strings.xml
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/SignUpUser.kt
  • gradle/libs.versions.toml
  • data/point/src/main/java/com/hmh/hamyeonham/data/point/repository/DefaultPointRepository.kt
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewPagerAdapter.kt
  • feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointActivity.kt
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/model/PointInfo.kt
  • feature/main/src/main/res/values/strings.xml
  • feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointViewModel.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/model/PointListResponse.kt
  • data/point/src/main/java/com/hmh/hamyeonham/data/point/di/PointBinder.kt
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt
  • domain/point/src/main/java/com/hmh/hamyeonham/domain/point/repository/PointRepository.kt
  • domain/login/src/main/java/com/hmh/hamyeonham/login/repository/AuthRepository.kt
  • feature/challenge/src/main/res/layout/activity_point.xml
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/point/PointService.kt
  • feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/point/PointAdapter.kt
  • domain/login/src/main/java/com/hmh/hamyeonham/login/model/SignRequestDomain.kt
  • feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt
✅ Files skipped from review due to trivial changes (3)
  • core/designsystem/src/main/res/values/themes.xml
  • data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/DefaultUserInfoRepository.kt
  • feature/main/src/main/java/com/hmh/hamyeonham/feature/main/MainActivity.kt
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/src/main/java/com/hmh/hamyeonham/StartActivity.kt
  • feature/mypage/src/main/res/values/strings.xml
  • core/database/src/main/java/com/hmh/hamyeonham/core/database/HMHRoomDatabase.kt
  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt
  • feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/MyPageFragment.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR Checker
🔇 Additional comments (16)
feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt (1)

65-65: LGTM: Constructor simplified correctly.

The removal of AuthRepository and HMHNetworkPreference dependencies aligns with the PR objective of transitioning from server-based to local authentication. The @Inject annotation is still appropriate for Hilt dependency injection.

feature/mypage/src/main/res/layout/fragment_my_page.xml (2)

77-78: Constraint updated correctly after point section removal

Anchoring tv_journey directly below tv_user_name is the right adjustment and keeps the vertical flow intact after deleting the point block. No issues spotted.


98-100: Divider view constraints look good

v_journey now references tv_user_name, matching the new hierarchy. Layout consistency maintained.

core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/viewmodel/MainViewModel.kt (1)

101-108: LGTM! Simplified logic aligns with point feature removal.

The method correctly simplifies the daily challenge failure handling by removing point-related logic while maintaining the core functionality of unlocking and refreshing challenge status.

feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeFragment.kt (1)

178-182: LGTM! Direct navigation simplifies the challenge creation flow.

The method correctly removes point-related conditional logic and provides direct navigation to the new challenge creation activity, which aligns with the broader removal of point features.

data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/mapper/MypageMapper.kt (1)

6-10: Verified: UserInfo Mapping Is Correct and Point-Free
The UserInfo data class now only defines name, and no point references remain in the codebase. This aligns perfectly with the PR’s goal of removing point-related functionality.

• domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/mapper/MypageMapper.kt – maps only name
• domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt – data class contains solely name

feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/viewmodel/MyPageViewModel.kt (1)

20-21: Approve the SharedFlow renaming.

The renaming from _userEffect/userEffect to _effect/effect is more concise and appropriate given the reduced scope of this ViewModel.

core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionActivity.kt (5)

3-3: LGTM on import optimizations.

The import changes properly reflect the updated dependencies - removing unused UsageStatsManager and adding AppOpsManager, Build, and androidx.core.net.toUri.

Also applies to: 7-7, 13-13


120-120: Good use of Kotlin extension function.

Using toUri() extension function is more idiomatic Kotlin compared to Uri.parse().


3-3: LGTM: Import updates align with implementation changes.

The import changes from UsageStatsManager to AppOpsManager and addition of Build are consistent with the refactored permission checking approach.

Also applies to: 7-7


13-13: LGTM: Modern URI extension usage.

Using the toUri() extension from androidx.core.net is a good improvement over Uri.parse().


120-120: LGTM: Consistent URI extension usage.

The change to use toUri() extension is consistent with the import addition and improves code readability.

core/common/src/main/java/com/hmh/hamyeonham/common/permission/PermissionExt.kt (4)

4-4: LGTM on adding AppOpsManager import.

Required for the improved permission checking implementation.


41-65: Excellent improvement to usage stats permission checking.

The new implementation using AppOpsManager is more reliable and direct compared to the previous approach of querying usage stats data. The version-specific handling for Android Q+ is appropriate, and the explicit mode checking covers all possible return states.


4-4: LGTM: Required import for new implementation.

Adding AppOpsManager import is necessary for the improved permission checking approach.


41-65: Excellent refactor to use AppOpsManager for more reliable permission checking.

The new implementation using AppOpsManager is more direct and reliable than the previous UsageStatsManager.queryUsageStats() approach. The API level handling for Android Q+ and comprehensive mode checking are well implemented.

Comment on lines 10 to 16
class AuthInterceptor @Inject constructor(
private val authenticatorUtil: AuthenticatorUtil
) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())

if (response.code == 404) {
val responseBody = response.body?.string() ?: ""
if (responseBody.contains("유저를 찾을 수 없습니다.")) {
runBlocking {
authenticatorUtil.handleLogout()
}

}
}

return response
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Remove dead code - this interceptor serves no purpose.

The interceptor now only passes through requests without any processing, making it effectively dead code. Since the authentication logic has been moved to a local Room DB approach, this network interceptor is no longer needed.

Consider removing this class entirely and updating the DI configuration to exclude it from the OkHttp client setup. This will:

  • Reduce unnecessary object instantiation
  • Eliminate confusion for future maintainers
  • Clean up the codebase as part of the authentication refactoring

Let me verify where this interceptor is still being used in the DI setup:


🏁 Script executed:

#!/bin/bash
# Description: Find all references to AuthInterceptor to ensure complete removal
# Expected: References in DI modules and potentially in tests

echo "=== Searching for AuthInterceptor references ==="
rg -A 3 -B 3 "AuthInterceptor"

echo "=== Searching for interceptor usage in DI modules ==="
rg -A 5 -B 5 "interceptor" --type kotlin | grep -E "(provides|inject|bind)"

Length of output: 1409


🏁 Script executed:

#!/bin/bash
# Description: Locate OkHttpClient setup to check interceptor bindings
echo "=== Searching for OkHttpClient usage in codebase ==="
rg --type kotlin "OkHttpClient" -A5 -B5

Length of output: 6646


Remove unused AuthInterceptor
AuthInterceptor isn’t referenced by any DI module or tests and only forwards requests. You can safely delete it and its TODO.

• Delete

  • core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt
🤖 Prompt for AI Agents
In
core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt
lines 10 to 16, the AuthInterceptor class is unused and only forwards requests
without modification. Remove the entire AuthInterceptor.kt file to clean up
unused code and delete any related TODO comments referencing it.

@@ -1,11 +1,10 @@
package com.hmh.hamyeonham.login.mapper
package com.hmh.hamyeonham.userinfo.mapper
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify that all imports referencing the old package are updated.

The package name has been changed from com.hmh.hamyeonham.login.mapper to com.hmh.hamyeonham.userinfo.mapper. Ensure that all files importing this mapper have been updated to use the new package path.


🏁 Script executed:

#!/bin/bash
# Description: Check for any remaining imports of the old package path
# Expected: No results should be found for the old package path

echo "Searching for old package imports..."
rg "com\.hmh\.hamyeonham\.login\.mapper" --type kotlin

echo "Searching for imports of MypageMapper..."
rg "import.*MypageMapper" --type kotlin -A 2 -B 2

Length of output: 518


Update remaining references to the old mapper package

The search revealed two files still importing or declaring the old package. Please update them to the new com.hmh.hamyeonham.userinfo.mapper path:

  • data/main/src/main/java/com/hmh/hamyeonham/data/main/DefaultMainRepository.kt
    Change
    import com.hmh.hamyeonham.login.mapper.toBanner
    to
    import com.hmh.hamyeonham.userinfo.mapper.toBanner

  • data/main/src/main/java/com/hmh/hamyeonham/data/main/mapper/Mapper.kt
    Change
    package com.hmh.hamyeonham.login.mapper
    to
    package com.hmh.hamyeonham.userinfo.mapper

This will ensure imports resolve correctly after the refactoring.

🤖 Prompt for AI Agents
In
data/main/src/main/java/com/hmh/hamyeonham/data/main/DefaultMainRepository.kt,
update the import statement from com.hmh.hamyeonham.login.mapper.toBanner to
com.hmh.hamyeonham.userinfo.mapper.toBanner. In
data/main/src/main/java/com/hmh/hamyeonham/data/main/mapper/Mapper.kt, change
the package declaration from com.hmh.hamyeonham.login.mapper to
com.hmh.hamyeonham.userinfo.mapper. These changes will align the references with
the new mapper package path.

Comment on lines +11 to +13
data class LogoutFail(val message: String) : UserEffect
data object WithdrawalSuccess : UserEffect

data object WithdrawalFail : UserEffect
data class WithdrawalFail(val message: String) : UserEffect
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused message parameters from failure cases.

The LogoutFail and WithdrawalFail data classes now include a message: String parameter, but the empty logout() and withdrawal() functions don't emit any failure events. This creates dead code and an inconsistent interface.

Apply this diff to simplify the interface:

-    data class LogoutFail(val message: String) : UserEffect
+    data object LogoutFail : UserEffect
     data object WithdrawalSuccess : UserEffect
-    data class WithdrawalFail(val message: String) : UserEffect
+    data object WithdrawalFail : UserEffect
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class LogoutFail(val message: String) : UserEffect
data object WithdrawalSuccess : UserEffect
data object WithdrawalFail : UserEffect
data class WithdrawalFail(val message: String) : UserEffect
data object LogoutFail : UserEffect
data object WithdrawalSuccess : UserEffect
data object WithdrawalFail : UserEffect
🤖 Prompt for AI Agents
In
feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/viewmodel/MyPageViewModel.kt
around lines 11 to 13, remove the unused message parameters from the LogoutFail
and WithdrawalFail data classes since the logout() and withdrawal() functions do
not emit failure events. Change LogoutFail and WithdrawalFail from data classes
with a message parameter to simple objects without parameters to simplify and
make the interface consistent.

Comment on lines 17 to 18
class MyPageViewModel @Inject constructor(
private val authRepository: AuthRepository,
private val hmhPreference: DefaultHMHNetworkPreference,
private val databaseManager: DatabaseManager
) : ViewModel() {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider removing the empty ViewModel or implementing placeholder logic.

The constructor has no dependencies and the core functions are empty, making this ViewModel essentially non-functional. This could lead to UI elements that appear interactive but don't perform any actions.

Consider one of these approaches:

  1. If authentication features are permanently removed, remove this ViewModel and update the UI accordingly
  2. If this is temporary during migration, add TODO comments and basic placeholder implementations
  3. If local authentication will be implemented later, add the necessary Room DB dependencies
@HiltViewModel
class MyPageViewModel @Inject constructor(
    // TODO: Add local auth dependencies when implementing Room DB authentication
) : ViewModel() {
🤖 Prompt for AI Agents
In
feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/viewmodel/MyPageViewModel.kt
at lines 17-18, the ViewModel class is empty with no dependencies or
functionality, making it non-functional and potentially confusing for the UI. To
fix this, either remove the ViewModel if authentication features are permanently
removed, or if this is temporary, add TODO comments indicating planned
implementations and include basic placeholder logic. If local authentication is
planned, add the necessary Room DB dependencies in the constructor and annotate
the class with @HiltViewModel to prepare for future development.

Comment on lines +23 to 29
fun logout() {

fun handleWithdrawal() {
viewModelScope.launch {
authRepository.withdrawal(hmhPreference.accessToken).onSuccess {
deleteAllDatabase()
clearPreference()
_userEffect.emit(UserEffect.WithdrawalSuccess)
Log.d("hmhPreferenceAccessToken", hmhPreference.accessToken)
}.onFailure {
_userEffect.emit(UserEffect.WithdrawalFail)
}
}
}

private fun clearPreference() {
hmhPreference.clear()
}
fun withdrawal() {

private fun deleteAllDatabase() {
viewModelScope.launch {
databaseManager.deleteAll()
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement basic functionality or add clear documentation.

The empty logout() and withdrawal() functions create a poor user experience where UI interactions appear to do nothing. This could confuse users and developers.

Apply this diff to add placeholder implementations with proper communication:

 fun logout() {
-
+    // TODO: Implement local logout when Room DB authentication is ready
+    viewModelScope.launch {
+        _effect.emit(UserEffect.LogoutSuccess)
+    }
 }

 fun withdrawal() {
-
+    // TODO: Implement local account deletion when Room DB authentication is ready  
+    viewModelScope.launch {
+        _effect.emit(UserEffect.WithdrawalSuccess)
+    }
 }

Don't forget to add the missing import:

import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
🤖 Prompt for AI Agents
In
feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/viewmodel/MyPageViewModel.kt
around lines 23 to 29, the logout() and withdrawal() functions are empty,
leading to a poor user experience. Implement basic placeholder functionality by
launching coroutines within viewModelScope that perform minimal actions or log
messages to indicate these functions are called. Also, add the missing imports
for androidx.lifecycle.viewModelScope and kotlinx.coroutines.launch to support
coroutine usage.

@kez-lab kez-lab merged commit be69df6 into develop Aug 5, 2025
2 checks passed
@kez-lab kez-lab deleted the feat/remove_server_login branch August 5, 2025 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✂️delete 불필요한 코드 삭제 🐱의진 의진이 작업 🔎refact 내부 로직은 변경하지 않고 기존의 코드를 개선하는 리팩토링

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants