[Feat] 마이페이지 홈 화면 UI 구현 - #81
Conversation
* feat: add MyPageHomeViewModel using Orbit MVI * feat: define MyPageHomeUiState, MyPageHomeUiModel, and MyPageHomeSideEffect
* chore: feature:mypage 모듈 내 코드 포맷팅 및 임포트 정리 * chore: MyPageHomeViewModel 및 UI State 클래스 스타일 수정
* feat: User 모델에 name 프로퍼티 추가 * feat: FakeUserRepository에 테스트용 이름 반영
* feat: add MyPageHomeRoute and MyPageHomeScreen to handle UI states * feat: implement MyPageMenuItem and SajuCard reusable components * feat: add MyPageHomeHeader for the MyPage landing screen * feat: implement profile card and menu list layout in MyPageHomeScreen
* feat: add `MyPageMenuType` enum to manage menu resources and styles * feat: add `ProfileCard` and `SajuPaljaGrid` UI components to display user information and Saju data * refactor: update `MyPageHomeScreen` to render menu items dynamically using `MyPageMenuType` * refactor: simplify navigation callbacks in `MyPageHomeScreen` by using a single menu click listener
* feat: app 모듈에 feature:mypage 의존성 추가 * refactor: MyPageHomeRoute에 Modifier 파라미터 추가 및 전달
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthrough마이페이지 홈 기능을 추가했습니다. 사용자 이름 필드와 가짜 사용자 데이터를 갱신했습니다. ViewModel에서 정보를 조회하고 Compose 화면에서 프로필, 사주팔자, 메뉴를 표시합니다. 관련 아이콘과 문자열 리소스도 추가했습니다. Changes마이페이지 홈
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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. Comment |
* apply trailing commas to UI components and function calls * reformat function signatures and when expressions for consistency * clean up spacing in comments and remove unused lines
|
@CodeRabbit review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (4)
core/designsystem/src/main/res/values/strings.xml (1)
51-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win[P2] MyPage 전용 문자열을
feature:mypage가 소유하도록 분리하세요.현재
mypage_*문자열을core:designsystem의 공용 리소스에 추가했습니다. 이 구조는 공용 모듈이 특정 기능의 문구까지 소유하게 만듭니다. 여러 기능이 사용하는 공통 리소스만core:designsystem에 두고, MyPage 전용 문자열은feature:mypage의 리소스로 이동하세요.As per path instructions: “프로젝트는 by-feature 멀티 모듈 구조이며, domain을 최상위 모듈로 둡니다.” 기능별 리소스의 소유 모듈도 기능 모듈로 분리하는 편이 구조에 맞습니다.
Also applies to: 60-61
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/designsystem/src/main/res/values/strings.xml` around lines 51 - 57, Remove the MyPage-specific resources identified by the mypage_* symbols from core:designsystem and define them in feature:mypage’s resource set instead. Keep only genuinely shared strings in the core module, preserving the existing resource names and text so MyPage consumers resolve them from the feature-owned resources.Source: Path instructions
feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt (1)
143-153: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win[P2]
EditButton에 버튼 의미와 최소 터치 영역을 지정하세요.Line 151의
clickable에는Role.Button이 없습니다. 현재 modifier에는 48.dp 이상의 터치 영역 보장도 없습니다.role = Role.Button을 전달하세요.sizeIn(minWidth = 48.dp, minHeight = 48.dp)또는 프로젝트 표준 최소 터치 영역 modifier를 추가하세요.As per path instructions, "ktlint 및 compose lint 컨벤션을 따르는지 확인합니다."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt` around lines 143 - 153, Update the EditButton modifier chain around clickable to declare Role.Button and enforce the project-standard minimum 48.dp touch target using sizeIn or the existing equivalent modifier. Preserve the current click behavior and follow ktlint and Compose lint conventions.Source: Path instructions
feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt (1)
118-128: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value[P3] 반복 렌더링에서 람다 재생성을 줄일 수 있습니다.
onClick = { onMenuItemClick(menuType) }는 리컴포지션마다 메뉴 개수만큼 새 람다를 만듭니다. 항목이 5개로 고정이라 영향은 작습니다. 원하면MyPageMenuItem이menuType과onClick: (MyPageMenuType) -> Unit을 받도록 바꿔 람다 캡처를 제거하세요.위 의견은 코딩 가이드의 "Composable 함수의 불필요한 리컴포지션 유발 요소(불안정 파라미터, 람다 재생성 등)가 있으면 P2로 안내합니다" 항목을 참고했습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt` around lines 118 - 128, Update MyPageMenuItem to accept MyPageMenuType and an onClick callback taking that type, then pass the callback directly from the MyPageMenuType.entries iteration instead of creating a per-item capturing lambda. Preserve each menu item's existing click behavior and other parameters.Source: Path instructions
feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt (1)
9-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value[P2] 색상 값을 enum에 두지 말고 Composable 계층에서 결정하는 방식을 검토하세요.
iconTint,textColor는 표현(presentation) 관심사입니다. enum 상수에 고정하면 테마 변경이나 다크 모드 대응 시 모델을 수정해야 합니다. enum은 아이콘·문자열·showChevron같은 의미 정보만 유지하고, 색상은MyPageMenuItem렌더링 시점에menuType == LOGOUT여부로 결정하는 편이 Compose 관례에 부합합니다.♻️ 제안 방향
enum class MyPageMenuType( `@param`:DrawableRes val iconRes: Int, `@param`:StringRes val labelRes: Int, val showChevron: Boolean = true, - val iconTint: Color = TodakunColor.gray975, - val textColor: Color = TodakunColor.black, + val isDestructive: Boolean = false, ) {렌더링 측에서
isDestructive값에 따라 색상을 선택합니다.위 의견은 코딩 가이드의 "Composable 함수의 불필요한 리컴포지션 유발 요소... 및 Compose 컨벤션" 항목을 참고했습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt` around lines 9 - 15, Remove the presentation-specific iconTint and textColor properties from MyPageMenuType, keeping only semantic menu data such as iconRes, labelRes, and showChevron. Update the MyPageMenuItem composable to derive the icon and text colors at render time from whether the menu type is LOGOUT, preserving destructive styling without storing theme-dependent colors in the enum.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/designsystem/src/main/res/values/strings.xml`:
- Around line 58-59: Update mypage_app_version_value and the screen logic that
consumes it so the displayed version uses the app’s runtime versionName as a
format argument instead of the hardcoded v1.0.0. Derive the 최신 버전 status from
the actual update state, passing the appropriate localized status text rather
than embedding a fixed 최신 여부 in the resource.
In `@feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt`:
- Around line 15-20: Update MyPageHomeRoute to collect and handle
MyPageHomeSideEffect, and pass the corresponding click callbacks to
MyPageHomeScreen for onEditClick, onViewMansaeryeokClick, and onMenuItemClick so
ViewModel actions are triggered. If navigation handling is intentionally
deferred, leave explicit TODOs in each side-effect/callback path.
In `@feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt`:
- Around line 145-149: Update the version display in MyPageHomeScreen so it
reads the installed app version from build metadata, such as
BuildConfig.VERSION_NAME or PackageManager, instead of the fixed
R.string.mypage_app_version_value resource; pass that dynamic value into the
existing Text while preserving its current styling.
- Around line 106-151: Update the Column containing ProfileCard, MyPageMenuItem
entries, and the app-version Row to support vertical scrolling, using
verticalScroll or LazyColumn. Preserve the existing Spacer weight behavior by
providing an appropriate minimum height when using a scrollable Column, so all
content remains accessible on small screens and with large font settings.
- Around line 84-96: Remove the unused CircularProgressIndicator import from
MyPageHomeScreen.kt while preserving the empty Box placeholder implementations
in MyPageHomeLoading and MyPageHomeError.
---
Nitpick comments:
In `@core/designsystem/src/main/res/values/strings.xml`:
- Around line 51-57: Remove the MyPage-specific resources identified by the
mypage_* symbols from core:designsystem and define them in feature:mypage’s
resource set instead. Keep only genuinely shared strings in the core module,
preserving the existing resource names and text so MyPage consumers resolve them
from the feature-owned resources.
In
`@feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt`:
- Around line 9-15: Remove the presentation-specific iconTint and textColor
properties from MyPageMenuType, keeping only semantic menu data such as iconRes,
labelRes, and showChevron. Update the MyPageMenuItem composable to derive the
icon and text colors at render time from whether the menu type is LOGOUT,
preserving destructive styling without storing theme-dependent colors in the
enum.
In
`@feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt`:
- Around line 143-153: Update the EditButton modifier chain around clickable to
declare Role.Button and enforce the project-standard minimum 48.dp touch target
using sizeIn or the existing equivalent modifier. Preserve the current click
behavior and follow ktlint and Compose lint conventions.
In `@feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt`:
- Around line 118-128: Update MyPageMenuItem to accept MyPageMenuType and an
onClick callback taking that type, then pass the callback directly from the
MyPageMenuType.entries iteration instead of creating a per-item capturing
lambda. Preserve each menu item's existing click behavior and other parameters.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8fd4efa7-cb91-426d-8a65-b4644b8c2613
⛔ Files ignored due to path filters (3)
app/build.gradle.ktsis excluded by!**/*.gradle.ktsfeature/mypage/build.gradle.ktsis excluded by!**/*.gradle.ktssettings.gradle.ktsis excluded by!**/*.gradle.kts
📒 Files selected for processing (25)
core/data/src/main/java/com/kikidan/data/repository/FakeUserRepository.ktcore/designsystem/src/main/res/drawable/ic_chevron_right.xmlcore/designsystem/src/main/res/drawable/ic_logout.xmlcore/designsystem/src/main/res/drawable/ic_mail.xmlcore/designsystem/src/main/res/drawable/ic_manage_saju_info.xmlcore/designsystem/src/main/res/drawable/ic_setting.xmlcore/designsystem/src/main/res/values/strings.xmlcore/domain/src/main/java/com/kikidan/domain/model/user/User.ktfeature/mypage/.gitignorefeature/mypage/consumer-rules.profeature/mypage/proguard-rules.profeature/mypage/src/main/AndroidManifest.xmlfeature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeSideEffect.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageMenuItem.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.ktfeature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.ktgradle/libs.versions.toml
| <string name="mypage_app_version_label">앱 버전</string> | ||
| <string name="mypage_app_version_value">v1.0.0 (최신 버전)</string> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
[P2] 앱 버전과 최신 여부를 정적 문자열로 고정하지 마세요.
mypage_app_version_value가 v1.0.0 (최신 버전)으로 고정되어 있습니다. versionName이 변경되거나 업데이트가 제공되어도 오래된 정보가 표시됩니다. 앱 빌드 메타데이터를 포맷 인자로 전달하고, 최신 여부는 실제 업데이트 상태로 계산하세요.
권장 리소스 형태
- <string name="mypage_app_version_value">v1.0.0 (최신 버전)</string>
+ <string name="mypage_app_version_value">v%1$s</string>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@core/designsystem/src/main/res/values/strings.xml` around lines 58 - 59,
Update mypage_app_version_value and the screen logic that consumes it so the
displayed version uses the app’s runtime versionName as a format argument
instead of the hardcoded v1.0.0. Derive the 최신 버전 status from the actual update
state, passing the appropriate localized status text rather than embedding a
fixed 최신 여부 in the resource.
| Text( | ||
| text = stringResource(R.string.mypage_app_version_value), | ||
| style = TodakunTypography.caption1Regular, | ||
| color = TodakunColor.gray400, | ||
| ) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
[P2] 앱 버전은 문자열 리소스가 아니라 빌드 정보에서 읽으세요.
R.string.mypage_app_version_value에 버전을 고정하면 릴리스마다 리소스를 수정해야 합니다. 값이 실제 앱 버전과 어긋날 수 있습니다. BuildConfig.VERSION_NAME 또는 PackageManager에서 조회한 값을 파라미터로 전달하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt`
around lines 145 - 149, Update the version display in MyPageHomeScreen so it
reads the installed app version from build metadata, such as
BuildConfig.VERSION_NAME or PackageManager, instead of the fixed
R.string.mypage_app_version_value resource; pass that dynamic value into the
existing Text while preserving its current styling.
There was a problem hiding this comment.
저희 패키지 구조도 통일하는게 좋을 것 같습니다!
아직 제 screen pr이 머지 전이라 만약 형 컨벤션을 따르게 된다면 제 pr에 바로 수정할게요!
| internal fun MyPageHomeHeader(modifier: Modifier = Modifier) { | ||
| Text( | ||
| text = stringResource(R.string.mypage_home_title), | ||
| style = TodakunTypography.heading4Bold, | ||
| color = TodakunColor.black, | ||
| modifier = modifier.padding(horizontal = 20.dp, vertical = 16.dp), | ||
| ) | ||
| } |
There was a problem hiding this comment.
저희 Scaffold는 공통 컴포넌트로 가져가나요?
There was a problem hiding this comment.
헤더, Snackbar를 어떻게 쓰냐에 따라서 갈릴것 같네요.
어떻게 할까요?
저는 대부분 Header를 Scaffold에 종속시키지 않고 구현하고,
SnackBar도 Global 하게 처리하게 하는데, 이게 좋은 방법이라고 단정짓기는 어렵습니다.
혹시 공식 문서나 제너럴한 방법을 알고 계신가요?
There was a problem hiding this comment.
사실 저도 형의 방법이 최선이라고 생각해요. Now in Android에서는 하나의 Scaffold에서 스낵바호스트를 종속시키고, Topbar, bottombar를 그냥 Column으로 배치하는 것 같아요
| onMenuItemClick = onMenuItemClick, | ||
| modifier = Modifier.weight(1f), | ||
| ) | ||
| } |
There was a problem hiding this comment.
figma 시안에는 헤더와 스크린 간에 간격에 20px 떨어져 있습니다!
vertical 패딩도 줘야할 것 같아요
| ) { | ||
| Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { | ||
| pillars.forEach { pillar -> | ||
| SajuCard( |
There was a problem hiding this comment.
figma에는 24.dp 간격으로 되어있는데 생각보다 시안과 크기차이가 꽤 나는 것 같습니다. 이 부분도 나중에 디자인 팀에게 확인 받아도 좋을 것 같습니다!
There was a problem hiding this comment.
개인적으론 string의 경우 각 화면에서만 쓰인다고 생각합니다!
그래서 core보단 feature에 두는게 더 보기 편할 것 같은데 어떻게 생각하시나요?
There was a problem hiding this comment.
@oungsi2000
오 그렇게 하면 나중에 아주 재미있는 일이 일어나는데, 소중한 경험을 뺏고 싶지 않네요...!
멀티 모듈을 적용하는 개발자들 사이에서 많이 일어나고, 또 매우매우 찾기 힘든 이슈가 발생하게 됩니다!
직접 경험해보면 좋은데 그 전에 "리소스 아이디"에 대해서 생각해보거나, AI에게 물어보면 좋을 것 같습니다!
| modifier | ||
| .fillMaxWidth() | ||
| .clickable(onClick = onClick) |
There was a problem hiding this comment.
저희 ripple 효과도 정하면 좋을 것 같아요! (
TodakunTheme에 MaterialTheme으로 안감싸고 있어서 별로 안예쁜 ripple 효과가 나옵니다. 그렇다고 ripple을 넣지 않으면 뭔가 또 밋밋한 것 같고 그렇습니다.
그냥 TodakunTheme을 MaterialTheme {} 으로 감쌀까요?

관련 이슈
close #44
작업 내용
마이페이지 홈 화면 UI(Compose) 및 관련 ViewModel/상태/DI를 구현했다.
변경사항 / 상세
User도메인 모델에name필드 추가UserRepository/SajuRepository,GetUserUseCase/GetSajuPaljaUseCase/GetMyPageInfoUseCase구현@Fake리포지토리(FakeUserRepository,FakeSajuRepository) + Hilt DI 모듈 추가feature:mypage모듈 신설,MyPageHomeViewModel(Orbit MVI) 및 UI 상태(MyPageHomeUiState) 구현MyPageHomeScreen을ProfileCard,SajuPaljaGrid,SajuCard,MyPageMenuItem,MyPageHomeHeader등 컴포넌트로 분리MyPageMenuTypeenum + 반복문 기반으로 리팩터링core:designsystem의 string resource로 추출(다국어 대비)중점 리뷰사항
MyPageMenuTypeenum 기반 메뉴 렌더링 방식이 적절한지@Fake어노테이션을 UseCase 생성자에 직접 붙이는 임시 DI 방식(추후 실제 Repository로 교체 필요)core:designsystem에 둔 것이 컨벤션에 맞는지스크린샷 (선택)
Summary by CodeRabbit
릴리스 노트