๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿฅ– Bread Basics/Flutter

[Flutter] Riverpod ์ƒํƒœ๊ด€๋ฆฌ:: ๊ตฌ์กฐํ™”๋œ ์ ‘๊ทผ๋ฒ•๊ณผ ํƒ€์ž… ์•ˆ์ „์„ฑ

by BreadDev 2025. 6. 27.
728x90

Flutter ์ƒํƒœ๊ด€๋ฆฌ์—์„œ Riverpod์„ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด Riverpod์„ ๊ณต๋ถ€ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Riverpod์˜ ๊ตฌ์กฐ์  ์ ‘๊ทผ

Riverpod์€ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์—ฌ๋Ÿฌ ๋ ˆ์ด์–ด๋กœ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์š”์†Œ๊ฐ€ ์–ด๋–ค ์—ญํ• ์„ ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ์ƒํƒœ ํด๋ž˜์Šค: ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์ •์˜

class CounterState {
  int count = 0;

  CounterState({required this.count});
}

์ด ์ ‘๊ทผ๋ฒ•์˜ ์žฅ์ ์€ ์ƒํƒœ์˜ ๊ตฌ์กฐ๊ฐ€ ๋ช…ํ™•ํ•ด์ง„๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. CounterState๋งŒ ๋ณด๋ฉด ์ด ๊ธฐ๋Šฅ์ด ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š”์ง€ ์ฆ‰์‹œ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.

2. Notifier: ๋กœ์ง๊ณผ ์ƒํƒœ ๋ณ€๊ฒฝ

class RiverpodCounterNotifier extends Notifier<CounterState> {
  // ๋กœ์ง
  void countUp() {
    state = CounterState(count: state.count + 1);
    print('RiverpodCounterNotifier - countUp count: ${state.count}');
  }
  
  void countDown() {
    state = CounterState(count: state.count - 1);
    print('RiverpodCounterNotifier - countDown count: ${state.count}');
  }

  @override
  CounterState build() {
    return CounterState(count: 0); // ์ดˆ๊ธฐ ์ƒํƒœ
  }
}

ํ•ต์‹ฌ ๋™์ž‘ ์›๋ฆฌ:

Notifier<CounterState> ์ œ๋„ค๋ฆญ

  • ํƒ€์ž… ์•ˆ์ „์„ฑ: state๋Š” ํ•ญ์ƒ CounterState ํƒ€์ž…

state ์ง์ ‘ ํ• ๋‹น

state = CounterState(count: state.count + 1);
  • ์ƒˆ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ: ๊ธฐ์กด ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑ
  • ์ž๋™ ์•Œ๋ฆผ: state ํ• ๋‹น ์‹œ ์ž๋™์œผ๋กœ UI์— ์•Œ๋ฆผ
  • ์ˆ˜๋™ ํ˜ธ์ถœ ๋ถˆํ•„์š”: notifyListeners() ๊ฐ™์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์—†์Œ

build() ๋ฉ”์„œ๋“œ

@override
CounterState build() {
  return CounterState(count: 0); // ์ดˆ๊ธฐ ์ƒํƒœ ๋ช…์‹œ
}
  • ์ดˆ๊ธฐํ™” ๋‹ด๋‹น: Notifier๊ฐ€ ์ฒ˜์Œ ์ƒ์„ฑ๋  ๋•Œ ํ˜ธ์ถœ
  • ์ดˆ๊ธฐ ์‹œ์ž‘์ : ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ€ ์–ด๋–ป๊ฒŒ ์„ค์ •๋˜๋Š”์ง€ ๋ช…์‹œ์ 

3. Provider ์ •์˜: ํƒ€์ž… ์•ˆ์ „์„ฑ

final riverpodCounterProvider = NotifierProvider<RiverpodCounterNotifier, CounterState>(() {
  return RiverpodCounterNotifier();
});

Provider์˜ ๊ตฌ์กฐ ๋ถ„์„:

NotifierProvider<Notifier, State>

  • ์ฒซ ๋ฒˆ์งธ ์ œ๋„ค๋ฆญ: RiverpodCounterNotifier (๋กœ์ง ๋‹ด๋‹น)
  • ๋‘ ๋ฒˆ์งธ ์ œ๋„ค๋ฆญ: CounterState (์ƒํƒœ ํƒ€์ž…)
  • ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜: () => RiverpodCounterNotifier() (์ธ์Šคํ„ด์Šค ์ƒ์„ฑ)

4. ์•ฑ ์„ค์ •: ProviderScope

void main() {
  runApp(const ProviderScope(child: RiverpodCounterApp()));
}

ProviderScope์˜ ์—ญํ• :

  • Riverpod ํ™œ์„ฑํ™”: ๋ชจ๋“  Provider ๊ธฐ๋Šฅ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ์˜์กด์„ฑ ์ปจํ…Œ์ด๋„ˆ: Provider๋“ค์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ
  • ๋ฃจํŠธ ์„ค์ •: ์•ฑ ์ „์ฒด์—์„œ Provider ์ ‘๊ทผ ๊ฐ€๋Šฅ

5. ConsumerWidget: ์ƒํƒœ ๊ตฌ๋…๊ณผ ์‚ฌ์šฉ

class RiverpodCounterHomePage extends ConsumerWidget {
  RiverpodCounterHomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(title: Text('RiverPod Counter App')),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            spacing: 10,
            children: [
              ConsumerCounterText(),
              ElevatedButton(
                onPressed: () {
                  ref.read(riverpodCounterProvider.notifier).countUp();
                },
                child: Text('์นด์šดํŠธ ์ฆ๊ฐ€'),
              ),
              ElevatedButton(
                onPressed: () {
                  ref.read(riverpodCounterProvider.notifier).countDown();
                },
                child: Text('์นด์šดํŠธ ๊ฐ์†Œ'),
              ),
            ],
          )
        ),
      ),
    );
  }
}

ConsumerWidget์˜ ํŠน์ง•

StatelessWidget ๋Œ€์‹  ConsumerWidget:

  • WidgetRef ref: ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ref ์ œ๊ณต
  • Provider ์ ‘๊ทผ: ref๋ฅผ ํ†ตํ•ด ๋ชจ๋“  Provider์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผ
  • ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ: ์œ„์ ฏ์ด dispose๋  ๋•Œ ์ž๋™์œผ๋กœ ์ •๋ฆฌ

ref.read()์˜ ์šฉ๋„

ref.read(riverpodCounterProvider.notifier).countUp();
  • ์ผํšŒ์„ฑ ์ ‘๊ทผ: ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์ด๋‚˜ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์šฉ
  • ๋ฆฌ๋นŒ๋“œ ์—†์Œ: ์ด ์ฝ”๋“œ๋กœ ์ธํ•ด ์œ„์ ฏ์ด ๋‹ค์‹œ ๋นŒ๋“œ๋˜์ง€ ์•Š์Œ
  • notifier ์ ‘๊ทผ: .notifier๋กœ Notifier ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผ

6. ์ƒํƒœ ๊ตฌ๋…: ๋ณ„๋„ Consumer ์œ„์ ฏ

class ConsumerCounterText extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Text(
      'Counter.count : ${ref.watch(riverpodCounterProvider).count}',
      style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
    );
  }
}

ref.watch()์˜ ํ•ต์‹ฌ

ref.watch(riverpodCounterProvider).count
  • ์ƒํƒœ ๊ตฌ๋…: Provider์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€
  • ์ž๋™ ๋ฆฌ๋นŒ๋“œ: ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ด ์œ„์ ฏ๋งŒ ๋‹ค์‹œ ๋นŒ๋“œ
  • ์ง์ ‘ ์ ‘๊ทผ: .count๋กœ ์ƒํƒœ ํ”„๋กœํผํ‹ฐ์— ์ง์ ‘ ์ ‘๊ทผ

์œ„์ ฏ ๋ถ„๋ฆฌ์˜ ์ด์ 

  • ์„ฑ๋Šฅ ์ตœ์ ํ™”: ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด๋„ ConsumerCounterText๋งŒ ๋ฆฌ๋นŒ๋“œ
  • ์ฑ…์ž„ ๋ถ„๋ฆฌ: ๋ฒ„ํŠผ๋“ค์€ ๋ฆฌ๋นŒ๋“œ๋˜์ง€ ์•Š์Œ
  • ์žฌ์‚ฌ์šฉ์„ฑ: ๋‹ค๋ฅธ ๊ณณ์—์„œ๋„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

ref์˜ ์„ธ ๊ฐ€์ง€ ํ•ต์‹ฌ ๋ฉ”์„œ๋“œ

Riverpod์—์„œ ref๋Š” Provider์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ํ•ต์‹ฌ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

ref.watch() - ์ƒํƒœ ๊ตฌ๋…

final state = ref.watch(riverpodCounterProvider);
// ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ด ์œ„์ ฏ์ด ๋ฆฌ๋นŒ๋“œ๋จ

ref.read() - ์ผํšŒ์„ฑ ์ ‘๊ทผ

ref.read(riverpodCounterProvider.notifier).countUp();
// ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ๋งŒ, ๋ฆฌ๋นŒ๋“œ ์—†์Œ

์ „์ฒด ๋ฐ์ดํ„ฐ ํ๋ฆ„

ProviderScope
    ↓
riverpodCounterProvider (NotifierProvider)
    ↓
RiverpodCounterNotifier (๋กœ์ง)
    ↓
CounterState (์ƒํƒœ)
    ↓
ConsumerWidget (UI)
    ↓
ref.watch() (๊ตฌ๋…) / ref.read() (์•ก์…˜)

๋™์ž‘ ์‹œ๋‚˜๋ฆฌ์˜ค:

  1. ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ
  2. ref.read().countUp() ํ˜ธ์ถœ
  3. Notifier๊ฐ€ ์ƒˆ๋กœ์šด CounterState ์ƒ์„ฑ
  4. state ํ• ๋‹น์œผ๋กœ ์ž๋™ ์•Œ๋ฆผ
  5. ref.watch()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์œ„์ ฏ๋งŒ ๋ฆฌ๋นŒ๋“œ

๋งˆ๋ฌด๋ฆฌ

Riverpod์€ ๊ตฌ์กฐํ™”๋œ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ†ตํ•ด ์•ˆ์ „ํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์•ฑ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

Riverpod์˜ ํ•ต์‹ฌ ์š”์†Œ๋“ค:

  • CounterState: ๋ช…ํ™•ํ•œ ์ƒํƒœ ๊ตฌ์กฐ
  • Notifier<T>: ํƒ€์ž… ์•ˆ์ „ํ•œ ๋กœ์ง ๊ด€๋ฆฌ
  • NotifierProvider: ์ปดํŒŒ์ผ ํƒ€์ž„ ๊ฒ€์ฆ
  • ConsumerWidget: ref๋ฅผ ํ†ตํ•œ ์•ˆ์ „ํ•œ ์ ‘๊ทผ
  • ref.watch/read: ๋ชฉ์ ์— ๋งž๋Š” ์ƒํƒœ ์‚ฌ์šฉ

์ฝ”๋“œ๊ฐ€ ์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•ด ๋ณด์˜€์ง€๋งŒ, ๊ฐ ์š”์†Œ์˜ ์—ญํ• ์ด ๋ช…ํ™•ํ•˜๊ณ  ํƒ€์ž… ์‹œ์Šคํ…œ์˜ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด ์˜คํžˆ๋ ค ๋” ์•ˆ์ „ํ•˜๊ณ  ํŽธ๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.