본문으로 건너뛰기

프로바이더란?

Riverpod 설치를 완료했다면 이제 "providers"(프로바이더)에 대해 알아보도록 하겠습니다.

프로바이더는 Riverpod 애플리케이션의 가장 중요한 파트 입니다.

프로바이더는 하나의 상태조각의 압축(encapsulates)된 객체이자 상태의 변화를 감시하는 역할을 가지고 있습니다.

왜 프로바이더를 사용하나요?

상태를 프로바이더로 감싸게 되면 아래의 서술된 내용들이 가능합니다.

  • 코드상 다양한 위치에서 상태를 쉽게 접근할 수 있습니다. 프로바이더는 Singletons, Service Locators, Dependency Injection 또는 InheritedWidgets 과 같은 디자인 패턴들을 완벽하게 대체할 수 있습니다.

  • 다른 프로바이더 상태와 간편하게 결합하여 사용할 수 있습니다. 혹시 여러 객체들을 하나로 병합하는데 어려움을 격은적이 있나요? 프로바이더를 사용하면 프로바이더의 간단한 문법을 사용하여 구현할 수 있습니다.

  • 퍼포먼스 최적화가 가능합니다. 위젯을 다시 빌드하는것을 필터링 하거나 비용이 높은 상태 계산을 캐싱하거나 프로바이더는 상태 변경의 영향을 받는 항목만 다시 계산합니다.

  • 애플리케이션의 태스트 용이성이 높아집니다. 프로바이더와 함께라면, 복잡한 setUp/tearDown 단계가 불필요 합니다. 게다가 어떠한 프로바이더이든지 테스트 중의 프로바이더 행위를 오버라이드 할 수 있습니다. 매우 특정한 행위(동작)을 테스트 하기 쉽습니다.

  • 고급 기능들과 함께 손 쉬운 통합이 가능합니다. 예를들어 로깅(logging) 또는 pull-to-refresh가 있습니다.

프로바이더 생성하기

다양한 프로바이더들이 있지만, 기본적으로 모두 동일한 방식으로 동작합니다.

가장 보편적으로 사용하는 방법으로 전역 변수(global constants)로 선언하여 사용하는 방법이 있습니다.

final myProvider = Provider((ref) {
return MyValue();
});
노트

프로바이더를 글로벌하게 전역변수로 선언하여 사용하는것을 두려워하지 마세요. 프로바이더는 완전 immutable 특성을 가집니다. 프로바이더를 선언하는 것은 함수를 선언하는것과 다르지 않습니다. 그리고 프로바이더들은 테스트할 수 있고 유지보수 할 수 있습니다.

위 코드 정보는 3개의 컴포넌트를 구성하고 있습니다.

  • final myProvider, 변수 선언입니다. 이 변수는 항상 불변(immutable) 특성을 가질 것 입니다. 그리고 추후 프로바이더의 상태를 읽기위해 사용하게 됩니다.

  • Provider, 우리가 코드에서 사용하기 위해 결정한 프로바이더 입니다. Provider는 모든 프로바이더 종류들 중 가장 기본이 되는 친구입니다. 이 객체는 위에서 설명한 것 처럼 변하지 않는 불변의 특성을 가지게 됩니다. 우리는 여기서 어떻게 상태(값)과 상호 작용하는지에 따라서 Provider를 다른 종류의 프로바이더로 바꿔 사용할 수 있습니다. 예를 들어 StreamProvider 또는 StateNotifierProvider 가 있습니다.

  • 공유 상태를 생성하는 함수입니다. 이 함수는 ref 객체를 파라미터로 받습니다. 이 객체는 다른 프로바이더들을 읽기 위해서 사용하거나 프로바이더의 상태가 소멸될 때 일부 작업을 수행할 수 있도록 합니다.

프로바이더 내부에서 생성되는 객체의 형태는 사용하는 프로바이더의 종류에 따라 다르게 생성됩니다. 예를 들어 Provider의 함수는 어떤 객체든 생성가능합니다. 반면에 StreamProviderStream 객체를 생성할 필요가 있습니다. 여기서 생성이라고 함은 StreamProvider에서 Stream을 반환해야합니다.

정보

어떠한 제한 없이 원하는데로 수 많은 종류의 프로바이더들을 선언할 수 있습니다. package:provider를 사용할때와 반대로, Riverpod에서는 같은 "type"의 상태를 노출하는 2개의 프로바이더를 가지게 됩니다.

final cityProvider = Provider((ref) => 'London');
final countryProvider = Provider((ref) => 'England');

사실 위의 예에서는 2개의 프로바이더가 String 값을 생성하는 것은 어떠한 문제의 원인이 되지 않습니다. (역자: '2개의 프로바이더가 String값을 생성하는데 문제될 것이 없다'라는 의미로 받아들이면 좋겠습니다.)

주의

프로바이더가 동작하기 위해서 ProviderScope를 Flutter 앱의 가장 최상위(root) 부모 위젯으로 감싸줘야 합니다.

void main() {
runApp(ProviderScope(child: MyApp()));
}

상태가 소멸되기 전에 액션 취하기

일부 케이스에서, 프로바이더의 상태는 소멸(파괴)되거나 재 생성될 수 있습니다. 통상적인 경우 이러한 상황들은 프로바이더 소멸 상태 전에 초기화를 진행하는 경우 입니다. 예를 들어 StreamController를 닫아주는 경우가 있습니다.

이것은 프로바이더 내부에서 사용하는 ref 객체의 onDispose 메소드를 사용할 수 있습니다.

다음 사용예시에서 onDispose를 사용하여 StreamController를 닫는 과정을 알아봅시다.

final example = StreamProvider.autoDispose((ref) {
final streamController = StreamController<int>();

ref.onDispose(() {
// 프로바이더의 상태가 소멸되기 전 StreamController를 닫습니다(close).
streamController.close();
});

return streamController.stream;
});
노트

사용하는 프로바이더에 따라서, 이러한 리소스 해제 처리 작업을 내부에서 자동으로 진행하는 경우도 있습니다. 예를 들어 StateNotifierProviderStateNotifierdispose메소드를 호출합니다.

프로바이더 수식자(Modifiers)

모든 프로바이더들은 다른 프로바이더와 추가적인 기능을 추가하기 위한 방법이 기본적으로 내장되어 있습니다. ref object에 새로운 특징을 추가하거나 프로바이더가 어떻게 상태를 소모(사용)하는지를 변경할 수 있습니다. Modifiers는 모든 프로바이더에서 사용할 수 있습니다. 이는 named constructor(생성자) 문법과 유사합니다.

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

지금은 2개의 사용가능한 수식어가 있습니다.

  • .autoDispose 는 더 이상 상태를 구독하지 않을때 자동으로 프로바이터를 소멸되도록 합니다.
  • .family 외부 파라미터로부터 프로바이더를 생성할 때 사용합니다.
노트

프로바이더는 복수의 수식어(modifiers)를 한번에 사용할 수 있습니다.

final userProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
return fetchUser(userId);
});

프로바이더에 대한 설명은 여기까지 입니다!

프로바이더 읽기 문서에서 계속됩니다. 또는 프로바이더 결합하기문서도 확인할 수 있습니다.