본문 바로가기
전체보기

[TIL-숙련-1일차] - Provider vs Riverpod 비교 및 예제 실습

by 오늘도잡학다식 2025. 4. 10.

 

 

TIL - Provider vs Riverpod 비교 및 예제 실습

오늘은 Flutter 상태관리 도구인 ProviderRiverpod을 비교하고, 같은 카운터 앱을 두 방식으로 구현해보며 차이를 직접 느껴봤습니다.


✅ Provider 예제

📁 counter_provider.dart


class CounterProvider with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}
  

📁 main.dart


void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CounterProvider(),
      child: MyApp(),
    ),
  );
}
  

📁 CounterScreen.dart


class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<CounterProvider>();

    return Column(
      children: [
        Text('현재 값: ${counter.count}'),
        ElevatedButton(
          onPressed: () => context.read<CounterProvider>().increment(),
          child: Text('증가'),
        ),
      ],
    );
  }
}
  

✅ Riverpod 예제

📁 counter_provider.dart


class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0;

  void increment() => state++;
}

final counterProvider = NotifierProvider<CounterNotifier, int>(() {
  return CounterNotifier();
});
  

📁 main.dart


void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}
  

📁 CounterScreen.dart


class CounterScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    final counterNotifier = ref.read(counterProvider.notifier);

    return Column(
      children: [
        Text('현재 값: $count'),
        ElevatedButton(
          onPressed: counterNotifier.increment,
          child: Text('증가'),
        ),
      ],
    );
  }
}
  

📌 정리

항목 Provider Riverpod
상태 클래스 ChangeNotifier Notifier
전역 상태 관리 MultiProvider 필요 ProviderScope만 있으면 됨
구조화 (MVVM) 불편 쉽고 명확함
테스트 용이성 낮음 높음 (override 가능)
러닝 커브 매우 쉬움 약간 있음

🔁 ref.read vs ref.watch

ref.watch()ref.read()는 Riverpod의 핵심 개념입니다.

구분 ref.watch() ref.read()
의미 상태를 구독하고, 변화가 생기면 위젯이 자동으로 다시 그림 상태를 1회성으로 읽기만 하고 위젯은 다시 그려지지 않음
용도 화면에 보여지는 값에 사용 (예: count 표시) 이벤트 발생 시 사용 (예: 버튼 클릭 → 함수 호출)
재빌드 발생 ✅ O ❌ X

💡 예시


// 상태를 화면에 표시할 때
final count = ref.watch(counterProvider);

// 버튼 클릭 시 함수 호출만 하고 싶을 때
ref.read(counterProvider.notifier).increment();

올바르게 구분해서 사용하면 성능 최적화정확한 상태 관리에 큰 도움이 됩니다!

결론

Provider는 간단한 앱이나 빠른 프로토타입에 적합하고,
Riverpod은 앱이 커지거나 구조화/유지보수가 중요할 때 강력한 선택입니다.
실무에선 Riverpod을 주로 쓰는 이유를 직접 체감할 수 있었습니다.

 

 

🔐 Provider vs Riverpod 로그인 상태 관리 구현 비교

사용자 로그인 기능을 동일하게 구현하면서, Provider와 Riverpod의 구조와 코드 차이를 비교합니다.

1. 상태 클래스 및 ViewModel

Provider (ChangeNotifier) Riverpod (Notifier)
class AuthProvider with ChangeNotifier {
  User? _user;
  bool _isLoading = false;
  String? _error;

  User? get user => _user;
  bool get isLoading => _isLoading;
  String? get error => _error;

  Future<void> login(String id, String pw) async {
    _isLoading = true;
    _error = null;
    notifyListeners();

    try {
      _user = await _repo.login(id, pw);
    } catch (e) {
      _error = '로그인 실패';
    }

    _isLoading = false;
    notifyListeners();
  }

  Future<void> logout() async {
    await _repo.logout();
    _user = null;
    notifyListeners();
  }
}
class AuthState {
  final bool isLoading;
  final User? user;
  final String? error;

  AuthState({this.isLoading = false, this.user, this.error});
}

class AuthNotifier extends Notifier<AuthState> {
  final _repo = AuthRepository();

  @override
  AuthState build() => AuthState();

  Future<void> login(String id, String pw) async {
    state = AuthState(isLoading: true);
    try {
      final user = await _repo.login(id, pw);
      state = AuthState(user: user);
    } catch (e) {
      state = AuthState(error: '로그인 실패');
    }
  }

  Future<void> logout() async {
    await _repo.logout();
    state = AuthState();
  }
}

2. Provider 선언

Provider Riverpod
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => AuthProvider()),
  ],
  child: MyApp(),
)
final authProvider =
  NotifierProvider<AuthNotifier, AuthState>(() => AuthNotifier());

3. 로그인 호출 방식

Provider Riverpod
final auth = context.watch<AuthProvider>();
context.read<AuthProvider>().login(id, pw);
final auth = ref.watch(authProvider);
ref.read(authProvider.notifier).login(id, pw);

4. 로그아웃 호출 방식

Provider Riverpod
context.read<AuthProvider>().logout();
ref.read(authProvider.notifier).logout();

📌 기능 및 아키텍처 비교 요약

항목 Provider Riverpod
상태 선언 방식 ChangeNotifier Notifier with state
위젯과의 연결 MultiProvider로 루트에 등록 ProviderScope 한 줄이면 끝
상태 접근 context.watch / read ref.watch / read
비동기 처리 직접 bool로 로딩/에러 상태 관리 AsyncNotifier 등으로 구조화 가능
ViewModel 역할 구현 구조 혼합되기 쉬움 ViewModel로 명확하게 분리 가능
테스트 용이성 Mock 직접 주입 복잡 overrideWithProvider로 매우 쉬움

✅ 결론

Provider는 간단한 상태에는 좋지만, 상태가 복잡해지고 분리가 필요한 상황에서는 구조화와 테스트가 어렵습니다.
Riverpod은 ViewModel 중심의 아키텍처에서 상태를 명확히 관리하고, 테스트 및 재사용이 쉽습니다.