Skip to content

[Feature/#7] 회원가입 기능을 구현합니다.#20

Merged
wjdrjs00 merged 4 commits intodevelopfrom
feature/7-signup
Oct 26, 2025
Merged

[Feature/#7] 회원가입 기능을 구현합니다.#20
wjdrjs00 merged 4 commits intodevelopfrom
feature/7-signup

Conversation

@wjdrjs00
Copy link
Collaborator

@wjdrjs00 wjdrjs00 commented Oct 26, 2025

Related issue 🛠

Work Description ✏️

  • 회원가입 비즈니스 로직을 구현했습니다.
    • 명세가 확정되지 않아 구현하지 못한 로직은 추후 domain 레이어를 구현하면서 같이 수정할 예정입니다.
  • 앱 진입 부터 회원가입 완료까지의 화면 flow를 구현했습니다.
    • 스플래시 이후 가입 여부 확인 화면을 추가 구현했습니다.
    • 가입 완료 화면을 추가 구현했습니다.
    • 로그인 화면은 아직 디자인 작업이 진행되지 않아 따로 구현하지 않았습니다.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 온보딩 시작 시 회원가입과 로그인 선택지를 제공하는 새로운 랜딩 화면 추가
    • 회원가입 완료 후 역할 선택 화면으로 이동하는 전환 화면 추가
    • 회원가입 프로필과 전화번호 인증 화면에 향상된 폼 유효성 검사 및 자동 포커스 기능 적용
  • UI/UX 개선

    • 온보딩 네비게이션 구조 개선으로 더 직관적인 사용자 흐름 제공
    • 형 입력 시 향상된 키보드 동작 및 필드 간 포커스 전환 개선
    • 오류 메시지 및 입력 상태 표시 강화

@wjdrjs00 wjdrjs00 self-assigned this Oct 26, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 26, 2025

Walkthrough

회원가입 흐름 구현을 위해 AuthLanding 화면, SignUpSharedViewModel을 통한 공유 상태 관리, SignUpComplete 화면을 추가하고, SignUpProfile 및 SignUpPhoneAuth 화면을 리팩토링했습니다. 네비게이션 구조를 재정렬하고 테마 투명성을 활성화했습니다.

Changes

Cohort / File(s) 변경 요약
네비게이션 및 아키텍처
app/src/main/kotlin/com/moa/app/main/MainActivity.kt, core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt
NavBackStackEntry 확장 함수 추가하여 부모 back stack entry에서 공유 ViewModel 조회 활성화. SignIn → AuthLanding으로 라우트 이름 변경, SignUp/SignUpComplete/SelectUserRole 라우트 추가. 중첩 SignUp 네비게이션 그래프 구성.
AuthLanding 흐름
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingScreen.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingViewModel.kt
AuthLandingScreen과 AuthLandingViewModel 새로 추가. 회원가입/로그인 선택 UI 제공.
회원가입 공유 상태 관리
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpSharedViewModel.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/state/SignUpProfileUiState.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/state/SignUpPhoneAuthUiState.kt
회원가입 화면 간 상태를 공유하는 SignUpSharedViewModel 추가. 프로필 및 전화번호 인증 상태 클래스 정의.
프로필 입력 흐름
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileViewModel.kt
SignUpProfileScreen을 SignUpSharedViewModel 사용하도록 리팩토링. 지역 상태를 uiState 기반 콜백으로 변경. 포커스 관리 및 입력 흐름 향상. SignUpProfileViewModel 제거.
전화번호 인증 흐름
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpPhoneAuthScreen.kt
SignUpPhoneAuthScreen을 SignUpSharedViewModel 사용하도록 리팩토링. 상태 기반 UI로 변경하고 오류 처리 및 버튼 활성화 상태 통합.
회원가입 완료 흐름
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpCompleteScreen.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpCompleteViewModel.kt
SignUpCompleteScreen과 SignUpCompleteViewModel 추가. 1초 지연 후 SelectUserRole 화면으로 자동 전환.
기타 화면 및 테마
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashScreen.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashViewModel.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt, feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInViewModel.kt, app/src/main/res/values/themes.xml
SplashViewModel 초기 지연 2000ms → 1000ms로 단축, SignIn → AuthLanding 네비게이션 변경. SignInScreen/ViewModel 배경 제거 및 미완성 상태 유지. Theme.MoA에 windowIsTranslucent 추가.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Splash as SplashScreen
    participant AuthLanding as AuthLandingScreen
    participant SignUpProfile as SignUpProfileScreen
    participant SignUpPhoneAuth as SignUpPhoneAuthScreen
    participant SignUpComplete as SignUpCompleteScreen
    participant SelectRole as SelectUserRoleScreen
    participant ViewModel as SignUpSharedViewModel
    
    User->>Splash: 앱 시작
    Splash->>AuthLanding: 1초 후 이동
    User->>AuthLanding: "처음이에요" 클릭
    AuthLanding->>SignUpProfile: 회원가입 시작
    User->>SignUpProfile: 인적사항 입력
    SignUpProfile->>ViewModel: updateName/updateYear 등 호출
    User->>SignUpProfile: "다음" 클릭
    SignUpProfile->>SignUpPhoneAuth: 네비게이션
    ViewModel->>SignUpPhoneAuth: signUpPhoneAuthUiState 제공
    User->>SignUpPhoneAuth: 전화번호 입력
    SignUpPhoneAuth->>ViewModel: updatePhoneNumber 호출
    User->>SignUpPhoneAuth: 인증번호 요청
    SignUpPhoneAuth->>ViewModel: requestAuthCode 호출
    User->>SignUpPhoneAuth: 인증번호 입력 후 확인
    SignUpPhoneAuth->>ViewModel: verifyAuthCode 호출
    ViewModel->>SignUpComplete: 네비게이션
    SignUpComplete->>SelectRole: 1초 후 자동 이동
    User->>SelectRole: 역할 선택 (화면 표시)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주요 검토 대상:
    • SignUpSharedViewModel.kt: 상태 관리 로직, 코루틴 처리, 네비게이션 흐름 확인 필요
    • SignUpProfileScreen.kt & SignUpPhoneAuthScreen.kt: 리팩토링된 상태 기반 UI 로직, 콜백 연결 완성도 확인
    • MainActivity.ktsharedViewModel 확장 함수: 제네릭 타입 안정성 및 back stack 로직 검증
    • 네비게이션 구조: 중첩 그래프 및 라우트 매핑 일관성 확인

Possibly related PRs

Poem

🐰 회원가입 터널 통과하며,
공유 상태가 손을 맞잡고,
화면에서 화면으로 부드럽게 도약,
인증 확인과 역할 선택까지,
온보딩 흐름 완성이라 외친다!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.11% 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 제목 "[Feature/#7] 회원가입 기능을 구현합니다."는 변경 세트의 핵심 목표를 명확하게 전달합니다. 제목은 회원가입 기능 구현이라는 주요 변경 사항을 간결하고 구체적으로 요약하고 있으며, 파일 목록이나 모호한 표현 없이 명확한 의도를 드러내고 있습니다. 실제 변경 사항들(회원가입 화면, ViewModel, 네비게이션 구조)이 모두 제목에서 언급한 회원가입 기능 구현과 일치합니다.
Linked Issues Check ✅ Passed 연결된 이슈 #7의 세 가지 요구 사항이 모두 충족되었습니다. 회원가입 화면 내 공통 디자인 컴포넌트는 AuthLandingScreen, SignUpProfileScreen, SignUpPhoneAuthScreen에서 MaButton 및 테마 스타일링으로 구현되었고(Theme.xml 수정 포함), 인적사항 입력 UI는 SignUpProfileScreen에서 이름, 생년월일, 성별 필드로 구현되었으며, 전화번호 인증 UI는 SignUpPhoneAuthScreen에서 전화번호 및 인증 코드 입력으로 구현되었습니다. 또한 회원가입 완료 화면(SignUpCompleteScreen)과 공유 상태 관리(SignUpSharedViewModel)가 추가되어 완전한 회원가입 플로우가 완성되었습니다.
Out of Scope Changes Check ✅ Passed 모든 변경 사항이 회원가입 기능 구현과 새로운 네비게이션 플로우 구축에 명확하게 연결되어 있으며, 범위 외 변경 사항은 발견되지 않습니다. SplashScreen 및 SignInScreen의 배경 수정자 제거, SignInViewModel의 네비게이션 로직 변경, Theme.xml의 테마 업데이트 등은 모두 새로운 AuthLanding 기반 회원가입 플로우를 지원하기 위한 필수적인 구조 조정입니다. 나머지 변경 사항들(AppRoute 업데이트, MainActivity 네비게이션 설정, 새로운 ViewModel 및 UI 상태 클래스)은 모두 회원가입 기능 구현의 핵심 부분입니다.
Description Check ✅ Passed PR 설명이 저장소의 템플릿 요구 사항 중 필수 항목들을 충족하고 있습니다. 관련 이슈(#7) 및 작업 설명(회원가입 비즈니스 로직 구현, 화면 플로우 구현)이 명확하게 작성되어 있습니다. 템플릿의 스크린샷과 미완료 작업 항목이 누락되어 있으나, 핵심 정보인 이슈 참조와 작업 내용은 충분하고 명확하게 작성되었으며, 비제공 리소스와 디자인 미진행에 대한 언급도 포함되어 있어 작업 상태를 잘 전달하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/7-signup

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/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt (1)

64-83: 로그인 버튼의 빈 onClick 핸들러를 추적하세요.

"이미 계정이 있어요" 버튼의 onClick 핸들러가 비어있습니다. PR 설명에서 로그인 화면이 아직 구현되지 않았다고 명시되어 있지만, TODO 주석을 추가하여 향후 구현이 필요함을 명확히 표시하는 것이 좋습니다.

다음과 같이 TODO 주석을 추가하는 것을 제안합니다:

         MaButton(
-            onClick = { },
+            onClick = { 
+                // TODO: 로그인 화면 디자인 완료 후 네비게이션 구현 필요
+            },
             colors = MaButtonColors(
🧹 Nitpick comments (14)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashViewModel.kt (1)

24-24: 스플래시 딜레이 감소를 확인해주세요.

딜레이가 2000ms에서 1000ms로 절반으로 줄었습니다. 스플래시 화면 표시 시간이 충분한지, 브랜드/로고 표시 요구사항을 만족하는지 확인해주세요.

추가로, 유지보수성 향상을 위해 하드코딩된 딜레이 값을 상수로 추출하는 것을 고려해보세요:

+    companion object {
+        private const val SPLASH_DELAY_MILLIS = 1000L
+    }
+
     private fun navigateToAuthLanding() {
         viewModelScope.launch {
-            delay(1000L)
+            delay(SPLASH_DELAY_MILLIS)
             navigator.navigate(
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpCompleteViewModel.kt (1)

24-24: 딜레이 값을 상수로 추출하는 것을 고려하세요.

하드코딩된 딜레이 값(1000L)은 매직 넘버입니다. 의미를 명확히 하고 유지보수를 쉽게 하려면 상수로 추출하는 것이 좋습니다.

+    companion object {
+        private const val NAVIGATION_DELAY_MILLIS = 1000L
+    }
+
     private fun navigateToSelectUserRole() {
         viewModelScope.launch {
-            delay(1000L)
+            delay(NAVIGATION_DELAY_MILLIS)
             navigator.navigate(
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/state/SignUpProfileUiState.kt (2)

3-11: 날짜 필드 유효성 검증을 고려하세요.

year, month, day 필드가 모두 String 타입이고, isNextEnabled는 단순히 비어있지 않은지만 확인합니다. 이는 잘못된 날짜 값(예: "99월", "32일")도 통과시킬 수 있습니다.

도메인 레이어 구현 시 적절한 날짜 유효성 검증을 추가하거나, 현재 UI 레벨에서 숫자 형식 검증을 추가하는 것을 권장합니다.


8-8: gender 필드의 타입 안전성을 개선하는 것을 고려하세요.

gender 필드가 String 타입으로 정의되어 있어 오타나 잘못된 값이 할당될 수 있습니다. SignUpSharedViewModel에서 "male"/"female"을 하드코딩하고 있는 것을 볼 때, sealed class나 enum을 사용하면 타입 안전성을 높일 수 있습니다.

enum class Gender {
    MALE, FEMALE
}

data class SignUpProfileUiState(
    // ... 
    val gender: Gender?,
)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpSharedViewModel.kt (1)

45-51: gender 값을 상수로 추출하는 것을 고려하세요.

"male"과 "female" 문자열이 하드코딩되어 있습니다. SignUpProfileUiState에서 gender를 enum이나 sealed class로 변경하거나, 최소한 상수로 추출하여 오타 가능성을 줄이는 것이 좋습니다.

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingScreen.kt (1)

66-85: 보조 버튼 스타일을 디자인 시스템에 추가하는 것을 고려하세요.

로그인 버튼의 MaButtonColors 설정이 장황하고 반복적입니다. 투명 배경에 특정 텍스트 색상을 사용하는 보조(secondary) 버튼 스타일이 다른 곳에서도 필요하다면, MaButtonDefaultssecondaryButtonColors() 같은 함수를 추가하는 것이 재사용성과 일관성을 높일 수 있습니다.

// MaButtonDefaults.kt에 추가
@Composable
fun secondaryButtonColors() = MaButtonColors(
    defaultBackground = Color.Transparent,
    pressedBackground = Color.Transparent,
    disabledBackground = Color.Transparent,
    defaultContentColor = MoaTheme.colors.coolGray60,
    pressedContentColor = MoaTheme.colors.coolGray60,
    disabledContentColor = MoaTheme.colors.coolGray60,
)
app/src/main/kotlin/com/moa/app/main/MainActivity.kt (1)

56-56: AuthLanding로 대체했다면 남은 SignIn 라우트/임포트 제거 권장

PR 요약과 불일치 가능성. AppRoute.SignIn이 제거되었거나 숨겨졌다면 본 엔트리와 SignInScreen 임포트를 정리해 주세요. 남아 있으면 네비게이션 경로 충돌/사일런트 데드 코드가 됩니다.

가능하면 아래처럼 삭제해도 플로우에 영향 없습니다:

-                        composable<AppRoute.SignIn> { SignInScreen() }
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt (3)

144-148: 연/월 필드에도 IME Next 연결

길이(4/2) 도달 시 자동 이동은 좋지만, 사용자가 Next를 누른 경우도 대비하세요.

-                    keyboardOptions = KeyboardOptions(
+                    keyboardOptions = KeyboardOptions(
                         keyboardType = KeyboardType.Number,
                         imeAction = ImeAction.Next
                     ),
+                    keyboardActions = KeyboardActions(
+                        onNext = { monthFocusRequester.requestFocus() }
+                    ),
-                    keyboardOptions = KeyboardOptions(
+                    keyboardOptions = KeyboardOptions(
                         keyboardType = KeyboardType.Number,
                         imeAction = ImeAction.Next
                     ),
+                    keyboardActions = KeyboardActions(
+                        onNext = { dayFocusRequester.requestFocus() }
+                    ),

Also applies to: 169-173


223-237: 성별을 문자열로 비교하면 오타/변경에 취약 — 타입으로 모델링 권장

"male"/"female" 하드코딩은 오류 소지 큽니다. enum class 혹은 sealed class로 전환해 비교를 안전하게 하세요.

예:

enum class Gender { Male, Female }
val selected = uiState.gender == Gender.Female

78-101: 하드코딩 문자열을 string 리소스로 이전

다국어/접근성/테스트 용이성을 위해 문자열("회원가입", "이름", "생년월일", "성별", "다음" 등)은 R.string.*로 관리하세요.

Also applies to: 122-127, 211-216, 256-260

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpPhoneAuthScreen.kt (4)

122-126: alpha로 숨기면 레이아웃 공간이 남습니다 — 조건부 렌더링으로 전환

현재는 보이지 않아도 공간을 차지합니다. 요청 이후에만 그리도록 바꾸면 UX가 자연스럽습니다.

-            FormField(
-                isError = uiState.isAuthCodeError,
-                errorMessage = uiState.authCodeErrorMessage,
-                modifier = Modifier.alpha(if (uiState.isAuthCodeRequested) 1f else 0f)
-            ) {
+            if (uiState.isAuthCodeRequested) {
+                FormField(
+                    isError = uiState.isAuthCodeError,
+                    errorMessage = uiState.authCodeErrorMessage,
+                ) {
                 Text(
                     text = "인증번호",
                     ...
                 )
                 MaTextField(
                     value = uiState.authCode,
                     onValueChange = onChangeAuthNumber,
                     isError = uiState.isAuthCodeError,
                     ...
                 )
-            }
+                }
+            }

151-159: ‘인증 확인’ 활성화 조건 추가

요청 전이거나 4자리 미만일 때는 비활성화가 안전합니다. 간단 조건으로도 UX/방어성 향상됩니다.

-            MaButton(
-                onClick = onAuthConfirmClick,
+            MaButton(
+                onClick = onAuthConfirmClick,
+                enabled = uiState.isAuthCodeRequested && uiState.authCode.length == 4 && !uiState.isAuthCodeError,
                 modifier = Modifier

뷰모델에서 canConfirm 같은 파생 상태를 제공할 수 있다면 그 값을 쓰는 것이 더 깔끔합니다.


103-106: ‘인증’ 버튼도 번호 유효 시에만 활성화

요청 후 비활성만으로는 부족합니다. 번호 에러 상태일 땐 비활성화하여 불필요한 호출을 막으세요.

-                        MaButton(
-                            onClick = onAuthCodeRequestClick,
-                            enabled = !uiState.isAuthCodeRequested,
+                        MaButton(
+                            onClick = onAuthCodeRequestClick,
+                            enabled = !uiState.isAuthCodeRequested && !uiState.isPhoneNumberError && uiState.phoneNumber.isNotBlank(),

UI 로직이 과해지면 uiState.canRequestCode 파생값으로 이전하는 것을 권장드립니다.

Also applies to: 89-93


57-61: 초기 포커스 처리 적절

전화번호 입력에 자동 포커스 부여가 자연스럽습니다. 이후 코드 요청 성공 시 인증번호 필드로 포커스 이동도 고려해 보세요(viewModel 이벤트 기반).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc17bcc and 2a1de0f.

📒 Files selected for processing (17)
  • app/src/main/kotlin/com/moa/app/main/MainActivity.kt (2 hunks)
  • app/src/main/res/values/themes.xml (1 hunks)
  • core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingScreen.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingViewModel.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInViewModel.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpCompleteScreen.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpCompleteViewModel.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpPhoneAuthScreen.kt (7 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt (8 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpSharedViewModel.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileViewModel.kt (0 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/state/SignUpPhoneAuthUiState.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/state/SignUpProfileUiState.kt (1 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashScreen.kt (2 hunks)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashViewModel.kt (1 hunks)
💤 Files with no reviewable changes (1)
  • feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileViewModel.kt
🧰 Additional context used
🧬 Code graph analysis (4)
app/src/main/kotlin/com/moa/app/main/MainActivity.kt (2)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt (1)
  • SignUpProfileScreen (34-51)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpPhoneAuthScreen.kt (1)
  • SignUpPhoneAuthScreen (32-46)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingScreen.kt (1)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/button/MaButton.kt (1)
  • MaButton (30-68)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpPhoneAuthScreen.kt (2)
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/core/textfield/MaTextField.kt (1)
  • MaTextField (32-99)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt (3)
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/core/textfield/MaTextField.kt (1)
  • MaTextField (32-99)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/button/MaSelectButton.kt (1)
  • MaSelectButton (30-64)
🪛 detekt (1.23.8)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingViewModel.kt

[warning] 17-17: This empty block of code can be removed.

(detekt.empty-blocks.EmptyFunctionBlock)

🔇 Additional comments (13)
app/src/main/res/values/themes.xml (2)

4-6: AppCompat 기반 테마 계층 구조 개선.

Theme.AppCompat.Light.NoActionBar를 부모로 하는 기본 테마를 도입하고, 이를 상속하는 구조는 현대적인 Android 개발 모범 사례를 따릅니다. 이는 하위 API 레벨 호환성을 보장하면서도 유지보수성을 향상시킵니다.


7-7: 투명 윈도우 속성의 적용 범위와 성능 영향을 검증하세요.

android:windowIsTranslucent="true" 속성은 회원가입 흐름의 특정 UI 효과를 지원하기 위해 추가된 것으로 보입니다. 하지만 이 속성은 창 렌더링 성능에 영향을 미칠 수 있으며, 특히 저사양 기기에서 성능 저하를 야기할 수 있습니다. 또한 화면 전환 및 컴포지션에 영향을 줄 수 있습니다.

다음을 확인하세요:

  • 투명성이 인증 흐름(AuthLanding, SignUp 화면)에만 적용되는지 확인
  • 하위 API 레벨에서 렌더링 문제 또는 성능 저하가 없는지 테스트
  • 필요시 특정 화면에만 투명성을 적용하는 대안 고려
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashScreen.kt (2)

26-26: 테마 기반 배경으로 변경된 것을 확인했습니다.

명시적인 배경색 설정을 제거하고 테마에서 배경을 상속받도록 변경되었습니다. 이는 AppCompat 기반 테마 스타일링으로의 전환과 일치하는 것으로 보입니다.

스플래시 화면이 여전히 의도한 배경색(흰색)을 표시하는지 확인해 주세요.


37-37: 프리뷰 개선이 좋습니다.

showBackground = true 파라미터를 추가하여 명시적 배경 제거를 보완했습니다. 프리뷰에서 적절한 배경이 표시되도록 하는 좋은 개선입니다.

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt (2)

24-31: 화면 구조가 잘 설계되었습니다.

ViewModel 주입과 컨텐츠 컴포저블을 분리한 구조가 좋습니다. 테스트 가능성과 프리뷰 지원에 유리한 패턴입니다.


87-93: 프리뷰 개선이 좋습니다.

showBackground = true 추가는 Android Studio에서 프리뷰 시각화를 개선합니다. 개발자 경험을 향상시키는 좋은 변경입니다.

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashViewModel.kt (1)

19-19: 네비게이션 변경사항이 올바르게 구현되었습니다.

SignIn에서 AuthLanding으로의 라우트 변경이 함수명 리팩토링과 함께 일관되게 적용되었습니다. PR 목표에서 언급된 "회원가입 상태 확인을 위한 포스트 스플래시 화면 추가"와 잘 부합합니다.

Also applies to: 22-22, 26-26

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpCompleteViewModel.kt (1)

18-20: init 블록에서의 자동 네비게이션을 검증하세요.

ViewModel의 생성자(init 블록)에서 즉시 네비게이션 로직을 실행하는 것은 일반적이지 않은 패턴입니다. 이는 테스트가 어렵고, ViewModel이 생성되는 즉시 부수 효과가 발생합니다.

회원가입 완료 화면이 단순히 대기 화면이고 자동으로 다음 화면으로 전환하는 것이 의도된 UX라면 문제없지만, 일반적으로는 명시적인 트리거(예: LaunchedEffect)를 사용하는 것이 더 명확합니다.

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/landing/AuthLandingViewModel.kt (1)

9-18: LGTM!

네비게이션 로직이 명확하고 올바르게 구현되었습니다. onSignInClicked()의 빈 구현은 PR 설명에서 언급한 대로 로그인 화면 디자인이 보류 중이므로 의도된 플레이스홀더로 보입니다.

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/state/SignUpPhoneAuthUiState.kt (1)

3-23: LGTM!

UI 상태가 잘 구조화되어 있습니다. 전화번호와 인증 코드의 에러 상태 및 메시지를 명확하게 분리하여 관리하고 있어 유지보수가 용이합니다.

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

11-11: AuthLanding과 SignIn 라우트의 공존 목적을 확인하세요.

AuthLandingSignIn이 모두 정의되어 있습니다. PR 설명과 다른 파일들을 보면 AuthLanding이 새로운 진입점으로 보이는데, SignIn이 여전히 존재하는 이유를 확인해야 합니다.

만약 SignIn이 더 이상 사용되지 않는다면 제거하는 것이 좋고, 다른 목적(예: 로그인 화면)이 있다면 명확한 네이밍으로 구분하는 것이 혼란을 줄일 수 있습니다.

Also applies to: 26-26

feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpCompleteScreen.kt (1)

16-41: LGTM!

회원가입 완료 화면이 간결하게 구현되었습니다. ViewModel의 자동 네비게이션 로직은 SignUpCompleteViewModel.kt에서 처리되므로 화면 자체는 단순하게 로고만 표시하는 것이 적절합니다.

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

60-67: 중첩 그래프에서의 shared VM 사용 적절

부모 NavGraph 스코프 공유 ViewModel 바인딩 패턴이 올바르게 적용되어 있습니다. 두 화면 간 상태 공유/백스택 복원에 유리합니다.

중첩 그래프 루트(AppRoute.SignUp)가 항상 백스택에 남아 있도록 pop 동작을 점검해 주세요(부모 엔트리 소멸 시 VM 스코프도 종료).

Comment on lines +78 to +83
@Composable
internal inline fun <reified T : ViewModel> NavBackStackEntry.sharedViewModel(navController: NavController): T {
val navGraphRoute = destination.parent?.route ?: return hiltViewModel()
val parentEntry = remember(this) { navController.getBackStackEntry(navGraphRoute) }
return hiltViewModel(parentEntry)
}
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

parent BackStackEntry 미존재 시 크래시 예방 및 키 안정화

현재 parent가 없거나 이미 pop된 경우 getBackStackEntry가 예외를 던질 수 있습니다. 키도 remember(this) 보다 (navController, navGraphRoute)가 안전합니다. 폴백까지 포함한 방어코드 제안드립니다.

 @Composable
 internal inline fun <reified T : ViewModel> NavBackStackEntry.sharedViewModel(navController: NavController): T {
-    val navGraphRoute = destination.parent?.route ?: return hiltViewModel()
-    val parentEntry = remember(this) { navController.getBackStackEntry(navGraphRoute) }
-    return hiltViewModel(parentEntry)
+    val navGraphRoute = destination.parent?.route
+    if (navGraphRoute == null) return hiltViewModel()
+    val parentEntry = remember(navController, navGraphRoute) {
+        navController.getBackStackEntry(navGraphRoute)
+    }
+    return try {
+        hiltViewModel(parentEntry)
+    } catch (_: IllegalArgumentException) {
+        // 부모 그래프가 백스택에 없다면 화면 스코프로 폴백
+        hiltViewModel()
+    }
 }

또한 공용 유틸로 재사용될 수 있으니 navigation/NavigationExt.kt 등으로 분리 권장.

🤖 Prompt for AI Agents
In app/src/main/kotlin/com/moa/app/main/MainActivity.kt around lines 78 to 83,
the helper assumes destination.parent exists and getBackStackEntry always
succeeds and uses remember(this) as the key; to fix, guard against a null parent
and wrap getBackStackEntry in a safe call or try/catch (handle
IllegalArgumentException) so if the parent is missing or popped you return
hiltViewModel() as a fallback, and change the remember key to
remember(navController, navGraphRoute) for stability; finally extract this
function into a shared file such as navigation/NavigationExt.kt for reuse.

Comment on lines 103 to 111
MaTextField(
value = name,
onValueChange = { newValue -> name = newValue },
value = uiState.name,
onValueChange = onChangeName,
modifier = Modifier.focusRequester(nameFocusRequester),
maxLines = 1,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next
),
placeholder = {
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

IME Next 동작 연결 누락 — 다음 필드로 포커스 이동 추가

이름 필드에서 Next를 눌러도 포커스 이동이 보장되지 않습니다. KeyboardActions로 연도 필드로 이동을 명시하세요.

             MaTextField(
                 value = uiState.name,
                 onValueChange = onChangeName,
                 modifier = Modifier.focusRequester(nameFocusRequester),
                 maxLines = 1,
                 keyboardOptions = KeyboardOptions(
                     imeAction = ImeAction.Next
                 ),
+                keyboardActions = KeyboardActions(
+                    onNext = { yearFocusRequester.requestFocus() }
+                ),
                 placeholder = {
📝 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
MaTextField(
value = name,
onValueChange = { newValue -> name = newValue },
value = uiState.name,
onValueChange = onChangeName,
modifier = Modifier.focusRequester(nameFocusRequester),
maxLines = 1,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next
),
placeholder = {
MaTextField(
value = uiState.name,
onValueChange = onChangeName,
modifier = Modifier.focusRequester(nameFocusRequester),
maxLines = 1,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = { yearFocusRequester.requestFocus() }
),
placeholder = {
🤖 Prompt for AI Agents
In
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt
around lines 103 to 111, the MaTextField for the name input is missing
KeyboardActions to handle the IME Next action; add KeyboardActions(onNext = {
yearFocusRequester.requestFocus() }) to the name field and ensure you have a
FocusRequester instance for the year field (e.g., yearFocusRequester) and that
the year TextField uses modifier = Modifier.focusRequester(yearFocusRequester)
so pressing Next moves focus to the year field.

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] 회원가입 기능을 구현합니다.

1 participant