Loading...
Loading...
Flutter 프로젝트의 타입 안전 에러 처리 패턴 — `Error` 마커 인터페이스, freezed 기반 `Result<D, E>` sealed 클래스, 기능별 에러 enum, `switch` 패턴 매칭으로 성공/실패를 처리하는 방법. "Result 래퍼", "에러 처리", "ResultSuccess", "ResultError", "NetworkError", "타입 안전 에러", "freezed sealed", "exception 대신 Result" 같은 표현에 트리거합니다.
npx skill4agent add junsuk5/survival-flutter-skills flutter-error-handlingResultResult.error(...)lib/core/domain/error/// lib/core/domain/error/error.dart
abstract interface class Error {}ErrorResultEextends ErrorException// lib/core/domain/error/result.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'error.dart';
part 'result.freezed.dart';
sealed class Result<D, E extends Error> with _$Result<D, E> {
const factory Result.success(D data) = ResultSuccess;
const factory Result.error(E error) = ResultError;
}sealedswitchimplements ErrortoString()// lib/core/domain/error/network_error.dart
enum NetworkError implements Error {
requestTimeout,
noInternet,
serverError,
unknown;
String toString() => switch (this) {
NetworkError.requestTimeout => '요청 시간이 초과되었습니다',
NetworkError.noInternet => '인터넷 연결을 확인해 주세요',
NetworkError.serverError => '서버에 문제가 발생했습니다',
NetworkError.unknown => '알 수 없는 문제가 발생했습니다',
};
}NetworkErrorlib/core/domain/error/lib/domain/error/<feature>_error.dartBookmarkErrorNewRecipeErrorResult// UseCase 시그니처
Future<Result<List<String>, NetworkError>> execute();
Future<Result<List<Recipe>, BookmarkError>> execute(int recipeId);DENetworkError.unknownBookmarkError.saveFailedswitchhome_view_model.dartvoid _fetchCategories() async {
final result = await _getCategoriesUseCase.execute();
switch (result) {
case ResultSuccess<List<String>, NetworkError>():
_state = state.copyWith(
categories: result.data,
selectedCategory: 'All',
);
notifyListeners();
case ResultError<List<String>, NetworkError>():
switch (result.error) {
case NetworkError.requestTimeout:
case NetworkError.noInternet:
case NetworkError.serverError:
case NetworkError.unknown:
_eventController.add(result.error);
}
}
}ResultSuccess<D, E>()ResultError<D, E>()result.dataresult.errorswitch (result.error)StreamControllerfinal _eventController = StreamController<NetworkError>();
Stream<NetworkError> get eventStream => _eventController.stream;eventStreamScaffoldMessenger.showSnackBarStateNetworkError? errorcopyWith(error: ...)error: nullFuture<Result<List<RecipeDto>, NetworkError>> getRecipes() async {
try {
final raw = await _recipeDataSource.getRecipes();
return Result.success(raw.map(RecipeDto.fromJson).toList());
} on SocketException {
return const Result.error(NetworkError.noInternet);
} on TimeoutException {
return const Result.error(NetworkError.requestTimeout);
} catch (_) {
return const Result.error(NetworkError.unknown);
}
}Result.errortry/catch| 시나리오 | 에러 타입 | 위치 |
|---|---|---|
| 네트워크 호출 실패 | | |
| 로컬 저장소/DB 실패 | | |
| 기능 전용 실패 (북마크 저장 실패 등) | | |
| 여러 DataSource 를 묶는 Repository | 상위 에러 타입 ( | 해당 feature |
implements ErrorFuture<Result<D, FooError>>switch (result)ResultSuccess<D, E>()ResultError<D, E>()switch (result.error)Result.errortry/catchFuture<List<Recipe>>throwResult<List<Recipe>, Exception>ExceptionErrorResultswitchdefault: