Riverpod

김동연·2025년 4월 18일

Riverpod

리버팟이란?

  • 개발자가 상태관리를 편하게 할 수 있도록 도와주는 라이브러리.
  • 기본적인 Provider 패턴에서 발전하여 더 강력한 상태 관리와 테스트 가능성, 유연성을 제공.
  • ViewModel의 역할을 쉽게 구현 가능.
  • View에서 ViewModel의 관찰을 쉽게 하게 해줌.

Riverpod 이론적인 부분

1. Provider 개념

  • Provider는 상태를 관리하는 기본적인 단위입니다. 상태를 읽거나 쓸 수 있는 객체를 생성하며, 앱에서 상태를 제공하고 구독할 수 있게 합니다.

  • Provider는 상태의 소스를 정의하며, 위젯 트리에서 해당 상태를 구독할 수 있게 해줍니다.

2. Provider의 종류:

  • StateProvider: 간단한 상태를 제공하는 Provider입니다. 예를 들어, 카운터나 플래그와 같은 값을 다룰 때 유용합니다.

  • StateNotifierProvider: 더 복잡한 상태 로직을 처리하는 Provider입니다. 상태를 변경하는 로직을 별도의 클래스로 분리하여 더 세밀하게 제어할 수 있습니다.

  • FutureProvider: 비동기적인 작업을 처리하는 Provider입니다. 예를 들어, API 요청 후 데이터를 가져올 때 사용됩니다.

  • StreamProvider: 실시간 데이터 흐름을 다룰 때 사용되는 Provider입니다. 예를 들어, 채팅 메시지나 실시간 알림을 처리할 때 유용합니다.

3. ProviderScope:

  • ProviderScope는 Riverpod의 상태를 관리하는 범위를 정의하는 위젯입니다. 앱 전체에서 상태를 관리할 수 있게 해주는 상위 위젯입니다.

  • ProviderScope는 앱의 루트 위젯에 감싸서 사용해야 하며, 이를 통해 Riverpod에서 관리하는 상태를 사용할 수 있습니다.

  • 모든 Provider는 ProviderScope 내에서만 작동합니다.

4. 상태 관리의 구독과 업데이트:

  • Riverpod에서 상태를 구독하려면 ConsumerWidget을 사용하거나, watch를 통해 상태를 구독하고 변화를 감지할 수 있습니다.

  • 상태가 변경되면 ConsumerWidget은 자동으로 리빌드되며, ScopedReader를 사용해 Provider로부터 값을 읽습니다.

🤔 read와 watch의 차이점:

  • read: 상태를 읽지만, 상태 변화에 따른 리빌드를 하지 않습니다.

  • watch: 상태를 읽고, 상태가 변경되면 해당 위젯을 리빌드합니다.


Riverpod 사용 방법

1. RiverPod 패키지 추가

RiverPod을 사용하려면 먼저 flutter_riverpod 패키지를 프로젝트에 추가해야 합니다.

flutter pub add flutter_riverpod

2. ProviderScope로 최상위 위젯 감싸기

ProviderScopeRiverPod에서 상태를 관리하는 상위 컨테이너 역할을 합니다. 앱의 루트 위젯을 ProviderScope로 감싸야 RiverPod에서 제공하는 상태 관리 기능을 사용할 수 있습니다.

void main() {
  runApp(const ProviderScope(child: MyApp())); // 앱을 ProviderScope로 감싸기
}

3. Widget 구현

뷰에서는 데이터를 표시하는 역할을 하며, Consumer 위젯을 사용하여 RiverPod의 상태를 구독하고 반영합니다. 이때 ref.watchref.read를 사용하여 상태를 읽거나 업데이트할 수 있습니다.


4. 데이터를 담을 Model 클래스 만들기

API로부터 받아온 데이터를 관리할 Model 클래스를 만듭니다. 예를 들어, 유저 정보를 담을 User 클래스를 정의할 수 있습니다.

class User {
  String name;
  int age;

  User({required this.name, required this.age});

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        age = json['age'];

  Map<String, dynamic> toJson() {
    return {
      "name": name,
      "age": age,
    };
  }
}

5. 모델을 가져오는 Repository 클래스 만들기

서버에서 데이터를 가져오는 역할을 하는 Repository 클래스를 만듭니다. 이 클래스는 비동기적으로 데이터를 받아오는 메서드를 구현합니다.

class UserRepository {
  Future<User> getUser() async {
    await Future.delayed(const Duration(seconds: 1)); // 서버 대기 시간

    String serverResponse = """
    {
      "name": "이지원",
      "age": 20
    }
    """;

    Map<String, dynamic> json = jsonDecode(serverResponse);
    return User.fromJson(json);
  }
}

6. Widget에서 관리될 상태 클래스 만들기

뷰에서 상태를 관리할 클래스를 작성합니다. 여기서는 HomeState를 사용하여 유저 정보와 데이터를 가져온 시간을 관리합니다.

class HomeState {
  User? user;
  DateTime? fetchTime;

  HomeState({required this.user, required this.fetchTime});
}

7. 상태를 관리할 ViewModel 만들기 (Notifier 상속)

상태를 관리하고 변경된 상태를 뷰에 알려주는 ViewModel을 만듭니다. Notifier 클래스를 상속받고, 초기 상태를 설정하는 build 메서드와 데이터를 가져와서 상태를 업데이트하는 메서드를 작성합니다.

class HomeViewModel extends Notifier<HomeState> {
  
  HomeState build() {
    return HomeState(user: null, fetchTime: null); // 초기 상태 설정
  }

  void getUserInfo() async {
    UserRepository userRepository = UserRepository();
    User user = await userRepository.getUser();
    
    state = HomeState(user: user, fetchTime: DateTime.now()); // 상태 업데이트
  }
}

8. ViewModel을 공급할 viewModelProvider 만들기

NotifierProvider를 사용하여 ViewModel을 공급하는 프로바이더를 생성합니다. 이를 통해 뷰에서 ViewModel을 구독하고 데이터를 관리할 수 있습니다.

final homeViewModelProvider = NotifierProvider<HomeViewModel, HomeState>((ref) {
  return HomeViewModel();
});

9. Widget에서 Consumer 위젯 사용하여 데이터 표시 및 함수 연결

Consumer 위젯을 사용하여 ViewModel에 접근하고 상태를 구독합니다. ref.watch를 사용하여 상태가 변경될 때마다 위젯이 리빌드되도록 할 수 있습니다. ref.read를 사용하면 상태를 읽을 수 있지만, 상태가 변경될 때 위젯을 리빌드하지 않습니다.

Consumer(
  builder: (context, ref, child) {
    HomeState homeState = ref.watch(homeViewModelProvider);
    return Column(
      children: [
        Text('이름: ${homeState.user?.name ?? ""}'),
        Text('나이: ${homeState.user?.age ?? ""}'),
        Text('데이터 가져온 시간: ${homeState.fetchTime ?? ""}'),
        GestureDetector(
          onTap: () {
            ref.read(homeViewModelProvider.notifier).getUserInfo(); // 상태 업데이트
          },
          child: Text('정보 가져오기'),
        ),
      ],
    );
  }
)

ViewModel로 사용할 수 있는 Notifier 종류

1. Notifier

Notifier는 상태가 앱 전역에서 계속 유지될 때 사용합니다. 예를 들어, 로그인 상태나 앱 전체에서 공유되는 상태를 관리할 때 적합합니다.

사용법:

class HomeViewModel extends Notifier<HomeState> {
  
  HomeState build() {
    return HomeState(counter: 0, updateTime: null);
  }
}

final homeViewModelProvider = NotifierProvider<HomeViewModel, HomeState>((ref) {
  return HomeViewModel();
});

사용 경우:

  • 로그인 정보나 앱에서 전반적으로 사용되는 상태를 관리할 때 유용합니다.

2. AutoDisposeNotifier

AutoDisposeNotifier는 참조가 끝날 때, 즉 위젯이 화면에서 사라지면 자동으로 상태가 제거되는 AutoDisposeNotifier는 화면 간 이동 시 상태를 자동으로 정리하는 데 사용됩니다.

사용법:

class HomeViewModel extends AutoDisposeNotifier<HomeState> {
  
  HomeState build() {
    return HomeState(counter: 0, updateTime: null);
  }
}

final homeViewModelProvider = NotifierProvider.autoDispose<HomeViewModel, HomeState>((ref) {
  return HomeViewModel();
});

사용 경우:

  • 로그인 페이지에서 회원가입 페이지로 이동할 때, 뒤로 가기 후 상태를 초기화하고 싶을 때 유용합니다.

3. AutoDisposeFamilyNotifier

AutoDisposeFamilyNotifierAutoDisposeNotifier와 비슷하지만, 위젯에서 값을 넘겨받아 특정 작업을 처리할 때 사용됩니다. 예를 들어, 블로그 포스트 상세 페이지에서 PostId를 넘겨서 해당 포스트의 정보를 받아올 때 사용됩니다.

사용법:

class HomeViewModel extends AutoDisposeFamilyNotifier<HomeState, int> {
  
  HomeState build(int postId) {
    return HomeState(user: null, updateTime: null);
  }

  void fetchUserInfo(int userId) {
    // 사용자 정보 가져오기
  }
}

final homeViewModelProvider = NotifierProvider.autoDispose.family<HomeViewModel, HomeState, int>((ref) {
  return HomeViewModel();
});

// Widget에서 값 넘겨주기
Consumer(
  builder: (context, ref, child) {
    HomeState homeState = ref.watch(homeViewModelProvider(1)); // 1은 postId
    return Text('');
  },
);

사용 경우:

  • 블로그 목록에서 블로그 상세 페이지로 넘어갈 때, PostId를 넘겨서 해당 정보를 불러올 때 사용합니다. 화면을 이동하면서 AutoDisposeFamilyNotifier를 사용하면 기존 상태를 지우고 새로 데이터를 받아올 수 있습니다.

결론

RiverPod는 상태 관리뿐만 아니라 API 통신 및 비즈니스 로직을 효율적으로 처리할 수 있는 강력한 라이브러리입니다. Notifier를 통해 상태 관리와 데이터를 쉽게 처리하고, AutoDispose를 활용하여 화면 전환 시 상태를 자동으로 정리할 수 있습니다. AutoDisposeFamilyNotifier는 매개변수를 넘겨받아 유연한 상태 관리를 가능하게 해줍니다. Consumer 위젯을 사용하여 상태 변화를 구독하고, 앱을 효율적으로 관리할 수 있습니다.


0개의 댓글