📘 Flutter ViewModel / Model / Provider 구조 이해 & 실제 사례
🌱 구조 개요
- Model: 데이터를 구조화 (예: Post)
- Repository: 데이터 소스 접근 (Firestore, API 등)
- ViewModel: UI에 필요한 상태 및 로직 보유
- Provider: ViewModel을 관리하고 UI에서 구독 가능하게 함
1. 🧩 Model - Post 클래스
Firestore에서 가져온 데이터를 구조화하는 클래스
class Post {
final String id;
final String writer;
final String title;
final String content;
final DateTime createdAt;
final String imgUrl;
Post({
required this.id,
required this.writer,
required this.title,
required this.content,
required this.createdAt,
required this.imgUrl,
});
Post.fromJson(Map<String, dynamic> json)
: id = json['id'],
writer = json['writer'],
title = json['title'],
content = json['content'],
createdAt = DateTime.parse(json['createdAt']),
imgUrl = json['imgUrl'];
Map<String, dynamic> toJson() => {
'writer': writer,
'title': title,
'content': content,
'createdAt': createdAt.toIso8601String(),
'imgUrl': imgUrl,
};
}
2. 🏗 Repository - PostRepository 클래스
Firestore와 직접 통신하여 데이터를 CRUD 처리
class PostRepository {
const PostRepository();
Future<List<Post>> getAll() async {
final snapshot = await FirebaseFirestore.instance.collection('posts').get();
return snapshot.docs.map((doc) {
return Post.fromJson({'id': doc.id, ...doc.data()});
}).toList();
}
Future<bool> insert(Post post) async {
final doc = FirebaseFirestore.instance.collection('posts').doc();
await doc.set(post.toJson());
return true;
}
}
3. 🧠 ViewModel - HomeViewModel 클래스
리스트 상태를 관리하고 Repository와 연결
class HomeViewModel extends Notifier<List<Post>> {
final postRepository = const PostRepository();
@override
List<Post> build() {
fetchData();
return [];
}
void fetchData() async {
final posts = await postRepository.getAll();
state = posts;
}
}
4. 🧪 Provider - ViewModel을 UI에 연결
Provider를 통해 ViewModel을 구독하고 상태 반영
final homeViewModel = NotifierProvider<HomeViewModel, List<Post>>(
() => HomeViewModel(),
);
5. 🖼 UI에서 상태 사용
Consumer
위젯을 사용해 ViewModel의 상태를 구독
Consumer(
builder: (context, ref, _) {
final posts = ref.watch(homeViewModel);
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
return Text(posts[index].title);
},
);
},
)
💡 TIP:
family
와 autoDispose
를 같이 쓰면 화면별 상태 분리 + 메모리 관리가 편리함!📌 마무리 요약
- Model: 데이터를 담는 틀
- Repository: 데이터를 실제로 불러오고 저장
- ViewModel: 데이터를 가공/관리하며 UI 상태 책임
- Provider: ViewModel을 구독하고 View에 바인딩
❗ ViewModel에 비즈니스 로직을 넣고, UI는 상태에 따라 반응만 하도록 역할을 분리하는 것이 핵심입니다.
📌 Today I Learned - Flutter에서 Riverpod은 pub/sub 구조와 유사하다
Flutter에서 MVVM 아키텍처를 사용할 때 Riverpod
는 pub/sub (publish-subscribe) 패턴과 매우 유사한 동작을 한다. ViewModel이 상태를 변경하면 View가 자동으로 업데이트되므로, 발행자 → 구독자 구조가 자연스럽게 형성된다.
🔧 기본 구조
- Model: 데이터 구조 및 비즈니스 로직
- ViewModel: 상태를 관리하고 비즈니스 로직을 수행
- View: UI 렌더링 및 ViewModel과 연동
🔁 pub/sub 구조와의 비교
요소 | pub/sub | Riverpod MVVM |
---|---|---|
Publisher | 이벤트 또는 메시지를 발행 | StateNotifier (ViewModel) |
Subscriber | 이벤트를 수신 | ref.watch() , Consumer , WidgetRef |
Bus / Channel | 발행자와 구독자를 연결 | Provider (예: StateNotifierProvider ) |
🧪 예제 코드 요약
1. ViewModel (Publisher)
final counterProvider = StateNotifierProvider<CounterViewModel, int>(
(ref) => CounterViewModel(),
);
class CounterViewModel extends StateNotifier<int> {
CounterViewModel() : super(0);
void increment() => state++;
}
2. View (Subscriber)
class CounterView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
final vm = ref.read(counterProvider.notifier);
return Scaffold(
body: Center(child: Text('Count: \$count')),
floatingActionButton: FloatingActionButton(
onPressed: vm.increment,
child: Icon(Icons.add),
),
);
}
}
📝 느낀 점
- Riverpod은 View와 상태를 강결합 없이 관리할 수 있도록 돕는다.
- MVVM 구조에 자연스럽게 어울리고, pub/sub처럼 명확한 역할 분리가 가능하다.
- DI, 상태 공유, 비동기 처리까지 폭넓게 확장 가능하다.
'전체보기' 카테고리의 다른 글
황선홍 감독 대전 복귀! 2025 대전하나시티즌 반전 드라마의 주역? (0) | 2025.04.12 |
---|---|
[2025 최신] 소상공인 고용보험, 고용보험료 지원사업 안내 및 신청 방법 (3.7만원 아끼세요.) (0) | 2025.04.11 |
미스터트롯3 김용빈, 팬과 함께 울고 웃은 감동의 무대 [2025년 서울 콘서트 리뷰] (0) | 2025.04.10 |
재난지역 후원 2025 가이드|기부부터 보험까지 총정리 (1) | 2025.04.10 |
홍준표 대선 출마 2025, 한국판 FBI 설립까지 제안한 이유는? (0) | 2025.04.10 |