(Flutter) Riverpod의 Provider란

반응형

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

[카카오페이로 후원하기] [토스페이로 후원하기]

반응형