Provider는 Riverpod 애플리케이션에서 가장 중요한 부분입니다. Provider는 상태(state)를 캡슐화하여 해당 상태를 수신할 수 있도록 하는 객체(object)입니다.
Provider를 사용하는 이유?
- 여러 위치에서 해당 상태에 쉽게 접근할 수 있습니다. Provider는 싱글톤(Singleton), 서비스 로케이터(Service Locator,), 의존성 주입(Dependency Injection) 또는 InheritedWidgets와 같은 패턴을 완전히 대체할 수 있습니다.
- 이 상태를 다른 Provider의 상태와 간단하게 결합할 수 있습니다. 여러 상태를 병합하여 하나의 상태에 정리하는데 어려움을 겪은 적이 있다면, Provider는 이를 위한 기능을 제공하고 있습니다.
- 앱의 성능을 최적화합니다. 예를 들어, 위젯 리빌드를 조건을 제한하거나 부하가 높은 상태 계산을 캐싱하는 등의 작업이 가능합니다. Provider는 상태 변화에 의한 외부에의 영향을 컨트롤해 줍니다.
- 앱의 테스트 용이성을 높여줍니다. Provider를 사용하면 복잡한
setUp
/tearDown
단계가 필요하지 않습니다. 또한 테스트 중에 모든 Provider가 다르게 동작하도록 재정의할 수 있습니다. 이렇게 하면 특정 조건에서의 동작을 쉽게 테스트할 수 있습니다. - 로깅 또는 풀 투 리프레시(당겨서 새로고침) 등 고급 기능과 쉽게 통합할 수 있습니다.
Provider 만들기
Provider 다양한 변형이 있지만 모두 동일한 방식으로 작동합니다.
다음과 같이 전역 상수(global constants)로 선언하는 것이 일반적인 사용 방법입니다.
final myProvider = Provider((ref) {
return MyValue();
});
[!note] 노트
글로벌로 선언하는 것에 불안하는 분도 있을 수 있습니다. 하지만 걱정할 필요 없습니다. Provider는 완전히 이뮤터블(불변)이며, 함수를 글로벌로 선언하는 것과 다르지 않습니다. 또한 Provider는 테스트 및 유지보수가 가능합니다.
위의 코드는 세 부분으로 구성되어 있습니다.
final myProvider
는 변수 선언입니다. Provider의 상태를 읽으려면 이 변수를 사용합니다. Provider는 항상final
로 선언해야합니다.Provider
는 여기에서 사용하는 Provider의 종류입니다. Provider 는 모든 Provider 중에서 가장 기본적인 Provider입니다. 외부에서는 절대 변경할 수 없는 객체를 노출합니다. 값이 상호 작용하는 방식을 변경하기 위해 Provider를 StreamProvider나 StateNotifierProvider 등 다른 Provider로 대체할 수 있습니다.- 나머지는, 공유 상태를 생성하는 함수입니다. 이 함수는 항상
ref
오브젝트를 파라미터로 받습니다. 이ref
를 사용해 다른 Provider를 읽거나, Provider의 상태가 소멸될 때 일부 작업을 수행하는 등의 작업을 수행할 수 있습니다.
Provider 내에서 생성할 수 있는 오브젝트의 형태는, 사용하는 Provider의 종류에 따라서 다릅니다. 예를 들어, Provider 에서는 모든 객체도 생성할 수 있습니다. 반면에 StreamProvider 에서는 Stream 객체를 반환할 것으로 예상됩니다.
[!info] 정보
선언할 수 있는 Provider의 수에는 제한이 없습니다. 또한 Riverpod 는package:provider
달리 동일한 유형의 개체를 노출하는 여러 Provider를 선언할 수 있습니다.final cityProvider = Provider((ref) => 'London'); final countryProvider = Provider((ref) => 'England');
위 예제에서는 두개의 Provider가 모두
String
오브젝트를 생성합니다. 하지만 문제가 발생하지는 않습니다.
[!warning] 주의
Provider를 사용하려면 Flutter 앱의 루트에 ProviderScope를 배치해야 합니다.void main() { runApp(ProviderScope(child: MyApp())); }
Provider의 종류
다양한 유형의 Provider가 있으며 각각 용도가 다릅니다.
각 상황에서 어떤 Provider를 사용하는 것이 좋을지 선택하기 어려운 경우가 있습니다. 아래 표를 참조하여 Provider를 선택하세요.
Provider 타입 | Provider 생성 함수 | 사용 사례 |
---|---|---|
Provider | 모든 타입을 반환 | 서비스 클래스/계산된 속성(필터링된 목록 등) |
StateProvider | 모든 타입을 반환 | 필터 조건/단순한 상태 객체 |
FutureProvider | 모든 Future 타입을 반환 | API 호출 결과 |
StreamProvider | 모든 Stream 타입을 반환 | API 호출 결과의 Stream |
StateNotifierProvider | StateNotifier의 하위 클래스를 반환 | 불변(변경할 수 없는)의 복잡한 상태 객체(인터페이스를 통하는 경우는 제외) |
ChangeNotifierProvider | ChangeNotifier의 하위 클래스를 반환 | 가변성(변하기 쉬운)의 복잡한 상태 객체 |
[!warning] 주의
모든 Provider는 목적이 있지만, 확장 가능한 앱 개발에 ChangeNotifierProvider를 사용하는 것은 권장되지 않습니다. 뮤터블(가변) 상태가 다양한 문제를 일으킬 수 있기 때문입니다. 이것은package:provider
에서의 마이그레이션을 용이하게 하기 위해서flutter_riverpod
패키지에 존재합니다. 그리고 Navigator 2.x 패키지로의 사용 등 Flutter 특정 사용 케이스에만 허용합니다.
Provider Modifiers
모든 Provider에는 다른 Provider에 기능을 추가할 수 있는 방법이 내장되어 있습니다.
ref
객체에 새로운 기능을 추가하거나 Provider가 사용되는 방식을 약간 변경할 수 있습니다. Modifiers는 명명된 생성자와 유사한 문법으로 모든 Provider에서 사용할 수 있습니다:
final myAutoDisposeProvider = StateProvider.autoDispose<int>((ref) => 0);
final myFamilyProvider = Provider.family<String, int>((ref, id) => '$id');
현재 사용할 수 있는 Modifiers는 두 종류가 있습니다.
- .autoDispose는 Provider를 더이상 사용하지 않을때, Provider가 자동적으로 상태를 파기하도록 합니다.
- .family는 Provider 외부의 파라미터를 이용해 Provider를 생성할 수 있습니다.
[!note] 노트
Provider는 한번에 여러 Modifiers를 사용할 수도 있습니다.final userProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async { return fetchUser(userId); });
or
'개발 > 플러터(Flutter)' 카테고리의 다른 글
(Flutter) Riverpod의 Provider 상태 결합하기 (0) | 2023.03.27 |
---|---|
(Flutter) Riverpod의 Provider 사용 방법 (0) | 2023.03.26 |
(Flutter) Riverpod 시작하기 (0) | 2023.03.26 |
(Flutter) Riverpod란? (0) | 2023.03.25 |
(Flutter) Auto Route 사용하기 (0) | 2022.03.30 |