Freezed & JSON Serialization 트러블슈팅
문제
- Freezed 적용 도중 part 'xxx.freezed.dart', part 'xxx.g.dart' 에러 발생
- fromJson(Map<String,Object>) 타입 불일치로 오류 (Map<String,dynamic> vs Map<String,Object>)
원인
- Freezed는 Map<String, dynamic> 형식이 일반적
- 타입 강제 지정 때문에 json_serializable이 매칭을 못 함
- part 파일 누락 시 build_runner가 생성 파일을 못 찾음
해결 방법
- factory ToDoModel.fromJson(Map<String, dynamic> json) 형태로 변경
- model 파일 상단에 정확히 아래 2개 추가:
part 'to_do_model.freezed.dart'; part 'to_do_model.g.dart';
- flutter pub run build_runner build --delete-conflicting-outputs 다시 실행
➡ Freezed는 ViewModel에서 모델을 copyWith로 불변 상태 관리할 때만 주로 사용하며, ViewModel 자체에는 필요 없음.
Repository 구조 & 역할 완전 이해
Repository에는 무엇이 들어가야 하는가?
- CRUD 메서드만
- 서버/DB와 통신 관련된 로직만
- ViewModel이나 View에서 쓰는 상태 로직은 포함 X
.set()의 정확한 의미
- 문서를 “덮어쓰기”
- SetOptions(merge: true)를 주면 해당 필드만 업데이트 가능
- Firebase Authentication과 Firestore 사용자 문서 연동할 때 매우 유용
실제 시나리오
로그인 후 Firestore users 컬렉션에 Auth 정보 덮어쓰기:
await db.collection('users').doc(uid).set({ 'uid': uid, 'createdAt': FieldValue.serverTimestamp(), }, SetOptions(merge: true));
Intro → SignUp 전체 플로우 설계 완성
IntroPage(View)
- Apple / Google 로그인 버튼 클릭
- 버튼은 IntroViewModel.loginWithGoogle() 호출
IntroViewModel
- Google/Apple 로그인 진행
- 로그인 성공 → UserRepository.setUser(uid) 호출
- Firestore에 기본 유저 필드 생성(merge)
- SignUpPage로 이동
SignUpPage(View)
- 이름 / 나이 / 성별 / 소개 입력 받음
- “프로필 등록" 클릭 → ViewModel or Repository 직접 호출
- UserRepository.updateProfile(uid) 사용하여 Firestore 업데이트
➡ 즉, Intro → 로그인 → 기본 유저 문서 생성 → SignUp → 입력값 저장 → 홈 이동 흐름 완성.
ThemeData 깊게 적용하기
seedColor 개념
- ColorScheme.fromSeed는 seedColor를 기준으로 전체 팔레트 자동 생성
- 단일 색을 기준으로 자연스러운 색 계열이 자동 생성됨
➡ seedColor는 앱의 **메인 브랜드 컬러(예: 0xFF983E24)**로 지정하는 것이 정석.
TextTheme 각 용도 정리
- display : 아주 큰 헤더 (스플래시·히어로 화면)
- headline : 큰 제목
- title : 일반 제목 (리스트 타이틀 등)
- body : 일반 본문 텍스트
- label : 버튼 텍스트, 작은 안내 텍스트
➡ 앱 화면의 “닉네임”은 titleMedium, 본문은 bodyMedium, 날짜는 bodySmall이 이상적.
BottomNavigationBar Theme 적용
iconSize는 직접 조절 불가 → iconTheme 사용해야 함
아래가 정석:
bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedIconTheme: IconThemeData(size: 27, color: Color(0xFF983E24)), unselectedIconTheme: IconThemeData(size: 27, color: Color(0xFF373737)), selectedLabelStyle: TextStyle(fontSize: 12, color: Color(0xFF983E24)), unselectedLabelStyle: TextStyle(fontSize: 12, color: Color(0xFF373737)), )
ColorScheme Extension 구현
컨테이너·텍스트필드·외곽선 색상 통일 관리 위해 ext 파일 생성.
color_scheme_ext.dart
extension CustomColorScheme on ColorScheme { Color get fieldBackground => const Color(0xFFFFFFFF); Color get borderDefault => const Color(0xFFD2D5DA); Color get borderFocus => const Color(0xFF983E24); }
사용 방법
Container( color: Theme.of(context).colorScheme.fieldBackground, child: Text("내용", style: Theme.of(context).textTheme.titleMedium), )
➡ View에서 매우 편하게 재사용 가능.
ElevatedButton & FAB 스타일링
ElevatedButton 전체 사이즈 지정
fixedSize: const Size(double.infinity, 70),
FAB는 원형이라 폭/높이 동일하게만 주면 됨
floatingActionButtonTheme: FloatingActionButtonThemeData( backgroundColor: Color(0xFF983E24), foregroundColor: Color(0xFFF2F2F2), sizeConstraints: BoxConstraints.tight(Size(64, 64)), )
Git 메시지 해석
“There are no staged changes…”
→ 스테이지에 올린 파일 없음
→ “변경된 파일을 전부 stage + commit 하겠냐?" 질문 메시지
오늘의 배움 요약
- Freezed 사용 시 타입, part 파일 위치가 중요함
- Repository는 오직 DB 통신 로직만 담당
- Auth → Firestore 사용자 문서 생성은 .set(merge: true)가 핵심
- MVVM 흐름(로그인→유저생성→프로필저장)을 완전히 이해
- ThemeData / ColorScheme Extension을 분리해 유지보수성 강화
- BottomNav / Buttons / FAB 등 Material Theme 시스템 이해
- Git stage/commit 흐름 이해