본문 바로가기
비전공자를 위한 Flutter/Flutter 심화과정

Flutter Helper Method vs Widgets 헬퍼 메소드와 위젯 중에 뭐가 더 좋을까?

by 밍잔 2022. 5. 1.

플러터 프레임워크로 앱을 만들다 보면 중복되는 위젯들이 많을 때가 있습니다. 대표적인 예로 '리스트'가 있죠. 리스트의 각 아이템마다 카드 디자인이 적용되어야 하는 경우, 우리는 카드 디자인이 적용된 위젯을 하나 만들고 재사용하는 객체지향 방법을 쓰죠. 이렇게 임의로 만든 위젯을 사용하는 방법은 크게 두 가지가 있습니다.

 

1. Class 안에 헬퍼 메소드로 위젯을 새로 정의해서 사용하는 방법

2. StatelessWidget 또는 StatefulWidget의 서브클래스를 만들어서 사용하는 방법

 

 

첫 번째 방법은 가장 편한 방법이죠. 하지만, 한 파일에 헬퍼 메소드가 많아지면 코드의 가독성이 떨어지니, 헬퍼 메소드로 만든 위젯만을 모아둔 extension 파일을 만들어 따로 관리하기도 합니다. 두 번째 방법은 귀찮기도 하고 파일이 지나치게 많아질 수 있다는 단점이 있죠. 오늘은 이 두 가지 방법 중에서 과연 어떤 방법이 성능 최적화에 효과적인지 알아봅시다. 아래는 해당 내용과 관련된 동영상입니다. 포스팅의 설명이 부족한 경우 동영상을 참고해보세요!

 


 

헬퍼 메소드가 뭔지 모르시는 분들을 위해 사진을 준비했습니다.

같은 위젯 클래스 안에 따로 column이라는 위젯 메소드를 만드는 거죠. build 메소드의 위젯들을 가독성 좋게 하기 위함입니다. 

 

UI 렌더링을 위해 반복되는 위젯 코드들은 어떻게 처리하는 게 좋을까요? 헬퍼 메소드를 이용하는 방법과 StatelessWidget, StatefulWidget을 새로 만드는 방법의 차이는 따로 분리하고자 하는 위젯이 같은 위젯 안에 있는가? 입니다. 코드를 옮기는 점에서 비슷하긴 하지만 같지는 않죠.

 

여러분이 지금까지 생각해 온 헬퍼 메소드의 장점은 아래와 같습니다.

'같은 위젯 클래스 안에 있다보니 클래스 안에 있는 모든 변수를 바로 사용할 수 있다.'
'그래서 성능 면에서 더 좋지 않나?'

 

강의 초반에 언급드렸던 여러분이 되어야 하는 모습, 귀찮은 장인 베짱이로서의 모습이긴 한데요. 사실 별개의 위젯을 선호하는 중요한 이유가 있습니다.

 

 

 

 

아시다시피 setState가 실행되면 build 메소드는 다시 구동됩니다. build 메소드를 다시 실행해서 변경된 정보로 UI를 새로 그려주는 덕분에 사용자가 버튼을 누르면 화면이 움직이는 효과 같은 걸 얻을 수 있죠. 

 

플러터 프레임워크에 있는 3가지 트리중 Widget 트리는 가장 상위의 트리입니다. Widget 트리에 위젯을 추가하는 건 앱의 속도를 늦추게 하지는 않지만, UI의 전체를 불필요하게 다시 생성하는 건 Element 트리와 RenderObject 트리가 영향을 받아 CPU를 낭비하게 됩니다.

결과적으로 앱이 느려지는 겁니다.

 

 

출처 : https://medium.flutterdevs.com/example-animations-in-flutter-2-1034a52f795b?gi=c5512ef24da2

 

 

가장 나쁜 케이스는 위젯 안에 애니메이션이 있을 때죠. 애니메이션을 위해 초당 60번이나 UI를 불필요하게 그리고 있는 겁니다. 사실 애니메이션이 적용된 위젯만 다시 그리면 되는 일인데 말이에요.

 

그래서 헬퍼 메소드가 아니라 완전히 다른 위젯으로 분리해야 우리가 원했던 애니메이션이라거나 변화가 필요한 위젯의 UI만 변경할 수 있습니다. StatelessWidget이나 StatefulWidget의 서브클래스는 const 생성자로 생성이 가능합니다. 가능하면 const 생성자를 사용해야 컴파일 타임에 미리 그 위젯 정보를 저장해두고, 런타임에 사용되는 자원을 줄여서 앱이 느려지는 일을 최소화할 수 있죠.

 

 

 

 

또 하나의 안 좋은 케이스는 서로 다른 buildContext를 기다리는 겁니다. 아래는 여러분이 자주 사용하실 빌더 중에서 BlocBuilder를 예로 들어 보겠습니다.

@override
  Widget build(BuildContext context) {
    return BlocBuilder<SubjectBloc, SubjectState>(
      builder: (innerContext, state) {
      return Container(
        color: Theme.of(context)...
      );
    },
  );
)

 

BlocBuilder의 자식 위젯들이 새로 만들어 준 innerContext가 아니라 build 메소드에서 넘겨주는 context를 사용하는 경우, 오래되고 신뢰도가 떨어지는 context 정보를 사용하게 됩니다. 이때 별개의 위젯으로 리팩토링 하면 이런 버그를 방지할 수 있습니다.

 

@override
  Widget build(BuildContext context) {
    return BlocBuilder<SubjectBloc, SubjectState>(
      builder: (innerContext, state) {
      return const TestContainer();
    },
  );
)

class TestContainer extends StatelessWidget {
  const TestContainer({ Key? key }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Theme.of(context)...
    );
  }
}

 

위와 같이 코딩하면 새로 만든 TestContainer 위젯은 build 메소드의 BuildContext에만 접근하기 때문에 항상 정확하게 됩니다. 

 


Class have a better default behavior.
The only benefit of methods is havin to write a tiny bit less code.
There's no functional benefit.

 

클래스는 더 나은 디폴트 행동 방식을 갖습니다. 헬퍼 메소드를 사용해서 얻는 이점은 아주 조금의 코드를 덜 쓰는 것 뿐이고, 기능적 이점은 없다고 합니다.

 

확실히 위젯을 분리하면 헬퍼 메소드에서 변수를 바로 사용할 수 있는 이점은 사라집니다. 하지만 이제 앱 성능이 저하된다는 점을 아셨죠? 이제부터는 가능하면 헬퍼 메소드가 아니라 별개의 위젯으로 분리하는 습관을 들이도록 합시다! const도 함께요!

댓글