Skip to content

에러 핸들링

에러핸들링과 Exception 은 서로 밀접한 관계가 있다.

앱을 개발하면서 여러가지 형태의 에러(Exception)이 발생하게 된다. 그리고 Fireflutter 에서 제공하는 위젯 또는 로직에서 FireFlutterException 을 발생 시키기도 한다. 이러한 Exception 들에 핸들링을 해 주어야하는데, 때로는 필요한 곳에서 적절히 핸들링되지 못하는 경우가 종종 발생한다. 이렇게 핸들링되지 않은 Exception 들을 모아서, runZoneGuarded 방식으로 관리를 하면 된다. 이렇게 에러를 핸들링하는 방법은 FireFlutter 를 사용할 때, 권장하는 방법이며 이를 Global Error Handler 라고 부른다. 물론, 가능하면 에러는 적절한 곳에서 핸들링하는 것이 좋겠다.

아래의 예제는 GlobalKey 에 BuildContext 를 담아서 쓰는 예제이다.

예제

import 'dart:async';
import 'package:example/firebase_options.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:fireflutter/fireflutter.dart';
import 'package:flutter/material.dart';

/// 글로벌 키
final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
BuildContext get globalContext => navigatorKey.currentState!.overlay!.context;

void main() async {
  /// Uncaught Exception 핸들링
  runZonedGuarded(
    () async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform,
      );

      runApp(const MyApp());

      FlutterError.onError = (FlutterErrorDetails details) {
        FlutterError.dumpErrorToConsole(details);
      };
    },
    zoneErrorHandler,
  );
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    UserService.instance.init();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: defaultLightTheme(context: context),
      navigatorKey: navigatorKey,
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('FireFlutter Quick Start App'),
      ),
      body: Column(
        children: [
          AuthReady(
            builder: (uid) => Column(
              children: [
                Text('UID: $uid'),
                ElevatedButton(
                  onPressed: () => UserService.instance.signOut(),
                  child: const Text('로그아웃'),
                ),
              ],
            ),
            notLoginBuilder: () => Theme(
              data: bigElevatedButtonTheme(context),
              child: const SimpleEmailPasswordLoginForm(),
            ),
          ),
        ],
      ),
    );
  }
}

/// 에러 핸들러
zoneErrorHandler(e, stackTrace) {
  print("----> runZoneGuarded() : Exceptions outside flutter framework.");
  print("--------------------------------------------------------------");
  print("---> runtimeType: ${e.runtimeType}");

  if (e is FirebaseAuthException) {
    String message = "${e.code} - ${e.message}";
    if (e.code == 'invalid-email') {
      message = '잘못된 이메일 주소입니다.';
    } else if (e.code == 'weak-password') {
      message = '비밀번호를 더 어렵게 해 주세요. 대소문자, 숫자 및 특수문자를 포함하여 6자 이상으로 입력해주세요.';
    } else if (e.code == 'email-already-in-use') {
      message = '메일 주소 또는 비밀번호를 잘못 입력하였습니다.';
    }
    toast(context: globalContext, message: '로그인 에러 :  $message');
  } else if (e is FirebaseException) {
    print("FirebaseException :  ${e.code}, ${e.message}");
    if (e.plugin == 'firebase_storage') {
      if (e.code == 'unknown') {
        error(
            context: globalContext,
            message: '파일 업로드 에러 :  ${e.message}\n\nStorage 서비스를 확인해주세요.');
      } else {
        error(context: globalContext, message: e.toString());
      }
    } else {
      error(context: globalContext, message: e.toString());
    }
  } else if (e is FireFlutterException) {
    print("FireshipException: (${e.code}) - ${e.message}");
    if (e.code == 'input-email') {
      error(context: globalContext, message: '이메일 주소를 입력해주세요.');
    } else if (e.code == 'input-password') {
      error(context: globalContext, message: '비밀번호를 입력해주세요.');
    } else {
      error(context: globalContext, title: e.code, message: e.message);
    }
  } else {
    print("Unknown Error :  $e");
    error(context: globalContext, message: e.toString());
  }
  debugPrintStack(stackTrace: stackTrace);
}