[Flutter] GetX 와 UI 업데이트

17928 단어 flutterdartgetxdart

개요

Flutter 에서는 위젯을 상태(State)에 따라 StatelessWidgetStatefulWidget 으로 분류해서 정의하고 위젯의 상태를 변경하여 UI 를 업데이트할 수 있는 setState 함수를 지원합니다. 하지만 이 함수에 대해서 플러터 공식 문서에서 이런 경고 내용을 볼 수 있습니다.

Avoid overly large single Widgets with a large build() function. Split them into different Widgets based on encapsulation but also on how they change

하나의 큰 위젯, build 함수를 피하라고 써있습니다. setState 의 작동방식은 결국 build 함수를 다시 돌려 rebuild 하는 것이기 때문이죠. 이 방식은 업데이트가 필요하지 않는 위젯도 rebuild 를 하게 되는 문제가 있습니다.

setState 함수의 문제점

다음 코드는 플러터를 시작했을 때 기본으로 작성되는 Counter 위젯입니다.

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _counter++;
          });
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

MyHomePageStatefulWidget으로 상태, State를 가집니다. _counter 라는 상태는 FloatingActionButton이 클릭됐을 때 setState 함수로 상태가 변경이 됩니다.

프로파일링을 돌려보겠습니다.

버튼을 눌렀을 때 rebuild 된 횟수를 보면 화면상 변경된것은 Text 뿐이지만 AppBarFloationActionButton도 rebuild 가 됐습니다. 즉 UI업데이트가 낭비되고 있는 것입니다. 이를 해결하기 위해선 build 함수를 매우 작게 나눠야 하고 setState 를 까먹지 않게 호출해줘야 합니다. 결국 "데이터가 변경 되면 변경 된 위젯만 자동으로 UI를 업데이트"하고 싶은 니즈가 생길것입니다. 이런 니즈를 충족할 수 있는 좋은 라이브러리가 GetX 라이브러리입니다.

Getx 라이브러리

GetX 라이브러리의 설명에 따르면

GetX는 Flutter를 위한 매우 가볍고 강력한 솔루션입니다. 고성능 상태 관리, 지능형 종속성 주입 및 빠르고 실용적인 라우트 관리가 결합되어 있습니다.

라고 설명합니다. 이번 포스팅에서는 고성능 상태 관리라는 것에 초점을 맞춰보겠습니다.

기본 내용

  1. GetX 라이브러리는 StatefulWidget 사용을 지양합니다.
  2. GetX 라이브러리는 .obs 연산자를 이용해 아주 간단하게 변수를 Observable 로 바꿀 수 있습니다.

우선 기존 Counter 를 GetX 로 변경한 코드부터 보겠습니다.

class GetMyHomePageController extends GetxController {
  RxInt counter = 0.obs;

  increase() {
    counter.value += 1;
  }
}

class GetMyHomePage extends StatelessWidget {
  const GetMyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  
  Widget build(BuildContext context) {
    return GetBuilder<GetMyHomePageController>(
      init: GetMyHomePageController(),
      builder: (controller) => Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Obx(
                () => Text(
                  '${controller.counter.obs}',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => controller.increase(),
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

GetxController

우선 GetxController 라는 것이 등장했습니다. GetXStatelessWidget을 사용하기 때문에 이 위젯에서는 데이터에 따른 UI 만을 보여주는것이 좋습니다. 따라서 상태가 변할 수 있는 데이터들을 따로 컨트롤러에서 관리를 하는데 MVVM 패턴의 ViewModel 과 유사하다고 생각하시면 됩니다.

GetBuilder 를 사용해서 init 에 사용하고자 할 컨트롤러를 정의를 해주면 builder 함수에서 해당 컨트롤러를 사용할 수 있습니다. 이 밖에도 GetX 에서는 GetxController를 관리하는 아주 많은 방법을 제공해주므로 다음에 포스팅하도록 하겠습니다.

.obs

Observable 을 변환시켜주는 연산자입니다. 데이터가 변경이 되면 자동으로 Notify 를 보내 UI 를 업데이트 할 수 있습니다. 예제에서는 increase 함수에서 counter.value += 1로 데이터를 업데이트 하고 있습니다.

.obs 객체가 Notify 를 보내면 UI 에서는 업데이트를 해야합니다. 하지만 실제 View 는 StatelessWidget이므로 업데이트를 하지 않고 업데이트를 하고 싶은 위젯을 Obx 위젯으로 감싸야합니다. 이 때 Obx(builder) 안에 .obs 객체가 없다면 에러가 나니 주의가 필요합니다.

프로파일링


똑같이 버튼을 눌렀을 때 rebuild 횟수 입니다. 데이터에 바인딩 되어있는 Text 뷰와 그걸 감싸는 Obx 위젯만 rebuild 됐고 다른 AppBarFloatingActionButton 은 rebuild 되지 않았습니다. 이 때문에 GetX 는 고성능 상태 관리를 지원할 수 있게 됩니다.

주의할 점

ObxText 가 아닌 상위 위젯인 Scaffold에 감싸게 된다면 마찬가지로 화면에 변경점이 없더라도 Obx로 감싼 모든 위젯들이 rebuild 가 됩니다. 예를들어 스켈레톤 UI 를 사용할 때 페이지 이동 후 데이터를 받아올 때까지 로딩화면을 보여줘야하므로 전체를 Obx 객체를 감싸게 됩니다. 하지만 그 이후로는 전체를 rebuild 하지 않고 부분적으로 rebuild 하고 싶을 수 있는데 이 문제는 Obx 객체 안에 Obx 객체를 넣어서 해결할 수 있습니다. 다음 포스팅에서 깊게 다뤄보겠습니다.

마무리

BlocProvider에 비해 GetX 는 정말 쉽게 사용할 수 있어서 회사 프로젝트에서도 적극적으로 도입했었습니다. 아주 간단하게 "변할 수 있는 데이터면 Rx 객체로 선언하고 그 데이터를 Obx 로 감싸기만 하면 된다"만 가르쳐줘도 처음 써보는 신입분들이 30분이면 익숙해졌습니다. 라우트 관리와 페이지 상태 관리도 정말 많이 지원하고 있어 기회가 된다면 관련 내용 정리해보겠습니다.

좋은 웹페이지 즐겨찾기