최근,,은 아니고 플러터 서울 오픈 카톡방에 const 관련 논란이 거세진 적이 있다
ARABOZA
Flutter 개발자들 사이에서는 const
키워드를 사용하면 앱의 성능이 크게 향상된다는 믿음이 널리 퍼져 있는데, 최근 Flutter 팀은 이러한 믿음에 의문을 제기하며, const
관련 린트(lint)가 실제로 그만한 가치가 있는지 조사하고 있다고
Flutter의 수석 엔지니어인 Michael Goderbauer는 2024년 6월에 다음과 같이 언급했습니다:
이론적으로 const는 앱의 성능을 향상시킬 수 있지만, 개발 중에 이러한 린트는 개발자들에게 지속적으로 "이것을 const로 만드세요" 또는 "이제는 const로 만들 수 없습니다"와 같은 알림을 보냅니다. 우리는 성가심과 성능 간의 균형을 제대로 맞추고 있는지 재평가해야 합니다.
또한, Flutter 팀은 실제 앱에서 const
를 사용하는 것이 통계적으로 유의미한 성능 향상을 제공하지 않는다는 결론을 내렸다. 따라서, 개발자들에게 지속적으로 알림을 보내는 이러한 린트는 그만한 가치가 없으며, flutter_lints
세트에서 제거하기로 결정했습니다.
이러한 결정은 const
키워드의 사용이 항상 성능 향상을 보장하지 않으며, 특히 개발 과정에서 불필요한 알림으로 인해 개발자들의 생산성을 저해할 수 있음을 시사합니다.
const
의 사용은 상황에 따라 신중하게 고려되어야 합니다.
라는 글이 올라온 것!
그런데 많은 사람들이 이에 대한 의문을 제기했다. 이것이 진짜 쓸모 없냐고,
여기에 대해 Ahmed Salama 이 올린 글을 보며 파헤쳐 들어가 보자
첫 번째 이미지에서 Ahmed Salama는 "const
키워드는 불변(immutable) 위젯과 반복적으로 사용되는 정적(static) 요소에 유용하다"고 주장하고 있다. const
를 사용하면 메모리 사용량을 줄이고 가비지 컬렉션(GC) 부담을 낮출 수 있다는 점을 강조한다
두 번째 이미지는 const
사용 여부에 따른 메모리 사용량 차이를 보여주는 예제인데 잘 안보이니 선명한 코드로 다시 보자면,
Widget build(BuildContext context) {
return ListView(
children: [
for (var i = 0; i < 100; i++) const TrackConstWidget(),
for (var i = 0; i < 100; i++) TrackNotConstWidget(child: Container()),
],
);
}
TrackConstWidget()
은const
를 사용하여 정의된 위젯TrackNotConstWidget(child: Container())
은const
를 사용하지 않은 위젯
🔍 메모리 할당 결과
Widget Class | 인스턴스 수 | 메모리 사용량 (Bytes) |
---|---|---|
TrackConstWidget |
1 | 16 |
TrackNotConstWidget |
100 | 3200 |
..!! 👁️👁️ 놀랍네
결과를 보면 const
가 적용된 위젯은 오직 하나의 인스턴스만 생성되고, 이를 재사용하는 방식이라 메모리 사용량이 훨씬 적음을 알 수 있다. 반면 const
없이 TrackNotConstWidget
을 사용한 경우, 100개의 개별 인스턴스가 생성되면서 메모리 소비량이 훨씬 높아진 것.. 유의미 하다고 할 수 있지요
오 그럼 정적 위젯을 사용했을땐, const 사용.. 괜찮은데? 라고 생각이 든다
자자 플러스로 다음 글을 보자
https://github.com/flutter/flutter/issues/149932#issuecomment-2512996754
id a simple investigation, so, let me share my findings :-)
The const keyword significantly impacts widget performance, especially noticeable during numerous rebuilds across many widgets.
Here are some clarifications and enhancements to the discussion:
Performance Gains:
Using const can lead to performance improvements because it allows Flutter to reuse instances of immutable widgets across multiple rebuilds. This is particularly beneficial in complex UIs with many static elements.
Build and Instantiation:
There is a common confusion between “building” and “instantiating” in the official documentation that could be clarified. The term “building” often refers to invoking the build method, which describes the widget tree. In contrast, “instantiating” refers to creating an instance of a widget. Although a const widget can be rebuilt multiple times, its instance remains unchanged, thus saving on allocation and reducing garbage collection overhead.
Behavior in All Build Modes:
It’s crucial to understand that const is effective in all build modes (debug, profile, and release). The Dart VM optimizes performance by reusing const objects wherever possible, not just in release mode.
Selective Rebuilding:
Flutter optimizes the rebuilding process by not calling the build method on const widgets if their descendant widgets are not marked as dirty. For instance, if a parent widget is listening to a state change and contains a Column with a const widget as a child, and neither the child nor any descendants depend on the changed state, then the const widget will not be rebuilt. This selective rebuilding helps in further enhancing performance by avoiding unnecessary widget tree rebuilds.
see & try a simple sample https://gist.github.com/ultraon/3d39d40f08d79c3c4e9316f127e5d146
see the comment https://gist.github.com/ultraon/3d39d40f08d79c3c4e9316f127e5d146?permalink_comment_id=5314599#gistcomment-5314599
Understanding const Instances:
A const widget creates a single, canonical instance when instantiated with the same parameters. This can be visualized as a cache where each unique set of parameters stores one instance of the widget.
You can have multiple const instances of the same widget class, each differentiated by its parameters.
class WidgetConstA extends StatelessWidget {
final String text;
const WidgetConstA({required this.text});
//
operator ==
isn't overridden here, so comparison defaults to identity
}
class WidgetNonConstB extends StatelessWidget {
final String text;
WidgetNonConstB({required this.text});
//
operator ==
isn't overridden here either
}
const widgetA1 = WidgetConstA(text: 'A');
const widgetA2 = WidgetConstA(text: 'A');
final isIdenticalA = identical(widgetA1, widgetA2); // prints
true
, same instance
final isEqualA = widgetA1 == widgetA2; // prints
true
, identity implies equality
final widgetB1 = WidgetNonConstB(text: 'B');
final widgetB2 = WidgetNonConstB(text: 'B');
final isIdenticalB = identical(widgetB1, widgetB2); // prints
false
, different instances
final isEqualB = widgetB1 == widgetB2; // prints
false
, different instances
This distinction underscores the importance of const in managing memory efficiency and performance in Flutter applications. Both StatelessWidget and StatefulWidget can benefit from const constructors, with mutable state managed within the State.
Summary:
the const avoids re-instantiation of the const widget with the same const params, the instance is resolved during compilation time and it doesn't need to be collected by GC
Flutter Framework can skip calling the build method of the const widget during the re-building phase in case the child widgets are not marked as dirty, so, they don't need to be rebuilt
그대로.. 긁어온건데 너무 가독성이 안좋다
번역하자면 다음과 같다
const
키워드는 위젯 성능에 중요한 영향을 미친다.- 특히 많은 위젯이 반복적으로 다시 빌드될 때 그 효과가 뚜렷하게 나타난다.
const
키워드의 성능 향상 효과
성능 향상 (Performance Gains)
const
를 사용하면 Flutter가 불변(immutable) 위젯의 인스턴스를 여러 빌드에서 재사용 가능.- 복잡한 UI에서 정적인 요소가 많을 경우 성능 향상 효과가 더 큼.
빌드(Build)와 인스턴스화(Instantiation)의 차이
- "빌드(Building)" →
build
메서드를 호출하여 위젯 트리를 그리는 과정. - "인스턴스화(Instantiating)" → 실제 위젯 인스턴스를 생성하는 과정.
const
위젯은 여러 번 다시 빌드될 수 있지만, 인스턴스는 변하지 않음.- 즉, 메모리 할당이 줄어들고, 가비지 컬렉션(GC) 부담이 감소.
모든 빌드 모드에서 효과적 (Behavior in All Build Modes)
const
는 디버그, 프로파일, 릴리스 모드 모두에서 최적화됨.- Dart VM은
const
객체를 모든 모드에서 재사용하여 성능을 최적화함.
선택적 리빌딩 (Selective Rebuilding)
- Flutter는 필요하지 않은 경우
const
위젯의build
메서드를 호출하지 않음.- 예: 부모 위젯이 상태(state)를 변경하더라도, 자식 위젯이 그 상태에 의존하지 않는다면
const
자식 위젯은 다시 빌드되지 않음.
- 예: 부모 위젯이 상태(state)를 변경하더라도, 자식 위젯이 그 상태에 의존하지 않는다면
- 불필요한 위젯 트리 재구성을 방지하여 성능 향상을 도움.
const
는 동일한 매개변수(params)로 인스턴스화하면 동일한 객체를 재사용
class WidgetConstA extends StatelessWidget {
final String text;
const WidgetConstA({required this.text});
}
class WidgetNonConstB extends StatelessWidget {
final String text;
WidgetNonConstB({required this.text});
}
const widgetA1 = WidgetConstA(text: 'A');
const widgetA2 = WidgetConstA(text: 'A');
final isIdenticalA = identical(widgetA1, widgetA2); // true (같은 인스턴스)
final isEqualA = widgetA1 == widgetA2; // true (같은 객체)
final widgetB1 = WidgetNonConstB(text: 'B');
final widgetB2 = WidgetNonConstB(text: 'B');
final isIdenticalB = identical(widgetB1, widgetB2); // false (다른 인스턴스)
final isEqualB = widgetB1 == widgetB2; // false (다른 객체)
const
위젯은 동일한 인자(parameters)로 생성하면 같은 인스턴스를 공유.- 반면,
const
가 없는 위젯은 매번 새로운 인스턴스를 생성하여 메모리 낭비 발생.
등이 있다.
결론은 !!!
const
는 동일한const
매개변수를 가진 위젯을 다시 인스턴스화하지 않음.const
인스턴스는 컴파일 타임에 결정되므로 가비지 컬렉션(GC)이 필요 없음.- 위젯이 변경되지 않는 경우, Flutter가
build
메서드를 호출하지 않아 불필요한 리빌드를 방지.
이것이고
여기에서 나온 중요한 개념🐶 (다들 노트 꺼내🗒️)
build 와 Instantiating 이 있다. 이를 헷갈리면 이 개념을 이해하기 참 골치아픈데, 이 둘을 잘 비교할 줄 알아야한다.
자 우리가 사용자라고 가정하고 앱의 실행 흐름을 보자
- 사용자가 앱을 실행하면
main()
함수에서runApp(MyApp())
이 실행됨. - Flutter는
build()
메서드를 호출하여 UI를 처음 그림. - 이후 버튼을 누르는 등 화면이 갱신될 때마다
build()
가 다시 실행됨 (물론 이과정에서 플러터는 최적화함 바뀐 ui만 그려줌). - 이 과정에서 새로운 객체가 생성됨 (Instantiating).
- 만약
const
를 사용하지 않으면, 같은 위젯도 계속 새롭게 생성됨 - 하지만
const
를 사용하면, 이미 있는 객체를 재사용하여 메모리 절약 가능 * 그치만 비약일 수 있음
이다
둘 개념을 명확히 정리하자면
Building 과정:
Flutter 프레임워크가 build() 메서드를 호출하여 위젯 트리를 생성(또는 재생성)하는 단계를 말함.
상태 변화(setState 등)로 인해 빌드가 다시 일어나면, “코드 상” 어떤 위젯을 반환하는지는 변함없이 다시 계산됨
즉, const 유무와 상관없이 “빌드는 필요한 만큼” 이뤄짐
Instantiating 과정:
“새로운 Widget 객체를 메모리에 실제로 생성하는” 과정
const를 사용하면, 완전히 동일한 인자(프로퍼티)로 생성되는 위젯의 경우 이미 만들어진 동일 객체를 재사용할 수 있음
즉, const를 쓰면 굳이 새로운 인스턴스를 새로 만들 필요가 없는 상황에서 재사용하여 객체 생성(Instantiating) 횟수를 줄이는 효과가 있다
인것이다.
이 둘의 차이를 이해하는게 이 const논의를 이해하는데 꽤나 중요하다. 그리고 flutter를 이해하는것에도..
그래서 나의 생각은..!!ㅋㅋㅋ
Flutter 팀의 공식 결론대로, 무조건 const를 일일이 붙인다고 해서 체감할 정도의 큰 성능 차이가 항상 발생하는 것은 아닌듯하다. 이로 인해 린트가 과도한 알림을 보내는 것은 좀 귀찮은 일이기도 하고,
개발자가 적절히 잘 붙일것!
+) chat gpt o1가 말하는 빌드와 객체 생성
1. 빌드(Build)와 객체 생성(Instantiation)
- 빌드(Build)
- build() 메서드를 호출해 화면에 보여줄 위젯 트리를 ‘정의’하는 단계입니다.
- Dart 코드 상에서 Widget 인스턴스(예: Container(), Text())를 새로 return하는 과정으로 볼 수 있습니다.
- 객체 생성(Instantiating)
- 실제로 Widget(클래스)의 “인스턴스가 메모리에 올라가는” 과정입니다.
- build()에서 return SomeWidget(...)을 하면, Dart는 그 순간 SomeWidget의 인스턴스를 생성합니다.
2. “빌드”가 다시 일어날 때 무슨 일이 벌어지나?
- StatefulWidget이라면, State 객체는 유지된 채로 build()가 재호출됩니다.
- 새로 만든 Widget 객체와 이전에 있던 Element(위젯이 화면에 배치된 실체) 간에 diff(차이점 확인) 과정을 거칩니다.
- 속성이 동일한 부분은 이미 생성된 RenderObject 등을 재사용하며, 실제로 화면에 그리는 과정은 최소화됩니다.
즉, Dart 레벨에서 보면 Widget 객체가 새로 생성되더라도, Flutter 엔진은 “이전 위젯과 동일하다”고 판단되면 렌더 단계에서 큰 변경 없이 화면을 유지할 수 있습니다.
3. “새로운 객체 생성 = 새로 그린다”인가?
아닙니다.
- Widget은 ‘불변(immutable) 구성 정보’만 담고 있는 가벼운 객체입니다.
- 실제 화면 렌더링은 RenderObject가 담당하는데, 이건 Flutter가 필요한 경우에만 새로 만들거나 수정합니다.
결국 “새로운 Widget 객체를 계속 만든다”는 개념과 “화면을 실제로 다시 그린다”는 개념은 별개의 단계입니다.
4. const를 사용하면 어때?
- const 위젯을 사용하면, 동일한 프로퍼티로 생성된 Widget을 매번 새로 만들지 않고, 이미 만들어둔 인스턴스를 재사용할 수 있습니다.
- 결과적으로 불필요한 Widget 인스턴스 생성과 GC(가비지 컬렉션) 부하를 줄일 수 있지만,
- Flutter 자체가 위젯 트리 재생성에 비교적 최적화되어 있기 때문에, “모든 곳에 const를 붙인다고 극적인 성능 차이를 보인다” 라고 단정하기는 어렵습니다.
결론
- 질문: “화면 갱신 시 build()가 다시 실행될 때, 객체가 새로 생성되는가?”
- 대답: “네, Dart 코드 차원에서는 새 Widget 인스턴스가 생성됩니다. 이건 Flutter의 구조상 정상 동작입니다.”
- 하지만: “새로 객체를 만들었다고 해서 실제로 매번 UI 전체를 다시 그리는 것은 아니며, Flutter 내부에서 필요한 부분만 업데이트하거나 렌더링 최적화를 수행합니다.”
정리하자면, “빌드가 다시 일어날 때 코드 상 새로운 위젯 객체가 생긴다”는 말 자체는 맞지만, 최적화된 Flutter 구조 덕분에 실제 화면 전체를 갈아엎는 것과는 다릅니다.
++) Chat gpt o1 위젯객체 생성은 렌더링과 상관없기에 메모리에 큰 영향을 주지 않는다? (추천)
Flutter에서 “Widget은 가벼운(소규모) 객체”라고 하는 주된 이유는 다음과 같은 구조적 특징에 기인합니다:
1. Widget은 불변(Immutable) 구성 정보만 담는다
- 대부분의 Widget 클래스는 “UI를 어떻게 구성할지에 대한 정보(프로퍼티)”만 담고 있습니다.
- 예: Container(color: Colors.red, child: Text('Hello'))
- 실제로 이 Widget 내부엔 “color가 무엇인지, child가 무엇인지” 등 간단한 값만 들어 있을 뿐, 복잡한 로직이나 렌더링 상태는 없습니다.
- Widget 자신이 변하는 대신, 상태가 변경되면 새로운 Widget 인스턴스를 만들어 교체하는 구조입니다.
2. 렌더링 로직과 상태 관리는 별도의 객체에서 처리
Flutter는 UI 동작을 크게 세 가지 객체로 나눕니다:
- Widget: 불변(immutable) 데이터 (레이아웃, 스타일, 자식 구성 정보 등)
- Element: 위젯이 트리에 배치된 위치(트리 구조)와 State를 관리
- RenderObject: 실제 화면에 “그리기(Render)”와 “레이아웃 측정(실제 크기, 위치 계산)”을 담당
즉, 무거운 작업(화면 렌더링, 동적 상태 관리)은 Widget 내부가 아니라 Element 및 RenderObject가 처리합니다.
Widget은 “UI가 어떻게 생길지”를 선언적으로 서술하는 ‘설계도’ 수준이어서 상대적으로 단순하고 가볍습니다.
3. Flutter 공식 문서 및 개발자들의 언급
Flutter 공식 문서나 Google I/O 발표 등에서 다음과 같은 언급을 자주 볼 수 있습니다:
“Widget 객체는 생성 비용이 매우 작도록 설계되었습니다. (Widgets are designed to be lightweight and fast to rebuild.)”
“Widget 자체는 별다른 상태나 논리를 갖지 않는 불변 객체이며, 많은 수가 생성·폐기되어도 성능에 큰 영향을 주지 않도록 고안되었습니다.”
예시 자료:
- Flutter 공식 문서: “An introduction to widgets”
- Flutter for iOS Devs (iOS 개발자를 위한 문서) 등
4. 실제 메모리 사용량 측면 예시
실제로 Widget 클래스 내부를 보면,
- 보통 final 프로퍼티 몇 개
- build() 메서드 한 개
- 혹은 @override 몇 개
정도로 매우 단순합니다.
이를 수천 ~ 수만 개 생성해도 대형 오브젝트를 다루는 것보다 훨씬 가벼운 메모리 footprint를 갖습니다.
5. GC(가비지 컬렉션) 측면 최적화
Dart VM의 GC는 소규모·단기 객체를 빠르게 생성·삭제하는 패턴에 최적화되어 있습니다.
- Widget은 build() 때마다 쉽게 생성·폐기되므로 “짧은 생명주기”를 가집니다.
- 불필요해지면 가비지 컬렉션 대상이 되며, 이 역시 빠르게 처리됩니다.
이처럼 Widget 객체가 큰 오버헤드 없이 생성·소멸될 수 있는 언어 런타임 구조도 한몫합니다.
결론
- Widget = “UI 구성 정보”만 담는 가벼운 설계도 객체
- 무거운 부분(렌더링, 상태)은 별도 객체(RenderObject, Element)에서 처리
- Dart VM의 GC 및 Flutter의 구조적 최적화 덕분에 많은 Widget 생성·재생성이 일어나도 큰 성능 문제 없이 처리 가능
이러한 근거들로 인해 “Widget은 가볍다”고 말하며,
Flutter의 전체 아키텍처가 Widget 대량 생성을 전제로 최적화되어 있다는 점이 그 핵심 배경입니다.
'Flutter > Flutter' 카테고리의 다른 글
webRTC 실험요약 (0) | 2024.10.16 |
---|---|
STFUL Lifecycle (1) | 2024.09.25 |
build method 와 render tree를 모르고 hot reload를 논하지마라 (4) | 2024.09.25 |
플러터 프로젝트에서 DI 주입은 어떻게 이루어지는가 (1) | 2024.09.19 |
Provider 기초개념정리 + 예제 (2) | 2024.09.18 |