
안녕하세요. FutureBuilder 많이 사용하고 계신가요?
회사 코드를 까보니 FutureBuilder 가 한 손에 꼽을 정도로 적었습니다. 모두 GetX의 Obx로 상태 관리하고, 재랜더링을 하고 있기 때문입니다.
GetX는 참 강력하지만 너무 남발한 코드를 보니 이곳저곳에 앱 성능 문제가 많이 있습니다. 그리고 명확하지 않아 왜 사용하는지, 어디서 사용하는지 알기 어렵다는 문제도 있습니다.
그래서 명확하게 하기 위해
FutureBuilder
를 도입하고 있는데요, builder에 validate 함수를 넣어둠으로 상태를 검증하고 있습니다.class MyWidget extends StatelessWidget {
final Future<String> _fetchData;
MyWidget(this._fetchData);
Widget validate(AsyncSnapshot<String> snapshot) {
if (snapshot.hasError) {
return false;
}
if (!snapshot.hasData) {
return false;
}
// 여기서 문제가 발생
if (myWidgetController.status == MyWidgetStatus.Loading) {
return false;
}
return true;
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _fetchData,
builder: (context, snapshot) {
// 상태 검증
if (!validate(snapshet)) {
return CircularProgressIndicator();
}
// 데이터가 있고 에러가 없는 경우에만 실행
final data = snapshot.data!;
return Text(data);
},
);
}
}
화면으로 보면 이 코드는 무한 로딩으로 표시될 것입니다. 이유는,
FutureBuilder
에서 사용하고 있는 상태기 2개라서 그렇습니다.FutureBuilder
내부적으로 snapshot
의 상태가 바뀔 때마다 재랜더링을 하지만, myWidgetController.status
가 바뀔 때마다 재랜더링은 하지 않기 때문에, 이 status
가 완료되기 전의 검증을 하고 끝마치기 때문입니다.그렇다고
Obx
로 FutureBuilder
를 감싸기엔 잘못 연결된 로직(상위 위젯에서 사용하는 상태를 하위 위젯에서 변경한다거나..)으로 문제가 생길 수 있기 떄문에 다른 방법을 사용했습니다.@override
void initState() {
super.initState();
// 컨트롤러의 상태가 변할 때마다 setState를 호출하는 리스너를 등록합니다.
// 만약 myWidgetController.status가 Rx 변수라면 아래처럼 사용 가능합니다.
ever(myWidgetController.status, (_) {
setState(() {}); // 컨트롤러 상태 변경 시 재렌더링
});
// 만약 컨트롤러가 Rx 변수를 사용하지 않는다면,
// 컨트롤러 내부에서 상태 변경 시 직접 setState를 호출하거나 다른 방식으로 notify 해줘야 합니다.
}
결국 Obx 내부의 Rx 변수 하나라도 바뀌면 그 아래 모든 위젯이 재랜더링되니까, 불필요한 렌더링이 발생할 가능성이 높습니다. 그래서 특정 상태 변화만 구독해 UI를 갱신하고 싶다면, 결국 ever 함수를 쓰는 게 깔끔하고 효과적인 방법입니다.
ever
함수는 원하는 Rx 변수의 변경만 감지해서 setState
를 호출하므로, Obx
처럼 전체 위젯 트리를 무조건 갱신하는 대신 꼭 필요한 부분만 업데이트할 수 있게 해줍니다. 즉, 상태 관리 방식은 그대로 유지하면서도 불필요한 재랜더링을 피할 수 있으니, 이 방법이 최적의 선택이 될 수 있습니다.Share article