Repository navigation
refactor(onboarding): 에러 표시 채널을 UiState 가 미리 정하지 않게 하고 스낵바 칸 이름을 드러낸다 #1863
Description
2026-09-08 구현·후속 순서
PR #1963이 develop 위에서 OnboardingFailure 단일 사유와 화면 표시 결정을 먼저 구현한다. 기존 인라인·팝업·스낵바 문구와 소비를 보존하며, 늦은 스낵바 소비는 새 인라인/팝업 사유를 지우지 않는다. 현재 로컬 검증은 온보딩 단위 테스트 52개, 앱·스크린샷 Kotlin 컴파일, konsist 및 ktlint를 통과했다.
MVI #1802(PR #1825)와 비밀번호 찾기 배선 #1789(PR #1826)는 이 선행 모델을 소비하도록 적응한다. 후속 PR을 먼저 머지한 뒤 옛 병렬 오류 필드를 다시 고치는 순서를 피한다. #1863 자체 완료는 후속 MVI/배선 완료를 기다리지 않는다.
카카오 전환 #943은 외부 계정 승계 계약 대기이며, 기존 이메일 사용자 복구 경로를 유지해야 하므로 이 내부 실패 모델 정리의 선행으로 추가하지 않는다.
작업 유형
refactor — 동작 변경 없는 구조·책임·경계 개선
주 담당 모듈
onboarding — 온보딩
개요
온보딩 UiState 는 실패를 표시 채널별 칸으로 들고 있다 — errorMessage(스낵바) · hasVerificationError(필드 인라인) · isSocialSignUpAccount(차단 팝업). 여기서 두 가지가 나온다.
1. 이름이 계약을 말하지 않는다. errorMessage 는 "모든 에러의 문구" 로 읽히지만 실제로는 스낵바 전용 칸이다. 그 사실이 KDoc 에만 있다(@property errorMessage 팝업으로 가르지 않는 실패 — 스낵바로 표시). 그래서 "에러인데 왜 errorMessage = null 이냐" 가 코드를 읽는 사람의 첫 질문이 된다.
2. 채널 배타성을 손으로 지킨다. 한 채널을 켜면 다른 칸을 직접 비워야 한다. 지금 두 곳이 그렇게 하고 있다.
FindPasswordViewModel—current.copy(isSocialSignUpAccount = true, errorMessage = null)FindIdViewModel.verifyCode—it.copy(hasVerificationError = true, errorMessage = null)
빠뜨리면 팝업·인라인과 아직 소비되지 않은 옛 스낵바 문구가 겹쳐 뜬다. errorMessage 를 지우는 경로가 onErrorConsumed 하나뿐이고 updateEmail 도 손대지 않아, 이메일을 고쳐 재발송하는 것만으로 이전 실패 문구가 살아남는다.
선례가 이미 있다. 애프터노트 에디터는 같은 문제를 다르게 풀었다 — AfternoteEditorRoute.kt
val errorEvent = uiState.errorEvent
// 오류 하나는 정확히 한 채널로만 간다 — 검증 실패는 확인 팝업, 그 외 전부는 스낵바.
val validationMessage: String?
val snackbarMessage: String?UiState 는 사유(errorEvent) 하나만 싣고, 채널 분배는 Route 가 한 곳에서 한다. 그래서 둘 중 하나만 채워지는 것이 구조로 보장되고, 다른 칸을 비우는 손질 자체가 없다. 이름도 채널을 말한다.
완료 조건
- 온보딩 UiState 가 표시 채널이 아니라 실패 사유를 싣는다.
- 채널 분배(스낵바 / 필드 인라인 / 팝업)를 화면·Route 가 한 곳에서 한다.
- 다른 칸을 손으로 비우는 코드가 사라진다.
- 남는 표시용 칸이 있다면 이름이 채널을 말한다(
snackbarMessage등).
대상
FindPasswordUiState/ViewModel · FindIdUiState/ViewModel · SignUpUiState/ViewModel · LoginUiState/ViewModel
자식 이슈
No response
참고
- 이 결함은 온보딩 전용이다(실측). 다른 표시 플래그를 켜면서
errorMessage를 손으로 비우는 곳은 저장소 전체에 3곳이고 전부 온보딩이다 —FindPasswordViewModel(소셜 차단) ·FindIdViewModel.verifyCode(인증번호 무효) ·SignUpViewModel(인증번호 무효). 남의 모듈에 같은 결함을 남겨 두고 미루는 것이 아니다. val errorMessage를 든 UiState 는 저장소 전체에 15곳이지만 나머지는 표시 채널이 스낵바 하나뿐이라 배타성 문제가 성립하지 않는다(예:ReceiverRegisterUiState는isLoading+errorMessage둘뿐). 거기 나오는errorMessage = null은 새 시도를 시작하며 이전 에러를 지우는 정상 패턴이고, 이 이슈가 말하는 「채널을 켜며 다른 칸을 비우는」 손질과 다르다. 그 15곳이 공유하는 것은 이름이 채널을 안 말한다는 약한 축뿐이며, 이름만 바꾸는 일은 각 모듈 담당 몫이다.- 공통 오류 안내의 상황별 문구·액션·적용 범위를 PM에게 확인한다 #446 은 표시 형태(작업 실패 안내를 공통 오류 팝업으로) 축이고 범위가 core·afternote·receiver 다. 온보딩은 그 범위에 없고 축도 다르지만(이쪽은 채널 결정 위치와 이름), 나중에 공통 오류 안내의 상황별 문구·액션·적용 범위를 PM에게 확인한다 #446 이 온보딩까지 넓히면 여기서 만난다.
- fix(onboarding): 로그인 실패 표시가 시안과 불일치 — 문구·형태(스낵바↔필드 인라인), 네트워크 실패는 모달 #628 이 온보딩의 채널 분기(스낵바 ↔ 필드 인라인, 네트워크는 모달)를 시안대로 세운 이슈다. 이 이슈는 그 결과물의 구조 정리에 해당한다.
- 현행 손질에 이유를 적어 둔 주석은
FindPasswordViewModel(비밀번호 찾기 — 이메일 인증 후 비밀번호 재설정 #457 브랜치)과FindIdViewModel.verifyCode에 있다. FindPasswordUiState는 비밀번호 찾기 — 이메일 인증 후 비밀번호 재설정 #457 머지 전에는develop에 없다.
Activity
refactor(onboarding): 실패 사유와 표시 채널을 분리한다 (#1863)