Skip to content

[Feature/#65] 보호자 설정 화면 UI 및 로직을 구현합니다.#66

Merged
wjdrjs00 merged 2 commits intodevelopfrom
feature/65-guardian-setting
Jan 4, 2026
Merged

[Feature/#65] 보호자 설정 화면 UI 및 로직을 구현합니다.#66
wjdrjs00 merged 2 commits intodevelopfrom
feature/65-guardian-setting

Conversation

@wjdrjs00
Copy link
Collaborator

@wjdrjs00 wjdrjs00 commented Jan 4, 2026

Related issue 🛠

Work Description ✏️

  • 보호자 설정 화면 UI 및 로직을 구현했습니다.
    • 보호자 화면 컴포넌트 공통 컴포넌트로 구현
    • 회원 탈퇴, 로그아웃 로직 구현

Screenshot 📸

  • N/A

Uncompleted Tasks 😅

  • N/A

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 보호자 설정 화면 추가: 프로필 정보, 회원코드 표시, 로그아웃 및 탈퇴 옵션 포함
    • 보호자 홈 화면에서 설정으로 이동 가능
    • 회원코드 복사 기능 추가
  • Refactor

    • 설정 관련 UI 컴포넌트를 공용 디자인 시스템으로 통합

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

@wjdrjs00 wjdrjs00 self-assigned this Jan 4, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 4, 2026

Walkthrough

Guardian(보호자) 설정 화면 기능이 구현되었습니다. 설정 화면 UI와 ViewModel을 추가하고, 프로필 및 기타 섹션 컴포넌트를 designsystem 패키지로 이동했으며, 로그아웃/탈퇴 다이얼로그 상태 관리와 사용자 프로필 조회 로직을 포함합니다. 또한 부모 사용자 정보를 다루기 위해 UserProfileResponse 데이터 모델을 업데이트했습니다.

Changes

Cohort / File(s) 변경 사항
네비게이션 및 라우팅
core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt, app/src/main/kotlin/com/moa/app/main/MainActivity.kt
AppRoute.GuardianSetting 경로 추가 및 MainActivity의 NavHost에 GuardianSettingScreen 매핑 등록
Designsystem 컴포넌트 마이그레이션
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/OptionItem.kt, OtherSection.kt, ProfileSection.kt
feature/senior/setting/component에서 designsystem/component/product/setting으로 패키지 이동, 컴포넌트 정적 멤버 접근 방식 수정, ProfileSectionisCodeEnabled 파라미터 추가
Guardian 설정 화면 UI
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingScreen.kt
새로운 GuardianSettingScreen 컴포저블 추가, 프로필 섹션, 기타 작업 섹션, 로그아웃/탈퇴 다이얼로그, 앱 버전 표시 포함
Guardian 설정 ViewModel 및 상태 관리
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingViewModel.kt, GuardianSettingUiState.kt, SettingDialogState.kt
GuardianSettingViewModel 클래스 추가, 사용자 프로필 조회 및 로그아웃/탈퇴 흐름 구현, 다이얼로그 상태 관리 (None, Confirm, Complete)
데이터 모델 업데이트
data/src/main/kotlin/com/moa/app/data/user/model/response/UserProfileResponse.kt, UserRepositoryImpl.kt
parentCode nullable로 변경, parentUserIdparentUserIds (List?)로 변경, 매핑 로직 업데이트
Guardian 홈 화면 업데이트
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/home/GuardianHomeScreen.kt, GuardianHomeViewModel.kt
onSettingClick 콜백을 viewModel::navigateToSetting으로 연결, 새로운 navigateToSetting() 메서드 및 NavigationOptions 적용
Senior 설정 화면 업데이트
feature/senior/src/main/kotlin/com/moa/app/feature/senior/setting/SeniorSettingScreen.kt, SeniorSettingViewModel.kt
designsystem으로 이동한 ProfileSection, OtherSection 임포트 변경, isCodeEnabled = true 추가, 불필요한 임포트 제거
제거된 컴포넌트
feature/senior/src/main/kotlin/com/moa/app/feature/senior/setting/component/ProfileSection.kt (삭제)
feature/senior/setting/componentProfileSection 컴포저블 제거 (designsystem으로 이동)

Sequence Diagram

sequenceDiagram
    participant User
    participant GuardianSettingScreen
    participant GuardianSettingViewModel
    participant UseCases as LogOut/<br/>Withdrawal<br/>UseCases
    participant Navigator
    participant DialogState as DialogState<br/>(UI)

    User->>GuardianSettingScreen: 화면 진입
    GuardianSettingScreen->>GuardianSettingViewModel: 프로필 로드 (init)
    GuardianSettingViewModel->>UseCases: FetchUserProfile 호출
    UseCases-->>GuardianSettingViewModel: 사용자 정보 반환
    GuardianSettingViewModel->>GuardianSettingScreen: uiState 업데이트

    rect rgba(100, 150, 200, 0.2)
    Note over User,DialogState: 로그아웃/탈퇴 흐름
    User->>GuardianSettingScreen: 로그아웃 선택
    GuardianSettingScreen->>GuardianSettingViewModel: showLogoutDialog()
    GuardianSettingViewModel->>DialogState: Confirm 상태로 변경
    GuardianSettingScreen->>DialogState: 확인 다이얼로그 표시
    User->>DialogState: 확인 클릭
    DialogState->>GuardianSettingViewModel: logout() 호출
    GuardianSettingViewModel->>UseCases: LogOutUseCase 호출
    UseCases-->>GuardianSettingViewModel: 로그아웃 성공
    GuardianSettingViewModel->>DialogState: Complete 상태로 변경
    GuardianSettingViewModel->>Navigator: navigateToClear (AuthLanding)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 보호자 설정 화면, 우아하게 등장했네요!
다이얼로그는 Confirm에서 Complete로 춤을 추고,
프로필은 designsystem으로 우아히 이사했어요.
로그아웃과 탈퇴는 이제 완벽히 안내되고,
모두가 행복한 Guardian의 세계! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 설명합니다. 보호자 설정 화면 UI 및 로직 구현이 정확히 표현되어 있습니다.
Description check ✅ Passed PR 설명이 제공된 템플릿을 따르고 있으며 관련 이슈, 작업 내용, 스크린샷, 미완료 작업을 모두 포함하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 이슈 #65의 목표를 충족합니다. 보호자 설정 화면 UI, 공통 컴포넌트 구현, 로그아웃 및 회원 탈퇴 로직이 모두 구현되어 있습니다.
Out of Scope Changes check ✅ Passed PR의 모든 변경사항이 이슈 #65 범위 내에 있으며, 보호자 설정 화면 구현과 직접 관련된 내용들입니다.
✨ 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: 5

Caution

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

⚠️ Outside diff range comments (1)
feature/senior/src/main/kotlin/com/moa/app/feature/senior/setting/SeniorSettingScreen.kt (1)

77-85: 복사 기능 구현이 누락되었습니다.

onCopyClick에 빈 람다가 전달되어 있어 복사 버튼을 클릭해도 아무 동작도 하지 않습니다. 사용자는 버튼을 볼 수 있고 클릭할 수 있지만 회원코드가 클립보드에 복사되지 않아 혼란을 줄 수 있습니다.

클립보드 복사 기능을 구현하는 코드를 생성해드릴까요? Android의 ClipboardManager를 사용하여 구현할 수 있습니다.

🔎 구현 제안

1. ViewModel에 복사 함수 추가:

SeniorSettingViewModel.kt에 다음 함수를 추가하세요:

fun copyCodeToClipboard(context: Context, code: String) {
    val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    val clip = ClipData.newPlainText("회원코드", code)
    clipboard.setPrimaryClip(clip)
    // Optional: Show a toast or snackbar to confirm
}

2. Screen에서 호출:

         ProfileSection(
             modifier = Modifier
                 .fillMaxWidth()
                 .padding(20.dp),
             name = uiState.userName,
             code = uiState.userCode,
             isCodeEnabled = true,
-            onCopyClick = {},
+            onCopyClick = { 
+                uiState.userCode?.let { code ->
+                    viewModel.copyCodeToClipboard(context, code)
+                }
+            },
         )

필요한 import:

import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.compose.ui.platform.LocalContext

그리고 Composable 내에서 context를 가져오세요:

val context = LocalContext.current
🧹 Nitpick comments (5)
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingScreen.kt (1)

134-134: 앱 버전을 BuildConfig에서 가져오도록 개선하세요.

앱 버전이 하드코딩되어 있어 매 릴리스마다 수동으로 업데이트해야 합니다. BuildConfig.VERSION_NAME을 사용하는 것이 좋습니다.

🔎 제안하는 수정 사항
            Text(
-               text = "v.1.0.0",
+               text = "v.${BuildConfig.VERSION_NAME}",
                color = MoaTheme.colors.coolGray90,
                style = MoaTheme.typography.body1Medium,
            )
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingViewModel.kt (1)

70-106: 로그아웃/탈퇴 로직이 올바르게 구현되었습니다.

UseCase를 통한 비즈니스 로직 분리와 UI 상태 업데이트가 잘 되어 있습니다. 다만 실패 시 사용자에게 오류 메시지를 표시하는 것을 고려해보세요 (현재는 로그만 남김).

core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/OptionItem.kt (1)

41-54: 불필요한 Companion 접근을 제거하는 것을 고려해보세요.

Kotlin에서는 companion object의 멤버에 직접 접근할 수 있으므로 .Companion을 명시적으로 작성할 필요가 없습니다. 코드를 더 간결하고 관용적으로 만들 수 있습니다.

🔎 제안하는 수정
-        verticalAlignment = Alignment.Companion.CenterVertically,
+        verticalAlignment = Alignment.CenterVertically,
         horizontalArrangement = Arrangement.spacedBy(10.dp)
     ) {
         Image(
-            imageVector = ImageVector.Companion.vectorResource(icon),
+            imageVector = ImageVector.vectorResource(icon),
             contentDescription = null,
-            modifier = Modifier.Companion.size(16.dp)
+            modifier = Modifier.size(16.dp)
         )

         Text(
             text = title,
             color = MoaTheme.colors.coolGray50,
             style = MoaTheme.typography.body1Medium,
-            modifier = Modifier.Companion.padding(vertical = 10.dp)
+            modifier = Modifier.padding(vertical = 10.dp)
         )
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/OtherSection.kt (1)

41-87: 불필요한 Companion 접근을 제거하는 것을 고려해보세요.

OptionItem.kt와 동일하게, Kotlin의 companion object 멤버는 .Companion 없이 직접 접근할 수 있습니다.

🔎 제안하는 수정
         Text(
             text = "기타",
             color = MoaTheme.colors.black,
             style = MoaTheme.typography.body1Bold,
-            modifier = Modifier.Companion
+            modifier = Modifier
                 .padding(vertical = 10.dp)
                 .padding(start = 20.dp)
         )
         
         // ... (OptionItem calls remain unchanged)
         
         Row(
-            modifier = Modifier.Companion
+            modifier = Modifier
                 .fillMaxWidth()
                 .clickable(
                     onClick = onWithdrawalClick,
                     indication = ripple(),
                     interactionSource = remember { MutableInteractionSource() },
                 )
                 .padding(start = 20.dp),
-            verticalAlignment = Alignment.Companion.CenterVertically,
+            verticalAlignment = Alignment.CenterVertically,
             horizontalArrangement = Arrangement.spacedBy(10.dp)
         ) {
             Image(
-                imageVector = ImageVector.Companion.vectorResource(R.drawable.ic_stop),
+                imageVector = ImageVector.vectorResource(R.drawable.ic_stop),
                 contentDescription = null,
-                colorFilter = ColorFilter.Companion.tint(color = MoaTheme.colors.red500),
-                modifier = Modifier.Companion.size(16.dp)
+                colorFilter = ColorFilter.tint(color = MoaTheme.colors.red500),
+                modifier = Modifier.size(16.dp)
             )

             Text(
                 text = "회원 탈퇴",
                 color = MoaTheme.colors.red500,
                 style = MoaTheme.typography.body1Medium,
-                modifier = Modifier.Companion.padding(vertical = 10.dp)
+                modifier = Modifier.padding(vertical = 10.dp)
             )
         }
data/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.kt (1)

39-47: 불필요하게 장황한 구현을 단순화하세요.

현재 구현은 Result를 명시적으로 풀었다가 다시 감싸고 있어 불필요하게 복잡합니다. userDataSource.connectToSenior(userId)가 이미 Result<Unit>을 반환한다면, 주석 처리된 코드처럼 직접 반환하는 것이 더 간결합니다.

만약 데이터소스가 다른 타입을 반환하여 변환이 필요한 경우에도, map 또는 mapCatching을 사용하는 것이 더 관용적입니다.

🔎 구현 단순화 제안

방법 1: 데이터소스가 이미 Result<Unit>을 반환하는 경우

 override suspend fun connectToSenior(userId: Long): Result<Unit> {
-//        return userDataSource.connectToSenior(userId)
-        val response = userDataSource.connectToSenior(userId)
-
-        return response.fold(
-            onSuccess = { Result.success(Unit) },
-            onFailure = { Result.failure(it) }
-        )
+    return userDataSource.connectToSenior(userId)
 }

방법 2: 데이터소스가 다른 타입을 반환하는 경우

 override suspend fun connectToSenior(userId: Long): Result<Unit> {
-//        return userDataSource.connectToSenior(userId)
-        val response = userDataSource.connectToSenior(userId)
-
-        return response.fold(
-            onSuccess = { Result.success(Unit) },
-            onFailure = { Result.failure(it) }
-        )
+    return userDataSource.connectToSenior(userId).map { Unit }
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2b5f8d and 22ccc04.

📒 Files selected for processing (16)
  • app/src/main/kotlin/com/moa/app/main/MainActivity.kt
  • core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/OptionItem.kt
  • core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/OtherSection.kt
  • core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/ProfileSection.kt
  • core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt
  • data/src/main/kotlin/com/moa/app/data/user/model/response/UserProfileResponse.kt
  • data/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.kt
  • feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/home/GuardianHomeScreen.kt
  • feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/home/GuardianHomeViewModel.kt
  • feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingScreen.kt
  • feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingViewModel.kt
  • feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/model/GuardianSettingUiState.kt
  • feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/model/SettingDialogState.kt
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/setting/SeniorSettingScreen.kt
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/setting/SeniorSettingViewModel.kt
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/setting/component/ProfileSection.kt
💤 Files with no reviewable changes (1)
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/setting/component/ProfileSection.kt
🧰 Additional context used
🧬 Code graph analysis (3)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/ProfileSection.kt (1)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/button/MaButton.kt (1)
  • MaButton (30-68)
app/src/main/kotlin/com/moa/app/main/MainActivity.kt (1)
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingScreen.kt (1)
  • GuardianSettingScreen (29-87)
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingScreen.kt (5)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/dialog/MaAlertDialog.kt (1)
  • MaAlertDialog (27-120)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/dialog/MaConfirmDialog.kt (1)
  • MaConfirmDialog (24-69)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/topbar/MaTopAppBar.kt (1)
  • MaTopAppBar (20-52)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/ProfileSection.kt (1)
  • ProfileSection (19-70)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/OtherSection.kt (1)
  • OtherSection (26-91)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run Unit Tests
🔇 Additional comments (7)
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/model/SettingDialogState.kt (1)

1-10: LGTM! 다이얼로그 상태 관리 패턴이 잘 설계되었습니다.

@Immutable 어노테이션과 sealed interface를 사용한 상태 관리 패턴이 Compose에 적합하며, data object를 사용하여 싱글톤 상태를 표현한 것이 적절합니다.

app/src/main/kotlin/com/moa/app/main/MainActivity.kt (1)

23-23: LGTM! 네비게이션 라우트가 올바르게 추가되었습니다.

보호자 설정 화면의 import와 composable 선언이 기존 패턴과 일관되게 구현되었습니다.

Also applies to: 98-98

feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/home/GuardianHomeScreen.kt (1)

51-51: LGTM! 설정 버튼이 올바르게 연결되었습니다.

이전의 빈 람다에서 실제 네비게이션 로직으로 변경되어 설정 버튼 기능이 활성화되었습니다.

feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/home/GuardianHomeViewModel.kt (1)

11-11: LGTM! 네비게이션 로직이 올바르게 구현되었습니다.

NavigationOptions(launchSingleTop = true)를 사용하여 백스택에 중복 화면이 쌓이지 않도록 처리한 것이 적절합니다. 두 네비게이션 함수가 일관된 패턴을 따르고 있습니다.

Also applies to: 108-119

core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt (1)

70-71: LGTM! 라우트 정의가 올바르게 추가되었습니다.

새로운 GuardianSetting 라우트가 기존 패턴과 일관되게 선언되었으며, GuardianHome 다음에 논리적으로 배치되었습니다.

feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingScreen.kt (1)

61-61: 다이얼로그 닫기 방지 패턴이 올바르게 구현되었습니다.

Complete 상태의 다이얼로그에서 onDialogDismissRequest = {}로 설정하여 사용자가 확인 버튼을 반드시 클릭하도록 강제하는 것은 로그아웃/탈퇴와 같은 중요한 작업의 완료를 사용자에게 명확히 알리는 좋은 UX 패턴입니다.

Also applies to: 83-83

feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/model/GuardianSettingUiState.kt (1)

5-20: UI 상태 모델이 잘 설계되었습니다.

@Immutable 어노테이션을 사용하여 Compose 재구성 최적화를 적용했고, INIT 값으로 합리적인 기본값을 제공하는 깔끔한 구현입니다.

Comment on lines +37 to +47
if (isCodeEnabled) {
Row(
modifier = Modifier.height(height = 30.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Text(
text = "회원코드: $code",
color = MoaTheme.colors.coolGray60,
style = MoaTheme.typography.body1Medium,
)
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

null 코드 값 처리를 확인하세요.

code 파라미터가 nullable이지만 line 44에서 문자열 보간 시 null 체크 없이 사용됩니다. isCodeEnabled가 true이고 code가 null인 경우 "회원코드: null"이 화면에 표시될 수 있습니다.

🔎 제안하는 수정
         if (isCodeEnabled) {
             Row(
                 modifier = Modifier.height(height = 30.dp),
                 verticalAlignment = Alignment.CenterVertically,
                 horizontalArrangement = Arrangement.spacedBy(10.dp),
             ) {
                 Text(
-                    text = "회원코드: $code",
+                    text = "회원코드: ${code.orEmpty()}",
                     color = MoaTheme.colors.coolGray60,
                     style = MoaTheme.typography.body1Medium,
                 )
📝 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
if (isCodeEnabled) {
Row(
modifier = Modifier.height(height = 30.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Text(
text = "회원코드: $code",
color = MoaTheme.colors.coolGray60,
style = MoaTheme.typography.body1Medium,
)
if (isCodeEnabled) {
Row(
modifier = Modifier.height(height = 30.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Text(
text = "회원코드: ${code.orEmpty()}",
color = MoaTheme.colors.coolGray60,
style = MoaTheme.typography.body1Medium,
)
🤖 Prompt for AI Agents
In
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/setting/ProfileSection.kt
around lines 37 to 47, the nullable parameter `code` is interpolated directly
into the Text when `isCodeEnabled` is true, which can render "회원코드: null";
update the logic to handle null safely by either: 1) checking `code` for null
before rendering the Row (skip the whole Row when null), or 2) provide a
non-null fallback string (e.g., "-" or localized "없음") and use that fallback in
the interpolated text, ensuring no "null" appears in the UI; implement one of
these approaches and keep styling/arrangement unchanged.

@SerialName("status") val status: String,
@SerialName("parentCode") val parentCode: String,
@SerialName("parentUserId") val parentUserId: Long?
@SerialName("parentCode") val parentCode: String?,
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all uses of parentCode in the codebase
rg -n --type=kotlin -C3 'parentCode'

Repository: team-memory-care/MoA-Android

Length of output: 8691


🏁 Script executed:

#!/bin/bash
# Check the domain UserProfile model
rg -n --type=kotlin 'data class UserProfile|interface UserProfile|class UserProfile' -A 10 -B 2

Repository: team-memory-care/MoA-Android

Length of output: 2471


parentCode nullable 변경은 API 계약의 breaking change입니다.

확인된 사항:

  • parentCode가 non-null String에서 nullable String?으로 변경됨 (line 15)
  • Repository 레이어에서 response.parentCode ?: ""로 null을 빈 문자열로 변환 중
  • 도메인 모델 UserProfileauthCode는 여전히 non-nullable String

문제점:
null을 빈 문자열로 변환하면서 중요한 정보가 손실됩니다:

  • "parentCode가 없음(null)"과 "parentCode가 빈 문자열"을 구분할 수 없음
  • 도메인 레이어에서도 이 차이를 표현할 수 없음
  • 이로 인해 로직 오류가 발생할 수 있음

백엔드 API가 실제로 parentCode에 null을 반환할 수 있는지, 그리고 null과 빈 문자열 간의 의미적 차이를 무시하는 것이 의도한 설계인지 확인이 필요합니다. 의도적이지 않다면, 도메인 모델의 authCode도 nullable로 변경하거나 null 처리 전략을 재검토해야 합니다.

🤖 Prompt for AI Agents
In
data/src/main/kotlin/com/moa/app/data/user/model/response/UserProfileResponse.kt
around line 15, parentCode was changed from non-null String to nullable String?
which is a breaking API contract; don’t silently convert null to empty string in
the repository mapping (response.parentCode ?: "") because that loses the
semantic difference between null and "" — verify whether the backend can truly
return null; then either (A) revert parentCode to non-null and enforce the API
contract, or (B) propagate nullability through to the domain layer by making
UserProfile.authCode nullable (or use an explicit wrapper/optional) and update
the repository mapping to pass response.parentCode as-is, plus update related
callers/tests to handle the nullable authCode accordingly.

@SerialName("parentCode") val parentCode: String,
@SerialName("parentUserId") val parentUserId: Long?
@SerialName("parentCode") val parentCode: String?,
@SerialName("parentUserIds") val parentUserId: List<Long>?
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, locate the file mentioned in the review
find . -name "UserProfileResponse.kt" -type f

Repository: team-memory-care/MoA-Android

Length of output: 154


🏁 Script executed:

#!/bin/bash
# Search for parentUserId usage across the codebase
rg -n --type=kotlin 'parentUserId' -B 2 -A 2

Repository: team-memory-care/MoA-Android

Length of output: 573


🏁 Script executed:

#!/bin/bash
# Check the full content of the UserProfileResponse file
fd "UserProfileResponse.kt" -type f | head -1 | xargs wc -l

Repository: team-memory-care/MoA-Android

Length of output: 261


🏁 Script executed:

#!/bin/bash
# Read the full UserProfileResponse.kt file
cat -n data/src/main/kotlin/com/moa/app/data/user/model/response/UserProfileResponse.kt

Repository: team-memory-care/MoA-Android

Length of output: 805


🏁 Script executed:

#!/bin/bash
# Search for parentUserId and parentUserIds across all files (not just kotlin)
rg -n 'parentUserId|parentUserIds' -B 2 -A 2

Repository: team-memory-care/MoA-Android

Length of output: 573


🏁 Script executed:

#!/bin/bash
# Check git history to see if this was recently changed
cd data/src/main/kotlin/com/moa/app/data/user/model/response/ && git log --oneline -n 10 -- UserProfileResponse.kt

Repository: team-memory-care/MoA-Android

Length of output: 103


🏁 Script executed:

#!/bin/bash
# Find all Kotlin files that might use UserProfileResponse
rg -l 'UserProfileResponse' --type=kotlin -A 5 | head -20

Repository: team-memory-care/MoA-Android

Length of output: 370


🏁 Script executed:

#!/bin/bash
# Search for usages of .parentUserId in the codebase
rg '\.parentUserId' -B 3 -A 3

Repository: team-memory-care/MoA-Android

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check if there are any other similar naming issues in the data models
rg '@SerialName.*\(.*[Ss]' data/src/main/kotlin/com/moa/app/data/user/model/ | head -30

Repository: team-memory-care/MoA-Android

Length of output: 2394


🏁 Script executed:

#!/bin/bash
# Check where UserProfileResponse is actually used
rg 'UserProfileResponse' -B 2 -A 5

Repository: team-memory-care/MoA-Android

Length of output: 5995


🏁 Script executed:

#!/bin/bash
# Look at the commit that introduced this change
cd data/src/main/kotlin/com/moa/app/data/user/model/response/ && git show 22ccc04 | grep -A 30 -B 30 parentUserId

Repository: team-memory-care/MoA-Android

Length of output: 2622


🏁 Script executed:

#!/bin/bash
# Check how parentUserId is accessed/used in UserRepositoryImpl and other places
rg 'parentUserId|parentUserIds' -B 5 -A 5 --context=10

Repository: team-memory-care/MoA-Android

Length of output: 1529


🏁 Script executed:

#!/bin/bash
# Look at the complete UserRepositoryImpl to see how the property is used
cat -n data/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.kt

Repository: team-memory-care/MoA-Android

Length of output: 3416


🏁 Script executed:

#!/bin/bash
# Search for any mapping or usage of the parent user IDs in the codebase
rg -i 'parent.*id' --type=kotlin data/src/main/kotlin/com/moa/app/domain/auth/model/UserProfile.kt data/src/main/kotlin/com/moa/app/domain/user/ 2>/dev/null || echo "Checking domain models..."

Repository: team-memory-care/MoA-Android

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Find domain model files related to user
fd "UserProfile|Profile" --type f data/src/main/kotlin/com/moa/app/domain/ 2>/dev/null

Repository: team-memory-care/MoA-Android

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check if parentUserId is being used in UI/presentation layer or other modules
rg 'parentUserId' --type=kotlin --type=java

Repository: team-memory-care/MoA-Android

Length of output: 215


🏁 Script executed:

#!/bin/bash
# Check the UserProfile domain model to see if it includes parentUserIds
fd "UserProfile.kt" --type f data/

Repository: team-memory-care/MoA-Android

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Search for files in the presentation/UI layer that might use the profile
rg 'getUserProfile' -B 2 -A 5 --type=kotlin presentation/ | head -50

Repository: team-memory-care/MoA-Android

Length of output: 125


🏁 Script executed:

#!/bin/bash
# Find presentation layer modules
fd --type d "presentation|feature|ui" | head -20

Repository: team-memory-care/MoA-Android

Length of output: 619


🏁 Script executed:

#!/bin/bash
# Check the structure of the repo
ls -la

Repository: team-memory-care/MoA-Android

Length of output: 1295


🏁 Script executed:

#!/bin/bash
# Look for any other response models that might be similar
rg '@SerialName.*parentUser' --type=kotlin

Repository: team-memory-care/MoA-Android

Length of output: 215


🏁 Script executed:

#!/bin/bash
# Check if there's a previous version of UserProfileResponse that had parentUserId as Long
git log -p -- data/src/main/kotlin/com/moa/app/data/user/model/response/UserProfileResponse.kt | head -100

Repository: team-memory-care/MoA-Android

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check the UserProfile domain model
fd "UserProfile.kt" --type f | xargs cat -n

Repository: team-memory-care/MoA-Android

Length of output: 327


🏁 Script executed:

#!/bin/bash
# Search more broadly for any use of parentUser in the codebase
rg -i 'parentuser' --type=kotlin -B 3 -A 3

Repository: team-memory-care/MoA-Android

Length of output: 688


🏁 Script executed:

#!/bin/bash
# Check if there are any test files that might show expected usage
fd ".*Test\.kt|.*Spec\.kt" --type f | xargs rg -l "UserProfileResponse|parentUserId" 2>/dev/null

Repository: team-memory-care/MoA-Android

Length of output: 56


프로퍼티 이름과 JSON 필드명의 불일치를 수정하세요.

@SerialName("parentUserIds")는 복수형이지만 프로퍼티 이름 parentUserId는 단수형입니다. 일관성을 위해 프로퍼티 이름을 parentUserIds로 수정하세요:

-    @SerialName("parentUserIds") val parentUserId: List<Long>?
+    @SerialName("parentUserIds") val parentUserIds: List<Long>?

참고: 현재 이 프로퍼티는 코드베이스에서 사용되지 않고 있습니다. UserRepositoryImpl에서 UserProfileResponse를 도메인 모델로 변환할 때 이 필드도 매핑해야 합니다.

🤖 Prompt for AI Agents
In
data/src/main/kotlin/com/moa/app/data/user/model/response/UserProfileResponse.kt
around line 16, the property name parentUserId doesn’t match the JSON SerialName
parentUserIds; rename the Kotlin property to parentUserIds to match the
serialized field and maintain consistency. After renaming, search for usages and
update any references (including mapping in UserRepositoryImpl when converting
UserProfileResponse to the domain model) to map this field appropriately; ensure
imports/serialization remain valid and run build/tests to verify no breakage.

phoneNumber = response.phoneNumber,
gender = Gender.fromString(response.gender),
authCode = response.parentCode
authCode = response.parentCode ?: ""
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Find UserProfile class definition
echo "=== Searching for UserProfile class definition ==="
find . -name "*.kt" -type f | xargs grep -l "class UserProfile\|data class UserProfile" | head -20

# Search for UserProfile.kt specifically
echo "=== Looking for UserProfile file ==="
fd -e kt "UserProfile" --max-one-result

# Check the structure
echo "=== Files containing UserProfile ==="
rg -l "UserProfile" --type=kt | head -20

Repository: team-memory-care/MoA-Android

Length of output: 627


🏁 Script executed:

#!/bin/bash

# Search for authCode property definition and usages
echo "=== Searching for authCode property definition ==="
rg -n "authCode" --type=kt -B2 -A2 | head -100

# Look for UserProfile class definition
echo "=== UserProfile class/data class definition ==="
rg -n "class UserProfile|data class UserProfile" --type=kt -A10 | head -50

Repository: team-memory-care/MoA-Android

Length of output: 241


🏁 Script executed:

#!/bin/bash

# Get the repository root and search more thoroughly
echo "=== Checking git ls-files for UserProfile ==="
git ls-files | grep -i userprofile

# Search in domain directory if it exists
echo "=== Searching in likely domain directories ==="
find . -type d -name "domain" -o -name "model" | head -10

Repository: team-memory-care/MoA-Android

Length of output: 886


🏁 Script executed:

#!/bin/bash

# Read the UserProfile domain model
echo "=== UserProfile.kt content ==="
cat -n domain/src/main/kotlin/com/moa/app/domain/auth/model/UserProfile.kt

# Read the UserProfileResponse model
echo "=== UserProfileResponse.kt content ==="
cat -n data/src/main/kotlin/com/moa/app/data/user/model/response/UserProfileResponse.kt

Repository: team-memory-care/MoA-Android

Length of output: 1131


🏁 Script executed:

#!/bin/bash

# Read UserRepositoryImpl.kt around line 24
echo "=== UserRepositoryImpl.kt lines 1-50 ==="
head -n 50 data/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.kt | cat -n

# Get total lines
echo "=== File line count ==="
wc -l data/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.kt

Repository: team-memory-care/MoA-Android

Length of output: 2410


🏁 Script executed:

#!/bin/bash

# Search for all usages of authCode across the project
echo "=== All usages of authCode ==="
grep -rn "authCode" --include="*.kt" . 2>/dev/null | head -50

Repository: team-memory-care/MoA-Android

Length of output: 7607


parentCode가 null일 때 빈 문자열로 기본값 설정을 재고려하세요.

response.parentCode ?: ""는 "인증 코드가 없음"과 "인증 코드가 빈 문자열"을 구분할 수 없게 만듭니다. API 응답의 parentCode가 이미 String? 타입인 반면, 도메인 모델 UserProfile.authCode는 비-null String으로 정의되어 있어 의미 손실이 발생합니다.

도메인 모델 UserProfileauthCodeString?으로 변경하여 nullable 의미를 명확히 하거나, 또는 null 값이 실제로 불가능한 경우 API 설계를 검토하는 것을 권장합니다.

🤖 Prompt for AI Agents
In
data/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.kt
around line 24, assigning response.parentCode ?: "" loses the distinction
between null and empty string; either make the domain property
UserProfile.authCode nullable (String?) and propagate nullability through
DTOs/mappers/DB/entities/serializers and update usages/tests, or if parentCode
should never be null add an explicit validation that throws/logs an error when
response.parentCode is null and remove the ?: "" fallback so the failure is
detected upstream.

Comment on lines +114 to +123
fun navigateToClear() {
navigator.navigate(
route = AppRoute.AuthLanding,
options = NavigationOptions(
popUpTo = AppRoute.SeniorSetting,
inclusive = true,
clearBackStack = true,
),
)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

네비게이션 라우트 참조 오류를 수정하세요.

Line 118에서 popUpTo = AppRoute.SeniorSetting을 사용하고 있는데, 이는 GuardianSettingViewModel에서 잘못된 라우트를 참조하고 있습니다. AppRoute.GuardianSetting 또는 현재 Guardian 관련 적절한 라우트로 수정해야 합니다. 이는 Senior 설정 화면에서 복사-붙여넣기한 것으로 보입니다.

🔎 제안하는 수정 사항
    fun navigateToClear() {
        navigator.navigate(
            route = AppRoute.AuthLanding,
            options = NavigationOptions(
-               popUpTo = AppRoute.SeniorSetting,
+               popUpTo = AppRoute.GuardianSetting,
                inclusive = true,
                clearBackStack = true,
            ),
        )
    }
📝 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
fun navigateToClear() {
navigator.navigate(
route = AppRoute.AuthLanding,
options = NavigationOptions(
popUpTo = AppRoute.SeniorSetting,
inclusive = true,
clearBackStack = true,
),
)
}
fun navigateToClear() {
navigator.navigate(
route = AppRoute.AuthLanding,
options = NavigationOptions(
popUpTo = AppRoute.GuardianSetting,
inclusive = true,
clearBackStack = true,
),
)
}
🤖 Prompt for AI Agents
In
feature/guardian/src/main/kotlin/com/moa/app/feature/guardian/setting/GuardianSettingViewModel.kt
around lines 114-123, the navigateToClear() function incorrectly references
AppRoute.SeniorSetting for popUpTo (copied from Senior screen); update popUpTo
to the correct guardian route (e.g., AppRoute.GuardianSetting or the actual
Guardian route constant used in the app) so the navigation clears backstack to
the guardian settings screen; ensure the route constant exists/import is correct
and run a quick compile to confirm no unresolved reference.

@wjdrjs00 wjdrjs00 merged commit 0d3933f into develop Jan 4, 2026
4 checks passed
@wjdrjs00 wjdrjs00 deleted the feature/65-guardian-setting branch January 4, 2026 18:32
@wjdrjs00 wjdrjs00 restored the feature/65-guardian-setting branch January 4, 2026 18:32
@wjdrjs00 wjdrjs00 deleted the feature/65-guardian-setting branch January 4, 2026 18:33
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.

[Feature] 보호자 설정 화면 UI 및 로직을 구현합니다.

1 participant