(핵심만 쏙!) Flutter Stateless vs Stateful widget 쉽게 이해하기
👀 TL;DR
- Stataless Widget이란?
- 해당 위젯 내에서 자체적으로 데이터가 변경되는 것을 관리하지 않음.
- Stateful Widget이란?
- 상태 객체가 추가되고 해당 객체에서 자체적으로 데이터가 변경되는 것을 관리함.
Stateless Widgets란?
Stateless 위젯은 이름에서 알 수 있듯이, 자체적으로 상태(데이터)를 관리(트래킹, 업데이트 등)하지 않는 위젯이에요.
위 예시에서 볼 수 있듯이,
DogName이라는 Stateless 위젯은 final로 선언된 name이라는 프로퍼티를 가진 위젯이에요.
하지만 이 프로퍼티는 DogName이라는 위젯의 인스턴스를 선언할 때 name이라는 프로퍼티로 사용할 값을 전달받아 UI에 그릴 뿐, 자체적으로 해당 데이터에 대해서 관리하고 있지 않아요!.
build methods가 작동하는 방식
플러터로 만든 앱은 위젯으로 구성된 트리로 생각할 수 있습니다. 이 때 위젯은 실제로 앱 UI의 일부에 대한 구성일 뿐인데요!
즉, 설계도라고 생각하면 되고 실제로 만들어져 화면에 탑재되는 것은 element라는 녀석입니다.
그리고 element tree는 특정 순간에 장치에 실제로 표시되는 것을 나타냅니다.
각 위젯 클래스에는 element를 만드는 메서드가 있습니다.
예를 들어, Stateless widget은 stateless element를 만듭니다. 해당 createElement 메서드는 위젯이 위젯 트리에 올라갈 때 호출됩니다.
Flutter는 위젯에 element를 요청한 다음 해당 element를 만든 위젯에 대한 참조와 함께 element tree에 pop합니다.
그리고 Stateless 요소는 “야, 내가 자식(element)을 가져야 하는지 궁금해”라고 말하며 위젯의 Build Method를 호출합니다.
위 DogName 클래스에서 봤던 것처럼, 3개의 자식 위젯을 가지고 있는 걸 볼 수 있습니다.
이러한 위젯들은 또 자신만의 elements를 만들고 만들어진 elements들은 element tree에 mount 됩니다.
이렇게 앱에는 2개의 트리가 있는데, 하나는 화면에 실제로 표시되는 것을 나타내는 Element이고, 하나는 elements들로부터 만들어진 blueprint를 포함하는 위젯입니다.
그럼 이 경우에는
1) elements를 만들도록 하는 시작은 무엇인지?
2) 무엇이 모든 것을 시작하는지 ?
와 같은 두 가지 질문이 있을 수 있습니다.
위에서 볼 수 있듯이 DogApp 클래스는 전체 앱을 나타내며 stateless 위젯입니다.
앱의 시작점인 Main 함수를 보면 runApp이라는 메서드를 볼 수 있는데, 이것이 이 모든 것의 시작점입니다.
이 RunApp 메서드는 위젯을 가져와 화면에 맞는 높이와 너비 제약 조건으로 앱의 root element를 탑재합니다.
Stateful Widget 이란?
Stateless Widgets과의 차이점
위에서 볼 수 있듯이, Stateless 위젯은 화면에 실제 표시되는 element로 이루어진 불변 구성 또는 청사진을 의미합니다.
그럼 데이터가 변하면, 그것을 추적하고 UI를 업데이트하려면 어떻게 해야 할까요?
바로 그 부분에서 Stateful 위젯이 등장하는 데요, 이 위젯은 불변의 구성 정보와 상태 객체(시간이 흐름에 따라 변경될 수 있고 UI를 다시 그리도록 trigger할 수 있음)를 제공합니다.
Stateless 위젯에 관한 코드인데요, 생성자에 name과 count가 필요하고 이를 Text 위젯을 이용해 화면에 표시합니다.
하지만 시간이 지남에 따라 count가 변해야 한다고 해보죠. 하지만 Stateless 위젯 내에서는 아무것도 변경할 수가 없어요! 왜냐면 count는 final로 선언되었기 때문이죠.
여기서 잠깐! final 과 const에 대해 핵심만 알아봅시다.
- Final: run-time constant, 즉 앱이 실행된 이후에 final로 선언된 변수에 값이 처음 할당되면 그 이후론 변수에 할당된 값이 바뀔 수 없음.
- Const: Compile-time constant, 즉 앱의 코드가 기계어로 바뀌기 시작할 때 이미 const로 선언된 변수는 값이 할당되어 있어야 하고 run-time 에도 바뀌면 안될 때 사용.
그래서 이 때, Stateful 위젯을 사용해야 돼요. 이제 class가 두 개가 되었는데요, 1) 위젯 과 2) state 객체 두 개입니다.
- 이 때 ItemCounter 위젯은 두 가지에 대한 책임이 있는데요 – 하나는 name 변수에 대한 값을 변하지 않게 끔 유지하고, 상태 객체를 만듦.
- 이 코드에서 상태 객체는 count 값을 들고 있고, 이 값은 final로 선언되지 않았어요
즉, 값이 바뀔 수 있다는 의미고 상태 객체는 build 메서드를 통해 자식 위젯을 build 합니다. 이 경우에는 Text 위젯이 되겠네요!
Stateful 위젯은 실제로 어떻게 작동할까요?
이를 알아보기 위해 위젯 트리와 Element Tree로 설명합니다.
위에서 말씀드린 것처럼,
- 요소(Element) 트리는 화면에 표시되는 것을 실제로 보여주고
- 위젯은 이러한 요소들에 대한 청사진일 뿐입니다.
위 사진은 stateless widgets가 화면에 표시되는 과정을 설명하려고 하는 것인데요.
플러터에게 stateless 위젯을 주고 화면에 표시되게 하려면 element가 있어야 돼서 위젯에게 element를 만들어달라고 요청합니다.
그럼 stateless element를 만들고 이 요소가 element tree에 mount됩니다.
그런데 stateful 위젯의 경우에는 추가적인 단계가 있다고 합니다. 위젯이 먼저 오고 위젯이 요소를 만들도록 요청하면 이번에는 Stateful 요소가 만들어집니다.
그리고 stateful element가 위젯에게 “야, state 객체도 만들어줄래?” 라고 요청합니다.
이것이 createState 메서드의 목적입니다.
createState 메서드는 새로운 상태 객체를 만들고 state element 는 상태를 유지합니다.
이제 자식 위젯을 build하기 위해 stateful element는 상태 객체의 Build 메서드를 호출합니다.
위 코드에서 보면 알 수 있듯이, Text 위젯을 build하려면 상위 위젯의 name 프로퍼티와 상태 객체에서 count 프로퍼티가 필요한데요.
이 때 State 객체는 상위 위젯의 상태에 대한 참조를 유지하고 있기 때문에 위 두 가지 프로퍼티 모두에 접근해 Text 위젯을 구성할 수 있어요.
그리고 Text 위젯은 Stateless 위젯이기 때문에 Stateless 요소를 만듭니다.
여기서 상태 객체에는 아무 변동이 일어날 수 없기 때문에, gesture detector 위젯을 추가해 setState 메서드를 사용해 count 값을 변경할 수 있어요.
setState 메서드란? 상태 개체에서 관리하는 데이터가 변경됐을 때, UI를 업데이트 하도록 트리거하는 역할을 해요.
setState 함수를 실행하면 count 값이 증가하게 되고,
여기서 중요한 점! 상태 객체는 자신을 참조하고 있는 element를 dirty 상태로 만들어요. 즉, 다음 프레임에 UI를 재구성하도록 알리는 역할을 하는 것이에요.
다음 프레임에서 상태 객체에서 build 메서드를 호출해 자식을 재구성하고 새 count 값을 표시하는 새 Text 위젯을 반환합니다.
그리고 이전 text 위젯은 사라지고 새로운 것이 위젯 트리에 mount 됩니다.
여기서 멋진 점은, 새 위젯은 이전 위젯과 동일한 유형이기 때문에(Text 위젯) stateless element는 원래 있던 곳에 유지되게 되고 새 위젯을 참조하도록 자체 업데이트를 한다고 해요.
지금까지 핵심적인 Stateful 위젯과 Stateless 위젯의 차이점을 알아보고 각 위젯의 작동 방식에 대해 알아봤는데 이해가 되셨나요?
정리
- Stateless 위젯은 위젯 내에서 자체적으로 데이터 변경을 트래킹하고 UI가 변경되도록 하지 않아요.
-
Stateful 위젯은 내부에 State Object를 생성하고 이 상태 객체 내부에 관리할 데이터를 선언하고 데이터 변경을 트래킹하고 변경되면 build 메서드에게 알려 UI에 업데이트하게 해요.
- 그리고 각 위젯은 UI에 실질적으로 표시하는 역할이 아니라 ui에 표시하기 위한 청사진이라는 점
- 실제 ui에 그려지는 역할은 Element라는 아이였어요.
- 위에서 배운 두 위젯은 위젯 트리에 mount 될 시, element객체를 만드는 createElement 메서드를 호출하게 되고 생성된 element는 element tree에 mount 되어서 자신을 만든 위젯을 참고하고 있어요.
참고자료
How to Create Stateless Widgets - Flutter Widgets 101 Ep. 1
How Stateful Widgets Are Used Best - Flutter Widgets 101 Ep. 2
댓글남기기