ChangeNotifierProvider란?
ChangeNotifierProvider는 Flutter의 기본 ChangeNotifier 클래스를 Riverpod 생태계에서 사용할 수 있게 해주는 브릿지 역할의 Provider입니다. 기존에 Provider 패키지나 setState로 관리하던 ChangeNotifier 기반 코드를 Riverpod으로 마이그레이션할 때 유용합니다. 하지만 새로운 프로젝트에서는 StateNotifierProvider나 NotifierProvider 사용을 권장합니다.
언제 사용하는가?
사용하기 좋은 경우:
• 기존 코드 마이그레이션: Provider 패키지에서 Riverpod으로 점진적 전환
• 서드파티 라이브러리 연동: ChangeNotifier를 사용하는 외부 패키지
• 레거시 코드 유지: 이미 잘 작동하는 ChangeNotifier 코드 재사용
• 팀 학습 곡선: Riverpod 도입 초기에 익숙한 패턴 유지
사용하면 안 되는 경우:
• 새 프로젝트: StateNotifierProvider나 NotifierProvider 사용
• 성능이 중요한 앱: 불필요한 리빌드가 발생할 수 있음
• 복잡한 상태 관리: 불변성이 보장되지 않아 예측하기 어려움
• 테스트가 중요한 코드: 상태 변경 추적이 어려움
• 기존 코드 마이그레이션: Provider 패키지에서 Riverpod으로 점진적 전환
• 서드파티 라이브러리 연동: ChangeNotifier를 사용하는 외부 패키지
• 레거시 코드 유지: 이미 잘 작동하는 ChangeNotifier 코드 재사용
• 팀 학습 곡선: Riverpod 도입 초기에 익숙한 패턴 유지
사용하면 안 되는 경우:
• 새 프로젝트: StateNotifierProvider나 NotifierProvider 사용
• 성능이 중요한 앱: 불필요한 리빌드가 발생할 수 있음
• 복잡한 상태 관리: 불변성이 보장되지 않아 예측하기 어려움
• 테스트가 중요한 코드: 상태 변경 추적이 어려움
기본 사용법
Dart
// 1. ChangeNotifier 클래스 정의
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 상태 변경 알림
}
void decrement() {
if (_count > 0) {
_count--;
notifyListeners();
}
}
void reset() {
_count = 0;
notifyListeners();
}
}
// 2. Provider 정의
final counterProvider = ChangeNotifierProvider<CounterModel>((ref) {
return CounterModel();
});
// 3. UI에서 사용
class CounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = ref.watch(counterProvider);
return Column(
children: [
Text('Count: ${counter.count}'),
Row(
children: [
ElevatedButton(
onPressed: counter.increment,
child: Text('+'),
),
ElevatedButton(
onPressed: counter.decrement,
child: Text('-'),
),
ElevatedButton(
onPressed: counter.reset,
child: Text('Reset'),
),
],
),
],
);
}
}
실무 예제: 기존 Provider 패키지 마이그레이션
Dart
// 기존 Provider 패키지 코드 (마이그레이션 전)
class ShoppingCartModel extends ChangeNotifier {
final List<CartItem> _items = [];
List<CartItem> get items => List.unmodifiable(_items);
int get totalPrice => _items.fold(0, (sum, item) => sum + item.price);
void addItem(CartItem item) {
final existingIndex = _items.indexWhere((i) => i.id == item.id);
if (existingIndex >= 0) {
_items[existingIndex] = _items[existingIndex].copyWith(
quantity: _items[existingIndex].quantity + 1,
);
} else {
_items.add(item);
}
notifyListeners();
}
void removeItem(String itemId) {
_items.removeWhere((item) => item.id == itemId);
notifyListeners();
}
void clearCart() {
_items.clear();
notifyListeners();
}
}
// Riverpod으로 마이그레이션 (코드는 그대로 유지)
final shoppingCartProvider = ChangeNotifierProvider<ShoppingCartModel>((ref) {
return ShoppingCartModel();
});
// 기존 Consumer 위젯도 그대로 사용 가능
class CartView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final cart = ref.watch(shoppingCartProvider);
return Column(
children: [
Text('총 ${cart.items.length}개 상품'),
Text('총 금액: ${cart.totalPrice}원'),
Expanded(
child: ListView.builder(
itemCount: cart.items.length,
itemBuilder: (context, index) {
final item = cart.items[index];
return ListTile(
title: Text(item.name),
subtitle: Text('${item.price}원'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => cart.removeItem(item.id),
),
);
},
),
),
],
);
}
}
주의사항과 팁
자주 하는 실수들:
1. notifyListeners() 빼먹기:
// ❌ 잘못된 방법
void increment() {
_count++;
// notifyListeners() 없으면 UI가 업데이트되지 않음
}
// ✅ 올바른 방법
void increment() {
_count++;
notifyListeners(); // 필수!
}
2. 너무 많은 notifyListeners() 호출:
// ❌ 비효율적인 방법
void updateUserInfo(String name, String email) {
_name = name;
notifyListeners(); // 불필요한 호출
_email = email;
notifyListeners(); // 불필요한 호출
}
// ✅ 효율적인 방법
void updateUserInfo(String name, String email) {
_name = name;
_email = email;
notifyListeners(); // 한 번만 호출
}
베스트 프랙티스:
• 상태를 private으로 만들고 getter 제공
• dispose()에서 리소스 정리
• notifyListeners()를 적절한 시점에만 호출
• 복잡한 로직은 메소드로 분리
• 마이그레이션 계획 수립 (StateNotifier로 점진적 전환)
1. notifyListeners() 빼먹기:
`dart// ❌ 잘못된 방법
void increment() {
_count++;
// notifyListeners() 없으면 UI가 업데이트되지 않음
}
// ✅ 올바른 방법
void increment() {
_count++;
notifyListeners(); // 필수!
}
`2. 너무 많은 notifyListeners() 호출:
`dart// ❌ 비효율적인 방법
void updateUserInfo(String name, String email) {
_name = name;
notifyListeners(); // 불필요한 호출
_email = email;
notifyListeners(); // 불필요한 호출
}
// ✅ 효율적인 방법
void updateUserInfo(String name, String email) {
_name = name;
_email = email;
notifyListeners(); // 한 번만 호출
}
`베스트 프랙티스:
• 상태를 private으로 만들고 getter 제공
• dispose()에서 리소스 정리
• notifyListeners()를 적절한 시점에만 호출
• 복잡한 로직은 메소드로 분리
• 마이그레이션 계획 수립 (StateNotifier로 점진적 전환)
다른 Provider와 비교
ChangeNotifierProvider vs StateNotifierProvider:
• ChangeNotifierProvider: 가변(mutable) 상태, 전체 객체 감시, 낮은 예측 가능성
• StateNotifierProvider: 불변(immutable) 상태, 부분적 감시 가능, 높은 예측 가능성
ChangeNotifierProvider vs NotifierProvider:
• ChangeNotifierProvider: Riverpod 1.0+, ChangeNotifier 상속, 마이그레이션 용도
• NotifierProvider: Riverpod 2.0+, Notifier 상속, 새 프로젝트 권장
선택 기준:
• 기존 코드 있음: ChangeNotifierProvider로 빠른 마이그레이션
• 새 프로젝트: StateNotifierProvider나 NotifierProvider 사용
• 점진적 개선: ChangeNotifier → StateNotifier → Notifier 순서로 전환
• ChangeNotifierProvider: 가변(mutable) 상태, 전체 객체 감시, 낮은 예측 가능성
• StateNotifierProvider: 불변(immutable) 상태, 부분적 감시 가능, 높은 예측 가능성
ChangeNotifierProvider vs NotifierProvider:
• ChangeNotifierProvider: Riverpod 1.0+, ChangeNotifier 상속, 마이그레이션 용도
• NotifierProvider: Riverpod 2.0+, Notifier 상속, 새 프로젝트 권장
선택 기준:
• 기존 코드 있음: ChangeNotifierProvider로 빠른 마이그레이션
• 새 프로젝트: StateNotifierProvider나 NotifierProvider 사용
• 점진적 개선: ChangeNotifier → StateNotifier → Notifier 순서로 전환
정리
ChangeNotifierProvider는 기존 Flutter 생태계와 Riverpod를 연결하는 다리 역할을 합니다. 마이그레이션이나 서드파티 라이브러리 연동에는 유용하지만, 새로운 코드에서는 더 나은 대안들을 고려해야 합니다.
핵심 포인트:
• 마이그레이션 도구: 기존 ChangeNotifier 코드를 Riverpod에서 활용
• 익숙한 패턴: Flutter 개발자에게 친숙한 방식
• 한계 인식: 가변 상태로 인한 예측 가능성 문제
• 장기 계획: 점진적으로 다른 Provider로 전환
다음 편 예고: 7편에서는 Riverpod 2.0의 최신 상태 관리 솔루션인 NotifierProvider에 대해 알아보겠습니다.
핵심 포인트:
• 마이그레이션 도구: 기존 ChangeNotifier 코드를 Riverpod에서 활용
• 익숙한 패턴: Flutter 개발자에게 친숙한 방식
• 한계 인식: 가변 상태로 인한 예측 가능성 문제
• 장기 계획: 점진적으로 다른 Provider로 전환
다음 편 예고: 7편에서는 Riverpod 2.0의 최신 상태 관리 솔루션인 NotifierProvider에 대해 알아보겠습니다.