From 233ef998379812fee722c86c84fa795d1243c462 Mon Sep 17 00:00:00 2001 From: Sn-Kinos Date: Mon, 10 Aug 2026 01:02:03 +0900 Subject: [PATCH] =?UTF-8?q?Flutter=20=EC=95=B1=20=EA=B3=A8=EA=B2=A9=20?= =?UTF-8?q?=C2=B7=205=ED=83=AD=20=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=C2=B7=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=C2=B7=20Firestore=20=EB=B3=B4=EC=95=88=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=C2=B7=EC=9D=B8=EB=8D=B1=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/.gitignore | 45 + app/.metadata | 33 + app/README.md | 308 +++ app/analysis_options.yaml | 0 app/android/.gitignore | 14 + app/android/app/build.gradle.kts | 91 + app/android/app/src/debug/AndroidManifest.xml | 7 + app/android/app/src/main/AndroidManifest.xml | 45 + .../kr/co/sweech/fancake/MainActivity.kt | 5 + .../drawable-hdpi/ic_launcher_foreground.png | Bin 0 -> 6107 bytes .../drawable-mdpi/ic_launcher_foreground.png | Bin 0 -> 3232 bytes .../res/drawable-v21/launch_background.xml | 12 + .../drawable-xhdpi/ic_launcher_foreground.png | Bin 0 -> 9025 bytes .../ic_launcher_foreground.png | Bin 0 -> 18170 bytes .../ic_launcher_foreground.png | Bin 0 -> 31462 bytes .../main/res/drawable/launch_background.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 9 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 4915 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2237 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 7752 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 17865 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 30495 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/colors.xml | 7 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + app/android/build.gradle.kts | 24 + app/android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 5 + app/android/settings.gradle.kts | 30 + app/assets/brand/app-icon-foreground.png | Bin 0 -> 167034 bytes app/assets/brand/app-icon.png | Bin 0 -> 722888 bytes app/firebase/firebase.json | 11 + app/firebase/firestore.indexes.json | 488 ++++ app/firebase/firestore.rules | 630 +++++ app/firebase/test/.gitignore | 4 + app/firebase/test/README.md | 28 + app/firebase/test/package-lock.json | 2294 +++++++++++++++++ app/firebase/test/package.json | 24 + .../test/scripts/index-emulator-probe.mjs | 74 + .../test/scripts/negative-control.mjs | 41 + app/firebase/test/support/environment.mjs | 59 + .../test/tests/firestore.rules.test.mjs | 700 +++++ app/firebase/test/vitest.config.mjs | 11 + app/ios/.gitignore | 34 + app/ios/Flutter/AppFrameworkInfo.plist | 24 + app/ios/Flutter/Debug.xcconfig | 2 + app/ios/Flutter/Release.xcconfig | 2 + app/ios/Podfile | 43 + app/ios/Podfile.lock | 159 ++ app/ios/Runner.xcodeproj/project.pbxproj | 753 ++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 101 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + app/ios/Runner/AppDelegate.swift | 16 + .../AppIcon.appiconset/Contents.json | 1 + .../Icon-App-1024x1024@1x.png | Bin 0 -> 713485 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 591 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1536 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 3019 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1011 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 2833 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 5780 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1536 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 5026 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 10676 bytes .../AppIcon.appiconset/Icon-App-50x50@1x.png | Bin 0 -> 2198 bytes .../AppIcon.appiconset/Icon-App-50x50@2x.png | Bin 0 -> 7386 bytes .../AppIcon.appiconset/Icon-App-57x57@1x.png | Bin 0 -> 2696 bytes .../AppIcon.appiconset/Icon-App-57x57@2x.png | Bin 0 -> 9557 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 10676 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 23877 bytes .../AppIcon.appiconset/Icon-App-72x72@1x.png | Bin 0 -> 4098 bytes .../AppIcon.appiconset/Icon-App-72x72@2x.png | Bin 0 -> 15547 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 4539 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 17193 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 20558 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 38 + app/ios/Runner/Base.lproj/Main.storyboard | 26 + app/ios/Runner/Info.plist | 70 + app/ios/Runner/Runner-Bridging-Header.h | 1 + app/ios/Runner/SceneDelegate.swift | 6 + app/ios/RunnerTests/RunnerTests.swift | 12 + app/lib/app.dart | 31 + app/lib/core/firebase/firebase_boot.dart | 69 + app/lib/core/router/app_router.dart | 109 + app/lib/core/theme/app_theme.dart | 105 + app/lib/core/theme/tokens.dart | 169 ++ app/lib/core/ui/tab_placeholder.dart | 66 + app/lib/features/auth/auth.dart | 102 + app/lib/features/auth/auth_repository.dart | 80 + app/lib/features/auth/login_screen.dart | 178 ++ app/lib/features/auth/social_providers.dart | 151 ++ app/lib/features/galaxy/galaxy_screen.dart | 16 + app/lib/features/home/home_screen.dart | 16 + app/lib/features/inside/inside_screen.dart | 16 + app/lib/features/my/my_screen.dart | 16 + app/lib/features/station/station_screen.dart | 16 + app/lib/main.dart | 12 + app/pubspec.lock | 490 ++++ app/pubspec.yaml | 69 + app/test/app_smoke_test.dart | 163 ++ app/tool/gen_tokens.py | 236 ++ design/design-tokens.md | 20 +- 113 files changed, 8549 insertions(+), 2 deletions(-) create mode 100644 app/.gitignore create mode 100644 app/.metadata create mode 100644 app/README.md create mode 100644 app/analysis_options.yaml create mode 100644 app/android/.gitignore create mode 100644 app/android/app/build.gradle.kts create mode 100644 app/android/app/src/debug/AndroidManifest.xml create mode 100644 app/android/app/src/main/AndroidManifest.xml create mode 100644 app/android/app/src/main/kotlin/kr/co/sweech/fancake/MainActivity.kt create mode 100644 app/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png create mode 100644 app/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png create mode 100644 app/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 app/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png create mode 100644 app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png create mode 100644 app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png create mode 100644 app/android/app/src/main/res/drawable/launch_background.xml create mode 100644 app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/android/app/src/main/res/values-night/styles.xml create mode 100644 app/android/app/src/main/res/values/colors.xml create mode 100644 app/android/app/src/main/res/values/styles.xml create mode 100644 app/android/app/src/profile/AndroidManifest.xml create mode 100644 app/android/build.gradle.kts create mode 100644 app/android/gradle.properties create mode 100644 app/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 app/android/settings.gradle.kts create mode 100644 app/assets/brand/app-icon-foreground.png create mode 100644 app/assets/brand/app-icon.png create mode 100644 app/firebase/firebase.json create mode 100644 app/firebase/firestore.indexes.json create mode 100644 app/firebase/firestore.rules create mode 100644 app/firebase/test/.gitignore create mode 100644 app/firebase/test/README.md create mode 100644 app/firebase/test/package-lock.json create mode 100644 app/firebase/test/package.json create mode 100644 app/firebase/test/scripts/index-emulator-probe.mjs create mode 100644 app/firebase/test/scripts/negative-control.mjs create mode 100644 app/firebase/test/support/environment.mjs create mode 100644 app/firebase/test/tests/firestore.rules.test.mjs create mode 100644 app/firebase/test/vitest.config.mjs create mode 100644 app/ios/.gitignore create mode 100644 app/ios/Flutter/AppFrameworkInfo.plist create mode 100644 app/ios/Flutter/Debug.xcconfig create mode 100644 app/ios/Flutter/Release.xcconfig create mode 100644 app/ios/Podfile create mode 100644 app/ios/Podfile.lock create mode 100644 app/ios/Runner.xcodeproj/project.pbxproj create mode 100644 app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 app/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 app/ios/Runner/AppDelegate.swift create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 app/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 app/ios/Runner/Base.lproj/Main.storyboard create mode 100644 app/ios/Runner/Info.plist create mode 100644 app/ios/Runner/Runner-Bridging-Header.h create mode 100644 app/ios/Runner/SceneDelegate.swift create mode 100644 app/ios/RunnerTests/RunnerTests.swift create mode 100644 app/lib/app.dart create mode 100644 app/lib/core/firebase/firebase_boot.dart create mode 100644 app/lib/core/router/app_router.dart create mode 100644 app/lib/core/theme/app_theme.dart create mode 100644 app/lib/core/theme/tokens.dart create mode 100644 app/lib/core/ui/tab_placeholder.dart create mode 100644 app/lib/features/auth/auth.dart create mode 100644 app/lib/features/auth/auth_repository.dart create mode 100644 app/lib/features/auth/login_screen.dart create mode 100644 app/lib/features/auth/social_providers.dart create mode 100644 app/lib/features/galaxy/galaxy_screen.dart create mode 100644 app/lib/features/home/home_screen.dart create mode 100644 app/lib/features/inside/inside_screen.dart create mode 100644 app/lib/features/my/my_screen.dart create mode 100644 app/lib/features/station/station_screen.dart create mode 100644 app/lib/main.dart create mode 100644 app/pubspec.lock create mode 100644 app/pubspec.yaml create mode 100644 app/test/app_smoke_test.dart create mode 100644 app/tool/gen_tokens.py diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..3820a95 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/app/.metadata b/app/.metadata new file mode 100644 index 0000000..cc2cda6 --- /dev/null +++ b/app/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "cc0734ac716fbb8b90f3f9db8020958b1553afa7" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7 + base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7 + - platform: android + create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7 + base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7 + - platform: ios + create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7 + base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..49c6de4 --- /dev/null +++ b/app/README.md @@ -0,0 +1,308 @@ +# Fancake 앱 골격 (Flutter) + +> 2주차 산출물 · 발주사 (주)팬케이크 / 수행사 스위치 주식회사(Sweech) +> 별첨2 2주차 「첫 빌드(앱 골격)」의 빌드 포함 범위 = **Flutter 앱 골격 · 5탭 네비게이션 · 소셜 로그인 · 디자인시스템** + +## 1. 이 골격의 범위 + +| | 내용 | +|---|---| +| **포함** | 앱 기동 · 코스믹 다크 테마(design-tokens.md 생성) · 5탭 셸 네비게이션 · 로그인 화면(S-A-LOGIN) 4종 소셜 버튼 · 인증 인터페이스와 Google/Apple 실 코드 경로 · Firebase 초기화(미설정 시 목 모드) | +| **미포함** | 각 탭의 실제 화면(3주차 이후) · 온보딩 나머지 6화면(S-A-INTRO · S-A-TERMS · S-A-PROFILE · S-A-ARTIST · S-A-PLANET-NEW · S-A-PERMS) · 도메인 로직(POP·FPI·퀘스트·이클립스) · 상태관리 라이브러리 | + +미포함 항목을 지금 만들지 않은 이유: **RN/Flutter 승인이 아직 없다(2주차_확인요청항목 Q1).** +미승인 시 폐기할 수 있도록 골격과 도메인 로직을 섞지 않았다. 5탭 라벨·화면 ID·토큰처럼 +언어와 무관한 계약 정보만 코드에 들어가 있다. + +## 2. 빌드·실행 + +전제: Flutter stable 3.41.7 / Dart 3.11.5 (검증에 쓴 버전), Android SDK 36, Xcode 26.6 + +```bash +cd app +flutter pub get +flutter run # 실기기·시뮬레이터 +flutter analyze # 정적 분석 +flutter test # 스모크 테스트 3종 +flutter build apk --debug # Android 디버그 APK +``` + +빌드 산출물은 `build/app/outputs/flutter-apk/app-debug.apk` 에 떨어진다. + +> ⚠️ **`pubspec.yaml` 의 의존성을 추가·변경·업그레이드하면 최소 지원 OS 하한을 재계산할 것.** +> 절차: `주간보고/2주차/src/2주차_플러그인_선검증리포트.md` §3.5 +> +> 최소 지원 OS 는 **별첨1 v1.0 에 계약값으로 박힌다**(견적서 1228행). 지킬 수 없는 하한을 +> 계약에 적으면 그 아래 단말의 장애가 개발사 책임이 된다. 선검증 리포트의 값은 +> 2026-08-08 시점 측정치이므로, 이 앱의 의존성이 바뀌면 그 값은 그 순간 거짓이 된다. +> +> 2026-08-08 기준 합집합: **Android API 29 · iOS 15.0** +> (이 프로젝트 단독값은 Android API 24 · iOS 15.0 — Android 29 는 `geofence_foreground_service`, +> iOS 15.0 은 `firebase_auth` 가 올린 값이다. 퀘스트 플러그인이 들어오는 6주차 전에 `minSdk` 를 29 로 올려야 한다.) + +## 3. 디자인 토큰 — 손으로 옮기지 않는다 + +`lib/core/theme/tokens.dart` 는 **생성물**이다. 직접 고치면 시안(`시안/assets/ds.css`)과 어긋난다. + +```bash +cd app +python3 tool/gen_tokens.py # PyYAML 필요: pip install pyyaml +``` + +* 입력: `design/design-tokens.md` 의 `` 아래 yaml 블록 (SoT) +* 출력: `lib/core/theme/tokens.dart` +* 부수 검증: `design-tokens.md` 의 `contrast_checks` 22쌍을 WCAG 로 재계산해 표로 출력하고, + **AA(4.5:1) 미달이 하나라도 있으면 exit 1**. 테마가 대비를 깎았는지 여기서 걸린다. +* `lib/core/theme/app_theme.dart` 가 이 토큰으로 `ThemeData` 를 조립한다. 색·크기를 여기서 새로 만들지 마라. + +**생성하지 않는 토큰** — CSS 전용 표현이라 Flutter 등가물이 없다: +`cosmic_fx.glow-*` · `grad-*` · `nebula` · `starfield` · `elevation.*`. +필요해지는 주차에 `tool/gen_tokens.py` 헤더의 목록에서 하나씩 내려서 추가한다. + +## 4. Firebase — 지금은 목(mock) 모드 + +`google-services.json` / `GoogleService-Info.plist` 가 아직 없다. **가짜 파일을 만들어 두지 않았다.** + +`lib/core/firebase/firebase_boot.dart` 가 `Firebase.initializeApp()` 을 시도하고, +실패하면 `FirebaseMode.mock` 으로 내려간 뒤 사유를 로그(`[FirebaseBoot] mode=mock :: ...`)로 남긴다. +앱은 정상적으로 뜬다. + +파일이 투입되면 **코드 수정 없이** `FirebaseMode.live` 로 올라가고, 목 모드 전용 UI는 자동으로 사라진다. + +### 목 모드에서의 로그인 + +Firebase 설정이 없으면 실 소셜 로그인이 물리적으로 불가능하다. 그런데 별첨2 2주차 발주사 확인 +항목에 「앱 설치·**로그인**·탭 이동 확인」이 있다. 그래서 로그인 화면 하단에 +**「목 세션으로 탭 둘러보기」** 입구를 목 모드에서만 노출한다. + +* 화면에 목 모드임과 그 사유가 함께 표시된다 — 실 로그인으로 오인할 여지가 없다. +* `FirebaseBoot.reason` 은 사용자에게 보이는 문자열이라 **내부 파일 경로를 넣지 않는다.** + 경로와 원시 예외는 `FirebaseBoot.detail` 로 분리해 로그에만 남긴다. + +**가드가 두 겹인 이유 (교차검증 A-01 반영)** + +```dart +bool get isMockSessionAvailable => !FirebaseBoot.isLive && !_releaseMode; +``` + +처음에는 `!FirebaseBoot.isLive` 한 겹만 두고 "설정 파일이 들어오면 자동으로 막힌다" 고 적었는데, +**그 설명은 틀렸다.** `FirebaseBoot.init()` 은 모든 예외를 삼키고 목 모드로 내려가므로, +설정 파일이 정상 투입된 릴리스 빌드라도 초기화가 어떤 이유로든 실패하면(플랫폼 초기화 실패, +리소스 패키징 오류, 기기 환경) 목 모드가 되고 우회 입구가 배포 앱에 노출된다. + +그래서 빌드 모드로 한 겹 더 막았다. **릴리스 빌드에서는 어떤 경우에도 열리지 않는다.** +`kReleaseMode` 는 컴파일 상수라 테스트에서 뒤집을 수 없어, `AuthRepository({bool? releaseMode})` +로 주입 지점을 열어 두고 릴리스 동작을 테스트로 고정했다(회귀 가드 4종). +가드를 지우면 테스트 3개가 깨진다 — 확인했다. + +## 5. 미투입 항목 + +### 5-1. 스위치(개발사) 내부 조치 — 발주사 요청 사항 아님 + +| ID | 항목 | 투입 위치 | 없으면 | +|---|---|---|---| +| S1 | Firebase 프로젝트(dev/prod) 개설 | — | 아래 S2 를 만들 수 없다 | +| S2 | `google-services.json` | `app/android/app/google-services.json` | 목 모드로 기동. Google 로그인 차단 | +| S2 | `GoogleService-Info.plist` | `app/ios/Runner/GoogleService-Info.plist` | 목 모드로 기동. Google·Apple 로그인 차단 | + +> S2 투입 시 Android 는 `android/settings.gradle.kts` 에 `com.google.gms.google-services` 플러그인 +> 추가가 함께 필요하다. 지금은 설정 파일이 없어 플러그인을 적용하지 않았다 — +> 적용한 채로 파일이 없으면 Gradle 빌드 자체가 실패한다. + +### 5-2. 발주사 확인 필요 + +| ID | 항목 | 투입 위치 | 없으면 | +|---|---|---|---| +| Q3 | 카카오 네이티브 앱 키 | 미정(플러그인 도입 시 확정) | 카카오 버튼이 「키 미투입」 안내를 반환 | +| Q3 | 네이버 client ID / secret | 미정 | 네이버 버튼이 「키 미투입」 안내를 반환 | +| Q5 | 스토어 등록 명의 | — | 3주차 TestFlight·Play 내부트랙 제출 불가 | +| Q14 | 앱 식별자 확정 | `android/app/build.gradle.kts` · `ios/Runner.xcodeproj` | 현재 `kr.co.sweech.fancake` 로 잠정. **스토어 등록 후에는 변경 불가** | +| Q6 | 확정 로고 원본(벡터·알파 PNG) | `assets/brand/` | 로그인 화면 워드마크를 텍스트로 대체 중 | +| Q15 | 하단 탭 아이콘 자산 | `assets/icons/` | Material 기본 아이콘 5종으로 잠정 | + +### 5-3. 폰트 + +폰트 파일이 리포에 없다. `pubspec.yaml` 의 `fonts:` 블록이 **주석 처리된 채로** 투입 위치만 표시돼 있다. + +| 용도 | 패밀리 | 투입 위치 | 출처 | +|---|---|---|---| +| 제목 | Gmarket Sans | `assets/fonts/GmarketSansTTF{Light,Medium,Bold}.ttf` | webfontworld/gmarket | +| 본문 | Pretendard Variable | `assets/fonts/PretendardVariable.ttf` | orioncactus/pretendard | +| 수치 | JetBrains Mono | `assets/fonts/JetBrainsMono-Regular.ttf` | JetBrains | + +미투입 상태에서는 **시스템 폰트로 폴백**된다. 크기·굵기·행간·자간은 토큰 값 그대로 적용되므로 +투입 전후 레이아웃 차이는 자소 폭 수준에 그친다. + +## 6. 소셜 로그인 상태 + +화면명세 `S-A-LOGIN.md` 와 시안이 4종(카카오·네이버·Apple·Google)이고, 버튼 순서·라벨을 그대로 옮겼다. + +| 제공자 | 상태 | 구현 | 막고 있는 것 | +|---|---|---|---| +| 카카오 | 스텁 | `KeylessSocialProvider` | 앱 키(Q3) | +| 네이버 | 스텁 | `KeylessSocialProvider` | 앱 키(Q3) | +| Apple | 실 코드 경로 | `AppleSignInProvider` | Firebase 설정(S2) + Xcode Capability | +| Google | 실 코드 경로 | `GoogleSignInProvider` | Firebase 설정(S2) | + +설계 원칙: **「키 미투입」은 예외가 아니라 상태다.** 예외를 던지지 않고 +`AuthNotConfigured(provider, missing)` 를 반환해 "무엇이 없어서 못 쓰는지"를 화면에 그대로 보여 준다. + +**이메일 가입은 구현하지 않았다.** CD-18 미확정(Q2)이라 `SocialAuthProvider` 인터페이스에 +구현체 하나가 더 붙을 자리만 비워 뒀다. **기능 플래그 상수는 두지 않았다** — 읽는 분기가 없는 +플래그는 "false 라서 닫혀 있다" 는 착각만 만들고, 누가 true 로 바꿔도 아무 일이 일어나지 않는다 +(교차검증 A-02). 지금 이메일을 닫고 있는 것은 `AuthRepository._defaultProviders()` 에 +구현체가 없다는 사실 하나다. + +## 7. 계약 고정 표기 — 5탭 + +``` +HOME · INSIDE · GALAXY · STATION · MY +``` + +근거 3개가 서로 일치한다: + +| 근거 | 위치 | +|---|---| +| 견적서 v2.1 「5탭 네비게이션」 | `Docs/Fancake_팬덤플랫폼_견적서.html` | +| 크래프트 게이트 C15 | `design/craft-allowlist.yaml` → `APP_TABS` | +| 시안 하단 탭바 | `시안/screens/S-{H-HOME,I-LOUNGE,G-PLANET,S-HUB,M-HOME}.html` 의 `