최근,,은 아니고 플러터 서울 오픈 카톡방에 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
Investigate `const`ness · Issue #149932 · flutter/flutter
We would like to investigate if the following lints related to const (which are currently enabled by package:flutter_lints) are carrying their weight: prefer_const_constructors prefer_const_constru...
github.com
const 키워드는 Flutter 위젯 성능에 상당한 영향을 줍니다. 특히 많은 위젯들이 반복적으로 리빌드되는 복잡한 UI 상황에서 그 효과가 더 두드러집니다. 다음은 이와 관련된 내용들을 정리하고 보완한 것입니다.
첫째, const를 사용하면 성능이 향상될 수 있습니다. 그 이유는 Flutter가 변경되지 않는(immutable) 위젯 인스턴스를 여러 번의 리빌드 과정에서도 재사용할 수 있기 때문입니다. 이는 많은 정적인 요소들을 포함한 복잡한 UI에서 특히 효과적입니다.
둘째, Flutter 문서에서는 종종 "build"와 "instantiating"이라는 용어가 혼동되어 사용되는데, 이를 명확히 구분할 필요가 있습니다. "build"는 위젯 트리를 구성하기 위해 build 메서드를 호출하는 것을 말하고, "instantiating"은 위젯 인스턴스를 실제로 생성하는 것을 의미합니다. const로 생성된 위젯은 여러 번 build될 수는 있지만, 인스턴스 자체는 변하지 않기 때문에 불필요한 객체 생성이 줄어들고, 이로 인해 메모리 할당과 GC(가비지 컬렉션) 부담이 줄어듭니다.
셋째, const는 디버그 모드, 프로파일 모드, 릴리즈 모드를 포함한 모든 빌드 모드에서 효과를 발휘합니다. Dart VM은 가능한 경우 const 객체를 재사용하여 성능을 최적화하며, 이러한 최적화는 릴리즈 모드에 한정되지 않습니다.
넷째, Flutter는 리빌드 시에도 자식 위젯이 dirty 상태가 아니면 const 위젯의 build 메서드를 호출하지 않습니다. 예를 들어, 어떤 부모 위젯이 상태 변경을 감지하고 있고 그 안에 Column이 있으며, 그 Column의 자식 위젯이 const로 선언되어 있고 상태 변경에 영향을 받지 않는다면 해당 자식 위젯은 리빌드되지 않습니다. 이러한 선택적인 리빌드 방식은 전체적인 성능을 더욱 향상시키는 데 기여합니다.
직접 실험해보고 싶은 경우, 다음 GitHub Gist 링크에서 간단한 샘플 코드를 확인할 수 있습니다.
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 위젯 인스턴스에 대한 이해도 중요합니다. const 위젯은 동일한 생성자 파라미터로 인스턴스화될 경우, 하나의 표준 인스턴스만 생성됩니다. 이는 마치 캐시처럼 작동하며, 같은 파라미터 조합이 있다면 항상 동일한 객체가 재사용됩니다.
예를 들어 WidgetConstA라는 StatelessWidget이 있고, 생성자에 const가 붙어 있다면 다음과 같은 결과가 나타납니다.
const WidgetConstA(text: 'A')를 두 번 만들면 identical() 비교 시 true가 됩니다. 즉, 같은 인스턴스라는 뜻입니다. 반면 const가 아닌 일반 클래스인 WidgetNonConstB는 동일한 파라미터로 두 번 생성해도 서로 다른 인스턴스로 인식됩니다.
이 차이점은 Flutter 애플리케이션에서 메모리 효율성과 성능 최적화 측면에서 const 키워드가 얼마나 중요한지를 잘 보여줍니다. StatelessWidget과 StatefulWidget 모두 const 생성자를 가질 수 있으며, 변경 가능한 상태는 내부의 State 클래스에서 관리됩니다.
요약하자면, const는 동일한 파라미터를 가진 위젯이 다시 인스턴스화되지 않도록 막아주며, 해당 인스턴스는 컴파일 타임에 결정되므로 런타임 중에는 GC 대상이 되지 않습니다. 또한 Flutter는 const 위젯이 포함된 위젯 트리에서 하위 위젯이 dirty 상태가 아닐 경우, 해당 위젯의 build 메서드 호출을 생략하여 성능을 더욱 높일 수 있습니다.
결론은 !!!
const
는 동일한const
매개변수를 가진 위젯을 다시 인스턴스화하지 않음.const
인스턴스는 컴파일 타임에 결정되므로 가비지 컬렉션(GC)이 필요 없음.- 위젯이 변경되지 않는 경우, Flutter가
build
메서드를 호출하지 않아 불필요한 리빌드를 방지.
이것이고
여기에서 나온 중요한 개념
build 와 Instantiating 이 있다. 이를 헷갈리면 이 개념을 이해하기 참 골치아픈데, 이 둘을 잘 비교할 줄 알아야한다.
자 우리가 사용자라고 가정하고 앱의 실행 흐름을 보자
- 사용자가 앱을 실행하면
main()
함수에서runApp(MyApp())
이 실행됨. - Flutter는
build()
메서드를 호출하여 UI를 처음 그림. - 이후 버튼을 누르는 등 화면이 갱신될 때마다
build()
가 다시 실행됨 (물론 이과정에서 플러터는 최적화함 바뀐 ui만 그려줌). - 이 과정에서 새로운 객체가 생성됨 (Instantiating).
- 만약
const
를 사용하지 않으면, 같은 위젯도 계속 새롭게 생성됨 const는 build()가 다시 호출될 때 위젯을 다시 인스턴스화하지 않도록 막아줌 - 하지만
const
를 사용하면, 이미 있는 객체를 재사용하여 메모리 절약 가능 * 그치만 비약일 수 있음
이다
그래서 나의 생각은..!!ㅋㅋㅋ
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' 카테고리의 다른 글
<번역/정리> 10 Lesser-Known Dart and Flutter Functionalities You Should Start Using - 2편 (2) | 2025.06.30 |
---|---|
<번역/정리> 10 Lesser-Known Dart and Flutter Functionalities You Should Start Using - 1편 (3) | 2025.06.30 |
webRTC 실험요약 (0) | 2024.10.16 |
STFUL Lifecycle (1) | 2024.09.25 |
build method 와 render tree를 모르고 hot reload를 논하지마라 (4) | 2024.09.25 |