-
Notifications
You must be signed in to change notification settings - Fork 1
서버 로그인 제거 및 Room DB 기반 로그인 구조 개선 #313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
2345816
6d47ab1
11c7abd
d033352
906279e
bc605e7
0ee991d
c8bc433
c05add5
20b60be
327e78a
57fcd19
7028ad9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package com.hmh.hamyeonham.core.database.dao | ||
|
|
||
| import androidx.room.Dao | ||
| import androidx.room.Insert | ||
| import androidx.room.OnConflictStrategy | ||
| import androidx.room.Query | ||
| import com.hmh.hamyeonham.core.database.entity.UserAuth | ||
| import kotlinx.coroutines.flow.Flow | ||
|
|
||
| @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?> | ||
|
|
||
| @Insert(onConflict = OnConflictStrategy.REPLACE) | ||
| suspend fun insertOrUpdateUserAuth(userAuth: UserAuth) | ||
|
|
||
| @Query("UPDATE user_auth SET isLoggedIn = :isLoggedIn WHERE id = 1") | ||
| suspend fun updateLoginStatus(isLoggedIn: Boolean) | ||
|
|
||
| @Query("DELETE FROM user_auth") | ||
| suspend fun clearUserAuth() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.hmh.hamyeonham.core.database.dao | ||
|
|
||
| import androidx.room.Dao | ||
| import androidx.room.Insert | ||
| import androidx.room.OnConflictStrategy | ||
| import androidx.room.Query | ||
| import com.hmh.hamyeonham.core.database.entity.UserProfile | ||
| import kotlinx.coroutines.flow.Flow | ||
|
|
||
| @Dao | ||
| interface UserProfileDao { | ||
| @Query("SELECT * FROM user_profile WHERE userId = :userId") | ||
| suspend fun getUserProfile(userId: Long): UserProfile? | ||
|
|
||
| @Query("SELECT * FROM user_profile WHERE userId = :userId") | ||
| fun observeUserProfile(userId: Long): Flow<UserProfile?> | ||
|
|
||
| @Insert(onConflict = OnConflictStrategy.REPLACE) | ||
| suspend fun insertOrUpdateUserProfile(userProfile: UserProfile) | ||
|
|
||
| @Query("DELETE FROM user_profile") | ||
| suspend fun clearUserProfile() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.hmh.hamyeonham.core.database.entity | ||
|
|
||
| import androidx.room.Entity | ||
| import androidx.room.PrimaryKey | ||
|
|
||
| /** | ||
| * 사용자 인증 정보를 저장하는 엔티티 | ||
| * 토큰은 SDK에서 관리하므로 저장하지 않음 | ||
| */ | ||
| @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 | ||
| ) | ||
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| package com.hmh.hamyeonham.core.database.entity | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import androidx.room.Entity | ||||||||||||||||||||||||||||||||||||||||||||||
| import androidx.room.PrimaryKey | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * 사용자 프로필 정보를 저장하는 엔티티 | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| @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 = 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.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,9 +6,9 @@ import javax.inject.Inject | |||||||||||||||||||||||||||||||||||||||||||||||||
| import javax.inject.Singleton | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| @Singleton | ||||||||||||||||||||||||||||||||||||||||||||||||||
| class DefaultHMHNetworkPreference @Inject constructor( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| class DefaultUserPreference @Inject constructor( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| private val preferences: SharedPreferences, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : HMHNetworkPreference { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : UserPreference { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| override var accessToken: String | ||||||||||||||||||||||||||||||||||||||||||||||||||
| get() = preferences.getString("access_token", "").orEmpty() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| set(value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -30,11 +30,11 @@ class DefaultHMHNetworkPreference @Inject constructor( | |||||||||||||||||||||||||||||||||||||||||||||||||
| putString("user_name", value) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| override var userId: Int | ||||||||||||||||||||||||||||||||||||||||||||||||||
| get() = preferences.getInt("user_id", -1) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| override var userId: Long | ||||||||||||||||||||||||||||||||||||||||||||||||||
| get() = preferences.getLong("user_id", -1) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| set(value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| preferences.edit(commit = true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| putInt("user_id", value) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| putLong("user_id", value) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+33
to
39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify migration impact for existing users. The 🏁 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 ktLength 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 • File: 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| override var autoLoginConfigured: Boolean | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,10 @@ | ||
| package com.hmh.hamyeonham.core.network.auth.datastore.network | ||
|
|
||
| interface HMHNetworkPreference { | ||
| interface UserPreference { | ||
| var accessToken: String | ||
| var refreshToken: String | ||
| var userName: String | ||
| var userId: Int | ||
| var userId: Long | ||
| var autoLoginConfigured: Boolean | ||
| fun clear() | ||
| } |
This file was deleted.
There was a problem hiding this comment.
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:
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.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
🤖 Prompt for AI Agents