카테고리 없음

[Flutter] Stateful widget

NoDapKeepGoing 2024. 9. 30. 02:27

볼드체만 읽으면 됩니당 (4,5 주차)

 

State

UI가 변경되도록 영향을 미치는 데이터로, StatefulWidget과 StatelessWidget이 있다

즉, State란 App의 상태를 바꾸는 모든 행위이다

 

 

플러터는 모두 위젯으로 이뤄져 있다.

화면에 보여지는 요소를 클래스로 표현하며, 이를 '위젯'이라 부른다

 

 

*Stateless Widget : State가 변하지 않는 위젯

*Hot Reload : 저장하면 바로 반영되는 것

 

 

Widget tree는 하나의 구성일 뿐, 직접적으로 스크린에 그려지는 건 아니다(설계도 역할 : UI에 그려줘~ 하는 느낌)

Element tree는 개발자가 만든 Widget tree에 근거해서 생성을 해주는 요소이다

 

Widget tree에 있는 모든 위젯 하나하나가 일대일 맞대응 형식으로 element tree에 링크되어 있음.

: 메모리에 등록되어 플러터에 의해 운영되는 요소라는 의미.

 

반면 Render tree는 직접적으로 스크린에 그림을 그려주는 일종의 하이레벨 시스템.

Element tree는 중간에서 이 render tree가 실질적으로 렌더링하기 위해 갖고 있는 Render 객체와

일대일 맞대응 형식으로 연결되어 있음.

=> 최종적으로 우리가 눈으로 보는 스크린 상의 모든 요소들은 Render tree의 작업 결과임.

 

stateless 위젯 내에 컨테이너 위젯이 존재한다고 가정하자

 

그러면 플러터는 즉시 이에 대응하는 컨테이너 엘리먼트를 엘리먼트 트리 상에 추가한다.

그리고 이 컨테이너 엘리먼트는 위젯 트리 상에 컨테이너 위젯을 포인트, 즉 가리키게 된다.

*C언어에서의 포인터라기 보단, 컨테이너 위젯의 정보를 가리킨다는 의미임. (예를 들어 : 위치, 타입, 가로/세로 크기, 배경색 등)

 

 

 

위젯 트리는 빌드 메소드가 호출될 때마다 새롭게 리빌드 됨.

Reload : 어떤 프레임을 그대로 둔 채 부수적인 요소들만 변경하는 것

위젯 트리가 매번 리빌드 된다는 것은 위젯 트리 자체도 스테이트리스 위젯처럼 한 번 생성되면 바뀌지 않게, 바꾸려면 아예 새로 만들어야 한다는 의미. 이는 위젯 트리 내에 모든 위젯들도 리빌드 된다는 의미임.

 

그렇다면 우리가 사소하게 텍스트 문자 하나만 바꾸어도 핫리로드 때문에 매번 빌드 메소드가 호출되면서 위젯 트리가 리빌드될 것임. 그러면 여기에 연결된 엘리먼트 트리도 리빌드 되고, 또한 이에 맞물려 랜더 트리까지 리빌드 된다면 플러터는 최악의 퍼포먼스를 보여주게 될 것임.

 

 

이 문제 역시도 Element tree가 해결해줌

Element tree는 Widget tree 구조를 그대로 복사하기에, 링크되었던 Widget의 위치와 타입, 속성들의 모든 정보를 가지고 있음.

그래서 어떤 Widget에 새롭게 추가된 코드 때문에 빌드 메소드가 호출되고, Widget tree가 리빌드 된다면 Element tree도 함께 리빌드 되는 것이 아니라, 새롭게 생성된 Widget들의 타입과 위치가 일치하면 그대로 링크만 업데이트 함.

그리고 타입과 위치는 같지만 새로운 쿼리의 추가로 다시 렌더링이 필요한 Widget에 한해, Render tree에게 정보를 전달해주고, Render tree는 이를 근거로 기존 위젯의 Render 객체에서 바뀐 부분만을 다시 그려주게 됨.

=> 위젯을 새로 생성하려면 build() 함수를 재실행 해야 한다

 

 

이때, Hot Reload로 빌드 함수를 재실행하는 방법과StatefulWidget으로 빌드 함수를 재실행하는 방법이 있다.

근데 Hot Reload는 개발자만 사용할 수 있는 기능이다!

그래서 StatefulWidget으로 구현해야 한다.

 

 

컨테이너 위젯 배경색이 화이트>블루로 바뀌었다면 이를 반영하기 위해 핫리로드가 실행되고, 빌드 메소드가 호출됨.

이어서 Widget tree가 리빌드 되면서 위젯 트리 상의 모든 위젯들 역시도 리빌드됨. 그리고 Element tree는 리빌드된 widget들을 기존 widget들의 정보와 비교한 후에, 일치하면 새로운 widget들로 link만 업데이트함.

최종적으로 다시 렌더링이 필요한 위젯에 대해 Render tree에 정보를 넘겨주고, Render tree는 컨테이너에 색상만들 바꾸어서 다시 그려주게 되는 것임.

 

 

플러터의 핫리로드는 실행될 때마다 모든 위젯들을 화면에 다시 그리는 것이 아니라, 이처럼 앨리먼트 트리를 활용하여 변경된 부분만을 다시 그리는 방법을 사용하기 때문에 빠르고 효율적인 것임.

또한 Stateless Widget은 한 번 빌드되면 절대 스테이트가 변하지 않으며, 새로운 스테이트를 적용하려면 리빌드 하는 방법밖에 없음.

우리가 Stateless Widget인 텍스트 위젯의 내용을 바꾸는 과정 역시도 생성자를 통해 외부에서 데이터를 전달하는 것이지, 결코 text Widget 내부의 State가 바뀌는 것은 아님.

 

그래서 최종적으로 핫리로드를 통해 빌드 메소드가 호출되면 텍스트 위젯은 리빌드 되고, Element tree는 이 정보를 Render tree에 전달하여 text widget의 바뀐 내용을 화면에 출력해주는 것임.

 

그리고 이런 효율적인 시스템과 더불어 플러터는 초당 60프레임 속도로 화면의 새로고침 기능을 지원하기 대문에, 우리는 순식간에 새롭게 추가된 코드의 결과를 확인할 수 있게 되는 것임.

 

 

 

우선 Stateful Widget과 Stateless Widget의 공통점은 두 위젯 모두 생성자를 통해 외부에서 데이터가 입력되면 그 결과를 반영하기 위해 빌드메소드가 호출되면서 위젯들이 리빌드되고, 필요한 부분의 UI를 다시 렌더링하게 됨.

 

하지만 Stateless Widget과의 결정적인 차이점은 Stateful Widget은 내부에 State라는 또 다른 클래스를 갖고 있다는 것임.

즉, 두 개의 클래스와 결합되어 Stateful Widget을 만들고 있는 것임.

 

Stateful Widget에서 빌드 메소드는 스테이트 클래스가 갖고 있음.

그래서 엄밀히 얘기하자면 Stateful Widget의 리빌드를 처리하는 것은 결과적으로 이 State Class이며, 이를 통해 스크린을 다시 렌더랑하게 만든다는 의미임.

 

 

지금처럼 간단한 것은 SetState로 할 수 있지만,

나중에 앱이 복잡해지면 상태 관리 프레임워크들을 써야 한다.

 

 

정리해보자면, Stateful Widget이 리빌드되는 경우는 두 가지!

1. Stateless Widget처럼 차일드 위젯 등의 생성자를 통해 새로운 데이터가 전달될 경우

2. Internal State가 변할 경우