카테고리 없음

Riverpod × MVVM 구조와 데이터 흐름 완전 이해

kwontete 2025. 11. 3. 20:57

오늘 공부한 내용

오늘은 Flutter에서 Riverpod을 이용한 상태관리 구조와 MVVM 패턴을 중심으로 학습했다.
특히 Model, Repository, ViewModel, View가 어떤 역할을 하는지, 그리고 각각이 어떤 순서로 데이터를 주고받는지에 집중했다.
아직 완벽히 이해된 건 아니지만, 흐름이 조금씩 잡혀가는 느낌이다.


MVVM과 Riverpod 구조 이해

전체 구조는 다음과 같다.

 
View(UI) ↑↓ (ref.watch / ref.read) ViewModel (HomeViewModel) ↑↓ Repository (BookRepository) ↑↓ Model (Book)
  • Model은 데이터의 형태를 정의하고, JSON 데이터를 객체로 바꾸거나 반대로 객체를 JSON으로 바꾸는 방법을 알고 있다.
  • Repository는 실제로 외부 API나 데이터베이스에서 데이터를 가져와 Model 객체로 변환하는 역할을 한다.
  • ViewModel은 Repository에서 받은 데이터를 상태(state)에 저장하고, 화면이 그 데이터를 표시할 수 있도록 관리한다.
  • View는 ref.watch()로 상태를 감시하고, ref.read()를 통해 행동(예: 버튼 클릭, 검색 실행)을 수행한다.

핵심 코드 해석

Model (Book)

 
Book.fromJson(Map<String, dynamic> map) Book.toJson()

Model은 JSON 데이터를 해석하는 법을 알고 있다.
fromJson은 서버에서 받은 JSON을 객체로 바꾸고, toJson은 반대로 객체를 JSON으로 만든다.


Repository (BookRepository)

 
Future<List<Book>> searchBooks(String query)

Repository는 네이버 도서 API를 호출해서 데이터를 받아온다.
받은 데이터는 JSON 문자열이기 때문에 jsonDecode를 사용해 Map 형태로 바꾸고,
각 항목을 Book.fromJson()을 통해 Book 객체로 변환한다.
정상 응답이 아닐 경우 빈 리스트를 반환한다.


ViewModel (HomeViewModel)

 
Future<void> searchBooks(String query) async { final bookRepository = BookRepository(); final books = await bookRepository.searchBooks(query); state = HomeState(books); }

ViewModel은 Repository에게 “데이터를 가져와 달라”고 요청한 뒤,
받아온 데이터를 상태(state)에 저장한다.
state가 바뀌면 Riverpod이 감지하고 UI를 자동으로 다시 그린다.


Provider

 
final homeViewModelProvider = NotifierProvider<HomeViewModel, HomeState>(() => HomeViewModel());

Riverpod이 ViewModel과 State를 전역으로 관리할 수 있게 해주는 선언이다.
이 Provider를 통해 View에서 ViewModel을 불러와 상태를 감시하거나 함수를 실행할 수 있다.


View

 
final homeState = ref.watch(homeViewModelProvider); final viewModel = ref.read(homeViewModelProvider.notifier);
  • watch는 상태를 감시한다. 값이 바뀌면 UI가 자동으로 새로 그려진다.
  • read는 행동을 실행할 때 사용된다. 예를 들어 버튼을 눌러 검색을 실행할 때 ViewModel의 함수를 호출한다.

데이터 흐름 요약

  1. 사용자가 검색어를 입력한다.
  2. View에서 ref.read()를 통해 ViewModel의 searchBooks() 함수를 실행한다.
  3. ViewModel은 Repository에게 데이터를 요청한다.
  4. Repository는 네이버 API에 요청을 보내고, JSON 데이터를 받아 Book 객체 리스트로 변환한다.
  5. ViewModel이 state를 업데이트한다.
  6. ref.watch가 state 변화를 감지하고, UI가 자동으로 갱신된다.

핵심 포인트

  • read()는 행동을 시작하는 트리거이고, watch()는 그 결과를 감시해서 UI를 새로 그린다.
  • Model은 데이터를 해석하는 방법을 알고 있고, Repository는 데이터를 실제로 가져온다.
  • ViewModel은 받은 데이터를 state로 관리해 View와 연결한다.
  • 전체 구조를 통해 코드의 역할이 명확히 분리되어 유지보수가 쉬워진다.

느낀 점

처음에는 Model, Repository, ViewModel이 왜 이렇게 나뉘어 있는지 이해가 잘 안 됐다.
특히 fromJson과 toJson이 왜 Model에 있어야 하는지도 헷갈렸는데,
이제는 “Model이 자기 데이터를 스스로 해석할 줄 알아야 한다”는 개념이 조금 이해됐다.

또 ref.watch와 ref.read가 단순히 비슷한 함수가 아니라,
하나는 상태 감시용, 하나는 행동 실행용이라는 점이 명확히 구분됐다.
아직 완전히 머릿속에 그려지는 건 아니지만,
ViewModel이 setState를 대신하는 구조라는 게 감이 잡히기 시작했다.


오늘의 한 줄 정리

read는 행동을 일으키고, watch는 그 결과를 감시한다.
Model은 데이터를 해석하고, Repository는 데이터를 가져오며,
ViewModel은 그것을 화면 상태로 바꾼다.