우당탕탕

Flutter 상태관리 Riverpod 직접 써본 경험과 자주 묻는 질문들 본문

Tech/Flutter

Flutter 상태관리 Riverpod 직접 써본 경험과 자주 묻는 질문들

모찌모찝 2026. 6. 4. 11:33

사실 처음에 Flutter 상태관리를 하려고 하면 여러 옵션이 너무 많아서 뭘 써야 할지 엄청 헷갈리더라고요. 저도 여러 라이브러리 고민하다가 Riverpod을 직접 써봤는데 생각보다 직관적이고 깔끔한 부분이 많아서 후기 남겨봅니다.

이 글에서는 Riverpod을 직접 적용하며 제일 많이 궁금했던 부분들 위주로 Q&A로 풀어나갈 거예요. 그래서 Flutter 상태관리 고민하는 분들이 Riverpod을 정확히 이해하고 빠르게 써볼 수 있을 것 같아요.

개발 환경 / 버전 정보

제가 사용한 Flutter 버전은 3.7.0이고, Riverpod은 2.3.6 버전을 썼어요. 기본적으로 Dart 3과도 호환이 잘 됩니다.

Riverpod 상태관리, 이렇게 시작하면 됩니다

사실 처음에는 Provider랑 뭘 어떻게 다르게 써야 할지 엄청 헷갈렸어요. 근데 Riverpod은 전역 상태를 안전하게 관리해주는 구조라 생각하면 쉬워요. 가장 기본은 final myProvider = StateProvider<int>((ref) => 0); 이런 식으로 프로바이더를 선언하고 사용합니다.

import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateProvider<int>((ref) => 0);

class CounterWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return ElevatedButton(
      onPressed: () => ref.read(counterProvider.notifier).state++,
      child: Text('Count: $count'),
    );
  }
}

가장 중요한 건 ConsumerWidget을 통해 쉽게 프로바이더를 읽고 쓸 수 있다는 점이에요. 그리고 ref.watch로 상태 변화를 구독해서 UI가 자동으로 다시 그려지는 것도 매우 편리합니다.

Riverpod 쓰면서 자주 묻는 질문들 모음

Q. Provider랑 Riverpod은 어떻게 다른가요?

A. Provider는 Flutter 위젯 트리 내부에 의존하는 반면 Riverpod은 완전히 독립적인 전역 상태 관리 시스템이에요. 그래서 더 안전하고 테스트하기도 쉽고, 어디서든 상태를 참조할 수 있다는 장점이 있습니다.

Q. 어떤 상황에서 StateNotifierProvider와 StateProvider를 골라 써야 할까요?

A. StateProvider는 단순한 상태(숫자, 문자열 등)를 관리할 때 적합하고, StateNotifierProvider는 상태 로직이 복잡하거나 여러 상태를 한꺼번에 조작할 때 쓰기 좋아요. 저는 복잡한 상태 관리가 필요하면 StateNotifier를 자주 애용했어요.

Q. Riverpod에서 비동기 데이터 처리 어떻게 하나요?

A. 비동기 처리는 FutureProviderStreamProvider를 쓰면 됩니다. 실제로 API 호출을 하거나 실시간 스트림을 구독할 때 편리해요. 아래처럼 간단히 선언할 수 있어요.

final userFutureProvider = FutureProvider<User>((ref) async {
  final response = await fetchUserFromApi();
  return response;
});

// 사용 예
final user = ref.watch(userFutureProvider);
user.when(
  data: (user) => Text('Hello, ${user.name}'),
  loading: () => CircularProgressIndicator(),
  error: (e, st) => Text('Error: $e'),
);

이렇게 쓰면 상태에 따라 UI가 알아서 바뀌니까 비동기 작업이 쉬워지더라고요.

Q. 상태 변경 시 UI 재빌드가 너무 잦은데 어떻게 줄이나요?

A. Riverpod은 기본적으로 상태 구독이 섬세하게 돼서 불필요한 재빌드는 적은 편입니다. 하지만 때때로 ref.watch를 너무 넓은 범위에서 쓰면 재빌드가 많아지고, 이럴 때는 ref.select로 특정 상태 값만 골라 구독하면 돼요.

예를 들어, 아래처럼 하면 상태 전체가 변경돼도 필요한 값만 재빌드 됩니다.

final myStateProvider = StateProvider<MyModel>(...);

final someValue = ref.watch(myStateProvider.select((state) => state.someValue));

이렇게 하니까 성능 최적화에 도움이 많이 됐어요.

Q. Riverpod에서 의존성 주입은 어떻게 되나요?

A. Riverpod은 ref.watch를 통해 다른 프로바이더를 참조할 수 있어서 의존성 주입이 자연스럽고 간단합니다. 예를 들어, API 클라이언트 프로바이더를 만들고, 그걸 서비스에서 참조해 쓰는 식이죠.

final apiClientProvider = Provider<ApiClient>((ref) => ApiClient());

final userServiceProvider = Provider<UserService>((ref) {
  final client = ref.watch(apiClientProvider);
  return UserService(client);
});

이 부분이 정말 깔끔해서 대규모 앱에서도 유연하게 상태와 의존성을 관리할 수 있었어요.

Q. Riverpod 테스트는 어떻게 하나요?

A. Riverpod은 ProviderContainer라는 클래스를 통해 프로바이더를 직접 인스턴스화 할 수 있어서 UI 없이도 상태 로직을 쉽게 테스트할 수 있어요. 예를 들어 아래처럼 쓸 수 있죠.

void main() {
  test('counterProvider increments value', () {
    final container = ProviderContainer();
    addTearDown(container.dispose);

    final listener = Listener<int>();
    container.listen<int>(counterProvider, listener, fireImmediately: true);

    container.read(counterProvider.notifier).state++;

    verify(listener(0, 1));
    expect(container.read(counterProvider), 1);
  });
}

테스트가 쉽다는 점은 개인적으로 Riverpod에 매우 큰 점수 줬던 부분입니다.

이 밖에도 Riverpod의 전역 상태 공유, 세밀한 리빌드 제어, 비동기 처리 지원 등 여러 장점 덕분에 저는 점점 더 이걸 쓰게 됐어요. 여러분도 앱 크기가 커질 때 상태관리로 고민 중이라면 한번 꼭 써보세요.

궁금했던 부분이 여기서 다 해결됐을 거라 믿어요. 다음에는 더 다양한 Riverpod 활용법과 팁을 공유해볼게요.

Comments