CH03 MVVM
MVVM ์ ๊ดํ ์ดํด
์ฌ์ค ์์๊ฐ VVMM ์ธ๋ฐ, ์ MVVM ์ผ๋ก ์ฉ์ด๊ฐ ์ ๋ฆฝ ๋์๋์ง ๋ชจ๋ฅด๊ฒ ๋ค. ์ด ํฌ์คํ ์ ๋ณด๊ณ ํ์ตํ๋ค.
์ ๋ง ๊ฐ๋จํ ์ ๋ฆฌํ์๋ฉด View๋ ๋ง ๊ทธ๋๋ก View ๋ค. ์ฌ์ฉ์์ ์ํธ์์ฉ ํ๋ฉด์ ์ฌ์ฉ์์ action ์ ViewModel ๋ก ์ ๋ฌํ๋ ์ญํ ๊ณผ, ViewModel ์ผ๋ก๋ถํฐ ์๋ต์ ๋ฐ์ ์ฌ์ฉ์์๊ฒ ์ ๋ฌํ๋ ๊ฒ์ ์ฑ ์์ง๋ค. ์ฌ๊ธฐ์ ํ์คํ ํด์ผํ ๊ฒ์ View ๋ ์ํ๊ด๋ฆฌ๋ฅผ ํ์ง ์๋๋ค๋ ๊ฒ์ด๋ค.
ViewModel ์ View ์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๋ฉด์ ์ํ๊ด๋ฆฌ๋ ๋ด๋นํ๋ค.
Model ์ data access layer ์ ๋์ผํ ์ญํ ์ด๋ค.
MVVM ๊ธฐ๋ณธ ํจํด
์ ํฌ๋ธ ์ ๊ด์ฐฎ์ ๊ฐ์๊ฐ ์์ด ์ฐธ๊ณ ํ๋ค. udemy ๊ฐ์์์๋ ๊ณ์ธต์ด ๋๋ฌด ๋ถ๋ฆฌ๊ฐ ๋์ด์ ์ผ๋จ MVVM ์์ฒด์ ๋ํ ์ดํด๋ฅผ ๋ํ๊ธฐ ์ํด์ ๊ธฐ๋ณธ ํจํด์ ๋จผ์ ํ์ตํ๋ค.
import 'package:flutter/material.dart';
import 'package:flutter_clean_architecture/presentation/simple_mvvm/simple_view_model.dart';
class SimpleScreen extends StatefulWidget {
@override
_SimpleScreenState createState() => _SimpleScreenState();
}
class _SimpleScreenState extends State<SimpleScreen> {
SimpleViewModel viewModel = SimpleViewModel();
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
StreamBuilder(
stream: viewModel.mvvmStream,
builder: (context, snapshot) {
print('StreamBuilder > build > snapshot : ${snapshot.data}');
int count = 0;
if (snapshot.data != null) {
count = snapshot.data!.count;
}
return Center(
child: Text(
count.toString(),
style: TextStyle(fontSize: 30),
),
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () {
viewModel.increaseCounter();
},
icon: Icon(Icons.exposure_plus_1),
),
IconButton(
onPressed: () {
viewModel.decreaseCounter();
},
icon: Icon(Icons.exposure_minus_1),
),
],
),
],
),
),
);
}
}Provider ์์ notifyListeners(); ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์๊ณผ ์ ์ฌํ๋ค. ๊ฒฐ๊ตญ Stream ๋ Publish, Subscribe ์๋ฆฌ์ด๋ฉฐ ์ด๋ฅผ ์ด์ฉํด์ ํด๋น Stream ์ ๊ตฌ๋ ํ๋ StreamBuilder ๋ฅผ ํตํด์ ๋ณ๊ฒฝ์ด ์์๋๋ง๋ค ์ด๋ฅผ ๋ฐ์ rebuild ํ๋ ๋ฐฉ์์ด๋ค.
์ ํฌ๋ธ ๊ฐ์์๋ Provider ๋ฅผ ์ฌ์ฉํ MVVM ๋ ์์๋๋ฐ, ์ฌ์ค ๋ช ์นญ์ ์ด๋ ๊ฒ ๊ฐ๋ค ๋ถ์ฌ์ ๋ค๋ฅธ ๊ฒ ๊ฐ์ง๋ง ๊ทธ๋ฅ ์ผ๋ฐ์ ์ธ Provider ์๋ค. View ์ ๋๋ฉ์ธ ๋ก์ง์ ์ ๋ถ Provider ์ ์์ํ๊ณ Provider ์ ์ฒ๋ฆฌ์ ๋ฐ๋ผ์ View ๋ rebuild ๊ฐ ํ์ํ ๋ ์์์ rebuild ๊ฐ ๋๋๋ก(react) Provider ๋ฅผ watch ํ๋ ํํ์ธ ๊ฒ์ด๋ค. ์ด๊ฑธ ๊ธฐ๋ณธํ ๋งฅ๋ฝ์์ ๋ณด๋ฉด StreamBuilder ๋ฅผ ํตํด์ rebuild ๋๋ ๊ฒ๊ณผ ๋๊ฐ์ ๋ฐฉ์์ด๋ค.
๊ฐ์์์ ์ฌ์ฉ๋ MVVM ํจํด
์์ค์ฝ๋ ๊ฐ ๋๋ฌด ๊ธธ์ด์ ๋งํฌ๋ง ๋จ๊ธด๋ค.
์ ๊ธฐ๋ณธํ๊ณผ ๋น์ทํ์ง๋ง ๊ฐ์ฅ ํฌ๊ฒ ๋ค๋ฅธ ๋ถ๋ถ์ ๊ณ์ธต์ด ๋ ์ธ๋ถํ ๋์ด ์๋ค๋ ๊ฒ์ด๋ค. ๋ํ์ ์ผ๋ก ์๋ ํด๋์ค๊ฐ ์๋ค.
BaseViewModel ๋ผ๋ ์ต์์ class ๋ฅผ ๋ง๋ค๊ณ ๋ชจ๋ ViewModel ์ด ์ด๋ฅผ ์์ํ๋๋ก ํ๋ค. ๊ฐ ViewModel ์์๋ ๋ ๊ณ์ธต์ ์๋์ ๊ฐ์ด ๋๋๋ค.
์ด๋ ๊ฒ ViewModel ์ด ์๋ .dart ์ ์์ ๊ฐ์ด abstract class ๋ฅผ ๋ ๋ง๋ค์ด์ BaseViewModel ์ ์์ํ๊ฒ ํ๋ค ์ ๊ฒ๋ค์ ๋ค ๊ฐ์ด mixin ํ๋ค.
goNext, goPrevious, onPageChanged ๋ ํด๋น ViewModel ํน์ ์ ๊ฒ์ด๋ผ์ ViewModel ์ ์ ์ธํด๋ ๋์ง๋ง ์๋ง๋ ๋ช ์์ ์ผ๋ก ~Input ๋ด์ ๋ฃ์ด์ฃผ์ด์ ํ์คํ๊ฒ ๋ถ๋ฆฌ์ํค๋ ค๋ ์๋๋ก ๋ณด์ธ๋ค.
~Input ๋ด์ Sink ๋ก ์ค์ ํด ์ค ๊ฒ์ ์ฌ์ค ๊ฒฐ๊ตญ StreamController ์ sink ์ธ๋ฐ, ์๋ฏธ๋ View -> ViewModel ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ(์ํธ์์ฉ)ํ ๋ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ๋ ์ ๊ตฌ๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๊ฐ์ธ์ ์ผ๋ก ๋ฐ๋ก ๋ถ๋ฆฌํ์ง ๋ง๊ณ ~controller.sink ๋ก ๊ทธ๋๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ํจ์ฌ ํด์์ด ๋จ์ํ๊ณ ์ข์ ๊ฒ ๊ฐ๋ค. ๋จ์ํ ViewModel ์ด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ stream ์ผ๋ก publish ํ ๋ ๊ณผ์ ์์ โ๋ฐ๋โ input ๊ฐ๋ ์ด๋ค.
Stream ์ ํ์ฉํ View, ViewModel ์ํธ์์ฉ ์ดํด
์๋ ๊ทธ๋ฆผ์ผ๋ก ๋ง๋ค์๋ค. ๊ฐ์ฌ์ ์ํ ์ฝ๋๊ฐ ์ค์ํ ๊ฒ์ด ์๋๋ผ ์๋ ๊ทธ๋ฆผ์ด ๊ณจ์๋ผ๋ ๊ฒ์ ์๊ณ ์ดํดํ๋ค.

Stream ์ ํ์ํ๋ค๋ฉด ์ฌ๋ฌ ๊ฐ๊ฐ ๋ ์ ์๋ค. ๊ฐ์์์๋ ๋ก๊ทธ์ธ์ name, password, validation, isLoginSuccess ๊ฐ๊ฐ์ ์ํ ๋ค ๊ฐ์ StreamController ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๊ณ ์๋ค. ๊ฐ์ ์ฝ๋๊ฐ ์ข ๊น๋ํ์ง ๋ชปํ๋ฏ๋ก View-ViewModel ๊ฐ์ Stream ์ด ๋ค์์ผ ์ ์๋ค๋ ๊ฒ๋ง ๊ธฐ์ตํ์. ๋ค์์ธ ์ํฉ์ด ์คํ๋ ค ๋ง์ ๊ฒ ๊ฐ๋ค.
Last updated