Flutter에서 상태 관리를 도와주는 도구로, 주로 Provider
와 함께 사용되어 널리 알려져 있다.
Riverpod
외에도 상태 관리 툴로는 Provider
, Bloc
, GetX
등이 있다.
💡 Riverpod
은 Provider
의 몇 가지 한계점을 해결하고자 하는 목적으로 탄생했다.✨
🤔 상태관리? 그게 뭔데?
말 그대로 상태(state)를 관리하는 것을 뜻한다. 여기서 상태(state)란 앱의 어느 시점에 가지고 있는 모든 정보를 의미한다. 예를 들어, 사용자 세션, 화면에서 보이는 데이터, 버튼이 활성화 되어있는지 여부 등이 상태에 포함될 수 있다.
방법에는 로컬 상태 관리, 글로벌 상태 관리, 의존성 주입 등이 있다.
물론, Riverpod
과 같은 상태 관리 도구를 써야만 상태를 관리 할 수 있는 건 아니다.
내가 플러터를 접하고, 처음 상태를 관리할 시점에 깨닫고 쓰지는 않았던 거 같지만, StatefulWidget
으로 이미 해왔다.
StatefulWidget의
setState()
StatefulWidget
의 한계프로젝트의 규모나 복잡도가 증가하면, setState
만으로는 다음과 같은 제한 사항과 문제점이 발생할 수 있다. 이런 문제점들 때문에 개발자들은 보다 고급 상태 관리 툴을 찾게 됩니다.
로컬 상태 제한: 다른 위젯이나 전체 애플리케이션에 영향을 미치는 상태를 관리하기 어렵다.
상태 접근 및 재사용성: 다양한 위치에서 상태를 접근하거나 재사용하려면, 상태를 위젯 트리의 상위로 옮겨야 하는데, 이는 코드의 복잡도를 증가시킬 수 있다.
의존성 관리: 위젯 간의 의존성이 복잡해질 경우, setState
만으로는 의존성 주입이나 상태의 전파가 어려워진다.
복잡한 상태 로직: 앱의 로직이 복잡해질 경우, 이 로직을 관리하고 테스트하기 위해서는 분리된 상태 관리 방식이 필요하다.
성능 최적화: setState
는 해당 위젯을 재빌드한다. 때문에 불필요한 재빌드를 최소화하기 위한 최적화가 필요하다.
상태 변경 추적: 고급 상태 관리 툴은 상태 변화의 기록이나 시간 여행 디버깅과 같은 기능을 제공하기도 한다.
비동기 작업: 복잡한 비동기 작업을 관리하기 위해서는, 보다 체계적인 상태 관리 도구가 필요할 수 있다.
위와 같은 이유로, setState
는 간단한 상황이나 작은 애플리케이션에서는 적합하지만, 규모가 크거나 복잡한 프로젝트에서는 다양한 문제점을 겪을 수 있다.
Riverpod
의 주요 특징 및 장점컨텍스트에 의존하지 않는다: Provider
의 경우 BuildContext에 의존하여 데이터에 접근하지만, Riverpod
은 그렇지 않다. 이로 인해 상태 접근이 더 유연해진다.
다양한 프로바이더 타입: Riverpod
에는 여러 프로바이더 타입이 제공되어 다양한 상황에 적합하게 사용할 수 있다.
강력한 타입 안전성: 강력한 타입 안전성을 제공한다. 각 프로바이더는 고유한 타입을 가진다, 따라서 런타임 오류보다는 컴파일 시간에 대부분의 오류를 찾을 수 있다.
재사용성: Riverpod
의 프로바이더는 재사용 가능하며, 동일한 프로바이더를 여러 곳에서 다르게 구성하여 사용할 수 있다.
프로바이더 체인: Riverpod
를 사용하면 여러 프로바이더를 쉽게 연결할 수 있어 복잡한 상태 의존성을 관리하기 쉽다.
공식 문서: 한글도 지원한다는 게 매우 반갑고 신기하다!
🥲 너무 어렵다! 아무튼 상태 관리 툴을 사용하면 많은 이점이 있다고 알아두자!
Provider:
FutureProvider:
StateNotifierProvider
사용StreamProvider:
StateProvider:
number++
정도의 간단한 경우로만 한정.StateNotifierProvider:
StateNotifier
를 기반으로 하는 프로바이더이다.StateNotifier
는 상태를 관리하는 객체로서, 복잡한 상태 로직을 갖는 경우에 적합.StateNotifier
의 state
가 변경될 때마다 위젯이 재빌드된다.StateNotifier
는 단일 상태 변경에 최적화된 설계를 가지고 있다.StateProvier
와 마찬가지로 "직접적으로"" 데이터를 변경할 수 있도록 사용하고 싶을 때ChangeNotifier
와 비교하여 StateNotifier
는 상태를 직접 변경하는 대신, 외부에서 새로운 상태를 전달하여 변경하는 패턴을 선호한다. 따라서 불변성을 중요시하는 Flutter와 잘 맞다.Provider 종류 | 반환값 | 사용 예제 |
---|---|---|
Provider | 아무 타입 | 데이터 캐싱 |
FutureProvier | Future 타입 | API 요청의 Future 결과값 |
StreamProvider | Stream 타입 | API 요청의 Stream 결과값 |
StateProvider | 아무 타입 | 간단한 상태값 관리 |
StateNotifierProvider | StateNotifier를 상속한 값 반환 | 복잡한 상태값 관리 |
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;