๐ŸŒฑSeSac Flutter ๊ฐ•์˜_8/21 : Firebase

KiKi ManGoยท2026๋…„ 8์›” 27์ผ

SeSac_DartFlutter

๋ชฉ๋ก ๋ณด๊ธฐ
18/18
post-thumbnail

โœ”๏ธํ•™์Šต๋‚ด์šฉ ์š”์•ฝ

1. Firebase ์„ค์น˜ - 8/14 ์ง„ํ–‰

Firebase ์ค€๋น„

  • Firebase Console ์ ‘์† - https://console.firebase.google.com/
  • ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ(์•„๋ž˜ ์ด๋ฏธ์ง€ ๋นจ๊ฐ„ ๋ฐ•์Šค ํด๋ฆญ)
    • ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ ์ž‘์„ฑ
    • Firebase ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•œ AI ์ง€์›: [๊ณ„์†] ํด๋ฆญ
    • Firebase ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•œ Google ์• ๋„๋ฆฌํ‹ฑ์Šค: [๊ณ„์†] ํด๋ฆญ
    • Google ์• ๋„๋ฆฌํ‹ฑ์Šค ๊ตฌ์„ฑ: ๊ณ„์ • ์„ ํƒOR๋งŒ๋“ค๊ธฐ
    • Firebase ํ”„๋กœ์ ํŠธ๊ฐ€ ์ค€๋น„๋˜์—ˆ์Šต๋‹ˆ๋‹ค: [๊ณ„์†] ํด๋ฆญ

App์— Firebase Project ์—ฐ๊ฒฐํ•˜๊ธฐ

  • [์•ฑ ์ถ”๊ฐ€] ํด๋ฆญํ•˜๊ณ ,
  • ๋งจ ์™ผ์ชฝ์— Flutter ๋กœ๊ณ  ํด๋ฆญ!

Firebase CLI ์„ค์น˜ - Windows

  • Firebase CLI ์„ค์น˜ ๋ฐ ๋กœ๊ทธ์ธ
    (ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์•„๋ž˜ ๋ช…๋ น์–ด ์‹คํ–‰ํ•ด์„œ ๋ฌธ์ œ ์—†๋Š”์ง€ ํŒŒ์•…)
    1๏ธโƒฃ nvm-windows๋ฅผ ์„ค์น˜(nvm-setup.exe download)
    2๏ธโƒฃ nvm ํ™•์ธ- PowerShell์„ ์—ด๊ณ  nvm ์ˆ˜ํ–‰
    3๏ธโƒฃ nvm์„ ์ด์šฉํ•ด node์„ค์น˜ - nvm install 20.11.0 ์ž…๋ ฅ ํ›„ node-vโ†’ npm -v๋กœ ํ™•์ธ
    4๏ธโƒฃ Firebase CLI ์„ค์น˜ - npm install -g firebase-tools
    5๏ธโƒฃ PowerShell์„ ๊ด€๋ฆฌ์ž๋กœ ์‹คํ–‰ํ•ด์„œ ๋ณ€๊ฒฝ ๋™์˜ - Set-ExecutionPolicy RemoteSignedโ†’ [A] ๋ชจ๋‘ ์˜ˆ(A) ์ž…๋ ฅ
    6๏ธโƒฃ Firebase CLI login
firebase login:list 
firebase projects:list

  • FlutterFire CLI ์„ค์น˜
dart pub global activate flutterfire_cli
  • flutterfire ๋ช…๋ น์–ด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒฝ๊ณ ๊ฐ€ ๋‚˜์˜ค๋ฉด:
    โ–ช๏ธ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํŽธ์ง‘โ†’ ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ถ”๊ฐ€โ†’ path ํ•ญ๋ชฉ์— ๋‹ค์Œ ์ถ”๊ฐ€
    C:/Users/User/AppData/Local/Pub/Cache/bin
    โ–ช๏ธterminal ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๊ป๋‹ค๊ฐ€ ์žฌ์‹คํ–‰
    (bash ํ™˜๊ฒฝ)
    export PATH="$PATH:C:/Users/User/AppData/Local/Pub/Cache/bin"
    alias flutterfire=flutterfire.bat
    (PowerShell ํ™˜๊ฒฝ)
    $env:Path += ";$env:LOCALAPPDATA\Pub\Cache\bin"
  • flutterfire ๋ฒ„์ „ ์ž˜ ๋‚˜์˜ค๋ฉด ๋œ ๊ฒƒ!flutterfire --version

App์— Firebase Project ์—ฐ๊ฒฐ

  • ์•„๋ž˜ ๋ช…๋ น์–ด ๋„ฃ๊ณ , ENTER ๊ณ„์† ๋ˆ„๋ฆ„
flutterfire configure --project=[projectname]

Firebase ์ดˆ๊ธฐํ™” ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ถ”๊ฐ€

1๏ธโƒฃ ํ•„์š”ํ•œ package ์ถ”๊ฐ€ - firebase_core: ^4.13.0
2๏ธโƒฃ main.dart์— package ์ถ”๊ฐ€

import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
. . .//code ์ค„์ž„ ํ‘œ์‹œ


void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

3๏ธโƒฃ ์ฝ˜์†”๋กœ ์ด๋™ํ•˜๊ธฐ

platform ๋ฒ„์ „ ์˜ค๋ฅ˜ - Android

  • ํ”„๋กœ์ ํŠธ์˜ android/app/build.gradle.kts ํŒŒ์ผ๋กœ ๋“ค์–ด๊ฐ€์„œ
    minSdk = flutter.targetSdkVersionโ†’ minSdk = 23์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ์„ธ์š”.

2. Authentication ์„ค์ •

Firebase Authentication Enable

  • Firebase ์ฝ˜์†” ์™ผ์ชฝ [๋ณด์•ˆ]โ†’ [Authentication]โ†’ [์‹œ์ž‘ํ•˜๊ธฐ]

Package ์ถ”๊ฐ€ ํ›„ pub get์„ ์ˆ˜ํ–‰

#filename:pubspec.yaml
firebase_auth: ^6.5.7

firebase authentication ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ๋ชฉํ‘œ:

  • Login, Registration ๋“ฑ
  • ์•ž์œผ๋กœ 3~5๋กœ ํ•  ์˜ˆ์ •

3. Login Form

Login ์ฝ”๋“œ ๋„ฃ๊ธฐ (๊ตฌํ˜„ ์ƒ๋žต)

// filename: login_page.dart
//... code ์ค„์ž„ํ‘œ์‹œ

class LoginPage extends StatelessWidget {
  LoginPage({super.key});

  
  Widget build(BuildContext context) {
    TextTheme textTheme = Theme.of(context).textTheme;
    ColorScheme colorScheme = Theme.of(context).colorScheme;

    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                'My perfect workout mate',
                style: textTheme.titleMedium?.copyWith(
                  fontWeight: FontWeight.w700,
                  color: colorScheme.primary,
                ),
              ),
              Row(
                crossAxisAlignment: CrossAxisAlignment.end,
                children: [
                  Text(
                    'Workout\nTracker',
                    style: textTheme.displayLarge?.copyWith(
                      fontWeight: FontWeight.w700,
                      color: colorScheme.primary,
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.only(bottom: 10),
                    child: Image.asset(
                      'assets/runner_icon.png',
                      width: 32,
                      color: colorScheme.primary,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

TextFormField

  • ํ…์ŠคํŠธ ์ž…๋ ฅ ์œ„์ ฏ
    • ํ…์ŠคํŠธ ์ž…๋ ฅ ์‹œ ์ฃผ๋กœ TextField์™€ TextFormField ์‚ฌ์šฉ
    • ์—ฌ๋Ÿฌ ์ž…๋ ฅ๊ฐ’์„ ํ•˜๋‚˜์˜ ํผ์œผ๋กœ ๊ด€๋ฆฌํ•  ๋•Œ TextFormField ์‚ฌ์šฉ
  • TextFormField ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
    • ์ผ๋ฐ˜์ ์œผ๋กœ Form ์œ„์ ฏ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ
    • Form์— key๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ๋‚ด๋ถ€ TextFormField์˜ ์ƒํƒœ ์ถ”์ 
    • ๋“ฑ๋กํ•œ key๋ฅผ ํ†ตํ•œ ์ž…๋ ฅ๊ฐ’ ์ €์žฅ ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
  • ๋ณ€์ˆ˜ ์ •์˜
    • ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์„ ์œ„ํ•œ email, password ๋ณ€์ˆ˜ ์ •์˜
    • Form ๋‚ด๋ถ€ ์ž…๋ ฅ๊ฐ’ ์ €์žฅ ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ _formKey ์ •์˜
  • obscureText: true ํ”„๋กœํผํ‹ฐ : ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์‹œ ํ™”๋ฉด์— *๋กœ ๋ณด์ด๋„๋ก ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ
  • validator, onSaved ํ”„๋กœํผํ‹ฐ ํ™œ์šฉํ•ด์„œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ด€๋ฆฌ
    • TextFormField ์‚ฌ์šฉํ•˜๋Š” ์ด์œ : ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
    • _formKey.currentState?.validate()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฐ TextFormField์˜ validator ์‹คํ–‰
    • _formKey.currentState?.save()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ชจ๋“  TextFormField์˜ onSaved ์‹คํ–‰
    • onSaved๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’์„ email, password ๋ณ€์ˆ˜์— ํ• ๋‹น

currentState

  • GlobalKey์™€ ์—ฐ๊ฒฐ๋œ ์œ„์ ฏ์˜ ํ˜„์žฌ State ๊ฐ์ฒด
  • ํ•ด๋‹น ์œ„์ ฏ์˜ ์‹ค์ œ ์ƒํƒœ์™€ ๋ฉ”์„œ๋“œ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
  • FormState์˜ validate(), save(), reset() ๋“ฑ์˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๊ฐ€๋Šฅ
//filename: login_page.dart
//...code ์ค„์ž„ํ‘œ์‹œ
class LoginPage extends StatelessWidget {
  LoginPage({super.key});
  final _formKey = GlobalKey<FormState>();

  String? email;
  String? password;
  
  
  Widget build(BuildContext context) {
   . . .//code ์ค„์ž„ํ‘œ์‹œ
     Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Text(
                'My perfect workout mate',
                style: textTheme.titleMedium?.copyWith(. . .),
              ),
              Row(. . .),
              SizedBox(height: 50),
              TextFormField(
                style: TextStyle(color: Colors.black),
                decoration: InputDecoration(
                  labelText: 'Email',
                  labelStyle: TextStyle(
                  	color: Colors.black, 
                    fontWeight: FontWeight.w500,
                    ),
                  filled: true,
                  fillColor: Colors.grey.shade200,
                  hintText: 'example@example.com',
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(10),
                    borderSide: BorderSide.none,
                  ),
                  prefixIcon: Icon(Icons.email_outlined),
                ),
              ),
              SizedBox(height: 20),
              TextFormField(
                style: TextStyle(color: Colors.black),
                decoration: InputDecoration(
                  labelText: 'Password',
                  labelStyle: TextStyle(
                  	color: Colors.black, 
                    fontWeight: FontWeight.w500,
                    ),
                  filled: true,
                  fillColor: Colors.grey.shade200,
                  prefixIcon: Icon(Icons.lock_outline, color: Colors.black),
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(10),
                    borderSide: BorderSide.none,
                  ),
                ),
                obscureText: true,
                validator: (value){
                    if(value==null || value.isEmpty){
                      return 'ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”';
                    }
                    if(value.length < 6){
                      return '6์ž๋ฆฌ ์ด์ƒ ์ž…๋ ฅํ•˜์„ธ์š”';
                    } // firebase๊ฐ€ 6์ž๋ฆฌ ์ดํ•˜๋Š” ์ง€์›ํ•˜์ง€ ์•Š์Œ
                    return null;
                  },
                onSaved: (value){
                  password=value;
                },
              ),
              SizedBox(
                  width: double.infinity,
                  child: ElevatedButton(
                    onPressed: () {
                      if(_formKey.currentState?.validate() ?? false){
                        _formKey.currentState?.save();
                    
                      }
                    },
                    style: ElevatedButton.styleFrom(
                      padding: EdgeInsets.symmetric(vertical: 15),
                      backgroundColor: colorScheme.primary,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                    child: Text(
                      '๋กœ๊ทธ์ธ',
                      style: textTheme.titleLarge?.copyWith(
                        color: colorScheme.onPrimary,
                      ),
                    ),
                  ),
                ),
            ],
          ),
        ),
      ),
    );
  }
}

FittedBox

  • FittedBox
    • ์˜์—ญ์— ๋งž๊ฒŒ Text ์œ„์ ฏ์˜ ํฌ๊ธฐ ์ž๋™ ์กฐ์ ˆ
  • Flexible
    • ํ‚ค๋ณด๋“œ๊ฐ€ ์—ด๋ฆด ๋•Œ ๋กœ๊ณ ์™€ ํ…์ŠคํŠธ ์˜์—ญ์˜ ํฌ๊ธฐ ์ž๋™ ์กฐ์ ˆ
    • ํ™”๋ฉด ๊ณต๊ฐ„ ๋ถ€์กฑ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” overflow ๋ฐฉ์ง€
//filename: login_page.dart
//. . .code ์ค„์ž„ํ‘œ์‹œ
Flexible(
  child: FittedBox(
    fit: BoxFit.scaleDown,
    child: Text(
      'My perfect workout mate',
      style: textTheme.titleMedium?.copyWith(
        fontWeight: FontWeight.w700,
        color: colorScheme.primary,
      ),
    ),
  ),
),
Flexible(
  child: FittedBox(
    fit: BoxFit.scaleDown,
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.end,
      children: [
        Text(
          'Workout\nTracker',
          style: textTheme.displayLarge?.copyWith(
            fontWeight: FontWeight.w700,
            color: colorScheme.primary,
          ),
        ),
        Padding(
          padding: EdgeInsets.only(bottom: 10),
          child: Image.asset(
            'assets/runner_icon.png',
            width: 32,
            color: colorScheme.primary,
          ),
        ),
      ],
    ),
  ),
),
//. . .code ์ค„์ž„ํ‘œ์‹œ

4. Registration Form

  • registration_page.dart ํŒŒ์ผ์— RegistrationPage StatelessWidget ์ƒ์„ฑ
  • my_router.dart์— ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ์ถ”๊ฐ€
  • context.goํ™œ์šฉํ•ด์„œ login_page.dart์—์„œ Sign up ๋ฒ„ํŠผ ํด๋ฆญํ•˜๋ฉด RegistrationPage๋กœ ์ด๋™ํ•˜๊ฒŒ ์„ค์ •
  • ๋น„๋ฐ€๋ฒˆํ˜ธ field๋ฅผ ์ถ”๊ฐ€(+controller) : final TextEditingController _passwordController = TextEditingController();
  • ๋น„๋ฐ€๋ฒˆํ˜ธ field์— helperText๋ฅผ ํ†ตํ•ด ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ทœ์น™์„ ์„ค๋ช…
  • ๋‚˜๋จธ์ง€๋Š” LoginPage๋ž‘ ๋น„์Šทํ•˜๊ฒŒ ๊ตฌํ˜„์ด๋‹ˆ ์ฐธ๊ณ 
// filename: registration_page.dart
//  . . .code ์ค„์ž„ํ‘œ์‹œ
class RegistrationPage extends StatelessWidget {
  RegistrationPage({super.key});
  String? name;
  String? email;
  String? password;
  final TextEditingController _passwordController = TextEditingController();

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ํšŒ์›๊ฐ€์ž…'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child:     Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(. . . ),
          TextFormField(. . .),//์ด๋ฆ„ field
          TextFormField(. . .),//์ด๋ฉ”์ผ field
          TextFormField(
            controller: _passwordController,
            decoration: InputDecoration(
              labelText: '๋น„๋ฐ€๋ฒˆํ˜ธ',
              labelStyle:  textTheme.headlineSmall,
              hintText: '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”',
              helperText: '*๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 6์ž ์ด์ƒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.',
              helperStyle: textTheme.bodySmall?.copyWith(
                color: colorScheme.primary,
              ),
              border: UnderlineInputBorder(),
              floatingLabelBehavior: FloatingLabelBehavior.always,
            ),
            obscureText: true,
            validator: (value) {
              if (value == null || value.isEmpty) {
                return '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”';
              }
              if (value.length < 6) {
                return '๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 6์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค';
              }
              return null;
            },
            onSaved: (value) {
              password = value;
            },
          ),
        ],
      ),
    ),        
  }
}

5. Authentication Service

  • Firebase Authentication ๊ธฐ๋Šฅ
    • signUpWithEmail : ํšŒ์›๊ฐ€์ž…
    • signInWithEmail : ๋กœ๊ทธ์ธ
    • signOut : ๋กœ๊ทธ์•„์›ƒ
    • resetPassword : ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •
    • Profile : ์ˆ˜์ •
    • deleteAccount : ํƒˆํ‡ด

FirebaseAuthService

  • ํ•„์š”ํ•œ package ์ถ”๊ฐ€: firebase_auth: ^6.5.7
  • firebase_auth_service.dart ํŒŒ์ผ์— FirebaseAuthService class ์ •์˜
    • instance ์–ธ์–ด๋ฅผ kr ์„ค์ •: _auth.setLanguageCode('kr');
//filename:firebase_aut_service.dart
import 'package:firebase_auth/firebase_auth.dart';

class FirebaseAuthService {
  final FirebaseAuth _auth;
  FirebaseAuthService() : _auth = FirebaseAuth.instance {
    _auth.setLanguageCode('kr');
  }
  Future<void> signUpWithEmail() async {
    //ํšŒ์›๊ฐ€์ž… ์ฝ”๋“œ ์ž‘์„ฑ
  }

  Future<void> signInWithEmail() async {
    //๋กœ๊ทธ์ธ ์ฝ”๋“œ ์ž‘์„ฑ
  }

  Future<void> resetPassword(String email) async {
    // ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ์ฝ”๋“œ ์ž‘์„ฑ
  }

  Future<void> signOut() async {
    //๋กœ๊ทธ์•„์›ƒ ์ฝ”๋“œ ์ž‘์„ฑ
  }

  Future<void> deleteAccount() async {
    //๊ณ„์ •์‚ญ์ œ ์ฝ”๋“œ ์ž‘์„ฑ
  }
}

signUp

  • signUpWithEmail ํ•จ์ˆ˜
  • ์ด๋ฆ„,์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ parameter ์ •์˜
    • createUserWithEmailAndPassword method: ํšŒ์›๊ฐ€์ž… ์ฒ˜๋ฆฌ
    • updateDisplayName() method: ์ด๋ฆ„ ์ €์žฅ
    • sendEmailVerification method: ์ด๋ฉ”์ผ verification(์ž๋™์œผ๋กœ ์ด๋ฉ”์ผ ๋ฐœ์†ก)
//filename:firebase_auth_service.dart
//. . .code ์ค„์ž„ํ‘œ์‹œ
Future<void> signUpWithEmail({
  required String email,
  required String password,
  String? name,
}) async {
  await _auth.createUserWithEmailAndPassword(email: email, password: password);
  await _auth.currentUser?.updateDisplayName(name);
  await _auth.currentUser?.sendEmailVerification();
}

error handling

  • ์˜ˆ์™ธ์‚ฌํ•ญ์€ ์ฃผ๋กœ try-catch ๊ตฌ๋ฌธ์„ ํ†ตํ•ด handling
  • ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ์‚ฌํ•ญ:
    • ์ด๋ฏธ ๋™์ผํ•œ email์„ ๊ฐ€์ง„ ํšŒ์›์ด ์žˆ์Œ : email-already-in-use
    • password๊ฐ€ ๊ธฐ์ค€์— ๋ชป ๋ฏธ์นจ : weak-password
//filename:firebase_aut_service.dart
//. . .code ์ค„์ž„ํ‘œ์‹œ
  Future<void> signUpWithEmail({
    required String email,
    required String password,
    String? name,
  }) async {
    String? errorMessage;
    try {
      await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      await _auth.currentUser?.updateDisplayName(name);
      await _auth.currentUser?.sendEmailVerification();
    } on FirebaseAuthException catch (error) {
      switch (error.code) {
        case 'weak-password':
          errorMessage = '์ทจ์•ฝํ•œ ํŒจ์Šค์›Œ๋“œ์ž…๋‹ˆ๋‹ค. ์ตœ์†Œ 6์ž๋ฆฌ ์ด์ƒ์˜ ๋ฌธ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.';
          break;
        case 'email-already-in-use':
          errorMessage = '์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•˜์„ธ์š”.';
          break;
        case 'invalid-email':
          errorMessage = '์œ ํšจํ•˜์ง€ ์•Š์€ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.';
          break;
        default:
          errorMessage = error.message ?? '์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ';
      }
    } catch (error) {
      errorMessage = '์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ';
    }
    if (errorMessage != null) {
      throw Exception(errorMessage);
    }
  }

snackbar ํ•จ์ˆ˜ ์ƒ์„ฑ

  • show_snackbar.dart ์ž‘์„ฑํ•ด์„œ ํ•จ์ˆ˜๋กœ refactoring
// filename: show_snackbar.dart
import 'package:flutter/material.dart';

void showSnackBar(BuildContext context, String text) {
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text(text),
    ),
  );
}
  • showSnackBar ํ•จ์ˆ˜ ์ด์šฉํ•˜๋„๋ก ์ˆ˜์ •
// filename: registration_page.dart
import 'show_snackbar.dart';
	. . .//code์ค„์ž„ํ‘œ์‹œ
class RegistrationPage extends StatelessWidget {
	. . .//code์ค„์ž„ํ‘œ์‹œ
  final _auth = FirebaseAuthService();
  // ...code ์ค„์ž„ ํ‘œ์‹œ
  ElevatedButton(
    onPressed: () {
      if (_formKey.currentState!.validate()) {
        _formKey.currentState!.save();
        _auth.signUpWithEmail(
          email: email!,
          password: password!,
          name: name!,
        )
        .then((_) {
          showSnackBar(context, 'ํšŒ์›๊ฐ€์ž…์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
          context.go('/workout_home');
        })
        .catchError((error) {
          showSnackBar(context, error.toString());
        });
      }
    },
  ),
  // ...code ์ค„์ž„ ํ‘œ์‹œ

์ •์ƒ/๋น„์ •์ƒ ํšŒ์›๊ฐ€์ž… ์‹œ๋„

  • ์‚ฌ์šฉ์ž ์ถ”๊ฐ€๋œ ๊ฒƒ ํ™•์ธ: firebase authentication web consoleโ†’ Authenticationโ†’ User
  • ์ด๋ฏธ ๊ฐ€์ž…๋œ ์ด๋ฉ”์ผ๋กœ snackbar๋กœ ์—๋Ÿฌ๋ฉ”์„ธ์ง€ ๋‚˜์˜ค๋Š”์ง€ ํ™•์ธ

Login

  • signInWithEmail ๋ฉ”์†Œ๋“œ ์ž‘์„ฑ
  • signInWithEmailAndPassword() method๋กœ login์ ˆ์ฐจ ์ง„ํ–‰
    โ†’ await _auth.signInWithEmailAndPassword(email: email, password: password);

Logout

profile
๐Ÿฅญ ํญ๋„“์€ ๊ฒฝํ—˜์„ ์”จ์•— ์‚ผ์•„, ๊นŠ์ด๋ฅผ ๋‹ค์ง€๋ฉฐ ์ง€๊ธˆ ์ต์–ด๊ฐ€๋Š” ์ค‘

0๊ฐœ์˜ ๋Œ“๊ธ€