user에 로딩 state가 있어야하는가!

topics
types
tags
#flutter #state #loading #riverpod

User에 로딩 state가 있어야하는가

Splash 스크린 처리

앱 초기 실행 시 사용자 정보를 불러오는 동안 로딩 화면을 보여줘야 한다.
이때 User 상태가 로딩 중인지 알아야 적절한 UI를 보여줄 수 있다.

null 체크만으로는 부족하다

// 나쁜 예 - 로딩 state 없음
class UserState {
  final User? user;
}

문제점: user == null일 때, 이게 "로딩 중"인지 "로그아웃 상태"인지 "에러 발생"인지 구분이 안 된다.

// 좋은 예 - 로딩 state 있음
class UserState {
  final User? user;
  final bool isLoading;
  final String? error;
}

// 또는 AsyncValue 사용
AsyncValue<User?> userState;

어떻게 구현할까

방법 1: 직접 상태 관리

class UserState {
  final User? user;
  final bool isLoading;
  final String? error;

  bool get isLoggedIn => user != null && !isLoading;
  bool get isInitializing => isLoading && user == null;
}

방법 2: AsyncValue 사용 (Riverpod)

final userProvider = AsyncNotifierProvider<UserNotifier, User?>(() {
  return UserNotifier();
});

// UI에서 사용
ref.watch(userProvider).when(
  loading: () => LoadingWidget(),
  error: (err, stack) => ErrorWidget(err),
  data: (user) => user != null ? HomeScreen() : LoginScreen(),
);

왜 AsyncValue를 권장하냐면: 로딩/에러/데이터 세 가지 상태가 이미 정의되어 있다. 직접 만들 필요 없다.

실제 사용 예시

Splash에서 분기 처리

class SplashScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userState = ref.watch(userProvider);

    return userState.when(
      loading: () => SplashLoadingWidget(),
      error: (e, _) {
        // 에러 발생 시 로그인 화면으로
        WidgetsBinding.instance.addPostFrameCallback((_) {
          context.go('/login');
        });
        return SplashLoadingWidget();
      },
      data: (user) {
        // 로딩 완료 후 분기
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (user != null) {
            context.go('/home');
          } else {
            context.go('/login');
          }
        });
        return SplashLoadingWidget();
      },
    );
  }
}

관련 문서