이번에는 MVC 와 MVVM 에 대해서 알아보겠습니다. 기본 개념을 아신다는 전제하에 MVC -> MVVM 으로 변경하는 장점을 중심으로 알아보겠습니다.
개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1
화면은 아래와 같습니다. +버튼은 숫자를 증가시키고, -버튼은 숫자를 감소 시킨다.
패턴을 적용하지않는 일반적인 코드 - flutter 에서 신규 프로젝트 샘플과 유사한 구조,
변수/함수/View 모두 하나의 파일에서 구현하여 간단한 화면 같은 경우, 쉽게 이해할 수 있으나 화면 및 데이터가 복잡해 경우 적합하지 않다.
import 'package:flutter/material.dart';
class NoPatternView extends StatefulWidget {
const NoPatternView({Key? key}) : super(key: key);
@override
State<NoPatternView> createState() => _NoPatternViewState();
}
class _NoPatternViewState extends State<NoPatternView> {
int count = 0;
void update() => setState(() {});
void incrementCounter() {
count++;
update();
}
void decrementCounter() {
count--;
update();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('NO 패턴')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(count.toString(), style: const TextStyle(fontSize: 150)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: incrementCounter,
child: const Text(
'+',
style: TextStyle(fontSize: 30),
)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: decrementCounter,
child: const Text(
'-',
style: TextStyle(fontSize: 30),
)),
),
],
)
],
),
),
);
}
}
MVC(Model View Controller) - View 와 Model 이 Controller 를 통해서 연결됨(Model 의 간단한 변경에도 모든 View 를 수정해야 함). 간단한 구조는 아래와 같습니다.
Model - 데이터
View - 화면
Controller - 비지니스 로직
예를 들어서 model.dart 에서 counter 변수명만 변경해도
// counter 의 이름만 변경해도, View 에서는 여기서 수정을 해야함.
Text(con.model.counter.toString(), style: const TextStyle(fontSize: 150)),
// model.dart
class Model {
int _count = 0;
// counter 의 이름을 변경하면 *****************************************
int get counter => _count;
int incrementCounter() => _count++;
int decrementCounter() => _count--;
}
// mvc_controller.dart
import 'package:mvc_pattern/mvc_pattern.dart';
import '../model/model.dart';
class MVCController extends ControllerMVC {
factory MVCController([StateMVC? state]) => _this ??= MVCController._(state);
MVCController._(StateMVC? state)
: model = Model(),
super(state);
static MVCController? _this;
final Model model;
void update() => setState(() {});
void incrementCounter() {
model.incrementCounter();
update();
}
void decrementCounter() {
model.decrementCounter();
update();
}
}
// mvc_view.dart
import 'package:flutter/material.dart';
import 'package:mvc_pattern/mvc_pattern.dart';
import 'mvc_controller.dart';
class MVCView extends StatefulWidget {
const MVCView({Key? key}) : super(key: key);
@override
State createState() => _MVCViewState();
}
class _MVCViewState extends StateMVC<MVCView> {
_MVCViewState() : super(MVCController()) {
con = controller as MVCController;
}
late MVCController con;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('MVC 패턴')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// counter 의 이름을 변경하면, 여기서 수정을 해야함. ******************************
Text(con.model.counter.toString(), style: const TextStyle(fontSize: 150)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
con.incrementCounter();
},
child: const Text('+', style: TextStyle(fontSize: 30))),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
con.decrementCounter();
},
child: const Text('-', style: TextStyle(fontSize: 30))),
),
],
)
],
),
),
);
}
}
MVVM(Model View ViewModel) - View 와 Model 간의 연결성을 제거하여 독립성을 유지할 수 있다.(Model 의 간단한 변경에 대해 ViewModel 만 수정하만 됨, View 에 대한 수정은 필요없음). 물론 Model 에서 많은 변경이 발생한다면 View 에 대한 수정이 발생할 수 있다. 간단한 구조는 아래와 같습니다.
Model - 데이터 구조
View - 화면
ViewModel - 비지니스 로직 및 화면과 모델의 데이터 연결
예를 들어서 model.dart 에서 counter 변수명 변경시
// ViewModel 에서는 여기서만 수정하면 됨.
int get count => _model.counter;
// model.dart 는 상기와 동일하여 생략
// mvvm_viewmodel.dart
import 'dart:async';
import '../model/model.dart';
class MvvmViewModel {
late Model _model;
StreamController<Model> controller = StreamController<Model>();
Stream<Model> get mvvmStream => controller.stream;
MvvmViewModel() {
_model = Model();
}
int get count => _model.counter; // 여기만 수정 **************************************
void update() {
controller.sink.add(_model);
}
void incrementCounter() {
_model.incrementCounter();
update();
}
void decrementCounter() {
_model.decrementCounter();
update();
}
}
// mvvm_view.dart
import 'package:flutter/material.dart';
import 'mvvm_viewmodel.dart';
class MVVMView extends StatelessWidget {
MVVMView({Key? key}) : super(key: key);
final MvvmViewModel viewModel = MvvmViewModel();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('MVVM 패턴')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StreamBuilder(
stream: viewModel.mvvmStream,
builder: ((context, snapshot) {
return Text(viewModel.count.toString(), style: const TextStyle(fontSize: 150));
})),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
viewModel.incrementCounter();
},
child: const Text('+', style: TextStyle(fontSize: 30))),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
viewModel.decrementCounter();
},
child: const Text('-', style: TextStyle(fontSize: 30))),
),
],
)
],
),
),
);
}
}
아주 간단한 예시라서 크게 공감이 없을수도 있지만, View 가 많아지고, Model 이 복잡해진다면 MVVM 패턴의 장점을 알수 있다.
참고자료: 개발하는 남자
https://www.youtube.com/watch?v=UJQRiypV6UA&t=27s
'Flutter > 03 Design Pattern' 카테고리의 다른 글
[Flutter] Design Pattern - MVC, MVVM with GetX, Provider (0) | 2022.08.02 |
---|---|
[Flutter] Design Pattern(2) - Controller with Getx (0) | 2021.08.08 |
[Flutter] Design Pattern(1) - Model (0) | 2021.08.08 |