에러 처리

luneah·2022년 10월 13일
0

Flutter

목록 보기
27/29
post-thumbnail

에러 처리

기본적으로 예외는 try-catch를 활용하여 처리한다. catch에서 적절한 에러 객체를 리턴해주는 것이 좋다.

목표

  • 원격 데이터 요청시 결과에 대한 처리를 효율적으로 할 만한 방법이 필요
  • 일반적인 방식으로는 데이터가 null인지 아닌지에 대한 처리만 가능

Result 클래스

  • Result 클래스는 성공시에는 데이터를, 실패시에는 Exception을 담는 객체를 정의한다.
  • 이외에도 더 많은 케이스가 필요하면 추가하면 된다.
// 데이터 처리의 성공 실패를 담을 객체
abstract class Result<T> {
  factory Result.success(T data) = Success;
  factory Result.error(Exception e) = Error;
}

class Success<T> implements Result<T> {
  final T data;
  
  Success(this.data);
}

class Error<T> implements Result<T> {
  final Exception e;

  Error(this.e);
}

Result 클래스 사용시 효과

  • Null 처리가 제거된다.
  • 여러가지 결과에 대한 처리를 하기가 용이하다.

Stream<Result<ChatUser>> authStateChanges() {
  return FirebaseAuth.instance.authStateChanges().map((User? user) {
    if (user != null) {
      return Result.success(ChatUser(user.email, user.photoURL, user.displayName),
      );
    }
    return Result.error(Exception('로그인 실패'));
  });
}

응답을 Result 클래스로 랩핑하기

예외가 예상되는 지점에서 try-catch 사용하기

Stream으로 UI에 예외 노출하기

Stream을 활용하여 UI 쪽으로 예외를 알린다. 일회성 알림이므로 notifyChange()는 사용 할 수 없다.

  1. ViewModel에 이벤트 스트림을 생성한다.
final _eventController = StreamController<UiEvent>();
Stream<UiEvent> get eventStream => _eventController.stream;
  1. 에러 발생시 이벤트를 스트림으로 내보낸다.
_eventController.add(UiEvent.showSnackBar(message));
  1. ViewModel에서 예외 처리 하기
final Result<List<Photo>> results = await useCases.getPhotos(query);

results.when(success: (photos) {
  _state = state.copyWith(photos: photos, isLoading: false);
  _eventController.add(const UiEvent.endLoading());
  notifyListeners();
}, error: (message) {
  _eventController.add(UiEvent.showSnackBar(message));
  _state = state.copyWith(isLoading: false);
});
  1. StatefulWidget에서 Stream을 관찰하며 처리한다.
class _MainScreenStateState extends State<MainScreen> {
  
  void initState() {
    Future.microtask(() {
      context.read<MainViewModel>().fetch('iphone');
      
      context.read<MainViewModel>().eventStream.listen((event) {
        event.when(showSnackBar: (message) {
          final snackBar = SnackBar(content: Text(message));
          ScaffoldMessenger.of(context).showSnackBar(snackBar);
        }, endLoading: () {
          const snackBar = SnackBar(content: Text('로딩이 완료되었습니다'));
          ScaffoldMessenger.of(context).showSnackBar(snackBar);
        });
      });
    });
    super.initState();
  }
profile
하늘이의 개발 일기

0개의 댓글