-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/#72 : 로그인 뷰모델, ui 일부 수정 #74
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 all commits
14c2486
1472ec9
347eda3
46c9dea
47b52cc
4d7c30c
5142dbe
44fb94b
84a0ff1
b3128aa
ae780cd
90b62c6
21ef2fb
ec7c27c
9d261c3
e7136a0
6a0d6ab
15c617b
eeac11a
b9dae13
602d2ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,15 +5,31 @@ import android.content.Context | |
| import androidx.datastore.preferences.core.Preferences | ||
| import androidx.datastore.preferences.core.booleanPreferencesKey | ||
| import androidx.datastore.preferences.core.edit | ||
| import androidx.datastore.preferences.core.longPreferencesKey | ||
| import androidx.datastore.preferences.core.stringPreferencesKey | ||
| import androidx.datastore.preferences.preferencesDataStore | ||
| import dagger.hilt.android.qualifiers.ApplicationContext | ||
| import kotlinx.coroutines.flow.Flow | ||
| import kotlinx.coroutines.flow.map | ||
| import javax.inject.Inject | ||
| import javax.inject.Singleton | ||
| import kotlinx.coroutines.flow.first | ||
|
|
||
| /** | ||
| * [지현이 사용법 요약] | ||
| * | ||
| * 1. 세션 데이터 읽기 (UI 표시용) | ||
| * - session.nickname, session.email, session.purposes 등 | ||
| * | ||
| * 2. 사용자 정보 수정 | ||
| * - MyPageViewModel.updateUserInfo() 호출하면 끝! | ||
| * - 서버 API + 세션 업데이트 모두 자동 처리됨 | ||
| * | ||
| * 3. 닉네임만 수정할 때 | ||
| * - sessionStore.updateNickname(nickname) 사용 가능 | ||
| */ | ||
| // 파일 최상위에 위치해야 합니다. | ||
|
|
||
| private val Context.dataStore by preferencesDataStore(name = "session_prefs") | ||
|
|
||
| @Singleton | ||
|
|
@@ -22,24 +38,22 @@ class SessionStore @Inject constructor( | |
| ) { | ||
| private object Keys { | ||
| val LOGGED_IN = booleanPreferencesKey("logged_in") | ||
| val USER_ID = stringPreferencesKey("user_id") | ||
| val USER_ID = longPreferencesKey("user_id") // String -> Long | ||
| val USER_NICK = stringPreferencesKey("user_nickname") | ||
| val USER_EMAIL = stringPreferencesKey("user_email") | ||
| val USER_GENDER = stringPreferencesKey("user_gender") | ||
| val USER_JOB_ID = stringPreferencesKey("user_job_id") | ||
| val USER_JOB_ID = longPreferencesKey("user_job_id") // String -> Long | ||
| val USER_JOB_NAME = stringPreferencesKey("user_job_name") | ||
| val USER_MY_LINKU = stringPreferencesKey("user_my_linku") | ||
| val USER_MY_FOLDER = stringPreferencesKey("user_my_folder") | ||
| val USER_MY_AI_LINKU = stringPreferencesKey("user_my_ai_linku") | ||
| } | ||
|
|
||
| suspend fun setLoggedIn(value: Boolean) { | ||
| context.dataStore.edit { p -> p[Keys.LOGGED_IN] = value } | ||
| val USER_MY_LINKU = longPreferencesKey("user_my_linku") // String -> Long | ||
| val USER_MY_FOLDER = longPreferencesKey("user_my_folder") // String -> Long | ||
| val USER_MY_AI_LINKU = longPreferencesKey("user_my_ai_linku") // String -> Long | ||
| val USER_PURPOSES = stringPreferencesKey("user_purposes") // 마이페이지 수정을 위해 추가. | ||
| val USER_INTERESTS = stringPreferencesKey("user_interests") // 마이페이지 수정을 위해 추가. | ||
| } | ||
|
|
||
| /** 앱 시작 시 오토로그인 분기용 */ | ||
| val isLoggedIn: Flow<Boolean> = | ||
| context.dataStore.data.map { prefs: Preferences -> | ||
| context.dataStore.data.map { prefs -> | ||
| prefs[Keys.LOGGED_IN] ?: false | ||
| } | ||
|
|
||
|
|
@@ -53,36 +67,70 @@ class SessionStore @Inject constructor( | |
| myLinku: Long, | ||
| myFolder: Long, | ||
| myAiLinku: Long, | ||
| purposes: List<String>, // 추가 - 마이페이지 수정. | ||
| interests: List<String>, | ||
| ) { | ||
| context.dataStore.edit { p -> | ||
| p[Keys.LOGGED_IN] = true | ||
| p[Keys.USER_ID] = userId.toString() | ||
| p[Keys.USER_ID] = userId | ||
| p[Keys.USER_NICK] = nickname | ||
| p[Keys.USER_EMAIL] = email | ||
| p[Keys.USER_GENDER] = gender | ||
| p[Keys.USER_JOB_ID] = jobId.toString() | ||
| p[Keys.USER_JOB_ID] = jobId | ||
| p[Keys.USER_JOB_NAME] = jobName | ||
| p[Keys.USER_MY_LINKU] = myLinku.toString() | ||
| p[Keys.USER_MY_FOLDER] = myFolder.toString() | ||
| p[Keys.USER_MY_AI_LINKU] = myAiLinku.toString() | ||
| p[Keys.USER_MY_LINKU] = myLinku | ||
| p[Keys.USER_MY_FOLDER] = myFolder | ||
| p[Keys.USER_MY_AI_LINKU] = myAiLinku | ||
| p[Keys.USER_PURPOSES] = purposes.joinToString(",") //추가 - 마이페이지 수정. | ||
| p[Keys.USER_INTERESTS] = interests.joinToString(",") | ||
|
Comment on lines
+70
to
+85
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. 🧩 Analysis chain🏁 Script executed: find . -name "SessionStore.kt" -type fRepository: LinkYou-2025/LinkU_Android Length of output: 131 🏁 Script executed: # Get the full SessionStore.kt file to understand the structure
git ls-files | grep -i sessionstoreRepository: LinkYou-2025/LinkU_Android Length of output: 129 🏁 Script executed: # Search for SessionStore in the codebase
fd -t f "SessionStore" --extension ktRepository: LinkYou-2025/LinkU_Android Length of output: 129 🏁 Script executed: wc -l core/src/main/java/com/example/core/session/SessionStore.ktRepository: LinkYou-2025/LinkU_Android Length of output: 133 🏁 Script executed: cat -n core/src/main/java/com/example/core/session/SessionStore.ktRepository: LinkYou-2025/LinkU_Android Length of output: 7148 🏁 Script executed: # Check if stringSetPreferencesKey is mentioned anywhere in the project
rg "stringSetPreferencesKey" --type ktRepository: LinkYou-2025/LinkU_Android Length of output: 96 🏁 Script executed: # Check gradle or build files for DataStore version
find . -name "build.gradle*" -o -name "build.gradle.kts" | head -10Repository: LinkYou-2025/LinkU_Android Length of output: 352 🏁 Script executed: # Look for DataStore dependency version
rg "datastore" -A 2 build.gradle.ktsRepository: LinkYou-2025/LinkU_Android Length of output: 52 🏁 Script executed: cat core/build.gradle.kts | grep -A 10 -B 2 "datastore"Repository: LinkYou-2025/LinkU_Android Length of output: 208 🏁 Script executed: # Search for stringSetPreferencesKey in the project
rg "stringSetPreferencesKey" .Repository: LinkYou-2025/LinkU_Android Length of output: 52 🏁 Script executed: # Check DataStore API documentation by examining imports and available keys
rg "import androidx.datastore.preferences.core" core/src/main/java/com/example/core/session/SessionStore.ktRepository: LinkYou-2025/LinkU_Android Length of output: 363 🏁 Script executed: # Check what keys are available in DataStore 1.1.1
web_search "androidx.datastore:datastore-preferences 1.1.1 stringSetPreferencesKey"Repository: LinkYou-2025/LinkU_Android Length of output: 120 🏁 Script executed: web_search "DataStore Preferences stringSetPreferencesKey availability android"Repository: LinkYou-2025/LinkU_Android Length of output: 120 🌐 Web query:
💡 Result: In import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringSetPreferencesKey
val TAGS_KEY: Preferences.Key<Set<String>> = stringSetPreferencesKey("tags")
Sources: [1] [2] 🌐 Web query:
💡 Result:
Example (Kotlin)import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringSetPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.map
private val Context.dataStore by preferencesDataStore(name = "settings")
private val TAGS = stringSetPreferencesKey("tags")
suspend fun addTag(context: Context, tag: String) {
context.dataStore.edit { prefs ->
val current = prefs[TAGS].orEmpty()
prefs[TAGS] = current + tag
}
}
val tagsFlow = context.dataStore.data.map { prefs ->
prefs[TAGS].orEmpty()
}Gradle (example): implementation "androidx.datastore:datastore-preferences:1.0.0"[2] Sources: Android API reference for [1] https://developer.android.com/reference/kotlin/androidx/datastore/preferences/core/package-summary purposes/interests를 콤마 문자열로 저장하면 데이터 손상이 가능합니다. 항목 값에 콤마가 포함되면 분해 시 원본이 깨집니다. 순서가 중요하지 않다면 ♻️ 제안 변경안 (순서 불필요한 경우)-import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.core.stringSetPreferencesKey
- val USER_PURPOSES = stringPreferencesKey("user_purposes")
- val USER_INTERESTS = stringPreferencesKey("user_interests")
+ val USER_PURPOSES = stringSetPreferencesKey("user_purposes")
+ val USER_INTERESTS = stringSetPreferencesKey("user_interests")
- p[Keys.USER_PURPOSES] = purposes.joinToString(",")
- p[Keys.USER_INTERESTS] = interests.joinToString(",")
+ p[Keys.USER_PURPOSES] = purposes.toSet()
+ p[Keys.USER_INTERESTS] = interests.toSet()
- p[Keys.USER_PURPOSES] = purposes.joinToString(",")
- p[Keys.USER_INTERESTS] = interests.joinToString(",")
+ p[Keys.USER_PURPOSES] = purposes.toSet()
+ p[Keys.USER_INTERESTS] = interests.toSet()
- purposes = p[Keys.USER_PURPOSES]?.split(",")?.map { it.trim() }?.filter { it.isNotBlank() } ?: emptyList(),
- interests = p[Keys.USER_INTERESTS]?.split(",")?.map { it.trim() }?.filter { it.isNotBlank() } ?: emptyList(),
+ purposes = p[Keys.USER_PURPOSES]?.toList() ?: emptyList(),
+ interests = p[Keys.USER_INTERESTS]?.toList() ?: emptyList(),적용 위치: 84-85, 110-111, 162-163 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
||
| suspend fun clear() { | ||
| context.dataStore.edit { p -> | ||
| p[Keys.LOGGED_IN] = false | ||
| p.remove(Keys.USER_ID) | ||
| p.remove(Keys.USER_NICK) | ||
| p.remove(Keys.USER_EMAIL) | ||
| p.remove(Keys.USER_GENDER) | ||
| p.remove(Keys.USER_JOB_ID) | ||
| p.remove(Keys.USER_JOB_NAME) | ||
| p.remove(Keys.USER_MY_LINKU) | ||
| p.remove(Keys.USER_MY_FOLDER) | ||
| p.remove(Keys.USER_MY_AI_LINKU) | ||
| p.clear() // 모든 세션 데이터 한 번에 삭제 | ||
| } | ||
| } | ||
|
|
||
| // TODO : 지현이에게 전달 | ||
| // 지현이를 위한 실시간 프로필 업데이트 지원 | ||
| // 프로필 수정 시 purposes/interests도 업데이트 | ||
| suspend fun updateProfile( | ||
| nickname: String, | ||
| jobId: Long, | ||
| jobName: String, | ||
| purposes: List<String>, | ||
| interests: List<String> | ||
| ) { | ||
| context.dataStore.edit { p -> | ||
| p[Keys.USER_NICK] = nickname | ||
| p[Keys.USER_JOB_ID] = jobId | ||
| p[Keys.USER_JOB_NAME] = jobName | ||
| p[Keys.USER_PURPOSES] = purposes.joinToString(",") | ||
| p[Keys.USER_INTERESTS] = interests.joinToString(",") | ||
| } | ||
| } | ||
|
|
||
| /** MyPageViewModel에서 사용방법 | ||
| * fun updateUserInfo(nickname: String, jobId: Long, jobName: String) { | ||
| * viewModelScope.launch { | ||
| * // 1. 서버 API 호출 (UserRepository) | ||
| * val isSuccess = userRepository.updateUserInfo(nickname, jobId, ...) | ||
| * | ||
| * if (isSuccess) { | ||
| * // 2. 서버 성공 시 세션 스토어만 업데이트 (이것만 하면 UI가 알아서 바뀜!) | ||
| * sessionStore.updateProfile(nickname, jobId, jobName) | ||
| * } | ||
|
Comment on lines
+115
to
+124
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. 사용 예시 주석이 변경된 시그니처와 불일치합니다.
✍️ 주석 수정 예시- * sessionStore.updateProfile(nickname, jobId, jobName)
+ * sessionStore.updateProfile(nickname, jobId, jobName, purposes, interests)🤖 Prompt for AI Agents |
||
| * } | ||
| * } | ||
| * */ | ||
|
|
||
| // 닉네임만 수정할 때 -> TODO : 지현이에게 전달 | ||
| suspend fun updateNickname(nickname: String) { | ||
| context.dataStore.edit { p -> p[Keys.USER_NICK] = nickname } | ||
| } | ||
|
|
||
| data class SessionSnapshot( | ||
| val loggedIn: Boolean, | ||
| val userId: Long?, | ||
|
|
@@ -94,21 +142,25 @@ class SessionStore @Inject constructor( | |
| val myLinku: Long?, | ||
| val myFolder: Long?, | ||
| val myAiLinku: Long?, | ||
| val purposes: List<String>, // 추가 - 마이페이지 수정을 위해. | ||
| val interests: List<String>, | ||
| ) | ||
|
|
||
| val session: Flow<SessionSnapshot> = | ||
| context.dataStore.data.map { p -> | ||
| SessionSnapshot( | ||
| loggedIn = p[Keys.LOGGED_IN] ?: false, | ||
| userId = p[Keys.USER_ID]?.toLongOrNull(), | ||
| nickname = p[Keys.USER_NICK], | ||
| email = p[Keys.USER_EMAIL], | ||
| gender = p[Keys.USER_GENDER], | ||
| jobId = p[Keys.USER_JOB_ID]?.toLongOrNull(), | ||
| jobName = p[Keys.USER_JOB_NAME], | ||
| myLinku = p[Keys.USER_MY_LINKU]?.toLongOrNull(), | ||
| myFolder = p[Keys.USER_MY_FOLDER]?.toLongOrNull(), | ||
| myAiLinku = p[Keys.USER_MY_AI_LINKU]?.toLongOrNull(), | ||
| loggedIn = p[Keys.LOGGED_IN] ?: false, | ||
| userId = p[Keys.USER_ID], | ||
| nickname = p[Keys.USER_NICK], | ||
| email = p[Keys.USER_EMAIL], | ||
| gender = p[Keys.USER_GENDER], | ||
| jobId = p[Keys.USER_JOB_ID], | ||
| jobName = p[Keys.USER_JOB_NAME], | ||
| myLinku = p[Keys.USER_MY_LINKU], | ||
| myFolder = p[Keys.USER_MY_FOLDER], | ||
| myAiLinku = p[Keys.USER_MY_AI_LINKU], | ||
| purposes = p[Keys.USER_PURPOSES]?.split(",")?.map { it.trim() }?.filter { it.isNotBlank() } ?: emptyList(), | ||
| interests = p[Keys.USER_INTERESTS]?.split(",")?.map { it.trim() }?.filter { it.isNotBlank() } ?: emptyList(), | ||
| ) | ||
| } | ||
|
|
||
|
|
||
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.
🧩 Analysis chain
🏁 Script executed:
Repository: LinkYou-2025/LinkU_Android
Length of output: 131
🏁 Script executed:
cat -n ./core/src/main/java/com/example/core/session/SessionStore.kt | head -100Repository: LinkYou-2025/LinkU_Android
Length of output: 4283
🏁 Script executed:
Repository: LinkYou-2025/LinkU_Android
Length of output: 135
🏁 Script executed:
cat -n ./core/src/main/java/com/example/core/session/SessionStore.kt | tail -80Repository: LinkYou-2025/LinkU_Android
Length of output: 3332
String → Long 타입 변경에 대한 마이그레이션 전략이 필요합니다.
user_id,user_job_id,user_my_linku,user_my_folder,user_my_ai_linku가 String 타입에서 Long 타입으로 변경되었습니다. 기존 사용자의 데이터스토어에 문자열로 저장된 값이 있다면, 업데이트 후longPreferencesKey로 읽을 때 타입 불일치로 인해 null이 반환되어 로그인 상태 검증이나 사용자 정보 로드가 실패할 수 있습니다.마이그레이션 전략이 필요합니다. 예를 들어:
또한
purposes와interests는joinToString(",")방식으로 저장되는데, 데이터에 쉼표가 포함되면 분할 시 손상될 수 있으므로 JSON 또는 더 안전한 포맷 사용을 검토하세요.🤖 Prompt for AI Agents