가입 수단 5종(이메일·Google·Apple·Kakao·LINE) · 로그인 에뮬레이터 연결 · 최소 지원 Android 10

This commit is contained in:
Sn-Kinos
2026-09-01 01:03:56 +09:00
parent 1d462ebe31
commit 304ad105d6
14 changed files with 770 additions and 54 deletions
+41
View File
@@ -1,3 +1,4 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
@@ -43,7 +44,32 @@ abstract final class FirebaseBoot {
/// 화면에 보여 줄 미투입 항목 이름. 내부 경로를 담지 않는다.
static const List<String> missingLabels = ['Firebase 설정'];
/// 에뮬레이터 호스트. `--dart-define=FANUZU_EMULATOR_HOST=127.0.0.1` 로 켠다.
///
/// **왜 두는가** — 실 Firebase 프로젝트가 열리기 전에도 로그인이
/// 실제로 도는지 확인할 수 있어야 한다.
///
/// 릴리스 빌드에서는 열리지 않는다. 배포된 앱이 로컬 호스트를 바라보면
/// 아무것도 동작하지 않고 원인도 보이지 않는다.
static const String emulatorHost =
String.fromEnvironment('FANUZU_EMULATOR_HOST');
static bool get useEmulator => emulatorHost.isNotEmpty && !kReleaseMode;
/// 에뮬레이터 전용 설정. 실 프로젝트 값이 아니며 비밀값도 아니다.
/// `demo-` 로 시작하는 프로젝트 ID 는 에뮬레이터가 외부 접속을 막는다.
static const FirebaseOptions _emulatorOptions = FirebaseOptions(
apiKey: 'demo-key',
appId: '1:0:android:0',
messagingSenderId: '0',
projectId: 'demo-fanuzu',
);
static Future<void> init() async {
if (useEmulator) {
await _initEmulator();
return;
}
try {
await Firebase.initializeApp();
_mode = FirebaseMode.live;
@@ -59,6 +85,21 @@ abstract final class FirebaseBoot {
if (_detail != null) debugPrint('[FirebaseBoot] detail :: $_detail');
}
static Future<void> _initEmulator() async {
try {
await Firebase.initializeApp(options: _emulatorOptions);
FirebaseAuth.instance.useAuthEmulator(emulatorHost, 9099);
_mode = FirebaseMode.live;
_reason = '로컬 에뮬레이터에 연결했습니다. 실 서버가 아닙니다.';
_detail = 'emulator host: $emulatorHost';
} on Object catch (error) {
_mode = FirebaseMode.mock;
_reason = '에뮬레이터에 연결하지 못해 임시(목) 모드로 동작 중입니다.';
_detail = '원인: $error';
}
debugPrint('[FirebaseBoot] mode=${_mode.name} :: $_reason');
}
/// 테스트에서 모드를 고정할 때만 쓴다.
@visibleForTesting
static void debugSetMode(FirebaseMode mode, {String reason = '테스트 지정'}) {
+7
View File
@@ -49,8 +49,15 @@ abstract final class AppColors {
abstract final class BrandColors {
static const kakao = Color(0xFFFEE500); // brand-kakao #FEE500
static const kakaoLabel = Color(0xFF000000); // brand-kakao-label #000000
// 네이버는 확정 가입 수단 5종에서 빠졌다(정책 §P-01 · 답변서 Q7). 토큰은
// 남겨 둔다 — 발주사께서 다시 넣으시면 그대로 쓴다.
static const naver = Color(0xFF03C75A); // brand-naver #03C75A
static const naverLabel = Color(0xFFFFFFFF); // brand-naver-label #FFFFFF
static const line = Color(0xFF06C755); // brand-line #06C755
static const lineLabel = Color(0xFFFFFFFF); // brand-line-label #FFFFFF
// 이메일은 브랜드가 없다. 앱 강조색을 그대로 쓴다.
static const email = AppColors.cosmicAccent;
static const emailLabel = AppColors.onCosmicAccent;
static const apple = Color(0xFF000000); // brand-apple #000000
static const appleLabel = Color(0xFFFFFFFF); // brand-apple-label #FFFFFF
static const appleOnlight = Color(0xFFFFFFFF); // brand-apple-onlight #FFFFFF