우당탕탕

Flutter 상태관리 Riverpod, 직접 써보니 단계별로 이렇게 했어요 본문

Tech/Flutter

Flutter 상태관리 Riverpod, 직접 써보니 단계별로 이렇게 했어요

모찌모찝 2026. 8. 7. 15:56

직접 Flutter 앱 만들면서 상태관리를 어떻게 해야 할지 한참 고민했는데, Riverpod을 써보니 생각보다 손쉽게 관리할 수 있었어요. 처음엔 세팅부터 코드 작성까지 막막했는데, 차근차근 따라 하다 보니 어느새 저만의 작업 흐름이 완성됐더라고요.

이 글에서는 Riverpod을 처음 설치하고 세팅하는 과정부터, 실제 상태를 다루는 코드 작성, 그리고 제가 하면서 겪은 실수와 그 해결법까지 모두 단계별로 알려드릴게요. Flutter 상태관리에 고민 있으셨다면 이 글 한 편이면 충분할 거예요.

개발 환경 / 버전 정보

제가 사용한 Flutter 버전은 3.7.0이고, Riverpod은 flutter_riverpod 2.3.6 버전을 썼어요. 이 조합에서 실제로 문제없이 잘 작동했답니다.

Flutter 상태관리 Riverpod 직접 써본 후기 관련 이미지

Flutter 상태관리 Riverpod 직접 써본 후기 관련 정보

Riverpod 설치하고 프로젝트 세팅 이렇게 했어요

사실 이 부분이 막상 시작할 때 가장 헷갈릴 수 있는데요, 저는 순서대로 이렇게 진행했어요.

  • Flutter 프로젝트 생성: flutter create riverpod_example
  • pubspec.yaml에 패키지 추가: flutter_riverpod: ^2.3.6
  • 터미널에서 flutter pub get 실행해서 설치 완료
  • main.dart에서 Riverpod ProviderScope로 앱 감싸기

main.dart 코드는 이렇게 바꿨는데, 이게 핵심이더라고요.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Riverpod Example')),
        body: const Center(child: Counter()),
      ),
    );
  }
}

여기서 ProviderScope로 앱 전체를 감싸야 Riverpod 상태관리가 동작하는 거라서 꼭 이 부분을 빼먹으면 안 돼요.

상태관리용 Provider 직접 만들어서 쓰기

그다음엔 실제 상태를 관리할 Provider를 만들었는데요, 카운터 앱 예제로 간단한 상태를 이렇게 구현했어요.

import 'package:flutter_riverpod/flutter_riverpod.dart';

// 상태를 담는 프로바이더 선언 (StateProvider)
final counterProvider = StateProvider<int>((ref) {
  return 0; // 초기값 0
});

초기 상태값을 0으로 설정했고, 상태 변경은 이 Provider를 통해서만 이뤄집니다.

다음으로 실제 화면에서 상태값을 읽고, 버튼 눌렀을 때 상태를 변경하는 위젯을 이렇게 만들었어요.

class Counter extends ConsumerWidget {
  const Counter({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 상태 읽기
    final count = ref.watch(counterProvider);

    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('현재 카운트: $count', style: const TextStyle(fontSize: 24)),
        const SizedBox(height: 20),
        ElevatedButton(
          onPressed: () {
            // 상태 변경
            ref.read(counterProvider.notifier).state++;
          },
          child: const Text('증가'),
        ),
      ],
    );
  }
}

이렇게 상태를 읽을 땐 ref.watch()를, 변경할 땐 ref.read(…notifier).state를 썼는데, 이 부분이 낯설기도 하지만, 자주 쓰다 보니 자연스러워졌어요.

Flutter 상태관리 Riverpod 직접 써본 후기 직접 정리한 자료

Flutter 상태관리 Riverpod 직접 써본 후기 관련 정보

여기서 막혔던 부분과 해결 과정

사실 처음에 ProviderScope를 빼먹고 그냥 runApp(MyApp())만 써서 앱이 동작은 하는데 상태가 전혀 업데이트 안 되는 현상이 있어서 당황했어요. 에러 메시지가 없어서 한참 헤맸는데, 공식 문서 보고 ProviderScope로 감싸야 한다는 걸 알고서 바로 해결했답니다.

또, 상태 업데이트를 할 때 ref.watch() 대신 ref.read()를 썼다가 UI가 실시간으로 안 바뀌는 문제도 있었어요. 이게 watch와 read의 차이를 잘 몰라서 생긴 문제였는데, watch는 상태를 구독하는 역할이고, read는 단순 조회라서 업데이트 감지가 안 되는 거였거든요.

// 이 코드는 UI가 안 바뀌었다
final count = ref.read(counterProvider);

// 수정 후에는 이렇게
final count = ref.watch(counterProvider);

조금 더 복잡한 상태도 이렇게 관리했어요

카운터 외에도 좀 더 실무에 가까운 복잡한 상태를 다룰 일이 있어서, StateNotifier와 StateNotifierProvider를 사용해봤어요. 개인적으로 이게 Riverpod 진짜 강력한 기능이라는 생각이 들더라고요.

예를 들어 할 일 목록을 관리하는 간단한 상태를 이렇게 구현했어요.

import 'package:flutter_riverpod/flutter_riverpod.dart';

class Todo {
  final String id;
  final String task;
  final bool completed;

  Todo({required this.id, required this.task, this.completed = false});

  Todo copyWith({bool? completed}) {
    return Todo(
      id: id,
      task: task,
      completed: completed ?? this.completed,
    );
  }
}

class TodoListNotifier extends StateNotifier<List<Todo>> {
  TodoListNotifier() : super([]);

  void addTodo(String task) {
    final newTodo = Todo(id: DateTime.now().toString(), task: task);
    state = [...state, newTodo];
  }

  void toggleTodo(String id) {
    state = state
        .map((todo) => todo.id == id ? todo.copyWith(completed: !todo.completed) : todo)
        .toList();
  }
}

final todoListProvider = StateNotifierProvider<TodoListNotifier, List<Todo>>((ref) {
  return TodoListNotifier();
});

이렇게 하고 위젯에서는 리스트를 watch하면서 새 할 일을 추가하거나 토글하는 UI를 만들었어요. 조금 길지만 상태 관리가 깔끔하게 분리돼서 상당히 만족스러웠죠.

Flutter 상태관리 Riverpod 직접 써본 후기 참고 사진

Flutter 상태관리 Riverpod 직접 써본 후기 관련 정보

자주 헷갈리는 부분, 제가 직접 답해봤어요

Q. Provider와 ConsumerWidget 차이가 뭔가요?

A. Provider는 상태를 저장하거나 계산하는 역할을 하는 객체이고, ConsumerWidget은 Provider가 제공하는 상태를 UI에서 구독해서 쓸 수 있게 만들어진 위젯이에요. 그래서 상태 변경 시 UI 업데이트를 원하면 ConsumerWidget이 필요해요.

Q. ref.watch와 ref.read는 언제 써야 하나요?

A. UI에서 상태 변화를 감지해 리빌드하려면 ref.watch를 써야 하고, 한 번만 읽거나 상태 변경 시에는 ref.read를 씁니다. 예를 들어 버튼의 onPressed 콜백 안에서는 ref.read를 더 많이 써요.

마무리하며, Riverpod 경험을 정리해봐요

Flutter 상태관리로 Riverpod이 확실히 강력하고 유연하다는 게 느껴졌어요. 제 경험상 설치부터 ProviderScope 감싸기, Provider 만들기, ConsumerWidget에서 상태 읽고 변경하기, 그리고 더 복잡한 StateNotifier 구조로 확장하는 단계까지 차근차근 따라가면 충분히 이해할 수 있더라고요.

앞으로도 Riverpod으로 더 다양한 상태관리 패턴을 써보고, 앱 규모가 커져도 성능과 유지보수에 좋은지 직접 경험해볼 생각이에요. Flutter 상태관리에 막막했던 분들에게 이 절차 중심 후기가 조금이나마 도움이 되길 바랍니다.

Comments