1. 데이터 통신의 기초와 JSON
서버와 앱 간의 데이터 교환은 직렬화(Serialization) 과정을 통해 이루어진다.
- 직렬화(Serialization): Dart 객체 → Map → JSON 문자열로 변환
- 역직렬화(Deserialization): JSON 문자열 → Map → Dart 객체로 변환
이 과정을 통해 서로 다른 언어로 작성된 시스템(예: Java 서버와 Flutter 클라이언트) 간에도 데이터를 주고받을 수 있다
챕터1-1 데이터 통신 기초와 JSON
.
JSON의 기본 구조는 {key: value} 쌍이며, value에는 다음 타입이 올 수 있다.
String, Number, Boolean, Array, Object, null
Dart에서는 dart:convert 패키지의 jsonEncode, jsonDecode 함수를 이용해 변환을 수행한다.
- jsonEncode(Map) → JSON 문자열
- jsonDecode(String) → Map<String, dynamic> 또는 List<dynamic>
Map을 거치는 이유는 Dart 객체를 직접 문자열로 바꾸는 것은 복잡하기 때문에, 중간에 Map 형태로 변환한 후 라이브러리로 처리하는 것이다
챕터1-1 데이터 통신 기초와 JSON
.
2. JSON 실습 — Dart 객체 변환
Dart에서는 JSON 데이터를 다루기 위해 클래스를 정의하고 fromJson, toJson 메서드를 구현한다.
class User {
String name;
int age;
User({required this.name, required this.age});
User.fromJson(Map<String, dynamic> json)
: this(name: json['name'], age: json['age']);
Map<String, dynamic> toJson() => {"name": name, "age": age};
}
이런 구조를 사용하면 단순한 Map보다 오타를 줄이고 타입 안정성을 높일 수 있다
복잡한 JSON은 클래스 안에 또 다른 클래스를 포함시켜 처리한다.
예를 들어 Pet 안에 Contact 객체를 포함시키면, 중첩된 구조를 모델링할 수 있다.
→ 서버 데이터 → Map → 객체로 변환 → UI에서 활용이라는 데이터 흐름을 직접 경험했다.
3. MVVM 아키텍처
MVVM은 Model-View-ViewModel의 약자로, Flutter 앱 구조를 체계적으로 관리하기 위한 설계 패턴이다
- Model : 데이터 계층 (예: JSON으로 받은 User, Pet 등)
- View : UI 계층 (예: 위젯 화면)
- ViewModel : View와 Model 사이에서 데이터를 가공하고 상태를 관리하는 계층
StatefulWidget에서는 하나의 클래스가 UI, 로직, 상태 관리를 모두 담당해 코드가 복잡해진다.
반면 MVVM은 이 역할을 분리해 코드의 가독성과 유지보수성을 높인다.
→ View는 ViewModel을 구독하고(ViewModel의 상태 변화를 감시),
→ ViewModel은 Model에서 데이터를 가져와 상태를 갱신한다.
이렇게 의존성이 낮은 구조가 되어 테스트와 확장이 쉬워진다.
4. RiverPod — 상태 관리 패키지
MVVM에서 ViewModel의 역할을 돕는 라이브러리가 RiverPod이다
.
핵심 개념
- Notifier : 상태를 저장하고 변경을 알리는 객체 (ViewModel의 역할)
- NotifierProvider : ViewModel을 위젯에 공급하는 관리자
- Consumer 위젯 : Provider로부터 상태를 구독해 화면을 업데이트
class HomeState {
int counter;
HomeState(this.counter);
}
class HomeViewModel extends Notifier<HomeState> {
@override
HomeState build() => HomeState(1);
void updateState() {
state = HomeState(state.counter + 1);
}
}
final homeViewModelProvider =
NotifierProvider<HomeViewModel, HomeState>(() => HomeViewModel());
View에서는 Consumer 위젯 안에서 ref.watch(homeViewModelProvider)로 상태를 구독하고,
ref.read(homeViewModelProvider.notifier)로 ViewModel 메서드를 호출할 수 있다.
이렇게 하면 상태 변화 → 자동 UI 갱신이 자연스럽게 이루어진다.
5. RiverPod 실습 — User 정보 가져오기
실습에서는 버튼 클릭 시 서버에서 JSON 데이터를 받아오는 시나리오를 구현했다
구조 요약
- User 클래스 : 데이터 모델
- UserRepository : 서버 통신(가상) → JSON → 객체 변환
- HomeViewModel : Notifier 상속, getUserInfo() 메서드로 상태 갱신
- HomeState : 현재 user 정보와 fetch 시간 보관
- Consumer : View에서 ViewModel 상태 구독 및 UI 반영
HomeState homeState = ref.watch(homeViewModelProvider);
HomeViewModel viewModel = ref.read(homeViewModelProvider.notifier);
이 구조를 통해 Flutter 위젯 내부에서 비즈니스 로직을 분리하고,
데이터의 흐름이 Repository → ViewModel → View로 깔끔히 이어지도록 만들 수 있었다.
6. RiverPod의 장점 및 확장형 구조
마지막으로 다양한 Notifier 형태를 학습했다
Notifier는 앱 전역에서 지속적으로 유지되는 상태 관리에 사용되고,
AutoDisposeNotifier는 위젯이 사라질 때 상태가 자동으로 해제된다.
AutoDisposeFamilyNotifier는 ViewModel에 특정 값을 전달하여 상황에 따라 새로운 상태를 생성할 수 있다.
예를 들어, 로그인 시 사용자 정보처럼 전역적으로 유지되어야 하는 상태는 Notifier로,
회원가입 페이지 입력값처럼 일시적인 데이터는 AutoDisposeNotifier로,
게시글 상세 페이지처럼 ID에 따라 상태가 달라지는 경우는 AutoDisposeFamilyNotifier로 관리한다.
이 구조 덕분에 앱의 메모리를 효율적으로 관리하고, 각 화면의 생명주기에 맞춰 상태를 유연하게 유지·초기화할 수 있다.
정리
이번 학습을 통해 Flutter 앱의 핵심 구조를 다음과 같이 체계적으로 이해했다.
- JSON을 통한 데이터 직렬화 / 역직렬화 원리
- MVC보다 명확한 MVVM 설계 패턴 구조
- RiverPod을 활용한 효율적인 상태 관리 및 의존성 분리
즉, 데이터의 흐름이
서버(JSON) → Model → ViewModel(RiverPod) → View(Widget)
으로 이어지는 전체 구조를 한 눈에 파악할 수 있게 되었고,
이는 앞으로 더 복잡한 Flutter 앱에서도 안정적이고 유지보수하기 쉬운 코드 작성을 위한 중요한 기반이 되었다.