Riverpod - Provider์ ํ๊ณ์ ๋์
์ง๋ ๊ธ์์ Provider๋ก ์ํ๋ฅผ ๊ณต์ ํด๋ดค๋ค. ์์ ์ฑ์์๋ ์ถฉ๋ถํ๋ฐ, ์ฑ์ด ์ปค์ง๋ฉด Provider๊ฐ ๊ฐ์ง ์ฝ์ ์ด ํ๋์ฉ ๋๋ฌ๋๋ค. ์ด๋ฒ์ ๊ทธ ์ฝ์ ์ ๋จผ์ ํ์ธํ๊ณ , ๊ทธ๊ฑธ ๋ณด์ํ Riverpod์ผ๋ก ๊ฐ์ ์นด์ดํฐ๋ฅผ ๋ง๋ค์ด๋ณด์. ์ด ๊ธ์ flutter_riverpod 3.x ๊ธฐ์ค์ด๋ค.
Provider์ ์ฝ์ ์ธ ๊ฐ์ง
๋จผ์ ์ง๋ ๊ธ์์ ์ด๋ฏธ ํ ๋ฒ ๊ฒช์ ์ผ์ด๋ค.
final count = context.watch<CounterModel>().count; // Provider๋ฅผ ๊ฑธ์ด๋์ง ์์๋ค๋ฉด?์ด ์ฝ๋๋ ์ปดํ์ผ์ด ๋๋ค. CounterModel์ ๊ฑธ์ด๋์ง ์์์ด๋ ์๋ํฐ๋ ์๋ฌด ๋ง์ด ์๊ณ , ์ฑ์ ์คํํด์ ๊ทธ ํ๋ฉด์ ๋ค์ด๊ฐ ์๊ฐ ProviderNotFoundException์ด ํฐ์ง๋ค. Provider๋ ๊ฐ์ CounterModel์ด๋ผ๋ ํ์
์ผ๋ก ์ฐพ๊ธฐ ๋๋ฌธ์, ์๋์ง ์๋์ง๋ฅผ ์ปดํ์ผ๋ฌ๊ฐ ๋ฏธ๋ฆฌ ์ ์ ์๋ค.
๋ ๋ฒ์งธ๋ context๊ฐ ๊ผญ ํ์ํ๋ค๋ ์ ์ด๋ค.
// ์์ ฏ์ด ์๋ ๊ณณ์์ CounterModel์ ์ฝ๊ณ ์ถ๋ค๋ฉด?
void saveCount() {
// context๊ฐ ์๋๋ฐ ์ด๋ป๊ฒ ์ฝ์ง?
}context.read๋ ์์ ฏ ํธ๋ฆฌ ์์ ์์น์์๋ง ์ธ ์ ์๋ค. ์์ ฏ์ด ์๋ ์ผ๋ฐ ํจ์๋ ๋ค๋ฅธ ๋ชจ๋ธ ์์์ ๊ฐ์ ์ฝ์ผ๋ ค๋ฉด context๋ฅผ ์ผ์ผ์ด ๋๊ฒจ์ค์ผ ํ๋ค.
์ธ ๋ฒ์งธ๋ ๊ฐ์ ํ์
์ ๊ฐ์ ๋ ์ด์ ๊ฑธ์ด๋ ์ ์๋ค๋ ์ ์ด๋ค. Provider๊ฐ ํ์
์ผ๋ก ์ฐพ์ผ๋, int ๊ฐ์ ๋ ๊ฐ ๊ฑธ์ด๋ ๊ฐ๊น์ด ์ชฝ ํ๋๋ง ๋ณด์ธ๋ค. ๋ ๊ฐ์ ์นด์ดํฐ๊ฐ ํ์ํ๋ฉด ๊ฐ๊ฐ ๋ค๋ฅธ ํด๋์ค๋ก ๊ฐ์ธ์ผ ํ๋ค.
ํด๊ฒฐ: Riverpod - ํ์ ์ด ์๋๋ผ ๋ณ์๋ก ์ฐพ๋๋ค
Riverpod์ Provider๋ฅผ ๋ง๋ ์ฌ๋์ด ๊ฐ์ ๋ฌธ์ ๋ฅผ ํ๋ ค๊ณ ์๋ก ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค. ์ค์น๋ถํฐ ํ๋ค.
flutter pub add flutter_riverpod๊ฐ์ final ๋ณ์๋ก ์ ์ญ์ ์ ์ธํ๋ค.
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = NotifierProvider<Counter, int>(Counter.new);
class Counter extends Notifier<int> {
@override
int build() => 0; // ์ฒ์ ๊ฐ
void increment() => state++;
}ํ ์ค์ฉ ๋ณด๋ฉด ์ด๋ ๋ค.
Notifier<int>: ๊ฐ์ ํ์ ์ดint์ธ ์ํ ๊ด๋ฆฌ์๋ค. ์ง๋ ๊ธ์ChangeNotifier์ ๋น์ทํ ์๋ฆฌ๋ค.build(): ์ฒ์ ๊ฐ์ ๋๋ ค์ค๋ค. ์ฌ๊ธฐ์0์ ๋๋ ค์ฃผ๋ฉด ์ฒ์ ์ํ๊ฐ0์ด๋ค.state: ์ง๊ธ ๊ฐ์ด๋ค. ๊ฐ์ ์๋ก ๋์ ํ๋ฉด(state++) ์๋์ผ๋ก ๊ตฌ๋ ์์๊ฒ ์๋ ค์ง๋ค.notifyListeners()๋ฅผ ๋ถ๋ฅผ ํ์๊ฐ ์๋ค.NotifierProvider<Counter, int>(Counter.new): ์ดNotifier๋ฅผ ๋ง๋ค์ด์counterProvider๋ผ๋ ๋ณ์๋ก ๊ฑธ์ด๋๋ค.Counter.new๋ ์์ฑ์๋ฅผ ๊ฐ์ฒ๋ผ ๋๊ธฐ๋ ๋ฌธ๋ฒ์ด๋ค.
์ฑ ๋งจ ์์๋ ProviderScope๋ฅผ ํ ๋ฒ ๊ฑธ์ด๋๋ค. Riverpod์ ๊ฐ๋ค์ด ์ด ์์ ๋ณด๊ด๋๋ค.
void main() {
runApp(const ProviderScope(child: MyApp()));
}ํ๋ฉด์์๋ StatelessWidget ๋์ ConsumerWidget์ ์ฐ๊ณ , build๊ฐ ref๋ฅผ ํ๋ ๋ ๋ฐ๋๋ค.
class CountText extends ConsumerWidget {
const CountText({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider); // ๊ฐ์ ์ฝ๊ณ ๊ตฌ๋
return Text('$count');
}
}
class PlusButton extends ConsumerWidget {
const PlusButton({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Text('+1'),
);
}
}ref.watch(counterProvider)๊ฐ ์ง๊ธ ๊ฐ(int)์ ๋๋ ค์ฃผ๊ณ , ๊ฐ์ด ๋ฐ๋๋ฉด ์ด ์์ ฏ์ ๋ค์ ๊ทธ๋ฆฐ๋ค. ๊ฐ์ ๋ฐ๊พธ๋ ํจ์๋ฅผ ๋ถ๋ฅผ ๋ ref.read(counterProvider.notifier)๋ก Counter ๊ฐ์ฒด ์์ฒด๋ฅผ ๊บผ๋ด์ increment()๋ฅผ ํธ์ถํ๋ค.
์ ์ฝ์ ์ด ์ฌ๋ผ์ง๋
- ์ปดํ์ผ ๋ ์ ์ ์๋ค:
counterProvider๋ ๋ณ์๋ผ์, ์ด๋ฆ์ ํ๋ฆฌ๊ฑฐ๋ ์ง์ด ๋ณ์๋ฅผ ์ฐ๋ฉด ์๋ํฐ๊ฐ ๋ฐ๋ก ๋นจ๊ฐ ์ค์ ์น๋ค.ProviderNotFoundException์ด ์คํ ์ค์ ํฐ์ง๋ ์ผ์ด ์๋ค. context๊ฐ ํ์ ์๋ค:ref๋ง ์์ผ๋ฉด ์ฝ๋๋ค. ๋ค๋ฅธ provider ์์์๋ref๋ก ์ฝ์ ์ ์์ด์, ๊ฐ๋ผ๋ฆฌ ์๋ก ์์กดํ๋ ๊ตฌ์กฐ๊ฐ ์ฝ๋ค.- ๊ฐ์ ํ์
๋ ์ฌ๋ฌ ๊ฐ ๊ฐ๋ฅํ๋ค: ๋ณ์ ์ด๋ฆ์ผ๋ก ์ฐพ์ผ๋
intprovider๋ฅผ ๋ช ๊ฐ๋ ๋ง๋ค ์ ์๋ค.
์ฝ์ง ํฌ์ธํธ 1: ์ฝ๋ฐฑ ์์์ ref.watch๋ฅผ ์ฐ๋ฉด ์กฐ์ฉํ ํ๋ฆฐ๋ค
onPressed: () {
final count = ref.watch(counterProvider); // ์ฌ๊ธฐ์ ๋ฌธ์
print(count);
},์ด ์ฝ๋๋ ์๋ฌ ์์ด ๋์๊ฐ์ ๋ ์ํํ๋ค. watch๋ ๊ตฌ๋
ํ๋ ๋์์ธ๋ฐ ๋ฒํผ ์ฝ๋ฐฑ์ build๊ฐ ์๋๋ผ์, ์๋์ ๋ค๋ฅด๊ฒ ๋์ํ๋ค. ์ง๋ ๊ธ๊ณผ ๊ฐ์ ๊ท์น์ด๋ค. build ์์์๋ ref.watch, ๋ฒํผ ๊ฐ์ ์ด๋ฒคํธ ์์์๋ ref.read๋ฅผ ์ด๋ค.
์ฝ์ง ํฌ์ธํธ 2: ProviderScope๋ฅผ ๋นผ๋จน์ผ๋ฉด ์์๋ถํฐ ์๋ฌ
void main() {
runApp(const MyApp()); // ProviderScope๊ฐ ์์
}ConsumerWidget์ด ๊ฐ์ ์ฐพ์ ๊ณณ์ด ์์ด์ ProviderScope๊ฐ ์๋ค๋ ์ทจ์ง์ ์๋ฌ๊ฐ ๋๋ค. ProviderScope๋ ์ฑ ๋งจ ์์์ ํ ๋ฒ๋ง ๊ฐ์ธ๋ฉด ๋๋ค.
ConsumerWidget์ด ๋ฒ๊ฑฐ๋ก์ธ ๋
StatefulWidget์์ ์ฐ๋ ค๋ฉด ConsumerStatefulWidget์, ์์ ฏ ํ๋ ์์์ ์ผ๋ถ๋ง ๊ตฌ๋
ํ๋ ค๋ฉด Consumer๋ฅผ ์ด๋ค.
Consumer(
builder: (context, ref, child) {
final count = ref.watch(counterProvider);
return Text('$count');
},
)์ญํ ์ ๊ฐ๊ณ ์์ ฏ์ ๋ง๋๋ ๋ฐฉ์๋ง ๋ค๋ฅด๋ค. ์ฒ์์๋ ConsumerWidget๋ง ์์๋ ๋๋ค.
๊ฐ์ ์ข ๋ฅ๋ Provider ํ๋๊ฐ ์๋๋ค
NotifierProvider๋ ๊ฐ์ด ๋ฐ๋๋ ์ํ์ ์ด๋ค. ๋ฐ๋์ง ์๋ ๊ฐ์ด๋ ์๋ฒ ์๋ต์ ๋ค๋ฅธ ์ข
๋ฅ๋ฅผ ์ด๋ค.
Provider: ๋ฐ๋์ง ์๋ ๊ฐ, ๋ค๋ฅธ provider๋ก ๊ณ์ฐํ ๊ฐNotifierProvider: ๊ฐ์ด ๋ฐ๋๊ณ , ๋ฐ๊พธ๋ ํจ์๋ ํ์ํ ๋FutureProvider: ์๋ฒ ์์ฒญ์ฒ๋ผ ๋์ค์ ๋์ฐฉํ๋ ๊ฐ (Future)AsyncNotifierProvider: ๋์ค์ ๋์ฐฉํ๋ ๊ฐ์ ๋ฐ๊พธ๋ ํจ์๊น์ง ํ์ํ ๋
์์ ํํ ๋ฆฌ์ผ์๋ StateProvider, StateNotifierProvider๋ ๋ง์ด ๋ณด์ด๋๋ฐ, ์๋ก ์์ํ๋ค๋ฉด NotifierProvider๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ฐฐ์ฐ๋ฉด ๋๋ค. ๋น๋๊ธฐ ์ชฝ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ๋จ๊ณ์์ AsyncValue์ ํจ๊ป ๋ค์ ๋ณธ๋ค.
3.0๋ถํฐ๋ ์คํจํ provider๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ๋ค์ ์๋ํ๋ค. ์๋ฒ ์์ฒญ์ด ์คํจํ์ ๋ ์๋์ผ๋ก ์ฌ์๋๊ฐ ์ผ์ด๋์ ๋๋ ์ ์์ผ๋ ๊ธฐ์ตํด ๋์.
Provider์ Riverpod ๋น๊ต
- ๊ฐ์ ์ฐพ๋ ๋ฐฉ๋ฒ: Provider๋ ํ์ / Riverpod์ ๋ณ์
- ์์ ๋: Provider๋ ์คํ ์ค
ProviderNotFoundException/ Riverpod์ ์ปดํ์ผ ๋ ์๋ํฐ๊ฐ ์๋ ค์ค contextํ์ ์ฌ๋ถ: Provider๋ ํ์ / Riverpod์ref๋ง ์์ผ๋ฉด ๋จ- ๊ฐ์ ๋ฐ๊ฟ ์๋ฆฌ๊ธฐ: Provider๋
notifyListeners()/ Riverpod์state์ ๋์ - ์์ ฏ: Provider๋
StatelessWidget+context.watch/ Riverpod์ConsumerWidget+ref.watch - ์ค์ : Provider๋ ์์ ฏ ํธ๋ฆฌ์ ๊ฑธ์ด๋ / Riverpod์ ๋ณ์ ์ ์ธ + ๋งจ ์
ProviderScope
Compose, SwiftUI์ ๋น๊ตํ๋ฉด
// Compose - Hilt ViewModel
@HiltViewModel
class CounterViewModel @Inject constructor() : ViewModel() {
private val _count = MutableStateFlow(0)
val count = _count.asStateFlow()
fun increment() { _count.value++ }
}// SwiftUI - @Observable (iOS 17+)
@Observable
class CounterModel {
var count = 0
func increment() { count += 1 }
}- ์ํ๋ฅผ ๊ฐ์ง ํด๋์ค: Flutter
Notifier/ ComposeViewModel/ SwiftUI@Observableํด๋์ค - ๊ฐ์ด ๋ฐ๋๋ฉด ์๋ฆผ: Flutter
state๋์ / ComposeStateFlow๊ฐ ๋ณ๊ฒฝ / SwiftUI ํ๋กํผํฐ ๋ณ๊ฒฝ์ด ์๋์ผ๋ก - ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ: Flutter
ref.watch(provider)/ ComposeviewModel()+collectAsState/ SwiftUI ํ๋กํผํฐ๋ฅผ ์ง์ ์ฝ๊ธฐ - ์์กด์ฑ ์ฃผ์
: Flutter๋
ref๊ฐ provider๋ผ๋ฆฌ ์ฐ๊ฒฐ / Compose๋ Hilt / SwiftUI๋ ํ๊ฒฝ์ด๋ ์ด๋์ ๋ผ์ด์ ๋ก ์ ๋ฌ
Compose์์ Hilt๊ฐ ViewModel์ ๋ง๋ค์ด์ ๋ฃ์ด์ฃผ๋ ์ญํ ์, Flutter์์๋ Riverpod์ด ref๋ก provider๋ผ๋ฆฌ ์ฐ๊ฒฐํ๋ฉฐ ๋์ ํ๋ค. ๊ทธ๋์ Riverpod์ ์ํ ๊ด๋ฆฌ์ ์์กด์ฑ ์ฃผ์
์ ํจ๊ป ํด๊ฒฐํ๋ ๋๊ตฌ๋ผ๊ณ ๋ ํ๋ค.
์ ๋ฆฌ
- Provider๋ ๊ฐ์ ํ์
์ผ๋ก ์ฐพ์์ ๊ฑธ์ด๋์ง ์์์ ๋ ์คํ ์ค์์ผ ์๋ฌ๊ฐ ๋๊ณ ,
context๊ฐ ํ์ํ๊ณ , ๊ฐ์ ํ์ ์ ์ฌ๋ฌ ๊ฐ ๋ ์ ์๋ค - Riverpod์ ๊ฐ์
final๋ณ์๋ก ์ ์ญ์ ์ ์ธํ๊ณ , ์ฑ ๋งจ ์์ProviderScope๋ฅผ ํ ๋ฒ ๊ฐ์ผ๋ค - ๋ฐ๋๋ ๊ฐ์
Notifier์build()๊ฐ ์ฒ์ ๊ฐ์ ๋๋ ค์ฃผ๊ณstate์ ๋์ ํด์ ๋ฐ๊พธ๋ฉฐ,notifyListeners()๊ฐ ํ์ ์๋ค - ์์ ฏ์
ConsumerWidget์ผ๋ก ๋ง๋ค๊ณbuild์์์๋ref.watch, ๋ฒํผ ๊ฐ์ ์ด๋ฒคํธ ์์์๋ref.read(provider.notifier)๋ฅผ ์ด๋ค - ์๋ฒ์์ ์ค๋ ๊ฐ์
FutureProvider, ๋ฐ๋์ง ์๋ ๊ฐ์Provider๋ฅผ ์ด๋ค
๋ค์ ๊ธ์์๋ ๋ ๋ค๋ฅธ ์ ํ์ง์ธ Bloc/Cubit์ ์ดํด๋ณด๊ณ , ํ๋ก์ ํธ ๊ท๋ชจ์ ํ์ ๋ง์ถฐ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ณ ๋ฅด๋ ๊ธฐ์ค์ ์ ๋ฆฌํ๊ฒ ๋ค.
