Skip to content

Conversation

@alswlekk
Copy link
Collaborator

@alswlekk alswlekk commented Jan 28, 2026

🔗 관련 이슈

📙 작업 설명

  • MutableStateOf는 Compose 전용 상태로 ViewModel에서 사용하기 간편하지만, UI 레이어와 비즈니스 로직 레이어 간의 의존성이 생기고 생명주기에 따른 리소스 관리가 어렵다는 단점이 존재해 이를 StateFlow와 collectAsStateWithLifecycle() 조합으로 개선

📸 스크린샷 또는 시연 영상 (선택)

기능 미리보기 기능 미리보기
기능 설명 기능 설명

💬 추가 설명 or 리뷰 포인트 (선택)

Summary by CodeRabbit

릴리스 노트

  • 리팩토링
    • 앱의 상태 관리 시스템을 현대화했습니다. 더욱 안정적인 데이터 추적과 개선된 오류 처리로 앱의 신뢰성이 향상되었습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

Walkthrough

애플리케이션 전역에서 Compose의 mutableStateOf 기반 상태 관리를 Kotlin Flow의 StateFlow로 일관되게 전환하는 리팩토링입니다. 여러 ViewModel과 UI 계층에서 상태 보유 및 갱신 메커니즘을 개선하여 생명주기 관리와 UI 의존성을 개선합니다.

Changes

Cohort / File(s) 변경 요약
Home 화면 상태 관리
app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt
isLoading 상태를 mutableStateOfMutableStateFlow 구조로 변경; 읽기 전용 StateFlow 노출
Glucose 데이터 관리
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/glucoselevel/viewmodel/GlucoseViewModel.kt
beforeMealData, afterMealDataMutableStateFlow 기반으로 변경; 데이터 갱신 로직을 update() 호출로 리팩토링
시간 선택 입력 컴포넌트
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/component/FirstTimeWheelPicker.kt
시간/분 상태를 mutableStateOfmutableIntStateOf로 변경
통화 시간 설정 화면 & ViewModel
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt
로컬 상태를 collectAsStateWithLifecycle로 ViewModel의 StateFlow 구독; 상태 변경을 ViewModel 메서드(setShowBottomSheet, setSelectedIndex 등)로 라우팅
로그인 정보 화면 & ViewModel
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt
기존 속성(phoneNumber, verificationCode, name 등)을 StateFlow 기반으로 전환; 새로운 UI 상태(showBottomSheet, checkedStates, allAgreeCheckState) 추가
설정 화면 - 상세 정보
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt
로컬 상태를 ViewModel 기반 StateFlow로 변경; LaunchedEffect로 폼 데이터 초기화; 입력 핸들러를 ViewModel 메서드로 위임
설정 화면 - 알림 설정
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingAlarmScreen.kt
체크 상태 4개를 StateFlow 기반으로 변경; setMasterChecked, setCompleteChecked 등 메서드 추가
설정 ViewModel들
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailHealthViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailMyDataViewModel.kt
UI 필드(isMale, name, birth 등) 및 비동기 상태(isLoading, isUpdateSuccess, errorMessage) 추가; 초기화/업데이트 메서드 제공
설정 ViewModel들 - 목록 관리
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersHealthViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/MyDataViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/NoticeViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/SubscribeViewModel.kt
데이터 목록과 에러 상태를 MutableStateFlow 기반으로 변경; 공개 인터페이스는 읽기 전용 StateFlow로 노출

Sequence Diagram

sequenceDiagram
    participant Composable as UI (Composable)
    participant ViewModel as ViewModel
    participant StateFlow as StateFlow
    participant Repository as Repository

    Composable->>ViewModel: LaunchedEffect: initializeForm(data)
    ViewModel->>ViewModel: Update _fieldName, _fieldBirth, etc.
    ViewModel->>StateFlow: _fieldName.value = newValue
    
    StateFlow->>Composable: collectAsStateWithLifecycle() emits value
    Composable->>Composable: Recompose with new state
    
    Composable->>ViewModel: User input: updateName(newName)
    ViewModel->>StateFlow: _name.value = newName
    StateFlow->>Composable: Emit updated value
    Composable->>Composable: Recompose with new value
    
    Composable->>ViewModel: onConfirm: setTimes(id, times)
    ViewModel->>StateFlow: _timeMap.update { ... }
    ViewModel->>Repository: submitAllByIds()
    Repository-->>ViewModel: onSuccess/onFailure
    ViewModel->>StateFlow: _isLoading.value = false
    ViewModel->>StateFlow: _errorMessage.value = error
    StateFlow->>Composable: Emit loading/error states
    Composable->>Composable: Recompose & show result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

refactor, state-management, kotlin-flow

Suggested reviewers

  • ikseong00
  • librawish808
  • ProtossManse

Poem

🌊 Mutable 상태를 Flow로 갈아타고,
생명주기는 이제 collectAsStateWithLifecycle 손에,
ViewModel과 UI 사이의 거리를 좁히며,
더 깔끔해진 상태 관리의 세계로 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경 사항의 핵심을 명확하게 설명합니다. 전체 프로젝트에서 Compose 상태 관리를 StateFlow 기반으로 리팩토링하는 주요 목표를 간결하게 표현했습니다.
Linked Issues check ✅ Passed PR 변경 사항이 이슈 #233의 모든 핵심 요구사항을 충족합니다. mutableStateOf를 MutableStateFlow로 대체하고, UI에서 collectAsStateWithLifecycle()를 사용하여 ViewModel 간 의존성 제거 및 생명주기 관리 개선을 완벽하게 구현했습니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항이 상태 관리 리팩토링 범위 내에 있습니다. ViewModel과 Screen 파일의 변경이 모두 Compose 상태에서 StateFlow 기반 상태로 일관되게 전환되어 있으며, 요구 범위를 벗어난 변경은 없습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt (1)

31-33: 파이프라인 실패: 사용되지 않는 import 제거 필요

Detekt NoUnusedImports 규칙에 따라 StateFlow 마이그레이션 후 더 이상 사용되지 않는 import들을 제거해야 합니다.

🔧 수정 제안
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/MyDataViewModel.kt (1)

45-49: 중복 로깅 제거 필요

onFailure 블록에서 동일한 에러가 두 번 로깅되고 있습니다. 중복을 제거해 주세요.

🔧 수정 제안
                 .onFailure {
                     Log.e("MyDataViewModel", "사용자 정보 불러오기 실패: ${it.message}", it)
                     it.printStackTrace()
-                    Log.e("MyDataViewModel", "사용자 정보 로딩 실패: ${it.message}", it)
                 }
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt (1)

66-73: replaceAll 메서드를 사용하여 중복 데이터 방지하기

force=true일 때 기존 데이터를 클리어하지 않고 addElderId를 호출하면 매번 refresh될 때마다 중복 데이터가 쌓입니다. ElderIdRepository에 이미 replaceAll 메서드가 구현되어 있으니, 현재 forEach 루프 대신 이를 사용하세요:

if (force || repoCurrent.isEmpty()) {
    elderIdRepository.replaceAll(mapped)
}
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt (1)

114-119: 불필요한 null 체크

isMaleStateFlow<Boolean>에서 수집되어 Boolean 타입입니다. isMale != null 조건은 항상 true이므로 제거해도 됩니다.

🔧 수정 제안
             CTAButton(
                 type = if (name.matches(Regex("^[가-힣a-zA-Z]*$")) &&
                     birth.length == 8 &&
-                    birth.isValidDate() &&
-                    isMale != null
+                    birth.isValidDate()
                 ) CTAButtonType.GREEN else CTAButtonType.DISABLED,
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt (1)

31-34: 사용되지 않는 import 제거 필요

파이프라인 실패 로그에서 확인된 것처럼 mutableStateOf import가 더 이상 사용되지 않습니다. StateFlow 마이그레이션 후 setValue import도 불필요해 보입니다.

🧹 제거할 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/home/screen/HomeScreen.kt`:
- Around line 6-26: Replace all wildcard imports in this file (e.g.,
androidx.compose.foundation.layout.*, androidx.compose.foundation.shape.*,
androidx.compose.material3.pulltorefresh.*, androidx.compose.runtime.*,
androidx.compose.ui.*, and com.konkuk.medicarecall.ui.feature.home.component.*)
with explicit imports of only the symbols actually used in the file (for example
Column, Row, Spacer, rememberScrollState, CircleShape, RoundedCornerShape,
PullToRefreshBox, PullToRefreshDefaults, rememberPullToRefreshState, remember,
mutableStateOf, Alignment, Modifier, Color, painterResource, dp, NameBar,
NameDropdown, and the specific components referenced from the home.component
package); update the import block so Detekt’s NoWildcardImports/WildcardImport
rule is satisfied while preserving the existing references to functions and
composables like HomeScreen, NameBar, NameDropdown and the components from the
home.component package.
- Around line 244-277: The call to HomeGlucoseLevelContainer in HomeScreen.kt is
missing a trailing comma after the last argument which triggers Detekt's
TrailingCommaOnCallSite rule; update the HomeGlucoseLevelContainer(...)
invocation (the argument passing the glucoseLevelAverageToday and
navigateToGlucoseDetailScreen) to include a comma after the final argument
(navigateToGlucoseDetailScreen), ensuring the call site ends with a trailing
comma before the closing parenthesis.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt`:
- Around line 43-45: The _isLoading StateFlow is never updated so isLoading
remains true; either derive isLoading from HomeUiState.isLoading or update
_isLoading whenever the view model updates _uiState. Concretely, in
HomeViewModel (where _uiState / HomeUiState is produced) remove the separate
_isLoading and expose isLoading = uiState.map { it.isLoading }.stateIn(...) or,
if keeping _isLoading, set _isLoading.value = newUiState.isLoading inside the
same place you update _uiState so they stay in sync.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/component/FirstTimeWheelPicker.kt`:
- Around line 22-24: Remove the now-unused import mutableStateOf from the import
list (leave mutableIntStateOf and remember if they are still used) so Detekt
stops failing; locate the import statement containing mutableStateOf in
FirstTimeWheelPicker (the line that currently imports mutableIntStateOf,
mutableStateOf, remember) and delete only mutableStateOf, then re-run the
linter/CI to verify.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt`:
- Around line 78-81: Remove the blank line immediately before the closing brace
in the block inside CallTimeViewModel where you call jobs.awaitAll() and
onSuccess(); specifically delete the empty line between onSuccess() and the
closing '}' so the closing brace follows onSuccess() directly to satisfy
Detekt's NoBlankLineBeforeRbrace rule.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt`:
- Around line 67-71: The code fails to collect three StateFlows from
LoginViewModel—name, dateOfBirth, and isMale—so references like
loginViewModel.name, loginViewModel.dateOfBirth, and loginViewModel.isMale no
longer compile; fix by adding collects using collectAsStateWithLifecycle (e.g.,
val name by loginViewModel.name.collectAsStateWithLifecycle(), val dateOfBirth
by loginViewModel.dateOfBirth.collectAsStateWithLifecycle(), val isMale by
loginViewModel.isMale.collectAsStateWithLifecycle()) and then replace all direct
uses of loginViewModel.name/dateOfBirth/isMale in the composable with the local
variables name, dateOfBirth, and isMale (affects usages around the checks at
lines referencing isNotEmpty(), length, and boolean checks).

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt`:
- Around line 87-94: When updating individual checkbox state in setCheckedState,
also update the aggregate _allAgreeCheckState: after applying the change to
_checkedStates (and guarding the index), set _allAgreeCheckState.value =
_checkedStates.value.all { it } so if any item becomes false the overall flag is
false and if all items are true it becomes true; keep setAllAgreeCheckState
as-is but ensure it continues to write both _allAgreeCheckState and
_checkedStates to remain consistent with the aggregate state.
- Around line 48-50: The ViewModel currently initializes _isMale as
MutableStateFlow(true) while the UI (LoginMyInfoScreen) checks isMale != null;
decide the intended behavior and make types consistent: if gender must be
explicitly chosen, change _isMale to MutableStateFlow<Boolean?>(null) and expose
val isMale = _isMale.asStateFlow() (update any code that assumes non-nullable
Boolean); otherwise remove the null check in LoginMyInfoScreen and keep _isMale
as MutableStateFlow(true) or set a clear default value — reference symbols:
_isMale and isMale in LoginViewModel and the isMale null-check in
LoginMyInfoScreen.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/NaverPayViewModel.kt`:
- Line 14: Remove the unused import kotlinx.coroutines.flow.update from
NaverPayViewModel (it's flagged by Detekt) and update any references in the file
to continue using direct state assignment (e.g., viewState.value = ...) instead
of .update; ensure no remaining usages of .update exist before deleting the
import.
- Around line 87-92: Remove the unnecessary blank line before the closing brace
of the onFailure block in NaverPayViewModel (the lambda starting with
".onFailure { e -> ... }"); locate the onFailure handler and delete the empty
line immediately before its final "}" so the closing brace directly follows the
last statement (keeping the CancellationException check, Log.e call, and
_errorMessage assignment intact) to satisfy the Detekt NoBlankLineBeforeRbrace
rule.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt`:
- Around line 22-24: The file still imports unused Compose symbols after
migrating to StateFlow—remove the unused imports
androidx.compose.runtime.mutableStateOf, androidx.compose.runtime.remember and
androidx.compose.runtime.setValue (and any other leftover Compose runtime
imports referenced in the same file) so Detekt stops flagging unused imports;
open ElderDetailScreen.kt, delete those import lines and run a quick
build/format to ensure there are no remaining unused-import warnings.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt`:
- Around line 19-21: Remove the now-unused Compose imports causing the Detekt
failure: delete the imports for mutableStateOf and remember (and setValue if
unused) from MyDetailScreen.kt since you migrated to StateFlow; ensure only
required imports remain so the file compiles and the Detekt warning is resolved,
and run a quick build/lint to confirm no other references to mutableStateOf,
remember, or setValue exist in the MyDetailScreen composable or related
functions.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingAlarmScreen.kt`:
- Around line 20-22: Detekt reports unused imports mutableStateOf and remember
in SettingAlarmScreen; remove those two imports from the import block (leaving
androidx.compose.runtime.setValue if it's actually used) or run the IDE's
"optimize imports" to clean up unused symbols in SettingAlarmScreen.kt so the
pipeline passes.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailHealthViewModel.kt`:
- Around line 41-44: The onFailure block in DetailHealthViewModel currently logs
and sets _errorMessage but does not rethrow CancellationException like
DetailElderInfoViewModel does; update the .onFailure { exception -> ... }
handler (in the coroutine/flow error handling where DetailHealthViewModel uses
.onFailure) to check for exception is CancellationException and rethrow it
(throw exception) before handling/logging so coroutine cancellation semantics
are preserved, then continue to Log.e and set _errorMessage for other
exceptions.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersHealthViewModel.kt`:
- Around line 35-41: In EldersHealthViewModel update the success branch to clear
any previous error so the UI doesn't keep showing stale messages: inside the
.onSuccess block (where _eldersInfoList.value is set and logs are written) set
_errorMessage.value to null (or empty) to reset error state after a successful
load.

In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt`:
- Around line 29-31: 파일의 연속된 빈 줄이 Detekt 규칙에 의해 실패하고 있으므로 클래스 내 선언부 주변의 불필요한 빈
줄을 하나로 줄여주세요; 특히 MutableStateFlow를 초기화하는 _elderNameIdMapList 선언(변수명:
_elderNameIdMapList, 초기화식: MutableStateFlow(elderIdRepository.getElderIds()))
주변에 연속된 빈 줄이 있다면 중복 빈 줄을 제거해 한 줄만 남기도록 수정하세요.
🧹 Nitpick comments (13)
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/SubscribeViewModel.kt (1)

31-39: 성공 시 에러 상태 초기화 고려해보세요.

현재 onSuccess 블록에서 _errorMessage를 초기화하지 않고 있습니다. 만약 재시도 로직이 추가되거나 다른 곳에서 loadSubscriptions()가 다시 호출되는 경우, 이전 에러 메시지가 남아있을 수 있습니다.

🔧 에러 상태 초기화 제안
 .onSuccess {
     Log.d("SubscribeViewModel", "구독 정보 불러오기 성공: ${it.size}개")
+    _errorMessage.value = null
     _subscriptions.value = it
 }
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/glucoselevel/viewmodel/GlucoseViewModel.kt (1)

55-65: 선택 사항: 업데이트된 값 캡처 방식 개선

현재 update { } 호출 후 .value를 읽는 패턴은 동작하지만, 업데이트된 리스트를 먼저 계산하고 이를 재사용하면 의도가 더 명확해집니다.

♻️ 제안된 리팩토링
                     val updatedDataList = when (type) {
                         GlucoseTiming.BEFORE_MEAL -> {
-                            _beforeMealData.update { current ->
-                                if (isRefresh) newData else newData + current
-                            }
-                            _beforeMealData.value
+                            val updated = if (isRefresh) newData else newData + _beforeMealData.value
+                            _beforeMealData.value = updated
+                            updated
                         }

                         GlucoseTiming.AFTER_MEAL -> {
-                            _afterMealData.update { current ->
-                                if (isRefresh) newData else newData + current
-                            }
-                            _afterMealData.value
+                            val updated = if (isRefresh) newData else newData + _afterMealData.value
+                            _afterMealData.value = updated
+                            updated
                         }
                     }
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/NaverPayViewModel.kt (2)

60-62: 매직 넘버 상수 추출 고려

unitPrice = 29000이 하드코딩되어 있네요. 나중에 가격 변경 시 찾기 어려울 수 있으니 companion object에 상수로 추출하면 관리가 편해질 것 같아요.

♻️ 리팩토링 제안 (선택사항)
companion object {
    private const val PREMIUM_PLAN_UNIT_PRICE = 29000
}

그리고 사용처에서:

-                    val unitPrice = 29000
+                    val unitPrice = PREMIUM_PLAN_UNIT_PRICE

104-107: 함수명과 동작 범위 확인

resetStatus()가 현재 _errorMessage만 초기화하는데, 함수명이 전체 상태 초기화를 암시할 수 있어요. 의도한 동작이라면 clearError() 같은 이름이 더 명확할 수 있지만, 현재 구현도 문제는 없습니다.

app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt (1)

525-531: ViewModel의 isLoading/errorMessage 상태 활용 고려

현재 orderCode.isNullOrBlank() || accessToken.isNullOrBlank() 조건으로 로딩을 표시하고 있는데, ViewModel에서 노출하는 isLoadingerrorMessage StateFlow를 활용하면 더 정확한 상태 표시와 에러 핸들링이 가능해요.

val isLoading by naverPayViewModel.isLoading.collectAsStateWithLifecycle()
val errorMessage by naverPayViewModel.errorMessage.collectAsStateWithLifecycle()

이후 에러 발생 시 사용자에게 안내하거나 재시도 옵션을 제공할 수 있습니다. 지금 당장 필수는 아니지만 UX 개선 포인트로 참고해주세요.

app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt (1)

74-76: 일관성을 위해 collectAsStateWithLifecycle 사용 고려

eldersInfoViewModel의 상태 접근이 .value 직접 접근 방식을 사용하고 있습니다. 이 PR의 범위를 벗어날 수 있지만, 일관성을 위해 향후 동일한 패턴으로 마이그레이션하는 것을 고려해 보세요.

app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt (2)

72-74: LaunchedEffect 키 검토 권장

LaunchedEffect(Unit)은 컴포지션 시 한 번만 실행됩니다. eldersInfoResponseDto가 변경될 가능성이 있다면, key를 eldersInfoResponseDto.elderId로 변경하는 것이 더 안전합니다.

💡 선택적 개선안
-    LaunchedEffect(Unit) {
+    LaunchedEffect(eldersInfoResponseDto.elderId) {
         detailViewModel.initializeForm(eldersInfoResponseDto)
     }

224-224: 불필요한 Boolean 비교

isMale은 non-nullable Boolean이므로 == true 비교가 불필요합니다.

✨ 간단한 수정
-                                gender = if (isMale == true) GenderType.MALE else GenderType.FEMALE,
+                                gender = if (isMale) GenderType.MALE else GenderType.FEMALE,
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/MyDataViewModel.kt (1)

19-24: 에러 상태 노출 검토

다른 ViewModel들(NoticeViewModel, EldersInfoViewModel 등)은 errorMessage: StateFlow<String?>을 노출하는데, 이 ViewModel은 에러 상태를 노출하지 않습니다. UI에서 에러 처리가 필요하다면 일관성을 위해 추가를 고려해 보세요.

app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailElderInfoViewModel.kt (1)

124-133: birthDate 파싱 방어 코드 고려

elderInfo.birthDate가 예상 형식(yyyy-MM-dd)이 아니거나 빈 문자열일 경우를 대비한 방어 코드를 추가하면 더 안전합니다.

💡 방어적 코드 예시
         // birth는 yyyy-MM-dd 형식을 yyyyMMdd로 변환
-        val birthFormatted = elderInfo.birthDate.replace("-", "")
-        _birth.value = birthFormatted
+        _birth.value = elderInfo.birthDate.takeIf { it.isNotBlank() }
+            ?.replace("-", "")
+            ?: ""
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailHealthViewModel.kt (1)

18-24: 클래스 시작 부분 빈 줄 정리

클래스 본문 시작 직후 불필요한 빈 줄이 있습니다. 다른 ViewModel들과 일관성을 위해 제거하면 좋겠습니다.

✨ 수정
 ) : ViewModel() {
-
     private val _isLoading = MutableStateFlow(false)
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailMyDataViewModel.kt (1)

98-124: [선택사항] 마스터 체크박스 자동 활성화 고려

현재 로직에서 개별 항목이 OFF되면 마스터가 OFF되지만, 모든 개별 항목이 ON이 되어도 마스터가 자동으로 ON되지 않습니다. 의도된 동작이라면 무시해도 됩니다.

// 예시: 모든 하위 항목이 ON일 때 마스터 자동 활성화
private fun updateMasterIfAllChecked() {
    if (_completeChecked.value && _abnormalChecked.value && _missedChecked.value) {
        _masterChecked.value = true
    }
}
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt (1)

96-98: updateAllCheckedStates 미사용 함수

이 함수가 현재 PR에서 사용되지 않는 것 같습니다. 필요하지 않다면 제거하거나, 사용 계획이 있다면 그대로 두셔도 됩니다.

Comment on lines +43 to 45
private val _isLoading = MutableStateFlow(true)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

_isLoading이 갱신되지 않아 로딩이 고정될 수 있어요.
현재 _isLoading을 업데이트하는 코드가 없어 HomeScreen에서 isInitialLoading이 계속 true로 남을 수 있습니다. HomeUiState.isLoading을 단일 소스로 파생하거나 _isLoading을 함께 업데이트해주세요.

✅ 단일 소스로 파생하는 예시
-    private val _isLoading = MutableStateFlow(true)
-    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
+    val isLoading: StateFlow<Boolean> by lazy {
+        _homeUiState.mapState(viewModelScope) { it.isLoading }
+    }
📝 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 val _isLoading = MutableStateFlow(true)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
val isLoading: StateFlow<Boolean> by lazy {
_homeUiState.mapState(viewModelScope) { it.isLoading }
}
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt`
around lines 43 - 45, The _isLoading StateFlow is never updated so isLoading
remains true; either derive isLoading from HomeUiState.isLoading or update
_isLoading whenever the view model updates _uiState. Concretely, in
HomeViewModel (where _uiState / HomeUiState is produced) remove the separate
_isLoading and expose isLoading = uiState.map { it.isLoading }.stateIn(...) or,
if keeping _isLoading, set _isLoading.value = newUiState.isLoading inside the
same place you update _uiState so they stay in sync.

Comment on lines +22 to 24
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

미사용 import 제거로 Detekt 실패 해결

mutableStateOf가 더 이상 사용되지 않아 Detekt가 실패합니다. import 정리만 하면 CI가 통과할 거예요.

🧹 제안 수정
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
📝 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 androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
🧰 Tools
🪛 GitHub Actions: Android CI

[error] 23-23: Detekt: Unused import [NoUnusedImports]

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/component/FirstTimeWheelPicker.kt`
around lines 22 - 24, Remove the now-unused import mutableStateOf from the
import list (leave mutableIntStateOf and remember if they are still used) so
Detekt stops failing; locate the import statement containing mutableStateOf in
FirstTimeWheelPicker (the line that currently imports mutableIntStateOf,
mutableStateOf, remember) and delete only mutableStateOf, then re-run the
linter/CI to verify.

Comment on lines +78 to +81
// 모든 요청이 끝날 때까지 대기
jobs.awaitAll()
onSuccess()

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파이프라인 실패: 닫는 중괄호 앞 빈 줄 제거 필요

Detekt NoBlankLineBeforeRbrace 규칙에 따라 81번 라인의 빈 줄을 제거해야 합니다.

🔧 수정 제안
                 // 모든 요청이 끝날 때까지 대기
                 jobs.awaitAll()
                 onSuccess()
-
             } catch (t: Throwable) {
📝 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
// 모든 요청이 끝날 때까지 대기
jobs.awaitAll()
onSuccess()
// 모든 요청이 끝날 때까지 대기
jobs.awaitAll()
onSuccess()
} catch (t: Throwable) {
🧰 Tools
🪛 GitHub Actions: Android CI

[error] 81-81: Detekt: Unexpected blank line(s) before closing brace [NoBlankLineBeforeRbrace]

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt`
around lines 78 - 81, Remove the blank line immediately before the closing brace
in the block inside CallTimeViewModel where you call jobs.awaitAll() and
onSuccess(); specifically delete the empty line between onSuccess() and the
closing '}' so the closing brace follows onSuccess() directly to satisfy
Detekt's NoBlankLineBeforeRbrace rule.

Comment on lines 19 to 21
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파이프라인 실패: 사용되지 않는 import 제거 필요

Detekt에서 mutableStateOfremember가 사용되지 않는다고 보고했습니다. StateFlow로 마이그레이션하면서 더 이상 필요하지 않은 import들입니다.

🔧 수정 제안
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
📝 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 androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
🧰 Tools
🪛 GitHub Actions: Android CI

[error] 19-19: Detekt: Unused import [NoUnusedImports]


[error] 20-20: Detekt: Unused import [NoUnusedImports]

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt`
around lines 19 - 21, Remove the now-unused Compose imports causing the Detekt
failure: delete the imports for mutableStateOf and remember (and setValue if
unused) from MyDetailScreen.kt since you migrated to StateFlow; ensure only
required imports remain so the file compiles and the Detekt warning is resolved,
and run a quick build/lint to confirm no other references to mutableStateOf,
remember, or setValue exist in the MyDetailScreen composable or related
functions.

Comment on lines 20 to 22
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파이프라인 실패: 사용되지 않는 import 제거 필요

Detekt에서 mutableStateOfremember가 사용되지 않는다고 보고했습니다.

🔧 수정 제안
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
🧰 Tools
🪛 GitHub Actions: Android CI

[error] 20-20: Detekt: Unused import [NoUnusedImports]


[error] 21-21: Detekt: Unused import [NoUnusedImports]

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingAlarmScreen.kt`
around lines 20 - 22, Detekt reports unused imports mutableStateOf and remember
in SettingAlarmScreen; remove those two imports from the import block (leaving
androidx.compose.runtime.setValue if it's actually used) or run the IDE's
"optimize imports" to clean up unused symbols in SettingAlarmScreen.kt so the
pipeline passes.

Comment on lines +41 to +44
.onFailure { exception ->
Log.e("DetailHealthViewModel", "건강 정보 수정 실패: ${exception.message}", exception)
_errorMessage.value = "건강 정보 수정에 실패했습니다. 다시 시도해주세요."
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

CancellationException 처리 누락

DetailElderInfoViewModel에서는 CancellationException을 rethrow하는데, 여기서는 처리가 빠져 있습니다. 일관성과 코루틴 취소 동작을 위해 추가해 주세요.

🔧 수정 제안
+import kotlinx.coroutines.CancellationException
...
                     .onFailure { exception ->
+                        if (exception is CancellationException) throw exception
                         Log.e("DetailHealthViewModel", "건강 정보 수정 실패: ${exception.message}", exception)
                         _errorMessage.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
.onFailure { exception ->
Log.e("DetailHealthViewModel", "건강 정보 수정 실패: ${exception.message}", exception)
_errorMessage.value = "건강 정보 수정에 실패했습니다. 다시 시도해주세요."
}
.onFailure { exception ->
if (exception is CancellationException) throw exception
Log.e("DetailHealthViewModel", "건강 정보 수정 실패: ${exception.message}", exception)
_errorMessage.value = "건강 정보 수정에 실패했습니다. 다시 시도해주세요."
}
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/DetailHealthViewModel.kt`
around lines 41 - 44, The onFailure block in DetailHealthViewModel currently
logs and sets _errorMessage but does not rethrow CancellationException like
DetailElderInfoViewModel does; update the .onFailure { exception -> ... }
handler (in the coroutine/flow error handling where DetailHealthViewModel uses
.onFailure) to check for exception is CancellationException and rethrow it
(throw exception) before handling/logging so coroutine cancellation semantics
are preserved, then continue to Log.e and set _errorMessage for other
exceptions.

Comment on lines 35 to +41
.onSuccess {
Log.d("EldersHealthViewModel", "건강 정보 불러오기 성공: ${it.size}개")
eldersInfoList = it
Log.d("EldersHealthViewModel", "노인 건강 정보: $eldersInfoList")
_eldersInfoList.value = it
Log.d("EldersHealthViewModel", "노인 건강 정보: ${_eldersInfoList.value}")
}
.onFailure { exception ->
errorMessage = "건강 정보를 불러오지 못했습니다: ${exception.message}"
_errorMessage.value = "건강 정보를 불러오지 못했습니다: ${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 | 🟡 Minor

성공 시 오류 상태 초기화 필요

성공 후에도 _errorMessage가 그대로면 UI가 이전 오류를 계속 표시할 수 있습니다. 성공 시점에 오류를 null로 클리어해 주세요.

♻️ 수정 제안
             .onSuccess {
                 Log.d("EldersHealthViewModel", "건강 정보 불러오기 성공: ${it.size}개")
+                _errorMessage.value = null
                 _eldersInfoList.value = it
                 Log.d("EldersHealthViewModel", "노인 건강 정보: ${_eldersInfoList.value}")
             }
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersHealthViewModel.kt`
around lines 35 - 41, In EldersHealthViewModel update the success branch to
clear any previous error so the UI doesn't keep showing stale messages: inside
the .onSuccess block (where _eldersInfoList.value is set and logs are written)
set _errorMessage.value to null (or empty) to reset error state after a
successful load.

Comment on lines +29 to +31


private val _elderNameIdMapList = MutableStateFlow(elderIdRepository.getElderIds())
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파이프라인 실패: 연속 빈 줄 제거

Detekt에서 연속된 빈 줄을 감지했습니다. 빈 줄 하나만 남겨주세요.

🔧 수정
     private val _error = MutableStateFlow<Throwable?>(null)
     val error: StateFlow<Throwable?> = _error.asStateFlow()
-
 
     private val _elderNameIdMapList = MutableStateFlow(elderIdRepository.getElderIds())
📝 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 val _elderNameIdMapList = MutableStateFlow(elderIdRepository.getElderIds())
private val _error = MutableStateFlow<Throwable?>(null)
val error: StateFlow<Throwable?> = _error.asStateFlow()
private val _elderNameIdMapList = MutableStateFlow(elderIdRepository.getElderIds())
🧰 Tools
🪛 GitHub Actions: Android CI

[error] 30-30: Detekt: Needless blank line(s) [NoConsecutiveBlankLines]

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt`
around lines 29 - 31, 파일의 연속된 빈 줄이 Detekt 규칙에 의해 실패하고 있으므로 클래스 내 선언부 주변의 불필요한 빈
줄을 하나로 줄여주세요; 특히 MutableStateFlow를 초기화하는 _elderNameIdMapList 선언(변수명:
_elderNameIdMapList, 초기화식: MutableStateFlow(elderIdRepository.getElderIds()))
주변에 연속된 빈 줄이 있다면 중복 빈 줄을 제거해 한 줄만 남기도록 수정하세요.

Copy link
Contributor

@ikseong00 ikseong00 left a comment

Choose a reason for hiding this comment

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

확인했습니다. 다만 추후 UiState 로 사용되는 변수를 통일할 필요가 있을 것 같습니다. 사용되는 변수가 너무 많아 추적하는데에 소요가 많이 드는 것 같습니다..!

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.*
Copy link
Contributor

Choose a reason for hiding this comment

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

와일드카드로 하는 방법은 지양하면 좋을 것 같습니다.
불필요한 참조가 있을 것 같아요.

val elderInfoList by homeViewModel.elderInfoList.collectAsStateWithLifecycle()
val elderNameList by homeViewModel.elderNameList.collectAsStateWithLifecycle()
val selectedElderId by homeViewModel.selectedElderId.collectAsStateWithLifecycle()
val isInitialLoading by homeViewModel.isLoading.collectAsStateWithLifecycle()
Copy link
Contributor

Choose a reason for hiding this comment

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

요 변수는 HomeViewModel 에서 바뀌지 않는 변수 같습니다!

Copy link
Collaborator

@librawish808 librawish808 left a comment

Choose a reason for hiding this comment

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

SleepDetailScreen 64번째 줄에서 ci빌드 실패가 나는거라 충돌해결후 일단 머지하셔도 될것같습니다

…oid into refactor/organize-naverpay-#232

# Conflicts:
#	app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/NaverPayRepositoryImpl.kt
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/component/PaymentPriceItem.kt
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt
…oid into refactor/improve-state-management-#233

# Conflicts:
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/home/screen/HomeScreen.kt
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/NaverPayViewModel.kt
…oid into refactor/improve-state-management-#233

# Conflicts:
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/home/screen/HomeScreen.kt
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt
#	app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/NaverPayViewModel.kt
…t-#233' into refactor/improve-state-management-#233
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt (2)

31-34: 사용하지 않는 import 제거 필요

파이프라인 실패 로그에서 mutableStateOf import가 사용되지 않는다고 표시되고 있어요. setState/getValue 등 관련 import도 함께 확인해서 불필요한 것들은 정리해주세요.

🧹 사용하지 않는 import 제거
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue

282-284: Nullable Boolean 처리 필요

isMaleBoolean? 타입이라면 (Line 172에서 != null 체크를 하는 것으로 보아), Line 284의 if (loginViewModel.isMale)은 타입 오류가 발생해요. Elvis 연산자나 null 체크가 필요합니다.

🐛 Null 안전 처리
                         loginViewModel.memberRegister(
-                            loginViewModel.name,
-                            loginViewModel.dateOfBirth,
-                            if (loginViewModel.isMale) GenderType.MALE else GenderType.FEMALE,
+                            name,
+                            dateOfBirth,
+                            if (isMale == true) GenderType.MALE else GenderType.FEMALE,
                         )

위의 StateFlow 수집 변경 후, 로컬 변수 isMale을 사용하면서 == true 비교로 nullable 처리를 해주세요.

app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt (2)

149-151: 데이터 로딩 시작 시 isLoadingfalse로 설정되고 있어요.

fetchHomeSummaryForToday 함수 시작 부분에서 isLoading = false로 설정하고 있는데, 로딩을 시작할 때는 true여야 합니다. 현재 로직대로라면 로딩 인디케이터가 표시되지 않을 수 있어요.

🐛 수정 제안
     private fun fetchHomeSummaryForToday(elderId: Int) {
         viewModelScope.launch {
-            _homeUiState.update { it.copy(isLoading = false) }
+            _homeUiState.update { it.copy(isLoading = true) }
             // val today = LocalDate.now()
             try {

57-66: !! 사용은 NPE 위험이 있어요.

selectedElderId.value!!에서 값이 null일 경우 앱이 크래시될 수 있습니다. early return이나 safe call로 처리하는 게 더 안전해요.

✨ 개선 제안
     fun callImmediate(
         careCallTimeOption: String,
     ) {
+        val elderId = selectedElderId.value ?: return
         viewModelScope.launch {
             homeRepository.requestImmediateCareCall(
-                elderId = selectedElderId.value!!,
+                elderId = elderId,
                 careCallOption = careCallTimeOption,
             )
         }
     }
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt (1)

31-33: 미사용 import 제거 필요 (Detekt 실패)
Detekt가 Line 31-33의 미사용 import로 실패 중입니다. 제거해야 CI가 통과합니다.

🧹 수정 제안
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingAlarmScreen.kt (1)

45-69: 토글 직후 업데이트가 이전 상태로 전송될 수 있음

set*Checked() 직후 updateSettings()가 실행되면, Compose가 아직 새 값으로 재구성되기 전이라 이전 값이 서버로 전송될 수 있습니다. 특히 마스터 토글에서 하위 토글 연쇄 갱신이 있을 경우, 업데이트 값이 부분적으로 어긋날 위험이 큽니다. UI에서 업데이트에 필요한 값을 명시적으로 전달하거나, ViewModel에서 상태 변경 후 업데이트를 처리하도록 이동하는 편이 안전합니다.

🔧 제안 수정 (UI에서 최신 값 전달)
-    val updateSettings = {
+    val updateSettings: (master: Boolean, complete: Boolean, abnormal: Boolean, missed: Boolean) -> Unit =
+        { master, complete, abnormal, missed ->
         myDataViewModel.updateUserData(
             userInfo = myDataInfo.copy(
                 pushNotification = PushNotificationDto(
-                    all = if (masterChecked) "ON" else "OFF",
-                    carecallCompleted = if (completeChecked) "ON" else "OFF",
-                    healthAlert = if (abnormalChecked) "ON" else "OFF",
-                    carecallMissed = if (missedChecked) "ON" else "OFF",
+                    all = if (master) "ON" else "OFF",
+                    carecallCompleted = if (complete) "ON" else "OFF",
+                    healthAlert = if (abnormal) "ON" else "OFF",
+                    carecallMissed = if (missed) "ON" else "OFF",
                 ),
             ),
         )
-    }
+    }
@@
-                    myDataViewModel.setMasterChecked(isChecked)
-                    updateSettings()
+                    myDataViewModel.setMasterChecked(isChecked)
+                    updateSettings(isChecked, completeChecked, abnormalChecked, missedChecked)
@@
-                    myDataViewModel.setCompleteChecked(isChecked)
-                    updateSettings()
+                    myDataViewModel.setCompleteChecked(isChecked)
+                    updateSettings(masterChecked, isChecked, abnormalChecked, missedChecked)
@@
-                    myDataViewModel.setAbnormalChecked(isChecked)
-                    updateSettings()
+                    myDataViewModel.setAbnormalChecked(isChecked)
+                    updateSettings(masterChecked, completeChecked, isChecked, missedChecked)
@@
-                    myDataViewModel.setMissedChecked(isChecked)
-                    updateSettings()
+                    myDataViewModel.setMissedChecked(isChecked)
+                    updateSettings(masterChecked, completeChecked, abnormalChecked, isChecked)

Also applies to: 102-162

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt`:
- Around line 49-58: LaunchedEffect is currently keyed with Unit so
initializeFormData(myDataInfo) won't re-run when myDataInfo changes; update the
key to myDataInfo (i.e., use LaunchedEffect(myDataInfo)) so
detailMyDataViewModel.initializeFormData(myDataInfo) is invoked whenever
myDataInfo updates, keeping the UI state in sync with the latest data.
🧹 Nitpick comments (1)
app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt (1)

352-358: mapState 확장 함수를 별도 유틸리티로 분리하면 좋겠어요.

이 함수는 범용 유틸리티라서 다른 ViewModel에서도 재사용할 수 있습니다. 별도 파일로 분리하면 코드 중복을 줄일 수 있어요. 지금 당장 필요한 건 아니고, 나중에 고려해볼 만한 부분이에요.

Comment on lines +49 to +58
// ViewModel 상태 구독
val isMale by detailMyDataViewModel.isMale.collectAsStateWithLifecycle()
val name by detailMyDataViewModel.name.collectAsStateWithLifecycle()
val birth by detailMyDataViewModel.birth.collectAsStateWithLifecycle()
val scrollState = rememberScrollState()

// 초기 데이터 로드
LaunchedEffect(Unit) {
detailMyDataViewModel.initializeFormData(myDataInfo)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

초기화 키가 Unit이면 최신 myDataInfo 반영이 누락될 수 있음

LaunchedEffect(Unit)myDataInfo가 변경되어도 재실행되지 않습니다. 데이터 갱신 경로가 있다면 최신 값이 반영되지 않을 수 있으니 myDataInfo를 키로 쓰는 편이 안전합니다.

🔧 제안 수정
-    LaunchedEffect(Unit) {
+    LaunchedEffect(myDataInfo) {
         detailMyDataViewModel.initializeFormData(myDataInfo)
     }
📝 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
// ViewModel 상태 구독
val isMale by detailMyDataViewModel.isMale.collectAsStateWithLifecycle()
val name by detailMyDataViewModel.name.collectAsStateWithLifecycle()
val birth by detailMyDataViewModel.birth.collectAsStateWithLifecycle()
val scrollState = rememberScrollState()
// 초기 데이터 로드
LaunchedEffect(Unit) {
detailMyDataViewModel.initializeFormData(myDataInfo)
}
// ViewModel 상태 구독
val isMale by detailMyDataViewModel.isMale.collectAsStateWithLifecycle()
val name by detailMyDataViewModel.name.collectAsStateWithLifecycle()
val birth by detailMyDataViewModel.birth.collectAsStateWithLifecycle()
val scrollState = rememberScrollState()
// 초기 데이터 로드
LaunchedEffect(myDataInfo) {
detailMyDataViewModel.initializeFormData(myDataInfo)
}
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt`
around lines 49 - 58, LaunchedEffect is currently keyed with Unit so
initializeFormData(myDataInfo) won't re-run when myDataInfo changes; update the
key to myDataInfo (i.e., use LaunchedEffect(myDataInfo)) so
detailMyDataViewModel.initializeFormData(myDataInfo) is invoked whenever
myDataInfo updates, keeping the UI state in sync with the latest data.

@alswlekk alswlekk merged commit 3b741c6 into develop Jan 28, 2026
1 of 2 checks passed
@alswlekk alswlekk deleted the refactor/improve-state-management-#233 branch January 28, 2026 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor] 상태 관리 개선

4 participants