Riverpod

박지훈·2023년 8월 10일
1

Widget & Package

목록 보기
3/4
post-thumbnail

Riverpod?

Flutter에서 상태 관리를 도와주는 도구로, 주로 Provider와 함께 사용되어 널리 알려져 있다.
Riverpod외에도 상태 관리 툴로는 Provider, Bloc, GetX 등이 있다.
💡 RiverpodProvider의 몇 가지 한계점을 해결하고자 하는 목적으로 탄생했다.✨

🤔 상태관리? 그게 뭔데?

말 그대로 상태(state)를 관리하는 것을 뜻한다. 여기서 상태(state)란 앱의 어느 시점에 가지고 있는 모든 정보를 의미한다. 예를 들어, 사용자 세션, 화면에서 보이는 데이터, 버튼이 활성화 되어있는지 여부 등이 상태에 포함될 수 있다.
방법에는 로컬 상태 관리, 글로벌 상태 관리, 의존성 주입 등이 있다.

물론, Riverpod과 같은 상태 관리 도구를 써야만 상태를 관리 할 수 있는 건 아니다.
내가 플러터를 접하고, 처음 상태를 관리할 시점에 깨닫고 쓰지는 않았던 거 같지만, StatefulWidget으로 이미 해왔다.

StatefulWidget의 setState()

StatefulWidget의 한계

프로젝트의 규모나 복잡도가 증가하면, setState만으로는 다음과 같은 제한 사항과 문제점이 발생할 수 있다. 이런 문제점들 때문에 개발자들은 보다 고급 상태 관리 툴을 찾게 됩니다.

  1. 로컬 상태 제한: 다른 위젯이나 전체 애플리케이션에 영향을 미치는 상태를 관리하기 어렵다.

  2. 상태 접근 및 재사용성: 다양한 위치에서 상태를 접근하거나 재사용하려면, 상태를 위젯 트리의 상위로 옮겨야 하는데, 이는 코드의 복잡도를 증가시킬 수 있다.

  3. 의존성 관리: 위젯 간의 의존성이 복잡해질 경우, setState만으로는 의존성 주입이나 상태의 전파가 어려워진다.

  4. 복잡한 상태 로직: 앱의 로직이 복잡해질 경우, 이 로직을 관리하고 테스트하기 위해서는 분리된 상태 관리 방식이 필요하다.

  5. 성능 최적화: setState는 해당 위젯을 재빌드한다. 때문에 불필요한 재빌드를 최소화하기 위한 최적화가 필요하다.

  6. 상태 변경 추적: 고급 상태 관리 툴은 상태 변화의 기록이나 시간 여행 디버깅과 같은 기능을 제공하기도 한다.

  7. 비동기 작업: 복잡한 비동기 작업을 관리하기 위해서는, 보다 체계적인 상태 관리 도구가 필요할 수 있다.

위와 같은 이유로, setState는 간단한 상황이나 작은 애플리케이션에서는 적합하지만, 규모가 크거나 복잡한 프로젝트에서는 다양한 문제점을 겪을 수 있다.

Riverpod의 주요 특징 및 장점

  1. 컨텍스트에 의존하지 않는다: Provider의 경우 BuildContext에 의존하여 데이터에 접근하지만, Riverpod은 그렇지 않다. 이로 인해 상태 접근이 더 유연해진다.

  2. 다양한 프로바이더 타입: Riverpod에는 여러 프로바이더 타입이 제공되어 다양한 상황에 적합하게 사용할 수 있다.

  3. 강력한 타입 안전성: 강력한 타입 안전성을 제공한다. 각 프로바이더는 고유한 타입을 가진다, 따라서 런타임 오류보다는 컴파일 시간에 대부분의 오류를 찾을 수 있다.

  4. 재사용성: Riverpod의 프로바이더는 재사용 가능하며, 동일한 프로바이더를 여러 곳에서 다르게 구성하여 사용할 수 있다.

  5. 프로바이더 체인: Riverpod를 사용하면 여러 프로바이더를 쉽게 연결할 수 있어 복잡한 상태 의존성을 관리하기 쉽다.

공식 문서의 메인화면

공식 문서: 한글도 지원한다는 게 매우 반갑고 신기하다!

🥲 너무 어렵다! 아무튼 상태 관리 툴을 사용하면 많은 이점이 있다고 알아두자!


Riverpod의 Providers

모든 Provider는 글로벌하게 선언된다!

  1. Provider:

    • 설명:
      • 가장 기본적인 프로바이더 유형. 값을 생성하고 제공하는 데 사용된다. 아무 타입이나 반환가능.
    • 적합한 사용 시기:
      • 단순한 값을 제공하거나, 의존성 주입에 적합하다. Service, 계산값들을 반환할때 사용.
      • 반환값을 캐싱할 때 유용하게 사용된다. -> 빌드횟수 최소화
      • 여러 Provider들을 묶어서 한번에 반환값을 만들어 낼 수 있다.
  2. FutureProvider:

    • 설명:
      • Future를 반환하는 함수를 사용하여 값을 제공.
      • 값의 로딩, 완료, 오류 상태에 따라 다른 위젯을 표시하는 데 유용하다.
      • 복잡한 로직 또는 사용자의 행동뒤에 future를 재실행 ❌
        • 필요한 경우 StateNotifierProvider 사용
    • 적합한 사용 시기:
      • API 호출이나 데이터베이스 쿼리와 같은 비동기 작업을 처리할 때 사용한다.
  3. StreamProvider:

    • 설명:
      • Stream을 반환하는 함수를 사용하여 값을 제공한다. Stream의 데이터가 업데이트될 때마다 위젯도 업데이트된다.
    • 적합한 사용 시기:
      • 실시간 데이터 업데이트가 필요한 경우, 예를 들면 Firebase Realtime Database와 같은 스트림 기반의 데이터 소스를 사용할 때 유용하다.
  4. StateProvider:

    • 설명:
      • 내부적으로 상태를 가진 프로바이더. 이 상태는 위젯의 생명주기 동안 변경될 수 있다.
    • 적합한 사용 시기:
      • 단순한 상태 변경을 위해, 예를 들면 토글 버튼의 상태나 단일 값을 위한 상태 관리에 적합하다. (Map, List 같은 복잡한 데이터 ❌)
      • 복잡한 로직이 필요한 경우 사용 ❌
        • number++ 정도의 간단한 경우로만 한정.
      • UI에서 "직접적으로" 데이터를 변경하고 싶을 때.
  5. StateNotifierProvider:

    • 설명:
      • StateNotifier를 기반으로 하는 프로바이더이다.
      • StateNotifier는 상태를 관리하는 객체로서, 복잡한 상태 로직을 갖는 경우에 적합.
      • StateNotifierstate가 변경될 때마다 위젯이 재빌드된다.
      • StateNotifier는 단일 상태 변경에 최적화된 설계를 가지고 있다.
    • 적합한 사용 시기:
      • StateProvier와 마찬가지로 "직접적으로"" 데이터를 변경할 수 있도록 사용하고 싶을 때
      • 복잡한 상태 로직을 다루거나 여러 상태 변경을 한 번에 처리해야 할 때.(클래스의 메소드를 이용한 상태관리)
      • ChangeNotifier와 비교하여 StateNotifier는 상태를 직접 변경하는 대신, 외부에서 새로운 상태를 전달하여 변경하는 패턴을 선호한다. 따라서 불변성을 중요시하는 Flutter와 잘 맞다.

💡 요약 정리 차트

Provider 종류반환값사용 예제
Provider아무 타입데이터 캐싱
FutureProvierFuture 타입API 요청의 Future 결과값
StreamProviderStream 타입API 요청의 Stream 결과값
StateProvider아무 타입간단한 상태값 관리
StateNotifierProviderStateNotifier를 상속한 값 반환복잡한 상태값 관리

Single source of truth(SSOT)

데이터 관리 원칙 중 하나로, 특정 데이터나 정보의 진실성(정확성 및 일관성)을 보장하기 위해 그 데이터나 정보의 복사본이 여러 군데에 분산되어 있지 않고, 한 곳에서만 관리되고 접근될 수 있도록 하는 원칙을 말한다.

ref.watch() vs ref.read()

ref.watch(): 반환값의 업데이트가 있을 때 지속적으로 build 함수를 실행한다. 필수적으로 UI관련 코드에만 사용한다.
ref.read(): 실행되는 순간 단 한번만 provider 값을 가져온다. onPressed() 콜백처럼 특정 액션뒤에 실행되는 함수 내부에서 사용된다.

값을 바꾸고 싶을 때는 아래와 같이 .notifier.update() 함수를 사용하면 된다.

ref.read(numberProvider.notifier).update((state) => state + 1);

또는 아래처럼 .state를 이용해 직접적으로 값을 바꿀 수도 있다.

// .state는 provider 값을 직접 가져온다.
ref.read(numberProvider.notifier).state = ref.read(numberProvider.notifier).state + 1;

Provider 예시코드들

예시코드 깃주소

profile
Flutter 개발자가 되어보자!

0개의 댓글