guricode

[flutter] 글 작성 후 게시글 리스트 새로고침 하는 법 본문

카테고리 없음

[flutter] 글 작성 후 게시글 리스트 새로고침 하는 법

agentrakugaki 2025. 10. 11. 01:41

이번에 커뮤니티 수정 삭제 기능을 만들면서 꽤 오래 붙잡고 있었던 문제가 있었다.
기능 자체는 잘 작동했는데, 글 작성 후 뒤로 돌아가면 새로 쓴 글이 리스트에 안 보였다.
화면은 그대로인데, Firestore에는 데이터가 들어가 있었다.
즉, 데이터는 갱신됐지만 UI는 리빌드되지 않은 상태였다.

수정 삭제 기능을 만들면서 수정 삭제는 context. pushReplacement를 사용해 게시글 리스트가 보이도록 대체했다.

 

처음엔 단순히 ref.listen을 사용해서 글이 추가되면 목록이 새로 고쳐지도록 만들었었다.
하지만 ref.listen은 빌드 안에서만 사용할 수 있는 메서드다.
initState나 dispose처럼 위젯 생명주기 단계에서 쓰면 Riverpod이 에러를 낸다.
로그에도 이렇게 찍혔다.

ref.listen can only be used within the build method of a ConsumerWidget

즉, 상태 변화를 감지하려면 빌드 시점 안에서만 ref.listen을 허용한다는 뜻이었다.
하지만 나는 build 전에 초기 로드와 동시에 구독을 걸고 싶었기 때문에, 이 구조로는 불가능했다.

ref.listen<T>(provider, (prev, next) { ... }) 콜백의 시그니처가 (T? prev, T next)로 고정이라서 위치 인자를 둘 다 선언해야 한다.

하지만 시그니처를 안쓴다면 그냥 (_,__)로 해도 상관없긴하다.

@override
void initState() {
  super.initState();
  ref.listen<int>(communityChangedTickProvider, (prev, next) {
    ref.invalidate(provider);
  });
}

이렇게 했더니 위 에러가 바로 발생했다.
빌드 외부에서 ref.listen을 호출했기 때문이다.

문제의 핵심은 빌드 밖에서 안전하게 Provider 변화를 감지하는 방법이었다.
그래서 ref.listenManual을 사용했다.
이건 이름 그대로 수동 구독 방식이다.
수동으로 구독을 만들고, 나중에 직접 닫아줘야 한다.
이 방식은 initState, dispose, 콜백 함수 같은 빌드 외부에서도 쓸 수 있다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
이때 반환되는 객체가 ProviderSubscription이다.
이 객체는 구독을 관리하는 핸들러 같은 역할을 한다.
이걸 들고 있다가 dispose에서 .close()로 닫아주면 깔끔하게 메모리가 정리된다.

 

구분                                          StatefulWidget                                 ConsumerWidget      ConsumerStatefulWidget

상태 보관 State 객체 없음 State + ref
생명주기 initState → build → dispose build만 있음 initState → build → dispose
Provider 접근 불가능 ref.watch 가능 ref.watch, ref.listen 가능
ref.listen 위치 사용 불가 build 안에서만 가능 build 안에서만 가능
ref.listenManual 사용 가능 가능 가능

수정한 코드

//vm에서 프로바이더선언
final communityChangedTickProvider = StateProvider<int>((ref) => 0);
//vm끝

//여기서부터 screen
//글작성 메서드에 추가
   ref.read(communityChangedTickProvider.notifier).state++;
 //글작성 메서드 끝
 
 //게시글 리스트
ProviderSubscription<int>? _changedSub;

@override
void initState() {
  super.initState();
  _maybeInitProviderAndLoad();

  _changedSub = ref.listenManual<int>(
    communityChangedTickProvider,
    (prev, next) {
      if (!_ready || !mounted) return;
      ref.invalidate(provider);
      Future.microtask(() => ref.read(provider.notifier).loadInitial(ref));
    },
  );
}

@override
void dispose() {
  _changedSub?.close();
  super.dispose();
}

이렇게 고치고 나니 에러가 사라졌다.

ref.invalidate(provider);는 해당 provider의 캐시를 폐기하고 다시 만들 준비 상태로 만드는 거다.

  • 지금 갖고 있던 상태를 dispose 시킴
  • 다음에 ref.watch나 ref.read가 일어나면 처음부터 재생성
  • NotifierProvider라면 build가 다시 돌고 내부 상태도 초기화됨
  • 진행 중이던 비동기 작업도 함께 정리됨
  • 즉, 리스트 페이징처럼 내부에 상태가 쌓여 있는 VM을 완전 초기화하고 싶을 때 쓰는 스위치라고 보면 된다.

초기화하고 기존에 게시글 리스트 초기화 메서드를 실행했따


새 글을 작성하고 뒤로 가면, 즉시 새로운 글이 목록에 나타났다.
리버팟의 상태 관리가 정상적으로 돌아가면서, 화면과 데이터의 싱크가 정확히 맞았다.

Riverpod은 단순히 상태만 관리하는 게 아니라, 위젯 생명주기와 긴밀하게 맞물려 있다.

일반 StatefulWidget과 ConsumerWidget은 위젯 생명주기 구조 자체는 비슷하지만,
Riverpod은 빌드 시점에 Provider 구독 그래프를 생성한다는 점에서 완전히 다르다.

그래서 ref.listen은 build 내부에서만 허용되는 것이다.
만약 initState나 비동기 콜백 등 빌드 외부에서 Provider를 감시해야 한다면
ref.listenManual을 사용해야 한다.