일괄적 오류 잡기!

🦥 일괄적 오류 잡기

나는 웹 프론트에서나 API 통신을 진행할 때에 항상! 빼놓지 않고 작성해왔던 코드가 있다.

함수에 try catch 문을 작성해두면 만능인줄 알았던!! 이 코드!

궁금증! 불 필요하지만, 모든 곳에 try catch 문을 쓰면 에러든 뭐든 잡아주지 않을까? 란 생각!!!!!!

오류는 어디서든 나타날수 있다는 것을 알아둬야 한다.

불 특정한 위치에서 모든 에러를 잡을 수 있다면 얼마나 좋을까?

try {
	...
	// API 통신
} catch(e) {
	...
	Logger(e.toString);
}

1. Flutter 내부 오류 발생하는 경우

밑의 코드는 버튼 클릭시 오류 발생 시키는 코드이다.

ElevatedButton(
	onPressed: makeError,
	child: const Text('첫번째 경우');
)

void makeError() {
	final list = ['a'];
	
	for(var n = 0; n < list.length + 1; n++) {
		log(list[n]);
	}
}

어제의 나 였다면, makeError() 함수 내부에 try {} catch(e) 문을 집어 넣었을 것이다.

runApp 호출 전, FlutterError.onError 를 설정을 해준다.

void main() {
	FlutterError.onError = (details) {
		FlutterError.presentError(details);
	};
	runApp(const MyApp());
}

2. 위젯 생성 과정에서 오류가 발생하는 경우

MaterialApp.builder 에서 ErrorWidget.builder 를 설정해주면 build내부 함수에서 오류 발생시 설정한 ErorWidget.builder 를 리턴해준다.

return MaterialApp(
	builder: (context, widget) {
		ErrorWidget.builder = (errorDetails) {
			Widget error = Text(errorDetails);
			if (widget is Scaffold || widget is Navigator) {
				error = Scaffold(body: SafeArea(child : error));
			}
			return error;
		};
		return widget!;
	},
	home: const MyHome(),
);

3. Flutter 외부에서 오류 발생하는 경우

Flutter 외부에서는 다양한 상황에서 오류가 발생 할 수 있다. (플랫폼별, 패키지 등.. )

밑의 코드는 존재하지 않는 플러그인을 호출해 발생하는 코드이다.

ElevatedButton(
	onPressed: () async {
		const channel = MethodChannel('fake-channel');
		await channel.invokeMethod('strange ~');
	},
)
void main() {
	runZonedGuarded(() { 
		runApp(const MyApp());
	}, (error, stackTrace) {
		log('error outside Flutter', error: error, stackTrce: stackTrace);
	}); 
}

전체 적용 사례

import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
	runZonedGuarded(() {
		WidgetsFlutterBinding.ensureInitialized();
		FlutterError.onError = (details) {
			log('플러터 내부 에러');
			FlutterError.presentError(details);
		};
		runApp(const MyApp());
	}, (error, stackTrace) {
		log('플러터 외부 에러', error: error, stackTrace: stackTrace);
	});
}

class MyApp extends StatelessWidget {
	const MyApp({Key? key}) : super(key: key);

	@override
	Widget build(BuildContext context) {
		return MaterialApp(
			builder: (context, widget) {
				ErrorWidget.builder = (errorDetails) {
					Widget error = Text(errorDetails);

					if(widget is Scaffold || widget is Navigator) {
						error = Scaffold(body: SafeArea(child: error));
					}
					return error;
				};
				return widget!;
			},
			home: cosnt MyHome();
		);	
	}
}

class MyHome extends StatefulWidget {
	const MyHome({Key? key}) : super(key: key);

	@oveeride
	State<StatefulWidget> createState() {
		return MyHomeState();
	}	
}

class MyHomeState extends State<MyHome> {
	var error = false;
	
	void makeError() {
		final list = ['a'];
		for (var n = 0; n < list.length + 1; n++) {
			log(list(n));
		}
	}

	@override
	Widget build(BuildContext context) {
		if (error) {
			makeError();
		}
		return Scaffold(
			body: SafeArea(
				child: Center(
					child: Column(
						children: [
							ElevatedButton(
								onPressed: () { makeError();},
								child: const Text('첫번째 플러터 내부 에러'),
							),
							ElevatedButton(
								onPressed: () { 
									setState(() => error = true);
								},
								child: const Text('두번째 플러터 내부 위젯 생성 과정 에러'),
							),
							ElevatedButton(
								onPressed: () async {
									const channerl = MethodChannel('fake-channel');
									await channel.invokeMethod('strange~');
								},
								child: const Text('세번째 플러터 외부 에러'),
							),
						],
					),
				),
			)
		);
	}
}

Categories:

Updated:

Leave a comment