2026년 8월 10일 월요일

Flutter 상태관리 Provider 헷갈리는 부분 정리 (watch vs read 차이)

Flutter 상태관리 Provider 헷갈리는 부분 정리 (watch vs read 차이)
Flutter 상태관리 Provider 헷갈리는 부분 정리 (watch vs read 차이)



Flutter 공부하면서 

상태관리 얘기 나오면 무조건 등장하는 게 Provider인데,

솔직히 처음엔 이게

패키지 이름인지, 위젯 이름인지, 개념인지도 헷갈렸다.


나도 Developer English 앱 만들면서 Hive랑 같이 계속 썼는데,

"그냥 쓰라니까 쓰는" 상태였다가 

어느 순간 "아 이게 이런 구조였구나" 싶은 순간이 와서,

그 정리를 오늘 해보려고 한다.





Provider는 정확히 뭐야?

결론부터 말하면

Provider는

Flutter에서 상태(state)를 위젯 트리 전체에 효율적으로 공유하기 위한 패키지다.


 InheritedWidget이라는 Flutter 기본 기능을 훨씬 쓰기 편하게 감싸놓은 wrapper라고 보면 된다.


즉,

Provider 자체가 상태관리를 "해주는" 마법 같은 존재가 아니라,

데이터를 어디서든 꺼내 쓸 수 있게 트리 위에 얹어두는 도구다.


실제 상태 변화 로직은 우리가 직접 짜는 ChangeNotifier 클래스가 담당한다.


구성 요소 역할
ChangeNotifier 실제 데이터와 로직을 담는 클래스. 상태가 바뀌면 notifyListeners()로 알림
ChangeNotifierProvider ChangeNotifier 객체를 위젯 트리에 등록(제공)하는 위젯
Consumer / context.watch 등록된 데이터를 꺼내서 쓰고, 변경되면 자동으로 다시 그려주는
부분





왜 굳이 Provider를 써야 해?

처음엔 "그냥 setState 쓰면 되는 거 아냐?" 싶었다.

근데 앱이 조금만 커져도 문제가 생긴다.


  • 화면 A에서 바꾼 데이터를 화면 C에서도 써야 하는 상황이 생김
  • 부모 → 자식 → 손자 위젯으로 값을 계속 넘기는 "props drilling"이 지저분해짐
  • setState는 그 위젯이랑 자식들만 다시 그리는데, 여러 화면에 흩어진 상태를 관리하기엔 부족함


나 같은 경우

Developer English 앱에서

퀴즈 결과, 오답노트, 학습 진행도를

홈/퀴즈/결과 화면이 전부 공유해야 했는데,

이게 Provider 없이는 진짜 답이 안 나오는 구조였다.


Provider 등록 전/후 위젯 트리 구조도
Provider 등록 전/후 위젯 트리 구조도



기본 사용법 (예시 코드)

가장 단순한 카운터 예시로 구조만 잡아보면 이렇다.


// 1. 상태를 담는 클래스
class CounterModel extends ChangeNotifier {
  int count = 0;

  void increment() {
    count++;
    notifyListeners(); // 변경사항 알림
  }
}

// 2. 트리 최상단에 등록
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: MyApp(),
    ),
  );
}

// 3. 아무 화면에서나 꺼내 쓰기
class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<CounterModel>();

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


여기서 헷갈리기 쉬운 포인트가 context.watchcontext.read의 차이다.


메서드 언제 쓰나
context.watch() 값이 바뀔 때마다 화면을 다시 그려야 할 때 (build 안에서)
context.read() 값을 한 번만 읽고 화면 갱신은 필요 없을 때 (버튼 클릭 같은 이벤트 안에서)


이걸 반대로 쓰면

"왜 화면이 갱신 안 되지?" 하면서 삽질하게 된다.

나도 초반에 이 부분에서 몇 시간 날렸다.


Provider vs Riverpod, 뭐가 다른데?

공부하다 보면 

Riverpod이라는 이름도 자주 보이는데,

Riverpod은 Provider를 만든 같은 개발자가 만든 "다음 버전" 같은 개념이다.


Provider는 BuildContext에 의존하는 반면,

Riverpod은 그 의존성을 없애서

테스트나 위젯 트리 바깥에서도 상태를 다루기 쉽게 만든 것에 가깝다.


다만 입문 단계에서는 Provider로 충분히 개념을 잡는 게 낫다고 생각한다.

구조 자체를 이해하고 넘어가야 Riverpod 볼 때도 헷갈리지 않는다.




정리

Provider는 결국

"ChangeNotifier로 만든 상태를, 

위젯 트리 어디서든 꺼내 쓰고 변경사항도 자동 반영되게 해주는 배급 시스템"

이라고 이해하면 제일 편했다.


처음엔 API가 낯설어서 무섭게 느껴지는데,

ChangeNotifier / Provider 등록 / watch·read

세 덩어리로 나눠서 보면 생각보다 단순한 구조다.

댓글 없음:

댓글 쓰기

커서(Cursor)로 바이브코딩을 처음 해본 후기|편하긴 한데 앱이 버그 덩어리가 됐다

커서(Cursor)로 바이브코딩을 처음 해본 후기 편하긴 한데 앱이 버그 덩어리가 됐다 오늘은 커서(C...