빠른 시작
이 섹션은 Provider 패키지에 익숙하고 Riverpod을 배우고 싶은 분들을 위한 것입니다.
무엇보다 먼저 짧은 시작하기 글을 읽고, 작은 sandbox 예제로 Riverpod의 기능을 직접 사용해 보세요. 마음에 든다면 마이그레이션을 적극적으로 고려해 볼 만합니다.
실제로 Provider에서 Riverpod으로의 마이그레이션은 아주 간단할 수 있습니다.
마이그레이션은 기본적으로 점진적으로 진행할 수 있는 몇 단계로 이루어집니다.
ChangeNotifierProvider로 시작하기
Riverpod으로 옮겨 가는 동안에는 ChangeNotifier를 계속 사용해도 괜찮으며,
최신 기능을 서둘러 도입할 필요도 없습니다.
실제로 다음과 같이 시작해도 전혀 문제없습니다:
// 이런 코드가 있다면...
class MyNotifier extends ChangeNotifier {
int state = 0;
void increment() {
state++;
notifyListeners();
}
}
// ... 이것만 추가하면 됩니다!
final myNotifierProvider = ChangeNotifierProvider<MyNotifier>((ref) {
return MyNotifier();
});
보다시피 Riverpod은 ChangeNotifierProvider 클래스를 제공하는데, 바로 pkg:Provider로부터의 마이그레이션을 지원하기 위한 것입니다.
새 코드를 작성할 때는 이 provider를 권장하지 않으며, Riverpod을 사용하는 최선의 방법도 아니라는 점을 기억하세요. 다만 마이그레이션을 시작하기에는 부드럽고 아주 쉬운 방법입니다.
ChangeNotifier를 더 현대적인 Notifier로 당장 바꾸려고 서두를 필요는 없습니다.
일부는 사고방식을 조금 바꿔야 하므로 처음에는 어려울 수 있습니다.
천천히 진행하세요. 먼저 Riverpod에 익숙해지는 것이 중요합니다. pkg:provider의 Provider는 거의 모두 pkg:riverpod에 정확히 대응하는 것이 있다는 사실을 금방 알게 될 것입니다.
리프부터 시작하기
아무것에도 의존하지 않는 Provider부터, 즉 의존성 트리의 리프(leaf)부터 시작하세요.
모든 리프를 마이그레이션했다면, 그다음 리프에 의존하는 provider로 넘어가면 됩니다.
다시 말해 처음에는 ProxyProvider 마이그레이션을 피하고, 그 의존성이 모두 마이그레이션된 뒤에 다루세요.
이렇게 하면 마이그레이션이 빨라지고 단순해지며, 오류를 줄이고 추적하기도 쉬워집니다.
Riverpod과 Provider는 함께 쓸 수 있습니다
Provider와 Riverpod을 동시에 사용하는 것도 얼마든지 가능하다는 점을 기억하세요.
실제로 import 별칭(alias)을 사용하면 두 API를 함께 쓸 수 있습니다.
이 방식은 가독성에도 좋고, API를 모호하게 사용하는 일도 없애 줍니다.
이렇게 할 계획이라면 코드베이스의 모든 Provider import에 import 별칭을 사용하는 것을 고려하세요.
import 별칭을 효과적으로 적용하는 방법에 대한 전체 가이드는 곧 제공될 예정입니다.
한 번에 provider 하나씩 마이그레이션하기
기존 앱이 있다면 모든 provider를 한꺼번에 마이그레이션하려고 하지 마세요!
장기적으로는 애플리케이션 전체를 Riverpod으로 옮기는 것을 목표로 해야 하지만,
무리하지 마세요.
한 번에 provider 하나씩 진행하세요.
위 예제를 보겠습니다. myNotifierProvider를 Riverpod으로 완전히 마이그레이션하려면 다음과 같이 작성해야 합니다:
class MyNotifier extends Notifier<int> {
int build() => 0;
void increment() => state++;
}
final myNotifierProvider = NotifierProvider<MyNotifier, int>(MyNotifier.new);
.. 그리고 그 provider를 사용하는 방식도 함께 바꿔야 합니다. 즉, 이 provider에 대한 모든 context.watch를 ref.watch로 바꿔 써야 합니다.
이 작업은 시간이 걸리고 오류가 생길 수도 있으니, 한꺼번에 서둘러 하지 마세요.
ProxyProvider 마이그레이션하기
pkg:Provider에서 ProxyProvider는 다른 Provider의 값을 조합하는 데 쓰이며,
그 빌드는 다른 provider의 값에 반응형으로 의존합니다.
반면 Riverpod의 Provider는 기본적으로 조합이 가능합니다. 따라서 ProxyProvider를 마이그레이션할 때
한 Provider에서 다른 Provider로의 직접적인 의존성을 선언하려면 ref.watch만 쓰면 됩니다.
오히려 Riverpod에서 값을 조합하는 쪽이 더 단순하고 직관적으로 느껴질 것이므로, 마이그레이션을 통해 코드가 크게 단순해질 것입니다.
게다가 세 개 이상의 provider를 조합할 때도 복잡한 요령이 필요 없습니다.
ref.watch를 하나 더 추가하기만 하면 됩니다.
즉시 초기화
Notifier는 전역 final 변수이므로 기본적으로 지연(lazy) 초기화됩니다.
앱 시작 시 미리 준비해 둘 데이터나 유용한 서비스를 초기화해야 한다면,
예전에 MultiProvider를 두던 자리에서 provider를 먼저 읽는 것이 가장 좋은 방법입니다.
다시 말해 Riverpod은 즉시 초기화를 강제할 수 없으므로, 시작 단계에서 provider를 읽어 캐시해 두면 애플리케이션의 나머지 부분에서 필요할 때 바로 사용할 수 있습니다.
pkg:Notifier의 즉시 초기화에 대한 전체 가이드는 여기에서 볼 수 있습니다.
코드 생성
Riverpod을 미래에도 대비된 방식으로 사용하려면 코드 생성을 권장합니다.
참고로, 메타프로그래밍이 도입되면 Riverpod에서는 코드 생성이 기본이 될 가능성이 높습니다.
안타깝게도 @riverpod는 ChangeNotifierProvider에 대한 코드를 생성할 수 없습니다.
이를 해결하려면 다음 유틸리티 확장 메서드를 사용할 수 있습니다:
extension ChangeNotifierWithCodeGenExtension on Ref {
T listenAndDisposeChangeNotifier<T extends ChangeNotifier>(T notifier) {
notifier.addListener(notifyListeners);
onDispose(() => notifier.removeListener(notifyListeners));
onDispose(notifier.dispose);
return notifier;
}
}
그런 다음 코드 생성 문법으로 ChangeNotifier를 다음과 같이 노출할 수 있습니다:
// ignore_for_file: unsupported_provider_value
MyNotifier example(Ref ref) {
return ref.listenAndDisposeChangeNotifier(MyNotifier());
}
"기본" 마이그레이션이 끝나면 ChangeNotifier를 Notifier로 바꿔
임시 확장이 더 이상 필요 없게 만들 수 있습니다.
앞의 예제를 이어 보면, "완전히 마이그레이션된" Notifier는 다음과 같습니다:
class MyNotifier extends _$MyNotifier {
int build() => 0;
void increment() => state++;
}
이 작업을 마치고 코드베이스에 ChangeNotifierProvider가 더 이상 없다는 것이 확실해지면
임시 확장을 완전히 제거해도 됩니다.
코드 생성은 권장 사항일 뿐 필수는 아니라는 점을 기억하세요.
마이그레이션은 점진적으로 생각하는 것이 좋습니다.
이 마이그레이션과 코드 생성 문법으로의 전환을 동시에 한 번에 진행하는 것이
부담스럽게 느껴진다면, 그래도 괜찮습니다.
이 가이드를 따라 진행한 뒤, 나중에 다음 단계로 코드 생성으로 마이그레이션할 수 있습니다.