๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

์‹œ์ž‘ํ•˜๊ธฐ โ€‹

์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” Flutter ๊ธฐ๋ณธ "์นด์šดํ„ฐ" ์•ฑ์„ MobX๋กœ ๋‹ค์‹œ ๋งŒ๋“ค์–ด ๋ด…๋‹ˆ๋‹ค.

MobX ๊ธฐ๋ฐ˜ ์นด์šดํ„ฐ ์•ฑ์˜ ์ตœ์ข… ๊ฒฐ๊ณผ

์ค€๋น„ํ•˜๊ธฐ โ€‹

  1. ํŠœํ† ๋ฆฌ์–ผ์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— Flutter๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ƒˆ Flutter ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. ์ปดํ“จํ„ฐ์— ์ƒˆ Flutter ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์„ค์น˜ โ€‹

pubspec.yaml ํŒŒ์ผ์— ๋‹ค์Œ dependencies๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

yaml
dependencies:
  mobx: ^2.6.1
  flutter_mobx: ^2.3.0

์ด์–ด์„œ ๋‹ค์Œ dev_dependencies๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

yaml
dev_dependencies:
  build_runner: ^2.16.1
  mobx_codegen: ^2.7.8

ํ”„๋กœ์ ํŠธ ํด๋”์—์„œ ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ํŒจํ‚ค์ง€๋ฅผ ๋ชจ๋‘ ๋‚ด๋ ค๋ฐ›์Šต๋‹ˆ๋‹ค.

sh
flutter pub get

์ด์ œ ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๊ฐ€ ๋ชจ๋‘ ์ค€๋น„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์Šคํ† ์–ด ์ถ”๊ฐ€ํ•˜๊ธฐ โ€‹

์ด์ œ MobX ์Šคํ† ์–ด๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. MobX์—์„œ ์Šคํ† ์–ด๋Š” ์„œ๋กœ ๊ด€๋ จ๋œ Observable ์ƒํƒœ๋ฅผ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋กœ ๋ฌถ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์Šคํ† ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฃผ์„(annotation)์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•ด์ง‘๋‹ˆ๋‹ค. \lib ํด๋”์— counter.dart ํŒŒ์ผ์„ ์ƒˆ๋กœ ๋งŒ๋“ค๊ณ  ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

dart
import 'package:mobx/mobx.dart';

// Include generated file
part 'counter.g.dart';

// This is the class used by rest of your codebase
class Counter = _Counter with _$Counter;

// The store-class
abstract class _Counter with Store {
  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }
}

์ด ์ฝ”๋“œ์—์„œ ๋ˆˆ์—ฌ๊ฒจ๋ณผ ๋ถ€๋ถ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Store ๋ฏน์Šค์ธ์„ ์‚ฌ์šฉํ•˜๋Š” ์ถ”์ƒ ํด๋ž˜์Šค _Counter๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ† ์–ด์™€ ๊ด€๋ จ๋œ ์ฝ”๋“œ๋Š” ๋ชจ๋‘ ์ด ์ถ”์ƒ ํด๋ž˜์Šค ์•ˆ์— ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. build_runner๊ฐ€ ์ƒ์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ํ•ฉ์น˜๊ธฐ ์œ„ํ•ด Counter ํด๋ž˜์Šค๋ฅผ ๋ณ„๋„๋กœ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒ์„ฑ๋œ ์ฝ”๋“œ๋Š” counter.g.dart ํŒŒํŠธ ํŒŒ์ผ์— ๋“ค์–ด๊ฐ€๋ฉฐ, part ์ง€์‹œ์ž๋กœ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด ์ง€์‹œ์ž๊ฐ€ ์—†์œผ๋ฉด build_runner๊ฐ€ ์•„๋ฌด ๊ฒฐ๊ณผ๋ฌผ๋„ ๋งŒ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ๋œ ํŒŒ์ผ์—๋Š” _$Counter ๋ฏน์Šค์ธ์ด ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

:::caution

ํŒŒ์ผ ์ด๋ฆ„์˜ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ •ํ™•ํžˆ ๋งž์ถฐ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด build_runner๊ฐ€ ์•„๋ฌด ๊ฒฐ๊ณผ๋ฌผ๋„ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํŒŒ์ผ ์ด๋ฆ„์ด counter.dart์ด๋ฏ€๋กœ ํŒŒํŠธ ํŒŒ์ผ ์ด๋ฆ„์€ counter.g.dart์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค(์†Œ๋ฌธ์ž์ž„์— ์œ ์˜ํ•˜์„ธ์š”).

:::

  • @observable ์ฃผ์„์€ value๋ฅผ Observable(๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ)๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
  • @action์€ increment() ๋ฉ”์„œ๋“œ๋ฅผ Action(์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋™์ž‘)์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ํด๋”์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด, ์ด๋ฏธ ํŒŒํŠธ ํŒŒ์ผ๋กœ ํฌํ•จํ•ด ๋‘” counter.g.dart์— ์ฝ”๋“œ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

sh
flutter pub run build_runner build

๋‹ค์Œ์€ ๋ช…๋ น์ค„์—์„œ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ๋‚˜์˜จ ์ถœ๋ ฅ์ž…๋‹ˆ๋‹ค. ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ์กฐ๊ธˆ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

text
โžœ  [mobx_getting_started]> flutter packages pub run build_runner build
[INFO] Generating build script...
[INFO] Generating build script completed, took 319ms

[INFO] Initializing inputs
[INFO] Reading cached asset graph...
[INFO] Reading cached asset graph completed, took 76ms

[INFO] Checking for updates since last build...
[INFO] Checking for updates since last build completed, took 705ms

[INFO] Running build...
[INFO] 1.1s elapsed, 0/3 actions completed.
[INFO] 6.7s elapsed, 2/3 actions completed.
[INFO] 7.7s elapsed, 3/3 actions completed.
[INFO] Running build completed, took 7.7s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 33ms

[INFO] Succeeded after 7.8s with 2 outputs (6 actions)

INFO

watch ๋ชจ๋“œ ์‚ฌ์šฉํ•˜๊ธฐ โ€‹

์Šคํ† ์–ด๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ๋งˆ๋‹ค *.g.dart ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

sh
flutter pub run build_runner watch

๊นจ๋—ํ•˜๊ฒŒ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๊ธฐ โ€‹

๊ธฐ์กด ํŒŒ์ผ ๋•Œ๋ฌธ์— ์ด ๋ช…๋ น์ด ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „์˜ build_runner๊ฐ€ ์ƒ์„ฑํ•œ ํŒŒ์ผ์ด ๋‚จ์•„ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋Œ€ํ‘œ์ ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿด ๋•Œ๋Š” ๋‹ค์Œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด *.g.dart ํŒŒ์ผ์„ ๋จผ์ € ์‚ญ์ œํ•œ ๋’ค ๋‹ค์‹œ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

sh
flutter pub run build_runner watch --delete-conflicting-outputs

์Šคํ† ์–ด๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ์œ„์ ฏ์— Observer ์ถ”๊ฐ€ํ•˜๊ธฐ โ€‹

์ด์ œ MobX ์Šคํ† ์–ด๋ฅผ ์œ„์ ฏ์— ์—ฐ๊ฒฐํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. main.dart ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ๋‹ค์Œ ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

dart
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';

import 'counter.dart'; // Import the Counter

final counter = Counter(); // Instantiate the store

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MobX',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('MobX Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            // Wrapping in the Observer will automatically re-render on changes to counter.value
            Observer(
              builder: (_) => Text(
                    '${counter.value}',
                    style: Theme.of(context).textTheme.headline4,
                  ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: counter.increment,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

์œ„ ์ฝ”๋“œ์—๋Š” StatefulWidget์ด ํ•˜๋‚˜๋„ ์—†๋‹ค๋Š” ์ ์— ์ฃผ๋ชฉํ•˜์„ธ์š”. ์ƒํƒœ๋Š” Counter ์Šคํ† ์–ด์— ์ €์žฅ๋˜๊ณ , Observer ์œ„์ ฏ์€ counter.value๋ฅผ ์ฝ์–ด ์นด์šดํŠธ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. counter.value๋ฅผ ์ฝ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ Observer๊ฐ€ ์ถ”์ ์„ ์‹œ์ž‘ํ•˜๋ฉฐ, ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

์™„์„ฑ์ž…๋‹ˆ๋‹ค! โ€‹

์ตœ์ข… ๊ฒฐ๊ณผ๋Š” ์ฒ˜์Œ ๋ณด๋˜ ์นด์šดํ„ฐ ์•ฑ๊ณผ ๋˜‘๊ฐ™์Šต๋‹ˆ๋‹ค. FloatingActionButton์„ ํƒญํ•˜๋ฉด ์นด์šดํ„ฐ๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ณ  ํ™”๋ฉด๋„ ์ž๋™์œผ๋กœ ๊ฐฑ์‹ ๋ฉ๋‹ˆ๋‹ค.

๊ณ„์† ๋‘˜๋Ÿฌ๋ณด์„ธ์š”! โ€‹

๋‹ค๋ฅธ ์˜ˆ์ œ๋„ ์‚ดํŽด๋ณด๋ฉด์„œ MobX์— ์ต์ˆ™ํ•ด์ ธ ๋ณด์„ธ์š”. MobX์˜ ํ•ต์‹ฌ์€ Observables, Actions, Reactions ์„ธ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค ๐Ÿ˜‡. ์ด ์ค‘ Observables์™€ Actions๋Š” ์ด๋ฏธ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. Reactions๋Š” ๋‹ค๋ฅธ ์˜ˆ์ œ์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

Dart์™€ Flutter ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ์œ„ํ•ด ์ •์„ฑ๊ป ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. MIT ๋ผ์ด์„ ์Šค.