effidevFlutter · Cloudflare 엣지 · 클라우드 비용 최적화
한국어

Flutter Riverpod 3.0 실전 개편 가이드: Notifier, AsyncNotifier와 자동 메모리 해제 패턴

Flutter Riverpod 3.0 Architecture Guide

Flutter Riverpod 3.0 실전 개편 가이드: Notifier, AsyncNotifier와 자동 메모리 해제 패턴

Flutter의 가장 대표적인 상태 관리 라이브러리인 Riverpod이 3.0 버전으로의 대대적인 아키텍처 개편을 맞이했습니다. 기존 1.x / 2.x 버전에서 혼용되던 StateNotifier, ChangeNotifier 및 주석 기반 코드 생성 방식이 클래스 기반 Notifier/AsyncNotifierriverpod_generator 전면 표준화로 정립되었습니다.

참고로 @riverpod 코드 생성 방식은 이미 Riverpod 2.0부터 기본적으로 autoDispose(화면 이탈 시 자동 메모리 해제) 정책을 적용해 왔으며, 3.0에서는 여기에 더해 AutoDispose/Family 등으로 파편화되어 있던 프로바이더 타입 인터페이스를 Provider/Notifier로 통합(unify)하여 API가 한층 단순해졌습니다.

이번 글에서는 Riverpod 3.0 개편 핵심 포인트와 Notifier, AsyncNotifier, family 파라미터를 활용한 프로덕션 상태 관리 구현 패턴을 살펴봅니다.


1. Riverpod 3.0 주요 변경 사항 비교

구분 Riverpod 2.x 이하 (Legacy) Riverpod 3.0 표준
타입 인터페이스 Provider / AutoDisposeProvider / FamilyProvider 등으로 세분화 Provider/Notifier로 통합 (autoDispose 기본 적용은 @riverpod 코드 생성 방식에서 2.0부터 동일)
상태 관리 주체 StateNotifierProvider / FutureProvider Notifier / AsyncNotifier (@riverpod 어노테이션)
인자 전달 (Family) .family 수동 래퍼 작성 메서드 파라미터 형태로 자동 생성
비동기 상태 분기 AsyncValue 수동 when() 분기 처리 AsyncValue.when() 및 신규 AsyncValue 헬퍼 메서드 고도화

2. Notifier 및 AsyncNotifier 실전 구현

1) 동기 상태 관리: Notifier 구현

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'counter_provider.g.dart';

// @riverpod 어노테이션 사용 시 기본적으로 autoDispose 적용
@riverpod
class CounterNotifier extends _$CounterNotifier {
  @override
  int build() {
    // 초기 상태 반환 (화면 재진입 시 build 재실행)
    return 0;
  }

  void increment() {
    state = state + 1;
  }

  void decrement() {
    state = state - 1;
  }
}

2) 비동기 상태 관리: AsyncNotifier & Family 구현

import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../models/user.dart';

part 'user_provider.g.dart';

// family 파라미터를 build 메서드의 인자로 직접 전달
@riverpod
class UserDetailNotifier extends _$UserDetailNotifier {
  @override
  Future<User> build(String userId) async {
    // 비동기 데이터 페칭
    return await ref.read(userRepositoryProvider).fetchUser(userId);
  }

  Future<void> updateUserName(String newName) async {
    // 로딩 상태로 전환
    state = const AsyncLoading();

    // 상태 업데이트 및 Guard 처리
    state = await AsyncValue.guard(() async {
      final updatedUser = await ref
          .read(userRepositoryProvider)
          .updateName(arg, newName); // arg는 family 인자 (userId)
      return updatedUser;
    });
  }
}

3. Widget 계층에서의 AsyncValue 소비 및 autoDispose 대응

UI 단에서는 ref.watch를 통해 상태를 구독하고 AsyncValue.when으로 로딩/에러/성공 상태를 깔끔하게 분기합니다.

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

class UserProfileScreen extends ConsumerWidget {
  final String userId;
  const UserProfileScreen({super.key, required this.userId});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // family 인자와 함께 프로바이더 구독
    final userAsync = ref.watch(userDetailNotifierProvider(userId));

    return Scaffold(
      appBar: AppBar(title: const Text('사용자 프로필')),
      body: userAsync.when(
        data: (user) => Column(
          children: [
            Text('이름: ${user.name}'),
            ElevatedButton(
              onPressed: () {
                ref
                    .read(userDetailNotifierProvider(userId).notifier)
                    .updateUserName('새 이름');
              },
              child: const Text('이름 변경'),
            ),
          ],
        ),
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (err, stack) => Center(child: Text('에러 발생: $err')),
      ),
    );
  }
}

4. autoDispose 관련 실전 주의사항

  1. 글로벌 인증/설정 상태: 전역 로그인 정보나 테마 설정처럼 앱 생명주기 내내 유지되어야 하는 프로바이더는 @Riverpod(keepAlive: true)를 명시해야 합니다.
  2. ref.keepAlive() 링크 활용: 특정 액션 수행 중에는 화면이 이탈하더라도 상태 해제를 유예하고 싶은 경우, build() 내에서 final link = ref.keepAlive();를 생성하고 완료 후 link.close()로 해제합니다.
  3. 가비지 컬렉션 타이밍: autoDispose는 위젯 트리에서 마지막 소비자(watch)가 사라져도 즉시 파기하지 않고 한 프레임(1 frame)의 유예 기간을 기다린 뒤, 그 시점에도 여전히 사용되지 않을 때 상태를 파기하므로 폼 입력 임시 저장 시 캐싱 전략을 고려해야 합니다.

요약 및 결론

Riverpod 3.0은 보일러플레이트 코드를 최소화하고 안전한 메모리 관리를 기본 탑재한 모던 상태 관리 체계입니다.

[!NOTE]

  1. StateNotifier 대신 클래스 기반 Notifier / AsyncNotifier로 전환하세요.
  2. riverpod_generator를 적극 활용하여 family 파라미터 및 프로바이더 생성을 자동화하세요.
  3. 기본 적용된 autoDispose 정책을 이해하고, 전역 상태는 @Riverpod(keepAlive: true)로 명시하여 메모리 누수를 원천 차단하세요.

새로운 Riverpod 3.0 패턴을 적용하면Flutter 앱의 유지보수성과 타입 안정성을 한 차원 높일 수 있습니다.