본문으로 바로가기
  1. Home
  2. Flutter/Package
  3. Flutter Riverpod Concepts(6-1) - Providers

Flutter Riverpod Concepts(6-1) - Providers

· 댓글개 · Dev_Whale

Providers

이제 Riverpod를 설치했으니 " provider"에 대해 이야기해 보겠습니다.


Provider는 Riverpod 애플리케이션에서 가장 중요한 부분입니다. provider는 상태의 일부를 캡슐화하고 해당 상태를 수신할 수 있도록 하는 객체입니다.

왜 Provider를 사용해야 하나요?

provider안에 상태 부분을 넣습니다:

  • 여러 위치에서 해당 상태에 쉽게 액세스할 수 있습니다. Provider는 Singleton, Service Locator, Dependency Injection 또는 InheritedWidgets와 같은 패턴을 완전히 대체합니다.
  • 이 상태를 다른 상태와 결합하는 작업을 단순화합니다. 여러 개체를 하나로 병합하는 데 어려움을 겪은 적이 있으신가요? 이 시나리오는 provider 내부에서 직접 구축됩니다.
  • 성능 최적화를 지원합니다. 위젯 리빌드를 필터링하거나 비용이 많이 드는 상태 계산을 캐싱할 때 provider는 상태 변경의 영향을 받는 부분만 다시 계산하도록 합니다.
  • 애플리케이션의 테스트 가능성을 높입니다. providers를 사용하면 복잡한 설정/해체 단계가 필요하지 않습니다. 또한 모든 프로바이더를 테스트 중에 다르게 동작하도록 재정의할 수 있으므로 매우 특정한 동작을 쉽게 테스트할 수 있습니다.
  • loggin 또는 pull-to-refresh와 같은 고급 기능과 쉽게 통합할 수 있습니다.

Creating a provider

Provider는 다양한 종류가 있지만 모두 동일한 방식으로 작동합니다.

 

가장 일반적인 사용법은 다음과 같이 전역 상수로 선언하는 것입니다:

@riverpod
MyValue my(MyRef ref) {
  return MyValue();
}
NOTE)
provider의 전역 측면에 겁먹지 마세요. Provider는 완전히 불변합니다. Provider를 선언하는 것은 함수를 선언하는 것과 다르지 않으며, Provider는 테스트 및 유지 관리가 가능합니다.

이 스니펫은 세 가지 구성 요소로 이루어져 있습니다:

  • final myProvider, 즉 변수의 선언입니다. 이 변수는 앞으로 providers의 상태를 읽는 데 사용할 변수입니다. Provider는 항상 final이 되어야 합니다.
  • Provider, 사용하기로 결정한 provider입니다. Provider는 모든 provider 중 가장 기본적인 것입니다. 절대 변하지 않는 객체를 노출합니다. Provider를 StreamProvider나 NotifierProvider와 같은 다른 프로바이더로 대체하여 값과 상호 작용하는 방식을 변경할 수 있습니다.
  • 공유 상태를 생성하는 함수입니다. 이 함수는 항상 ref라는 객체를 매개변수로 받습니다. 이 객체를 통해 다른 provider를 읽고, provider의 상태가 소멸될 때 일부 작업을 수행하는 등의 작업을 수행할 수 있습니다.

Provider로 전달된 함수가 반환하는 객체의 유형은 사용된 Provider에 따라 다릅니다. 예를 들어, Provider의 함수는 모든 객체를 생성할 수 있습니다. 반면에 StreamProvider의 콜백은 Stream을 반환할 것으로 예상됩니다.

INFO)
제한 없이 원하는 만큼의 프로바이더를 선언할 수 있습니다. package:provider를 사용할 때와 달리 Riverpod에서는 동일한 “type"의 상태를 노출하는 여러 provider를 만들 수 있습니다:
@riverpod
String city(CityRef ref) => 'London';
@riverpod
String country(CountryRef ref) => 'England';​

두 provider 모두 문자열을 생성해도 문제가 발생하지 않습니다.

CAUTION)
Provider를 사용하려면 Flutter 애플리케이션의 main에 ProviderScope를 추가해야 합니다:
void main() {
  runApp(ProviderScope(child: MyApp()));
}​

Different Types of Providers

다양한 사용 사례를 위한 여러 유형의 provider가 있습니다.

이러한 모든 provider를 사용할 수 있으므로 어떤 provider 유형을 다른 provider 유형보다 사용해야 하는지 이해하기 어려울 때가 있습니다. 아래 표를 참조하여 위젯 트리에 제공하려는 내용에 맞는 공급자를 선택하세요.

  • Provider: 서비스 클래스나 계산된 속성(필터된 리스트 등)을 위해 사용됩니다.

서비스 클래스 :

더보기

"서비스 클래스"는 일반적으로 애플리케이션의 비즈니스 로직을 캡슐화하는 클래스를 의미합니다. 이 클래스는 특정 기능을 구현하는 메소드를 제공하며, 이 메소드들은 데이터베이스 접근, 네트워크 통신, 계산 등 다양한 작업을 수행할 수 있습니다.

예를 들어, 애플리케이션에서 사용자의 정보를 관리하는 서비스 클래스가 있을 수 있습니다. 이 클래스는 사용자의 데이터를 데이터베이스에서 불러오거나, 사용자의 정보를 업데이트하거나, 사용자를 삭제하는 등의 메소드를 제공할 수 있습니다. 이러한 서비스 클래스는 애플리케이션의 다른 부분(예: UI 컴포넌트)에서 이용되어 기능을 구현하게 됩니다.

서비스 클래스는 애플리케이션의 비즈니스 로직을 한 곳에서 관리하게 해 주어 코드의 유지보수가 용이하게 합니다. 또한, 이를 통해 비즈니스 로직과 UI 로직을 분리함으로써 코드의 가독성과 재사용성을 높일 수 있습니다.

  • StateProvider: 필터 조건이나 간단한 상태 객체를 위해 사용됩니다.
  • FutureProvider: API 호출 결과를 위해 사용됩니다. 이는 Future 객체를 반환합니다.
  • StreamProvider: API에서 스트림 결과를 위해 사용됩니다. 이는 Stream 객체를 반환합니다.
  • (Async)NotifierProvider: 변경 가능한 복잡한 상태 객체를 위해 사용됩니다. Notifier의 서브 클래스를 반환합니다.

변경 가능한 복잡한 상태 객체의 기준 :

더보기

"변경 가능한 복잡한 상태 객체"의 기준은 상황에 따라 다르며, 일반적으로 아래와 같은 몇 가지 요소를 고려하여 판단됩니다:

상태의 복잡성: 상태가 여러 개의 변수나 필드를 가지고 있거나, 이들 간에 복잡한 상호작용이 있는 경우 복잡한 상태로 간주될 수 있습니다. 예를 들어, 사용자 인터페이스의 여러 부분이 서로 다른 방식으로 상태를 공유하거나 수정해야 하는 경우에는 복잡한 상태로 볼 수 있습니다.

변경 가능성: 상태가 시간이 지나면서 변경될 수 있거나, 사용자의 입력이나 외부 이벤트에 의해 변경될 수 있다면, 그 상태는 "변경 가능한" 상태로 간주됩니다.

동시성: 여러 스레드나 프로세스가 동시에 상태를 접근하거나 변경할 수 있는 경우, 이는 복잡한 상태 관리의 문제를 야기할 수 있습니다. 이런 상황에서는 적절한 동기화 메커니즘을 통해 상태의 일관성을 유지해야 합니다.

  • StateNotifierProvider: 변경 가능한 복잡한 상태 객체를 위해 사용됩니다. NotifierProvider의 사용이 권장됩니다. 이는 StateNotifier의 서브 클래스를 반환합니다.
  • ChangeNotifierProvider: 변동성이 필요한 복잡한 상태 객체를 위해 사용됩니다. 이는 ChangeNotifier의 서브 클래스를 반환합니다.
CAUTION)
모든 provider에는 목적이 있지만 변경 가능한 상태와 관련된 문제로 인해 확장 가능한 애플리케이션에는 ChangeNotifierProvider를 권장하지 않습니다. package:provider에서 쉽게 마이그레이션할 수 있는 경로를 제공하기 위해 flutter_riverpod 패키지에 존재하며, 일부 Navigator 2 패키지와의 통합과 같은 일부 Flutter 전용 사용 사례에 사용할 수 있습니다.

Provider Modifiers

모든 Provider는 다양한 Provider에 추가 기능을 추가할 수 있는 기능이 내장되어 있습니다.

 

ref 객체에 새로운 기능을 추가하거나 provider가 사용되는 방식을 약간 변경할 수 있습니다. Modifier는 명명된 생성자와 유사한 구문으로 모든 provider에 사용할 수 있습니다:

final myAutoDisposeProvider = StateProvider.autoDispose<int>((ref) => 0);
final myFamilyProvider = Provider.family<String, int>((ref, id) => '$id');

현재 두 가지 수정자를 사용할 수 있습니다:

  • .autoDispose - 더 이상 수신하지 않을 때 Provider가 자동으로 상태를 파기하도록 합니다.
  • .family -  외부 매개변수에서 provider를 생성할 수 있습니다.
provider는 한 번에 여러 개의 modifier를 사용할 수 있습니다:
final userProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
  return fetchUser(userId);
});​

이 가이드는 여기까지입니다!

provider를 읽는 방법을 계속 읽어보세요. 또는 provider를 결합하는 방법을 살펴보세요.

💬 댓글 개
이모티콘창 닫기
울음
안녕
감사해요
당황
피폐

이모티콘을 클릭하면 댓글창에 입력됩니다.