-
Notifications
You must be signed in to change notification settings - Fork 0
[Refactor] 네비게이션 인자 전달 방식 최적화 #234 #241
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
Conversation
주요 변경사항: - Route.kt: 모든 Route를 ID 기반으로 유지 - NavGraph.kt: typeMap 제거, 모든 composable에서 ID만 전달 - 화면들: DTO 파라미터 제거, ID 기반으로 변경 - ViewModel: StateFlow로 데이터 로드 (Repository 필터링) - 상세 화면들: when 분기처리로 로딩/에러/데이터 상태 관리 신규 ViewModel: - DetailNoticeViewModel: ID로 공지사항 로드 - DetailSubscribeViewModel: ID로 구독정보 로드 업데이트 ViewModel: - DetailElderInfoViewModel: StateFlow 추가, loadElderDataById() 메서드 추가 - DetailHealthViewModel: 데이터 로드 로직 추가 - DetailMyDataViewModel: StateFlow 추가, 자동 로드 - DetailSubscribeViewModel: 신규 생성 - DetailNoticeViewModel: 신규 생성 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…dicare-Call/Medicare-Call-Android into refactor/optimization-navigation-#234
- DetailElderInfoViewModel에 isLoading StateFlow 추가 (HealthDetailScreen 패턴 동일화) - loadElderDataById에서 로딩 상태 토글 구현 - processElderInfo 메서드 구현 (등록/수정 통합 처리) - elderId 파라미터 추가하여 ID 기반 업데이트 - 수정 모드에서만 데이터 자동 재로드 (-1이 아닐 때) - ElderDetailScreen에 CircularProgressIndicator import 추가 - ElderDetailScreen 상태 분기 처리 추가 - 수정 모드 && 로딩 중: 로딩 인디케이터 표시 - 등록 모드 또는 데이터 로드 완료: 폼 UI 표시 - processElderInfo 호출에 elderId 파라미터 전달 이제 모든 Detail Screen이 일관된 패턴으로 상태를 관리합니다 (로딩/에러/데이터). Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…dicare-Call/Medicare-Call-Android into refactor/optimization-navigation-#234
Walkthrough이 PR은 설정 화면의 네비게이션 아키텍처를 리팩토링하여 DTO 객체 대신 원시 ID를 전달하도록 변경합니다. 화면에서 DTO를 직접 받는 대신 ID를 기반으로 ViewModel에서 데이터를 로드합니다. Changes
Sequence DiagramsequenceDiagram
actor User as 사용자
participant Nav as Navigation Layer
participant Screen as Detail Screen
participant VM as ViewModel
participant Repo as Repository
participant API as Backend API
User->>Nav: navigate(elderId: Int)
Nav->>Screen: ElderDetailScreen(elderId)
Screen->>VM: LaunchedEffect { loadElderDataById(elderId) }
VM->>Repo: getElders()
Repo->>API: fetch all elders
API-->>Repo: [EldersInfoResponseDto...]
Repo-->>VM: data list
VM->>VM: filter by elderId
VM-->>Screen: uiState = elderData
Screen->>Screen: render form with loaded data
User->>Screen: click save
Screen->>VM: processElderInfo(elderId, requestDto)
VM->>Repo: updateElderInfo(request)
Repo->>API: POST/PATCH update
API-->>Repo: success
Repo-->>VM: updated data
VM->>VM: reload elderData
VM-->>Screen: updated state
Screen->>Nav: onBack()
Nav->>User: navigate back
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingAlarmScreen.kt (1)
43-76: 로딩 전 토글 조작 시 변경이 저장되지 않을 수 있음
myDataInfo가 null인 초기 상태에서도 토글이 즉시 바뀌고updateSettings는 no-op이라 사용자 입장에서 “저장된 듯 보이는” 상태가 될 수 있어요. 로딩 완료 전에는 토글을 비활성화하거나,onCheckedChange에서 null이면 무시하도록 가드하는 게 안전합니다.✅ 예시 수정(가드 추가)
@@ - val myDataInfo by myDataViewModel.myDataInfo.collectAsStateWithLifecycle() + val myDataInfo by myDataViewModel.myDataInfo.collectAsStateWithLifecycle() + val canEdit = myDataInfo != null @@ SwitchButton( checked = masterChecked, onCheckedChange = { isChecked -> + if (!canEdit) return@SwitchButton // 4. 상태를 먼저 모두 변경하고, masterChecked = isChecked completeChecked = isChecked abnormalChecked = isChecked missedChecked = isChecked // 5. 마지막에 변경된 최종 상태로 ViewModel을 호출합니다. updateSettings() }, )다른 스위치
onCheckedChange에도 동일 가드를 적용해주세요.
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementScreen.kt`:
- Line 30: MainNavigator's navigateToNoticeDetail currently accepts a
NoticesResponseDto but AnnouncementScreen and SettingNavigation expect an Int
noticeId; update the MainNavigator.navigateToNoticeDetail signature to accept
noticeId: Int (replace NoticesResponseDto) and update all implementations/call
sites to pass the Int id instead of the DTO (locate usages in
AnnouncementScreen, SettingNavigation.navController.navigateToNoticeDetail
extension, and any other callers) so the types align and compilation succeeds.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt`:
- Around line 70-76: The isMale state is initialized as nullable
(mutableStateOf<Boolean?>(null)) causing submission to default to FEMALE despite
the UI showing “남성”; change its initialization to a non-null Boolean that
matches the UI default (e.g., mutableStateOf(true)) and ensure any places that
read isMale (submission logic, e.g., the function that maps isMale to
Elder.Gender or the submit handler) handle a non-null Boolean rather than
treating null as FEMALE; update all occurrences (including the similar blocks
referenced around lines 203-269) to use the non-null default and adjust mapping
code if it expects nullable.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthInfoScreen.kt`:
- Around line 34-37: MainNavigator's legacy method navigateToHealthDetail
currently accepts an EldersHealthResponseDto but the app now passes an Int
(elderId) from HealthInfoScreen; update MainNavigator.navigateToHealthDetail to
accept an Int (or remove the unused DTO overload) and adjust any internal
routing/callback wiring to use the elderId, ensuring HealthInfoScreen's call
signature (navigateToHealthDetail: (Int) -> Unit) matches the navigator; search
for navigateToHealthDetail and EldersHealthResponseDto usages to update callers
and remove the obsolete DTO-based method.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDataSettingScreen.kt`:
- Line 54: The MyDataSettingScreen callback signature was changed to
navigateToUserInfoSetting: () -> Unit but MainNavigator still declares
navigateToUserInfoSetting(myInfo: MyInfoResponseDto), causing a mismatch; update
MainNavigator's declaration (and all implementing classes/usages) to a
no-argument function named navigateToUserInfoSetting with no MyInfoResponseDto
parameter, and adjust all call sites (implementations, overrides, and
invocations) to call navigateToUserInfoSetting() instead of passing a
MyInfoResponseDto so the interface and MyDataSettingScreen match.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingsScreen.kt`:
- Line 48: SettingsScreen defines navigateToNotificationSetting as a no-arg
callback but MainNavigator still exposes navigateToNotificationSetting(myInfo:
MyInfoResponseDto), causing a signature mismatch; update MainNavigator's method
(and any implementing classes) to a no-argument function named
navigateToNotificationSetting so it matches SettingsScreen, remove the
MyInfoResponseDto parameter from MainNavigator.navigateToNotificationSetting and
adjust all call sites and implementations that previously passed
MyInfoResponseDto to instead obtain needed info inside the navigator or via
another agreed mechanism.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt`:
- Around line 62-63: The Log.d call in DetailElderInfoViewModel currently prints
the full updateInfo (and similarly the request at lines 82-83), which may
contain sensitive data; change these logs to avoid exposing PII by logging only
a non-sensitive identifier (e.g., elderId) or a sanitized/masked representation
(e.g., mask phone/name) instead of the full updateInfo/request objects, and
centralize masking via a helper (e.g., maskSensitiveFields(updateInfo) or log
only updateInfo.id) so both the Log.d("DetailElderInfoViewModel", ...) at the
updateInfo site and the similar log at the request site no longer output raw
personal data.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailMyDataViewModel.kt`:
- Around line 32-45: The loadMyData function leaves _isLoading true if
getMyInfo() throws; wrap the repository call inside a try/finally in
viewModelScope.launch so _isLoading.value is set true before the call and always
set to false in the finally block; update the loadMyData implementation (around
viewModelScope.launch { ... }) to use try { userRepository.getMyInfo()... }
catch { ... } finally { _isLoading.value = false } and keep setting
_errorMessage in the catch/onFailure path and _myDataInfo on success.
🧹 Nitpick comments (3)
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailHealthViewModel.kt (1)
28-45: 로딩 플래그가 예외 상황에서 해제되지 않을 수 있음
Repository 호출이 예외를 던지면_isLoading이 true로 남을 여지가 있어요.try/finally로 해제 보장하면 더 안전합니다.✅ 예시 수정(try/finally)
fun loadHealthInfoById(elderId: Int) { _isLoading.value = true _errorMessage.value = null viewModelScope.launch { - eldersHealthInfoRepository.getEldersHealthInfo() - .onSuccess { list -> - val elderHealth = list.firstOrNull { it.elderId == elderId } - _healthData.value = elderHealth - if (elderHealth == null) { - _errorMessage.value = "건강 정보를 찾을 수 없습니다" - } - } - .onFailure { exception -> - _errorMessage.value = "건강 정보를 불러오지 못했습니다: ${exception.message}" - Log.e("DetailHealthViewModel", "건강 정보 로딩 실패", exception) - } - _isLoading.value = false + try { + eldersHealthInfoRepository.getEldersHealthInfo() + .onSuccess { list -> + val elderHealth = list.firstOrNull { it.elderId == elderId } + _healthData.value = elderHealth + if (elderHealth == null) { + _errorMessage.value = "건강 정보를 찾을 수 없습니다" + } + } + .onFailure { exception -> + _errorMessage.value = "건강 정보를 불러오지 못했습니다: ${exception.message}" + Log.e("DetailHealthViewModel", "건강 정보 로딩 실패", exception) + } + } finally { + _isLoading.value = false + } } }app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthDetailScreen.kt (1)
84-133: 초기/빈 상태 분기 추가 권장
isLoading=false,errorMessage=null,healthData=null조합이면 화면이 비어 보일 수 있어요. 안전하게else분기 하나만 추가해도 UX가 안정됩니다.✅ 간단한 fallback 예시
when { isLoading && healthData == null -> { // Loading state Column( modifier = Modifier .fillMaxSize() .padding(20.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { CircularProgressIndicator() Spacer(modifier = Modifier.height(16.dp)) Text("건강정보를 불러오는 중입니다...") } } errorMessage != null -> { // Error state Column( modifier = Modifier .fillMaxSize() .padding(20.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { Text( text = errorMessage ?: "오류가 발생했습니다", color = MediCareCallTheme.colors.negative, style = MediCareCallTheme.typography.M_17, ) Spacer(modifier = Modifier.height(20.dp)) CTAButton( type = CTAButtonType.GREEN, text = "다시 시도", onClick = { detailViewModel.loadHealthInfoById(elderId) }, ) } } healthData != null -> { // Data loaded state HealthDetailContent( healthInfoResponseDto = healthData!!, detailViewModel = detailViewModel, onBack = onBack, ) } + else -> { + Column( + modifier = Modifier + .fillMaxSize() + .padding(20.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text("데이터를 불러오지 못했습니다.") + } + } }app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/navigation/SettingNavigation.kt (1)
33-34: 매직 넘버(-1) 상수화 제안.
신규 등록 모드를 나타내는 값은 상수로 두면 의미가 더 명확해집니다.♻️ 제안 변경
+private const val NEW_ELDER_ID = -1 + -fun NavController.navigateToElderPersonalDetail(elderId: Int = -1) { +fun NavController.navigateToElderPersonalDetail(elderId: Int = NEW_ELDER_ID) { navigate(Route.ElderPersonalDetail(elderId)) }
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementScreen.kt
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthInfoScreen.kt
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDataSettingScreen.kt
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingsScreen.kt
Show resolved
Hide resolved
.../main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt
Outdated
Show resolved
Hide resolved
...src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailMyDataViewModel.kt
Outdated
Show resolved
Hide resolved
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.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt (1)
31-33: 사용하지 않는 import 제거 필요파이프라인에서
mutableIntStateOf,mutableStateOf,rememberimport가 사용되지 않는다고 플래그되었습니다. ViewModel로 상태 관리를 이전하면서 더 이상 필요하지 않은 것으로 보입니다.🧹 수정 제안
-import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.rememberapp/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/sleep/screen/SleepDetailScreen.kt (2)
64-73: 스텁 함수의 미사용 파라미터들이 CI 실패를 유발하고 있어요.
// TODO: UI ...주석으로 보아 아직 구현 예정인 함수인 것 같은데, 파이프라인에서UnusedParameter에러가 발생하고 있습니다.당장 구현 계획이 없다면 함수를 제거하거나, 파라미터에
@Suppress("UnusedParameter")어노테이션을 추가하는 방법을 고려해보세요.💡 임시 해결 방안
Option 1: Suppress 어노테이션 추가
+@Suppress("UNUSED_PARAMETER") `@Composable` fun SleepDetailScreen( modifier: Modifier = Modifier, sleep: SleepUiState = SleepUiState(), onBack: () -> Unit = {}, onDateSelected: (LocalDate) -> Unit = {}, onMonthClick: () -> Unit = {}, ) { // TODO: UI ... }Option 2: 사용하지 않는 함수 제거
-@Composable -fun SleepDetailScreen( - modifier: Modifier = Modifier, - sleep: SleepUiState = SleepUiState(), - onBack: () -> Unit = {}, - onDateSelected: (LocalDate) -> Unit = {}, - onMonthClick: () -> Unit = {}, -) { - // TODO: UI ... -}
35-51:elderId파라미터가 실제로 사용되고 있지 않네요.다만 확인해보니
SleepViewModel은 이미 네비게이션 라우트의SavedStateHandle을 통해elderId를 추출하고 있습니다 (라인 51). 그래서loadSleepDataForDate()메서드는elderId파라미터를 받지 않는 구조네요.이 경우 몇 가지 옵션이 있습니다:
- 추천: 불필요한
elderId파라미터를 제거하기 (ViewModel이 이미 라우트에서 처리하므로)- 아니면 이것이 의도적인 설계라면, 문서화가 필요할 수 있습니다
현재 구조에서는 제안하신 diff의
loadSleepDataForDate(elderId, selectedDate)형태로 수정할 수 없습니다.app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderInfoScreen.kt (1)
29-29: 사용하지 않는 import 제거 필요CI 파이프라인에서
EldersInfoResponseDtoimport가 사용되지 않는다고 감지되었습니다. ID 기반 네비게이션으로 변경되면서 더 이상 명시적으로 필요하지 않습니다.🔧 제안하는 수정
-import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDtoapp/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingSubscribeScreen.kt (1)
27-27: 사용하지 않는 import 제거 필요CI 파이프라인에서
EldersSubscriptionResponseDtoimport가 사용되지 않는다고 감지되었습니다.🔧 제안하는 수정
-import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDtoapp/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingsScreen.kt (1)
36-36: 파이프라인 실패: 사용되지 않는 import 제거 필요
MyInfoResponseDtoimport가 사용되지 않아 CI가 실패하고 있습니다. 이제myInfo가 ViewModel의 StateFlow에서 직접 수집되므로 이 import는 더 이상 필요하지 않습니다.🔧 수정 제안
-import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDtoapp/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDataSettingScreen.kt (1)
42-42: 파이프라인 실패: 사용되지 않는 import 제거 필요
MyInfoResponseDtoimport가 사용되지 않아 CI가 실패하고 있습니다.🔧 수정 제안
-import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDtoapp/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt (1)
31-34: CI 실패 원인: 사용하지 않는 import 제거 필요.
NoUnusedImports가 발생하니 미사용 import 정리 부탁드려요.🧹 제안 수정
-import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt`:
- Around line 257-268: The name validation currently uses
name.matches(Regex("^[가-힣a-zA-Z]*$")) which allows an empty string; update the
condition in the CTA button type expression so name cannot be empty — either
change the regex to use + (e.g., "^[가-힣a-zA-Z]+$") or add name.isNotEmpty() to
the combined predicate used to decide CTAButtonType.GREEN (the block referencing
name.matches(...), birth.isValidDate(), phoneNum checks, and isMale != null).
- Around line 109-116: The LaunchedEffect that listens to isSuccess won't
retrigger if isSuccess stays true, and deleteElderInfo() never sets success, so
navigation may not occur on subsequent operations; after handling navigation in
the LaunchedEffect
(navController.previousBackStackEntry?.savedStateHandle?.set("ELDER_NAME_UPDATED",
name) and onBack()), reset the backing flag (_isSuccess) to false so isSuccess
toggles for future operations, and update the ViewModel methods
processElderInfo(), updateElderInfo(), and deleteElderInfo() so they set
_isSuccess = true on success (and the first two also ensure _isSuccess is reset
immediately after the LaunchedEffect handles it), or alternatively convert
isSuccess to a one-shot event (SharedFlow/Channel/Event wrapper) to guarantee
single consumption.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthDetailScreen.kt`:
- Line 7: Remove the unused import androidx.compose.foundation.layout.Row from
HealthDetailScreen.kt to fix the CI failure; locate the import statement
referencing Row at the top of the file (unused symbol: Row) and delete it so the
file only imports actually used Compose layout types.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingAlarmScreen.kt`:
- Around line 61-76: The updateSettings lambda can read stale composition state
right after setXChecked; change updateSettings to accept the new push-toggle
values as parameters and use those parameters when calling
myDataViewModel.updateUserData instead of reading
masterChecked/completeChecked/abnormalChecked/missedChecked from closure; update
all callers (the toggle handlers at the locations noted) to pass the immediate
boolean/new "ON"/"OFF" values into updateSettings, and ensure you construct
PushNotificationDto from those arguments when copying myDataInfo in
updateSettings (refer to updateSettings, myDataViewModel.updateUserData,
myDataInfo, and PushNotificationDto).
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SubscribeDetailScreen.kt`:
- Around line 134-137: SubscribeDetailContent 함수의 onBack 파라미터가 사용되지 않아 빌드가 실패하고
있으니, 호출부와 함께 파라미터를 제거하거나(더 간단) 또는 확인 버튼 클릭 시 뒤로가기 동작으로 활용하도록 수정하세요; 구체적으로는
SubscribeDetailContent 선언에서 onBack 매개변수를 삭제하고 모든 호출지에서 해당 인자 전달을 제거하거나, 만약 뒤로가기
동작을 의도했다면 Compose 내의 확인 버튼 클릭 핸들러(예: 확인 버튼의 onClick)에서 onBack을 호출하도록
SubscribeDetailContent 선언과 버튼 클릭 로직을 연결하고 호출부는 그대로 두세요.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt`:
- Around line 52-53: The CI failure is due to ktlint requiring a blank line
between declarations and the following comment/declaration; in
DetailElderInfoViewModel locate the closing brace shown before the "// UI State"
comment and insert a single empty line between that brace (the previous
declaration) and the "// UI State" comment/declaration so there is a blank line
separating declarations per ktlint rules.
In `@gradle.properties`:
- Around line 24-33: The gradle.properties flags need alignment with AGP 9+
expectations: ensure android.newDsl and android.builtInKotlin are set together
(keep both false for now or both true) and add a short migration TODO noting
they will be removed in AGP 10.0; remove or move
android.defaults.buildfeatures.resvalues from this global file into per-module
build.gradle.kts files where resValues are actually required; update
android.enableAppCompileTimeRClass to true to match the new default; keep
monitoring the other flags (android.usesSdkInManifest.disallowed,
android.uniquePackageNames, android.dependency.useConstraints) and add a comment
summarizing these decisions for future AGP upgrades.
In `@gradle/libs.versions.toml`:
- Line 2: Update the project's Java and Kotlin target settings from 11 to 17+:
change any occurrences of sourceCompatibility = JavaVersion.VERSION_11 and
targetCompatibility = JavaVersion.VERSION_11 to JavaVersion.VERSION_17 (or
higher) and update any kotlinOptions.jvmTarget = "11" to "17" (or higher);
ensure build scripts that set javaToolchain or org.gradle.jvmargs are aligned to
Java 17, and verify CI/build agents use JDK 17+ so AGP 9.0.0 and Gradle 9.1.0
can run.
🧹 Nitpick comments (4)
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statehealth/screen/StateHealthDetailScreen.kt (1)
56-76: 로딩 상태 처리는 깔끔해졌네요. 에러 상태 처리도 고려해보시면 좋을 것 같아요.
isLoading을 직접 Boolean으로 사용하는 변경은 위임자 패턴과 일관성 있게 잘 적용되었습니다.한 가지 선택적 제안: AI 요약에 따르면 ViewModel에
errorMessageStateFlow가 있는 것으로 보이는데, 현재 화면에서는 에러 상태를 처리하지 않고 있어요. 네트워크 실패 등의 상황에서 사용자에게 피드백을 제공하는 것이 UX 관점에서 좋을 수 있습니다.💡 에러 상태 처리 추가 예시 (선택사항)
val isLoading by viewModel.isLoading.collectAsStateWithLifecycle() val errorMessage by viewModel.errorMessage.collectAsStateWithLifecycle() // ... 기존 코드 ... when { isLoading -> Box( Modifier .fillMaxSize() .background(color = MediCareCallTheme.colors.white), ) { CircularProgressIndicator( modifier = Modifier.align(Alignment.Center), color = MediCareCallTheme.colors.main, ) } errorMessage != null -> { // 에러 UI 표시 } else -> StateHealthDetailScreenLayout(...) }app/src/main/java/com/konkuk/medicarecall/ui/navigation/Route.kt (1)
59-60: 등록/수정 통합을 위한 기본값 사용이 적절함
elderId = -1을 기본값으로 사용해 등록(신규)과 수정(기존) 흐름을 통합한 설계가 좋습니다. 다만, 매직 넘버-1대신 상수로 정의하면 의도가 더 명확해질 수 있어요.companion object { const val CREATE_NEW_ELDER = -1 }필수는 아니지만 참고해주세요~
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt (2)
100-105: 빈 catch 블록은 디버깅을 어렵게 만들 수 있어요.날짜 파싱 실패 시 예외가 조용히 무시되어
birth필드가 빈 상태로 남습니다. 최소한 로그를 남기거나, 원본 값을 그대로 사용하는 폴백 처리를 추가하면 좋겠어요.🔧 수정 제안
// 날짜 형식 변환 (yyyy-MM-dd -> yyyyMMdd) try { val parseDate = LocalDate.parse(it.birthDate) birth = parseDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) } catch (e: Exception) { - // 파싱 실패 시 기존 값 유지하거나 빈 값 처리 + // 파싱 실패 시 원본에서 숫자만 추출해서 사용 + birth = it.birthDate.filter { char -> char.isDigit() }.take(8) }
303-308:toDashedDate함수를private으로 변경하거나 유틸 파일로 이동하는 것을 고려해 보세요.현재 top-level public 함수로 선언되어 있는데, 이 파일 내에서만 사용된다면
private으로 제한하는 게 좋고, 여러 곳에서 재사용한다면 공통 유틸 파일로 분리하는 게 관리하기 편해요.
| // 작업 성공(등록/수정/삭제) 시 네비게이션 처리 | ||
| LaunchedEffect(isSuccess) { | ||
| if (isSuccess) { | ||
| // 변경된 이름을 이전 화면으로 전달 (Toast 메시지 등을 위해) | ||
| navController.previousBackStackEntry?.savedStateHandle?.set("ELDER_NAME_UPDATED", name) | ||
| onBack() | ||
| } | ||
| } |
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:
# ElderDetailScreen.kt 파일 위치 확인
fd -t f "ElderDetailScreen.kt" --full-path
# 파일 내용 확인 (전체)
fd -t f "ElderDetailScreen.kt" --full-path -x cat -n {}Repository: Medicare-Call/Medicare-Call-Android
Length of output: 15587
🏁 Script executed:
# DetailElderInfoViewModel 파일 찾기
fd -t f "DetailElderInfoViewModel.kt" --full-path
# 파일 크기 확인
fd -t f "DetailElderInfoViewModel.kt" --full-path -x wc -l {}
# 파일 내용 확인
fd -t f "DetailElderInfoViewModel.kt" --full-path -x cat -n {}Repository: Medicare-Call/Medicare-Call-Android
Length of output: 9997
🏁 Script executed:
# 더블 체크: deleteElderInfo 메서드와 isSuccess 관계 확인
rg -n "_isSuccess" app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt
# LaunchedEffect 사용 패턴 다시 확인
rg -n "LaunchedEffect.*isSuccess" app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt -A 5Repository: Medicare-Call/Medicare-Call-Android
Length of output: 668
isSuccess 상태 리셋 로직 필수
isSuccess가 성공 후 false로 리셋되지 않아서, 같은 화면에서 두 번째 작업을 하면 네비게이션이 작동하지 않습니다. LaunchedEffect는 의존성(isSuccess)이 실제로 변할 때만 재실행되는데, true 상태로 유지되면 두 번째 작업도 true → true가 되어 트리거되지 않기 때문입니다.
추가로 deleteElderInfo()는 _isSuccess를 설정하지 않아서 삭제 작업 후에도 화면이 돌아가지 않습니다.
해결책:
processElderInfo()와updateElderInfo()성공 후 네비게이션 처리 직후_isSuccess리셋deleteElderInfo()성공 시에도_isSuccess = true설정- 또는
isSuccess를 일회성 이벤트로 변경 (SharedFlow, Channel, Event wrapper 등)
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt`
around lines 109 - 116, The LaunchedEffect that listens to isSuccess won't
retrigger if isSuccess stays true, and deleteElderInfo() never sets success, so
navigation may not occur on subsequent operations; after handling navigation in
the LaunchedEffect
(navController.previousBackStackEntry?.savedStateHandle?.set("ELDER_NAME_UPDATED",
name) and onBack()), reset the backing flag (_isSuccess) to false so isSuccess
toggles for future operations, and update the ViewModel methods
processElderInfo(), updateElderInfo(), and deleteElderInfo() so they set
_isSuccess = true on success (and the first two also ensure _isSuccess is reset
immediately after the LaunchedEffect handles it), or alternatively convert
isSuccess to a one-shot event (SharedFlow/Channel/Event wrapper) to guarantee
single consumption.
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthDetailScreen.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingAlarmScreen.kt
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SubscribeDetailScreen.kt
Show resolved
Hide resolved
.../main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt
Outdated
Show resolved
Hide resolved
gradle.properties
Outdated
| android.defaults.buildfeatures.resvalues=true | ||
| android.sdk.defaultTargetSdkToCompileSdkIfUnset=false | ||
| android.enableAppCompileTimeRClass=false | ||
| android.usesSdkInManifest.disallowed=false | ||
| android.uniquePackageNames=false | ||
| android.dependency.useConstraints=true | ||
| android.r8.strictFullModeForKeepRules=false | ||
| android.r8.optimizedResourceShrinking=false | ||
| android.builtInKotlin=false | ||
| android.newDsl=false |
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
🌐 Web query:
AGP 9.0.0 android.defaults.buildfeatures.resvalues android.newDsl android.builtInKotlin deprecated removed
💡 Result:
In Android Gradle Plugin (AGP) 9.0.0 (January 2026):
-
android.defaults.buildfeatures.resvalues- Not removed in 9.0.0, but its default changes from
true→false(soresValuesis no longer enabled globally by default). - Migration: enable it only where needed:
android { buildFeatures { resValues = true } }
[1]
- Not removed in 9.0.0, but its default changes from
-
android.newDsl- Not removed in 9.0.0; its default changes from
false→true. - Setting
android.newDsl=falseis a temporary opt-out to keep legacy DSL/variant API behavior, but this opt-out is planned to be removed in AGP 10.0 (mid‑2026).
[1]
- Not removed in 9.0.0; its default changes from
-
android.builtInKotlin- Not removed in 9.0.0; its default changes from
false→true(built-in Kotlin enabled by default). - If you previously applied
org.jetbrains.kotlin.android/kotlin-android, you generally need to remove that plugin and follow the built-in Kotlin migration steps; you can temporarily opt out withandroid.builtInKotlin=false(and the docs note you also needandroid.newDsl=falseto opt out cleanly).
[1][2]
- Not removed in 9.0.0; its default changes from
Sources: AGP 9.0.0 release notes [1] and “Migrate to built-in Kotlin” guide [2].
🌐 Web query:
Android Gradle Plugin 9.0.0 gradle.properties deprecated flags migration guide
💡 Result:
For Android Gradle Plugin (AGP) 9.0.0 (January 2026), the “deprecated flags in gradle.properties” work is mainly about removing global/legacy toggles and moving configuration into per-module DSL.
1) Remove flags that AGP 9.0.0 no longer supports (will break the build)
AGP 9.0.0 errors out if these are set in gradle.properties (delete them):
android.r8.integratedResourceShrinkingandroid.enableNewResourceShrinker.preciseShrinking(resource shrinking is now always precise and integrated with R8) [1]
2) Remove flags that were deleted (no longer recognized) and migrate to module DSL
AGP 9.0.0 removed these global “defaults” flags (delete them), and enable features only in modules that actually need them via android { buildFeatures { ... } }:
android.defaults.buildfeatures.aidl→ in the relevant module(s):buildFeatures { aidl = true }android.defaults.buildfeatures.renderscript→ in the relevant module(s):buildFeatures { renderScript = true }[1]
3) Expect default flips (may require opting out temporarily during migration)
AGP 9.0.0 changes defaults for several gradle.properties switches; if you were relying on old behavior, set an explicit value while you migrate (then remove the opt-out later):
android.newDslnow defaults totrue(legacyandroid.applicationVariants/old variant API not accessible). Temporary opt-out:android.newDsl=false[1]android.builtInKotlinnow defaults totrue(built-in Kotlin enabled by default; AGP has a runtime dependency on KGP 2.2.10). You must migrate or opt out. [1][2]android.defaults.buildfeatures.resvaluesnow defaults tofalse→ enable per module:buildFeatures { resValues = true }[1]android.defaults.buildfeatures.shadersnow defaults tofalse→ enable per module:buildFeatures { shaders = true }[1]
Quick checklist
- Search
gradle.propertiesforandroid.r8.integratedResourceShrinkingandandroid.enableNewResourceShrinker.preciseShrinking→ delete. [1] - Search for
android.defaults.buildfeatures.*→ delete removed ones; move toandroid { buildFeatures { ... } }in only the modules that need them. [1] - If upgrading breaks third-party plugins/build logic, temporarily set
android.newDsl=false, then update plugins and remove the opt-out. [1]
Sources: AGP 9.0.0 release notes and AGP release page. [1][2]
🌐 Web query:
AGP 9.0 enableAppCompileTimeRClass builtInKotlin newDsl support status
💡 Result:
AGP 9.0 (9.0.0, January 2026) support/status of the flags
-
android.enableAppCompileTimeRClass: Supported. In AGP 9.0 its default changes totrue(apps compile against a non-finalRclass). You can opt out by setting it back tofalseif needed. [1] -
android.builtInKotlin: Supported and enabled by default in AGP 9.0 (you no longer need to applyorg.jetbrains.kotlin.android/kotlin-androidjust to compile Kotlin). You can temporarily opt out viaandroid.builtInKotlin=false, but Google notes this opt-out won’t be available in a future AGP 9.x release (before AGP 10.0). Also, opting out requires opting out of the new DSL (android.newDsl=false) because the Kotlin Android plugin is not compatible with the new DSL. [1][2] -
android.newDsl: Supported. In AGP 9.0 its default changes totrue(new DSL interfaces; legacy variant API likeandroid.applicationVariantsis no longer accessible). You can opt out withandroid.newDsl=false. [1]
Sources: Android Developers AGP 9.0.0 release notes [1] and “Migrate to built-in Kotlin” guide [2].
AGP 9.0.0에서 설정 플래그들은 여전히 지원되지만, 일부 임시 옵트-아웃에 대한 마이그레이션 계획 필요
라인 24-33의 플래그들은 AGP 9.0.0에서 제거되지 않았습니다. 다만 주의할 점:
android.newDsl=false와android.builtInKotlin=false는 반드시 함께 설정되어야 하며, 이는 AGP 10.0(2026년 중반 예정)에서 제거될 예정입니다. 향후 마이그레이션 계획을 세우세요.android.defaults.buildfeatures.resvalues는 기본값이true→false로 변경되었습니다. 필요한 모듈에서만 per-module DSL로 설정하는 것이 좋습니다.android.enableAppCompileTimeRClass는 기본값이false→true로 변경되었습니다.
나머지 플래그들(android.usesSdkInManifest.disallowed, android.uniquePackageNames, android.dependency.useConstraints 등)도 현재 지원되지만, AGP 진화 과정에서 지속적으로 모니터링하세요.
🤖 Prompt for AI Agents
In `@gradle.properties` around lines 24 - 33, The gradle.properties flags need
alignment with AGP 9+ expectations: ensure android.newDsl and
android.builtInKotlin are set together (keep both false for now or both true)
and add a short migration TODO noting they will be removed in AGP 10.0; remove
or move android.defaults.buildfeatures.resvalues from this global file into
per-module build.gradle.kts files where resValues are actually required; update
android.enableAppCompileTimeRClass to true to match the new default; keep
monitoring the other flags (android.usesSdkInManifest.disallowed,
android.uniquePackageNames, android.dependency.useConstraints) and add a comment
summarizing these decisions for future AGP upgrades.
gradle/libs.versions.toml
Outdated
| @@ -1,5 +1,5 @@ | |||
| [versions] | |||
| agp = "8.13.0" | |||
| agp = "9.0.0" | |||
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
🌐 Web query:
Android Gradle Plugin 9.0.0 compatibility requirements Gradle version Java version Kotlin version
💡 Result:
Android Gradle Plugin (AGP) 9.0.0 compatibility requirements
- Gradle: 9.1.0 (minimum and default) [1]
- Java (JDK): 17 (minimum and default) [1]
- Kotlin: AGP 9.0 enables built-in Kotlin by default and has a runtime dependency on Kotlin Gradle Plugin (KGP) 2.2.10 (minimum for built-in Kotlin; older KGP versions are auto-upgraded to 2.2.10) [2]
Notes
- Gradle 9.x itself requires JVM 17+ to run the Gradle daemon, which aligns with AGP’s JDK 17 requirement. [3]
Sources:
[1] Android Developers — AGP 9.0.0 release notes (Compatibility table)
[2] Android Developers — AGP 9.0.0 release notes (Built-in Kotlin / KGP 2.2.10)
[3] Gradle Docs — Upgrading to Gradle 9.0.0 (JVM 17+ requirement)
🏁 Script executed:
# Find and examine gradle configuration files
find . -type f -name "libs.versions.toml" -o -name "gradle.properties" -o -name "build.gradle.kts" | head -20Repository: Medicare-Call/Medicare-Call-Android
Length of output: 168
🏁 Script executed:
cat -n gradle/libs.versions.tomlRepository: Medicare-Call/Medicare-Call-Android
Length of output: 5011
🏁 Script executed:
cat -n gradle.propertiesRepository: Medicare-Call/Medicare-Call-Android
Length of output: 2049
🏁 Script executed:
head -100 build.gradle.ktsRepository: Medicare-Call/Medicare-Call-Android
Length of output: 483
🏁 Script executed:
find . -name "gradle-wrapper.properties" -type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 121
🏁 Script executed:
cat -n gradle/wrapper/gradle-wrapper.propertiesRepository: Medicare-Call/Medicare-Call-Android
Length of output: 352
🏁 Script executed:
grep -A 5 -B 5 "java\|jdk\|sourceCompatibility\|targetCompatibility\|toolchain" build.gradle.kts app/build.gradle.ktsRepository: Medicare-Call/Medicare-Call-Android
Length of output: 836
Java 버전 업그레이드 필수 (11 → 17 이상)
AGP 9.0.0과 Gradle 9.1.0은 Java 17 이상을 필요로 하는데, 현재 프로젝트는 sourceCompatibility = JavaVersion.VERSION_11, targetCompatibility = JavaVersion.VERSION_11, jvmTarget = "11"로 설정되어 있습니다. Java 버전을 17 이상으로 업그레이드하세요.
참고: Kotlin 2.2.20과 Compose BOM 2024.09.00은 AGP 9.0.0과 호환되므로 추가 변경이 필요 없습니다.
🤖 Prompt for AI Agents
In `@gradle/libs.versions.toml` at line 2, Update the project's Java and Kotlin
target settings from 11 to 17+: change any occurrences of sourceCompatibility =
JavaVersion.VERSION_11 and targetCompatibility = JavaVersion.VERSION_11 to
JavaVersion.VERSION_17 (or higher) and update any kotlinOptions.jvmTarget = "11"
to "17" (or higher); ensure build scripts that set javaToolchain or
org.gradle.jvmargs are aligned to Java 17, and verify CI/build agents use JDK
17+ so AGP 9.0.0 and Gradle 9.1.0 can run.
…dicare-Call/Medicare-Call-Android into refactor/optimization-navigation-#234
…dicare-Call/Medicare-Call-Android into refactor/optimization-navigation-#234
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.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/navigation/SettingNavigation.kt`:
- Around line 161-162: Remove the consecutive blank lines causing the Detekt
NoConsecutiveBlankLines violation by collapsing extra empty lines around the
composable<Route.NoticeDetail> block and the other affected region (the block
around lines 184–186); locate the composable<Route.NoticeDetail> declaration in
SettingNavigation.kt and ensure there is only a single blank line between
adjacent statements/blocks so the file has no back-to-back empty lines.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementDetailScreen.kt`:
- Around line 116-121: The unused onBack parameter causes a Detekt
UnusedParameter error; remove the onBack parameter from the
AnnouncementDetailContent invocation and from the AnnouncementDetailContent
composable signature (and any intermediate forwarding parameters) or
alternatively implement its usage; specifically update the call site in
AnnouncementDetailScreen (where AnnouncementDetailContent(noticeInfo =
noticeData!!, onBack = onBack) is used) and the corresponding
AnnouncementDetailContent composable declaration to either accept and invoke
onBack or drop the onBack parameter entirely so no unused parameter remains.
🧹 Nitpick comments (1)
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt (1)
132-134: 매직 넘버-1사용
elderId != -1로 수정 모드를 구분하고 있는데, 의도가 명확하지 않을 수 있습니다. 상수나 sealed class로 표현하면 가독성이 향상됩니다.💡 예시
companion object { const val NEW_ELDER_ID = -1 } // 사용 시 if (elderId != NEW_ELDER_ID) { loadElderDataById(elderId) }
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/navigation/SettingNavigation.kt
Outdated
Show resolved
Hide resolved
...src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementDetailScreen.kt
Show resolved
Hide resolved
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.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt`:
- Around line 137-147: The current condition mixes loading and missing-data
states so when isEditMode && elderData == null the UI shows an infinite
CircularProgressIndicator; separate the branches so isEditMode && isLoading
shows the spinner (Column + CircularProgressIndicator) but handle isEditMode &&
elderData == null && !isLoading by rendering a clear empty/error state with a
message and a retry action (e.g., a Button) that calls the same loader function;
update the when block around isEditMode/isLoading/elderData to include an
explicit null/failure branch and ensure any retry invokes the loader used to
populate elderData.
| when { | ||
| // 수정 모드인데 데이터가 로딩 중이거나 아직 없을 때 | ||
| isEditMode && (isLoading || elderData == null) -> { | ||
| Column( | ||
| modifier = Modifier.fillMaxSize(), | ||
| horizontalAlignment = Alignment.CenterHorizontally, | ||
| verticalArrangement = Arrangement.Center, | ||
| ) { | ||
| CircularProgressIndicator() | ||
| } | ||
| } |
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.
편집 모드에서 데이터 미존재 시 무한 로딩으로 고정될 수 있어요.
elderData == null 조건이 로딩과 묶여 있어, 실패/미존재 상황에서도 스피너가 계속 노출됩니다. 실패 분기를 분리해 재시도/안내 UI를 주세요.
🛠️ 수정 제안
- when {
- // 수정 모드인데 데이터가 로딩 중이거나 아직 없을 때
- isEditMode && (isLoading || elderData == null) -> {
- Column(
- modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- ) {
- CircularProgressIndicator()
- }
- }
-
- else -> {
+ when {
+ // 수정 모드에서 로딩 중
+ isEditMode && isLoading -> {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ ) {
+ CircularProgressIndicator()
+ }
+ }
+ // 수정 모드인데 데이터가 없을 때(오류/미존재)
+ isEditMode && elderData == null -> {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ ) {
+ Text("데이터를 불러오지 못했습니다.")
+ Spacer(modifier = Modifier.height(16.dp))
+ CTAButton(
+ type = CTAButtonType.GREEN,
+ text = "다시 시도",
+ onClick = { detailViewModel.loadElderDataById(elderId) },
+ )
+ }
+ }
+ else -> {🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt`
around lines 137 - 147, The current condition mixes loading and missing-data
states so when isEditMode && elderData == null the UI shows an infinite
CircularProgressIndicator; separate the branches so isEditMode && isLoading
shows the spinner (Column + CircularProgressIndicator) but handle isEditMode &&
elderData == null && !isLoading by rendering a clear empty/error state with a
message and a retry action (e.g., a Button) that calls the same loader function;
update the when block around isEditMode/isLoading/elderData to include an
explicit null/failure branch and ensure any retry invokes the loader used to
populate elderData.
|
코드는 문제 없는 것 같습니다! ci만 체크해주세요~ |
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.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementDetailScreen.kt`:
- Around line 75-122: The when block lacks a fallback for the initial state
(isLoading=false, noticeData=null, errorMessage=null) causing a blank screen;
add a final else branch (or change to if/elseChain) that renders a sensible
fallback UI (e.g., a loading/empty state with a message and CTAButton) and/or
triggers detailViewModel.loadNoticeById(noticeId) so users can retry,
referencing isLoading, noticeData, errorMessage, AnnouncementDetailContent,
CTAButton and detailViewModel.loadNoticeById to locate where to add the new
branch.
🧹 Nitpick comments (2)
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailMyDataViewModel.kt (1)
55-78:onFailure에서CancellationException처리가updateUserData와 일관되지 않아요.
updateUserData함수(Line 96-98)에서는onFailure콜백 내에서CancellationException을 확인하고 다시 throw하는데,loadMyInfo에서는 이 처리가 빠져있습니다. 만약getMyInfo()가Result.failure(CancellationException)을 반환하는 경우, 불필요한 에러 메시지가 설정될 수 있습니다.♻️ 일관성을 위한 수정 제안
.onFailure { exception -> + if (exception is CancellationException) { + throw exception + } _errorMessage.value = "내 정보를 불러오지 못했습니다: ${exception.message}" Log.e("DetailMyDataViewModel", "내 정보 로딩 실패", exception) }app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementDetailScreen.kt (1)
171-177: Preview에서 Koin 의존으로 실패 가능
@Preview에서AnnouncementDetailScreen()호출 시koinViewModel()이 주입되지 않아 프리뷰가 깨질 수 있어요. 프리뷰 전용으로AnnouncementDetailContent에 더미 데이터를 넣거나, 프리뷰용detailViewModel을 주입하는 형태로 분리하면 안정적입니다.
| when { | ||
| isLoading && noticeData == null -> { | ||
| // Loading state | ||
| Column( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .padding(20.dp), | ||
| verticalArrangement = Arrangement.Center, | ||
| horizontalAlignment = Alignment.CenterHorizontally, | ||
| ) { | ||
| CircularProgressIndicator() | ||
| Spacer(modifier = Modifier.height(16.dp)) | ||
| Text("공지사항을 불러오는 중입니다...") | ||
| } | ||
| } | ||
|
|
||
| errorMessage != null -> { | ||
| // Error state | ||
| Column( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .padding(20.dp), | ||
| verticalArrangement = Arrangement.Center, | ||
| horizontalAlignment = Alignment.CenterHorizontally, | ||
| ) { | ||
| Text( | ||
| text = errorMessage ?: "오류가 발생했습니다", | ||
| color = MediCareCallTheme.colors.negative, | ||
| style = MediCareCallTheme.typography.M_17, | ||
| ) | ||
| Spacer(modifier = Modifier.height(20.dp)) | ||
| CTAButton( | ||
| type = CTAButtonType.GREEN, | ||
| text = "다시 시도", | ||
| onClick = { | ||
| detailViewModel.loadNoticeById(noticeId) | ||
| }, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| noticeData != null -> { | ||
| // Data loaded state | ||
| AnnouncementDetailContent( | ||
| noticeInfo = noticeData!!, | ||
| ) | ||
| } | ||
| } |
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:
# Find DetailNoticeViewModel file
find . -type f -name "*DetailNoticeViewModel*"Repository: Medicare-Call/Medicare-Call-Android
Length of output: 177
🏁 Script executed:
# Search for DetailNoticeViewModel definition
rg "class\s+DetailNoticeViewModel" -A 20Repository: Medicare-Call/Medicare-Call-Android
Length of output: 3002
🏁 Script executed:
# Check errorMessage property and its handling
rg "errorMessage" -B 2 -A 2 --type kotlinRepository: Medicare-Call/Medicare-Call-Android
Length of output: 34464
🏁 Script executed:
# Find loadNoticeById implementation
rg "loadNoticeById" -B 2 -A 15 --type kotlinRepository: Medicare-Call/Medicare-Call-Android
Length of output: 7055
초기 상태에서 화면 공백 문제 해결 필요
현재 when 블록은 isLoading=false, noticeData=null, errorMessage=null인 초기 상태에 매칭되는 조건이 없어 화면이 비는 구간이 생길 수 있어요. errorMessage 초기화는 잘 구현되어 있지만, 데이터 우선 표시와 fallback 상태를 추가하는 게 안전합니다.
🛠️ 개선 예시
- when {
- isLoading && noticeData == null -> {
+ when {
+ noticeData != null -> {
+ // Data loaded state
+ AnnouncementDetailContent(
+ noticeInfo = noticeData!!,
+ )
+ }
+ isLoading -> {
// Loading state
Column(
modifier = Modifier
.fillMaxSize()
.padding(20.dp),
@@
- errorMessage != null -> {
+ errorMessage != null -> {
// Error state
Column(
modifier = Modifier
.fillMaxSize()
.padding(20.dp),
@@
- noticeData != null -> {
- // Data loaded state
- AnnouncementDetailContent(
- noticeInfo = noticeData!!,
- )
- }
+ else -> {
+ // Fallback (e.g., initial/idle)
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(20.dp),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ CircularProgressIndicator()
+ Spacer(modifier = Modifier.height(16.dp))
+ Text("공지사항을 불러오는 중입니다...")
+ }
+ }
}DetailNoticeViewModel.loadNoticeById()에서 errorMessage 초기화는 올바르게 구현되어 있습니다.
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementDetailScreen.kt`
around lines 75 - 122, The when block lacks a fallback for the initial state
(isLoading=false, noticeData=null, errorMessage=null) causing a blank screen;
add a final else branch (or change to if/elseChain) that renders a sensible
fallback UI (e.g., a loading/empty state with a message and CTAButton) and/or
triggers detailViewModel.loadNoticeById(noticeId) so users can retry,
referencing isLoading, noticeData, errorMessage, AnnouncementDetailContent,
CTAButton and detailViewModel.loadNoticeById to locate where to add the new
branch.
🔗 관련 이슈
📙 작업 설명
📸 스크린샷 또는 시연 영상 (선택)
💬 추가 설명 or 리뷰 포인트 (선택)
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항
✏️ Tip: You can customize this high-level summary in your review settings.