0️⃣ 오랜만에 Flutter 이론 공부다...
마지막으로 Flutter 이론 공부한 게 2년 전 쯤 같은데, 아무래도 좀 더 심화 버전으로 공부를 더 해야할 것 같아서 복습 겸 적어본다. 우선, 내가 처음 Flutter를 공부하면서 처음 깨달음을 얻고 흥미를 느꼈던 부분이 바로 context다. 이때부터 Flutter를 아주 재미있게 공부하며 실무에 적용한 것 같다.
Flutter에서 context란 도대체 무엇일까?
Flutter의 context란 도대체 무엇일까?context의 개념을 이해하기 위해선 먼저 Widget Tree에 대해 알아야한다.1️⃣ Widget Tree Widget은 Flutter의 모든 것이라고 해도 과언이 아닐 정도로 위젯으로 시작해서
kkevido.tistory.com
위 포스팅은 지금 보면 조금 빈약하지만 그래도 예비 Flutter 개발자가 읽기에 매우 쉽고 간단하게 요약되어 있어서 지금 보아도 나름 알차다. 해당 내용을 모두 이해했다면 슬슬 궁금해지는 것이 있을 것이다. 도대체 왜? Tree가 3개나 있는거지? 라는 생각이... 지금부터 하나하나 Widget Tree, Element Tree, RenderObject Tree에 대해서 파헤쳐 보겠다.
그 전에, 구글 공식 유튜브에서 매우 자세히 잘 설명 되어있는 게 있으므로 해당 영상을 한 번 먼저 보자. 공식 계정만큼 설명을 잘 해주는 게 없다. 난 해당 영상을 바탕으로 정리한 것이니 짧고 굵게 영상만 보아도 된다.
1️⃣ The 3 Trees

예전에 내가 그려둔 3개의 트리구조다. (추억이군...) Flutter는 Widget에서 시작하여 Widget으로 끝난다는 말이 있듯이 Widget Tree가 가장 맨 앞에 있는 것을 알 수 있다. 하지만 공식 유튜브에서는 3 Trees를 조금 다른 그림으로 표현한다.

이런 식으로 생겼다고 한다. 결국, 해당 3개의 트리가 서로 유기적으로 얽혀있다는 건 변하지 않는 사실이다. 각 트리의 고유 속성을 알아보자.
- Widget Tree
- 실제 개발자가 작성하는 코드 (그냥 설계도)
- 선언형 API 제공
- 불변형이자 일회용
- 예시)
- StatelessWidget, StatefulWidget (99%의 개발자가 여기까지만 다룬다.)
- RenderObjectWidget, InheritedWidget (이건 심화과정이라 나중에 별도로 다루겠다. 궁금하면 공식 유튜브 3편을 마저 보면 알 수 있다.)
- Element Tree
- Widget Tree와 RenderObject Tree를 잇는 중간 다리. Widget을 읽고 RenderObject에게 명령함.
- 명령형 구현 제공
- 긴 수명
- RenderObject Tree
- 실제로 화면을 그릴 수 있도록 Paint를 담당하는 엔진
- 긴 수명
- 그래픽 처리를 위한 첫 단계
2️⃣ Widget Tree
Widget은 그냥 우리가 흔히 아는 그 위젯 맞다. 뭐... 설명할 것도 없다. 매우 가볍고 일회용이기 때문에 쉽게 폐기하고 쉽게 다시 새로 위젯을 구성할 수 있다. 실제로 개발자가 작성하는 트리로서, 가장 많이 쓰이는 건 StatelessWidget, StatefulWidget 이렇게 2개가 있다. 아래 게시물에서 충분히 설명해두었으니 해당 게시물을 참고하여 Widget에 대해 공부해도 좋다.
Flutter의 Widget 종류 : Stateless와 Stateful
0️⃣ State, 상태란? State, 상태는 사실상 개발을 하다 보면 필연적으로 마주하게 되는 단어이다. 그렇다면 상태는 대체 무엇일까? 앱의 정보 또는 데이터 를 통칭하여 상태라고 이야기한다. 따라
kkevido.tistory.com
3️⃣ Element Tree
오늘 공부의 핵심, Element Tree! Widget Tree가 그저 "설계도"에 지나지 않는 code 라면, Element Tree는 실체화된 위젯 (Instantiation)으로 실제로 메모리에 올라가 살아있는 객체가 된 것이 Element다. (아마도 DartVM 위로 올라가겠지?) Element Tree가 생성되는 과정을 보자.
- Widget이 선언됨 (immutable)
- createElement()를 스스로 호출하여
- Element(살아있는 객체)가 생성됨 (mutable)
- Element는 메모리에 고정되어있고 Widget이 바뀌면 참조갱신 한다.
생각보다 간단한 방법으로 생성된다.
특히 Widget Tree와 Element Tree는 서로 재귀적으로 연결되어있어 서로가 서로를 호출하는 현상이 발생한다. 이때 바로 그 'context'가 활용되는 것이다!
Widget이 Element를 만들면 그 Element가 다시 Widget의 build() 함수를 호출함으로서 그 자식이 또 다시 Element를 만드는 과정이 반복 된다.
Element의 역할: 가벼운 Widget이 계속 바뀌더라도, 무거운 RenderObject는 최대한 죽이지 않고 재사용할 수 있도록 중간에서 '중재'하는 관리자.
그리고, Element에도 종류가 있다!
👉 1. Component Element
- Scaffold, Container, StatelessWidget, StatefulWidget 등등
- 직접 그림을 그리지 않고, 다른 위젯을 조합하는 역할만 함
- build() 함수를 실행하며, build()는 반드시 자식 위젯을 리턴해야 한다.
👉 2. RenderObject Element
- Row, Column, Stack 등등
- 이 녀석들은 내부적으로 RenderObjectWidget을 상속 받는다. (아까 위에서 말한 공식 유튜브 3편에 나오는 Widget)
- 실제로 화면을 그리기 위해 레이아웃 잡는다.
물론 실제로 이 Element들이 그림을 직접 그리진 않는다. RenderObject Tree에게 그림을 직접 그리라고 명령을 내릴 뿐!
4️⃣ RenderObject Tree
화면을 직접 그리는 트리로서, 직접 레이아웃을 계산하고 사용자의 제스처까지 감지하기 때문에 매우 무겁다. Widget처럼 쉽게 폐기하고 새로 만드는 것보다는 있는 걸 재사용하는 것이 훨씬 용이하기 때문에 이를 Element Tree가 관리해주는 것이다.
무엇보다 RenderObject Tree는 Skia, Impeller에 의해 그려지는데, 이 부분도 심화 과정이므로 나중에 공부하여 별도로 포스팅 해보겠다.

6️⃣ Widget이 바뀌면 Element도 바뀔까?
RenderObject Tree를 가볍게 건너뛰고, 이제 재귀적으로 연결되어있는 Widget과 Element의 상관관계를 좀 더 깊이 알아보자. 공식 유튜브에서 설명한 예시를 캡처해와서 설명해보겠다.


위 그림 2개를 자세히 보자. 모든 Widget은 그대로인데 ColoredBox의 color hex code값만 바꿔서 분홍색 박스를 선홍색 박스로 바뀐 게 끝이다. 그럼 Element는 파괴되고 새로 교체될까?

앞서 설명했듯이 Widget은 매우 가볍기 때문에 단순히 color를 바꾸기만 해도 쉽게 폐기 하고 다시 만들어도 비용이 들지 않는다. 하지만 Element와 RenderObject는 그렇지 않다. Element는 이미 메모리에 올라가있고 심지어 레이아웃을 계산하여 화면을 다 그린 RenderObject 파괴하고 다시 만들면 엄청난 비용이 소모된다.
때문에 Element는 '중재'하는 관리자로서 어떤 Widget이 교체 되었고 이를 파괴하고 새로 만들지, 재활용해도 될런지를 판단한다. 이를 Diffing 알고리즘이라고 한다.
~ Diffing 알고리즘 ~
솔직히 Diffing 알고리즘이라 하면 너무 어렵다. 일단 알고리즘이란 말 자체가 싫다. 대충 뭔말인지 알기 쉽게 정리해보겠다.
static bool canUpdate(Widget oldWidget, Widget newWidget) {
// 1. 런타임 타입이 같은가? (둘 다 ColoredBox인가?)
return oldWidget.runtimeType == newWidget.runtimeType &&
oldWidget.key == newWidget.key; // 2. 키(Key)가 같은가?
}
Flutter 프레임워크에 포함되어있는 내장 함수, canUpdate다. 즉 개발자가 직접 뜯어볼 일 없이 Element가 알아서 잘 해결한다는 것!
- Case A (True): runtimeType과 key가 같다.
- 이 경우엔 RenderObject를 재활용
- ColoredBox(color:F25D58)로 속성 값만 바꾸고, ColoredBox를 재사용
- RenderObject를 재활용 하므로 성능 비용이 매우 절약되어 효율적임
- Case B (False): runtimeType이나 key 중 하나라도 다르다.
- Element 스스로가 파괴되고, 따라서 렌더링 되어있는 RenderObject도 함께 파괴됨
- ColoredBox()가 Image()로 바뀌는 경우 같은 예시
- Element와 RenderObject 모두 새로 생성, 성능 비용이 발생
이 때문에 Flutter가 1초에 60번씩 위젯을 새로 만들어도 화면이 부드러운 것이다.
7️⃣ context도 결국 Element이다.
이전 포스팅에서 내가 context는 Widget이 스스로 어느 Tree에 위치하면 좋을지 판단하는 위치 정보라고 정의했었다. 물론 맞는 말이고, context의 기초이다. 하지만 이제는 좀 더 성장한 Flutter 개발자로서 context가 뭔지 다시 물어본다면, context는 Element 자기 자신이라고 할 수 있다.
Widget에게 Element(메모리 위에 올라간 객체)의 모든 기능을 다 알려주면 트리 조작 등 중요한 기능을 함부로 건드릴 수 있기 때문에 안전한 Interface만 노출한 것이 바로 context이다.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container()
);
}
우리가 익히 자주 보던 코드다. 여기서 보이는 BuildContext가 바로 Element 객체의 참조라는 말이다. 때문에 context.findRenderObject() 와 같은 메소드를 사용하면 내가 갖고 있는 RenderObject와 내 자식이 갖고 있는 RenderObject 모두를 찾아올 수 있는 것이다.
쉬운 말로는, 이정표! 어려운 말로는 객체 참조!
'Flutter' 카테고리의 다른 글
| Flutter는 컴파일 언어? 인터프리터 언어? JIT와 AOT의 원리 (2) | 2025.12.27 |
|---|---|
| Flutter의 아이콘, Symbol 적용하기 (0) | 2025.10.24 |
| Flutter 프로젝트 생성하기 (0) | 2025.09.11 |
| Flutter로 WebView 만들어보기 (0) | 2024.04.24 |
| Flutter의 Widget 종류 : Stateless와 Stateful (0) | 2024.03.27 |