Skip to content

[#5] 아기 등록 화면 및 로직 구현#11

Open
jazz-ing wants to merge 18 commits intodevfrom
feature/5
Open

[#5] 아기 등록 화면 및 로직 구현#11
jazz-ing wants to merge 18 commits intodevfrom
feature/5

Conversation

@jazz-ing
Copy link
Copy Markdown
Collaborator

@jazz-ing jazz-ing commented Mar 23, 2026

📝 Summary

  • 로그인 이후 온보딩 플로우 구현
    • 아기 등록(CreateBabyFlow) 및 초대 코드 참여(JoinBabyFlow) 두 가지 경로 지원
    • 환영 → 아기 등록/초대코드 입력 → 아기 프로필 설정
  • Provider 기반 MVVM 아키텍처 적용: ChangeNotifierProvider로 ViewModel 주입, View는 상태 관찰만 수행

🔍 Changes

프로젝트 설정

  • go_router 패키지 추가 및 라우팅 설정(lib/routing/router.dart)
  • AppTextStyles 디자인 시스템 추가 (display ~ caption 스케일, bold/semibold/medium/regular 가중치)
  • AppColors에 error 색상 추가

Data Layer

  • BabyRegistrationRepository 인터페이스 정의: createBaby, verifyInviteCode, joinBabyByInviteCode
  • BabyRegistrationRepositoryImpl 구현: Supabase RPC 호출 (create_baby_with_owner, verify_invite_code, join_baby_by_invite_code)
  • BabySummary 모델: API 응답 파싱

Domain Layer

  • OnboardingFlow sealed class: CreateBabyFlow / JoinBabyFlow로 두 흐름을 타입 안전하게 표현
  • Gender, Relationship enum 추가

UI Layer (MVVM)

  • ₩WelcomeView: 온보딩 진입점. 아기 등록 방식 선택(아기 등록 or 초대 코드 )
  • BabyRegistrationView + BabyRegistrationViewModel: 아기 정보 입력 폼 (이름, 성별, 생년월일, 출산 예정일, 약관 동의)
  • InviteCodeView + InviteCodeViewModel: 초대 코드 입력 → ViewModel에서 검증 → 아기 확인 모달
  • BabyProfileSetupView + BabyProfileSetupViewModel: 공통 마지막 단계 (관계 선택, 닉네임 입력, Repository 호출)

DI 및 라우팅

  • main.dart에서 BabyRegistrationRepository를 Provider로 등록
  • router.dart에서 각 화면별 ChangeNotifierProvider로 ViewModel 주입
  • View는 context.watch로 상태 관찰, context.read로 액션 전달

🧪 Test

  • BabyRegistrationRepositoryImpl
    • createBaby: Service 성공(Ok) 시 babyId 반환, 실패(Error) 시 예외 전파
    • verifyInviteCode: 유효한 코드 시 BabySummary 반환, null 반환, 실패 시 예외 전파
    • joinBabyByInviteCode: Service 성공 시 babyId 반환, 실패 시 예외 전파
  • BabyRegistrationViewModel
    • 초기 상태 검증 (name, gender, birthDate 등 기본값)
    • 각 setter(setName, setGender, setBirthDate, setDueDate) 호출 시 값 갱신 및 리스너 알림
    • toggleAgreedToTerms 토글 동작 검증
    • isValid: 필수 필드(이름, 성별, 생년월일, 약관동의) 누락 시 false, 모두 입력 시 true, dueDate 선택사항 확인
  • InviteCodeViewModel
    • 초기 상태 검증
    • setCode, toggleAgreedToTerms 값 갱신 및 리스너 알림
    • isValid: 코드 미입력/공백/약관 미동의 시 false, 모두 충족 시 true
    • verifyInviteCode: 성공 시 verifiedBaby 설정, null 반환 처리, 실패 시 에러 메시지 설정, 로딩 상태 변화 추적, 로딩 중 isValid false, 재호출 시 이전 에러/결과 초기화
    • clearVerifiedBaby: verifiedBaby와 error 초기화 및 리스너 알림
  • BabyProfileSetupViewModel
    • 초기 상태 검증 (relationship, nickname, isLoading, error)
    • setRelationship, setNickname 값 갱신 및 리스너 알림
    • setNickname: 최대 길이 초과 시 무시, 최대 길이 정확히 허용
    • isValid: relationship 미선택/nickname 미입력/공백 시 false, 모두 입력 시 true
    • submit (CreateBabyFlow): createBaby 호출 및 babyId 반환, dueDate null 처리, 실패 시 에러 설정 및 예외 재전파
    • submit (JoinBabyFlow): joinBabyByInviteCode 호출 및 babyId 반환, 실패 시 에러 설정 및 예외 재전파
    • submit 로딩 상태: 리스너에 loading true→false 순서 알림, 로딩 중 isValid false

🔗 Related Issues

@jazz-ing jazz-ing requested a review from f-lab-joey March 23, 2026 10:42
@jazz-ing jazz-ing self-assigned this Mar 23, 2026
@jazz-ing jazz-ing added the feature New feature or request label Mar 23, 2026
jazz-ing and others added 6 commits March 30, 2026 10:09
… Supabase

BabyRegistrationRepositoryImpl이 SupabaseClient를 직접 의존하지 않도록
BabyRegistrationService 인터페이스를 도입하고 Supabase 구현을 분리

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
모든 뷰의 하드코딩된 문자열을 AppStrings로 중앙 관리하여
향후 i18n 적용 및 문자열 일관성 유지를 용이하게 함

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ucture

뷰에 흩어진 Color 리터럴을 AppColors로 통합하고
_Pink, _Violet 그룹 및 시맨틱 색상(textTertiary, brandAccent 등) 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
서비스 Ok/Error 응답에 따른 반환값 및 예외 발생 검증

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BabyRegistrationViewModel, InviteCodeViewModel, BabyProfileSetupViewModel의
상태 관리, 유효성 검증, 비동기 동작 및 에러 처리 테스트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jazz-ing jazz-ing marked this pull request as ready for review March 30, 2026 06:29
Comment on lines +15 to +16

Future<Result<String>> joinBabyByInviteCode({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

parameter 중 code를 재사용 할 필요없음. 아기의 id 로 조인하는거 고려해보기

required String nickname,
});

Future<Result<BabySummary?>> verifyInviteCode(String code);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

검증용 데이터 타입을 만들고 id 를 같이 전달 하면 어떨까요?

if (result == null) return Result.ok(null);

if (result is Map<String, dynamic>) {
return Result.ok(BabySummary.fromJson(result));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

from json이 실패할경우를 result 로 처리 혹은 최소 로그 추가 권장 드립니다.

return Result.ok(BabySummary.fromJson(result));
}

if (result is List && result.isNotEmpty) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

57 번 조건과, 72번 조건, 60번 조건중 return 될 데이터 타입에 맞춰서 하나만 사용.


return Result.ok(result.toString());
} on Exception catch (e) {
return Result.error(AppException('아기 참여에 실패했습니다.', cause: e));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

label로 해당 메세지를 사용한다면, presentation / data layer 책임 분리가 필요해보임

} catch (e) {
_error = '오류가 발생했습니다. 다시 시도해주세요.';
notifyListeners();
rethrow;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

exception 을 propagate 를 할 필요 있는지 확인

:final birthDate,
:final dueDate,
) =>
await _repository.createBaby(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

아기 등록과 유저 롤 설정 스탭을 분리
아기 등록 혹은 코드 등록시 유저와 아기를 매핑
로그인시 유저의 아기 id 확인 -> 아기가 있다면 역할 확인.

역할이 없다면 롤 설정으로 routing.

State<BabyProfileSetupView> createState() => _BabyProfileSetupViewState();
}

class _BabyProfileSetupViewState extends State<BabyProfileSetupView> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

stateful widget 제거

import 'package:yuktoe/onboarding/view_models/invite_code_view_model.dart';
import 'package:yuktoe/routing/router.dart';

class InviteCodeView extends StatefulWidget {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

stateful widget 필요한지 체크해보기

}

void _showBabyConfirmModal(BabySummary baby) {
final genderText = baby.gender == Gender.male ? AppStrings.maleLabel : AppStrings.femaleLabel;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

view model 로 presentation 로직 옮기기

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants