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

[Flutter] GetX ์ƒํƒœ๊ด€๋ฆฌ: ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ

by BreadDev 2025. 6. 27.
728x90

์ง€๊ธˆ๊นŒ์ง€ StatefulWidget → MVVM ChangeNotifier → Provider ์ˆœ์œผ๋กœ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ๊ณต๋ถ€ํ•ด๋ดค๋Š”๋ฐ

์ด๋ฒˆ์—๋Š” GetX ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๊ฒฝํ—˜ํ•ด๋ณด๊ณ , ์ƒํƒœ๊ด€๋ฆฌ๊ฐ€ ์–ผ๋งˆ๋‚˜ ๊ฐ„๋‹จํ•ด์งˆ ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

์ด์ „ ๋ฐฉ์‹๋“ค์˜ ๊ณตํ†ต๋œ ์•„์‰ฌ์›€

Provider๊นŒ์ง€์˜ ํŒจํ„ด๋“ค

// ChangeNotifier ๊ธฐ๋ฐ˜ (MVVM, Provider ๊ณตํ†ต)
class CounterViewModel extends ChangeNotifier {
  int count = 0;
  
  void countUp() {
    count = count + 1;
    notifyListeners(); // ๐Ÿค” ์ด๊ฑธ ๋งค๋ฒˆ ํ˜ธ์ถœํ•ด์•ผ ํ•จ
  }
  
  void countDown() {
    count = count - 1;
    notifyListeners(); // ๐Ÿค” ๊นœ๋นกํ•˜๋ฉด UI ์—…๋ฐ์ดํŠธ ์•ˆ๋จ
  }
}

// UI์—์„œ ์‚ฌ์šฉ
Consumer<CounterViewModel>(
  builder: (context, viewModel, child) {
    return Text('${viewModel.count}'); // ๐Ÿค” builder ํ•จ์ˆ˜ ํ•„์š”
  }
)

 

๊ณตํ†ต๋œ ๋ถˆํŽธํ•จ๋“ค:

  1. notifyListeners() ๊ฐ•์ œ: ๋งค๋ฒˆ ์ˆ˜๋™์œผ๋กœ ํ˜ธ์ถœํ•ด์•ผ ํ•จ
  2. Builder ํŒจํ„ด: Consumer, ListenableBuilder ๋“ฑ์˜ ๋ž˜ํผ ํ•„์š”

GetX : ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ

GetX๋Š” ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ(Reactive Programming) ๊ฐœ๋…์„ Flutter์— ๋„์ž…ํ•ฉ๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์•„์ด๋””์–ด

๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด → ์ž๋™์œผ๋กœ → UI๊ฐ€ ์—…๋ฐ์ดํŠธ

"๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•ด์„œ ์ž๋™์œผ๋กœ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์ฃผ๋Š”" ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ๋ถ„์„: GetX

Controller: .obs์˜ ์—ญํ• 

class GetXCounterController extends GetxController {
  // ๋ฐ์ดํ„ฐ (.obs = Observable)
  var count = 0.obs; // ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜๋กœ ์„ ์–ธ
  
  // โšก ๋กœ์ง
  void countUp() {
    print('GetXCounterController - countUp count: $count');
    count = count + 1; // notifyListeners() ํ˜ธ์ถœ ๋ถˆํ•„์š”!
  }
  
  void countDown() {
    print('GetXCounterController - countUp count: $count');
    count = count - 1; // ์ž๋™์œผ๋กœ UI ์—…๋ฐ์ดํŠธ๋จ!
  }
}

ํ•ต์‹ฌ ๋ณ€ํ™”:

  • .obs: ๋ณ€์ˆ˜๋ฅผ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ฆ
  • ์ž๋™ ์•Œ๋ฆผ: ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์ž๋™์œผ๋กœ ๊ตฌ๋…์ž๋“ค์—๊ฒŒ ์•Œ๋ฆผ
  • notifyListeners() ์ œ๊ฑฐ: ๋” ์ด์ƒ ์ˆ˜๋™ ํ˜ธ์ถœ ๋ถˆํ•„์š”

View: ์ปจํŠธ๋กค๋Ÿฌ ๋“ฑ๋ก๊ณผ ์‚ฌ์šฉ

class GetxCounterHomePage extends StatelessWidget {
  GetxCounterHomePage({super.key});
  
  // ์ปจํŠธ๋กค๋Ÿฌ ๋“ฑ๋ก
  final GetXCounterController controller = Get.put(GetXCounterController());
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('GetX Counter App')),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            spacing: 10,
            children: [
              // Obx: ๋ฐ˜์‘ํ˜• ์œ„์ ฏ
              Obx(() => Text(
                'Counter.count : ${controller.count}',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
              )),
              ElevatedButton(
                onPressed: () {
                  controller.countUp(); // ๋‹จ์ˆœํ•œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
                },
                child: Text('์นด์šดํŠธ ์ฆ๊ฐ€'),
              ),
              ElevatedButton(
                onPressed: () {
                  controller.countDown();
                },
                child: Text('์นด์šดํŠธ ๊ฐ์†Œ'),
              ),
            ],
          )
        ),
      ),
    );
  }
}

ํ•ต์‹ฌ ๋ณ€ํ™”:

  • Get.put(): ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ GetX ์‹œ์Šคํ…œ์— ๋“ฑ๋ก
  • Obx(): ๋ฐ˜์‘ํ˜• ์œ„์ ฏ, ๋‚ด๋ถ€ ๋ณ€์ˆ˜ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ๋ฆฌ๋นŒ๋“œ
  • ์ง์ ‘ ์ ‘๊ทผ: controller.count๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ ‘๊ทผ

main.dart: GetMaterialApp

void main() {
  runApp(GetMaterialApp(home: GetXCounterApp()));
}

๋ณ€ํ™”:

  • MaterialApp → GetMaterialApp: GetX ๊ธฐ๋Šฅ ํ™œ์„ฑํ™”
  • Provider ์„ค์ • ๋ถˆํ•„์š”: ๋ณ„๋„ Provider ๋ž˜ํ•‘ ์—†์Œ

์ด์ „ ๋ฐฉ์‹๋“ค๊ณผ์˜ ๋น„๊ต

์ƒํƒœ ๋ณ€์ˆ˜ ์„ ์–ธ

// ChangeNotifier/Provider: ์ผ๋ฐ˜ ๋ณ€์ˆ˜ + ์ˆ˜๋™ ์•Œ๋ฆผ
class CounterViewModel extends ChangeNotifier {
  int count = 0; // ์ผ๋ฐ˜ ๋ณ€์ˆ˜
  
  void countUp() {
    count++;
    notifyListeners(); // ์ˆ˜๋™ ํ˜ธ์ถœ ํ•„์š”
  }
}

// GetX: ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜ + ์ž๋™ ์•Œ๋ฆผ
class GetXCounterController extends GetxController {
  var count = 0.obs; // ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜
  
  void countUp() {
    count++; // ์ž๋™์œผ๋กœ ์•Œ๋ฆผ
  }
}

UI ์—…๋ฐ์ดํŠธ

// Provider: Builder ํŒจํ„ด ํ•„์š”
Consumer<CounterViewModel>(
  builder: (context, viewModel, child) {
    return Text('${viewModel.count}');
  }
)

// GetX: ๊ฐ„๋‹จํ•œ Obx ๋ž˜ํ•‘
Obx(() => Text('${controller.count}'))

์ธ์Šคํ„ด์Šค ๊ด€๋ฆฌ

// Provider: main.dart์—์„œ ์„ค์ •
void main() {
  runApp(
    ChangeNotifierProvider<CounterViewModel>(
      create: (context) => CounterViewModel(),
      child: MyApp(),
    )
  );
}

// GetX: ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์—์„œ ๋ฐ”๋กœ ๋“ฑ๋ก
class HomePage extends StatelessWidget {
  final controller = Get.put(GetXCounterController());
}

๊ฐœ๋ฐœ ์†๋„

// ๊ธฐ์กด ๋ฐฉ์‹: 5-6๋‹จ๊ณ„
// 1. ViewModel ์ƒ์„ฑ
// 2. ChangeNotifier ์ƒ์†
// 3. notifyListeners() ํ˜ธ์ถœ
// 4. Provider ์„ค์ •
// 5. Consumer ๋ž˜ํ•‘
// 6. UI ๊ตฌ์„ฑ

// GetX: 3๋‹จ๊ณ„
// 1. Controller ์ƒ์„ฑ (.obs ๋ณ€์ˆ˜)
// 2. Get.put()์œผ๋กœ ๋“ฑ๋ก
// 3. Obx()๋กœ UI ๋ž˜ํ•‘

๋งˆ๋ฌด๋ฆฌ

GetX๋Š” ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ Flutter์—์„œ ์‰ฝ๊ฒŒ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

GetX์˜ ํŠน์ง•:

  • .obs: ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜๋กœ ์ž๋™ ์•Œ๋ฆผ
  • Obx(): ๋ฐ˜์‘ํ˜• UI ์—…๋ฐ์ดํŠธ
  • Get.put(): ๊ฐ„๋‹จํ•œ ์˜์กด์„ฑ ๋“ฑ๋ก

GetX๋Š” "์ƒํƒœ๊ด€๋ฆฌ๊ฐ€ ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•  ์ˆ˜ ์žˆ๊ตฌ๋‚˜!"ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.