Riverpod

하요·2024년 7월 7일
0

Flutter State Management

목록 보기
5/7
post-thumbnail
post-custom-banner

Flutter에서 Provider의 개선: Riverpod

Riverpod은 Flutter에서 상태 관리를 보다 안전하고 효율적으로 할 수 있도록 도와주는 패키지로, 기존의 Provider 패키지를 개선하여 다양한 기능과 유연성을 제공합니다. 이를 통해 애플리케이션의 상태를 보다 쉽게 관리하고, 유지보수를 용이하게 합니다.

주요 개념

  • Riverpod: Provider의 개선판으로, 상태 관리의 편의성과 안전성을 높여줍니다. 전역 변수로 제공자를 선언하고, ProviderScope로 감싸 사용합니다.
  • ProviderScope: Riverpod의 상태를 유지하고, 위젯 트리에서 상태를 공유할 수 있도록 하는 역할을 합니다.
  • WidgetRef: Riverpod에서 상태를 읽거나, 상태 변경을 감지할 수 있도록 도와주는 객체입니다.
  • ConsumerWidget: StatelessWidget과 유사하지만, Riverpod의 상태를 구독할 수 있는 기능을 제공합니다.

주요 속성 및 메서드

  • ref.watch(): 상태를 구독하고, 상태가 변경될 때마다 빌드합니다.
  • ref.read(): 상태를 읽기만 하고, 변경 사항을 구독하지 않습니다.
  • NotifierProvider: 상태 변경을 알리는 Notifier 객체를 제공하는 Provider입니다.
  • StateProvider: 간단한 상태를 관리하기 위한 Provider입니다.

주요 활용도

  • 글로벌 상태 관리: 앱 전체에서 공유되는 상태를 관리하는 데 유용합니다.
  • 상태의 안전한 변경: 상태 변경 로직을 중앙 집중화하여 버그를 줄이고 유지보수를 용이하게 합니다.
  • 비동기 작업 관리: 비동기 작업의 상태를 쉽게 관리할 수 있습니다.

코드 예제

아래 예제는 간단한 카운터 앱을 통해 Riverpod의 사용 방법을 보여줍니다.

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

// 상태 제공자 선언
final counterProvider = StateProvider<int>((ref) => 0);

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterApp(),
    );
  }
}

class CounterApp extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterProvider).state;

    return Scaffold(
      appBar: AppBar(title: Text('Counter App')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text('$counter', style: Theme.of(context).textTheme.headline4),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider).state++;
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Riverpod의 특징

1. 제공자 등록 편의성

  • Provider:
    • 위젯 트리에 직접 명시적으로 등록해야 하며, 의존성이 있는 경우 등록 순서에 주의해야 합니다.
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) => A()),
          ChangeNotifierProvider(create: (context) => B(a: context.read())),
        ],
        child: MyApp(),
      );
      
  • Riverpod:
    • 제공자를 전역 변수로 선언하며, ProviderScope로 감싸면 의존성 순서에 상관없이 등록됩니다.
      final a = StateProvider<int>((ref) => 0);
      final b = StateProvider<int>((ref) => ref.watch(a) * 2);
      
      ProviderScope(
        child: MyApp(),
      );
      

2. 제한 없는 Provider

  • Provider:
    • 같은 클래스를 여러 개 등록하는 경우, 가장 가까운 클래스가 호출됩니다.
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) => Counter()), // 1
          ChangeNotifierProvider(create: (context) => Counter()), // 2
        ],
        child: MyApp(),
      );
      
      Counter counter = context.read<Counter>(); // 2번만 접근 가능
      
  • Riverpod:
    • 같은 클래스도 다른 이름으로 생성하고 접근할 수 있습니다.
      final a = NotifierProvider<Counter, int>(() => Counter());
      final b = NotifierProvider<Counter, int>(() => Counter());
      
      Counter counter1 = ref.read(a.notifier);
      Counter counter2 = ref.read(b.notifier);
      

3. Flutter에 의존하지 않음

  • Provider:
    • Flutter 프레임워크에서 제공하는 BuildContext를 사용합니다.
      class MyWidget extends StatelessWidget {
        
        Widget build(BuildContext context) {
          Counter counter = context.watch<Counter>();
          return Text("${counter.count}");
        }
      }
      
  • Riverpod:
    • Flutter 프레임워크와 무관한 WidgetRef를 별도로 만들어 사용합니다.
      class MyWidget extends ConsumerWidget {
        
        Widget build(BuildContext context, WidgetRef ref) {
          Counter counter = ref.watch(counterProvider);
          return Text("${counter.count}");
        }
      }
      

Riverpod의 단점

1. 전역 변수의 증가

Riverpod에서는 제공자를 전역 변수로 선언합니다. 이는 동일한 클래스를 여러 개 등록하고 사용할 수 있는 유연성을 제공하지만, 전역 변수가 늘어나게 되어 코드 관리가 어려워질 수 있습니다.

2. WidgetRef 사용의 필요성

Riverpod은 Flutter의 BuildContext를 사용하지 않고 WidgetRef를 사용합니다. 이는 Flutter에 의존하지 않는다는 장점이 있지만, ConsumerWidget과 같은 위젯을 사용하기 위해 추가적인 코드가 필요하게 됩니다.

class MyWidget extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    Counter counter = ref.watch(counterProvider);
    return Text("${counter.count}");
  }
}

추가 팁

  • 성능 최적화: ref.watch는 필요한 경우에만 호출하여 불필요한 위젯 빌드를 최소화합니다.
  • 복잡한 상태 관리: 앱의 상태가 여러 위젯 간에 공유되거나, 글로벌 상태 관리가 필요한 경우, Riverpod와 같은 상태 관리 도구를 사용하는 것이 좋습니다.

관련 자료

profile
flutter 개발자(진)
post-custom-banner

0개의 댓글