2026๋ Flutter ์ฑ๋ฅ ์ต์ ํ: Impeller, ๋ฆฌ๋น๋, ๋ชจ๋ฒ ์ฌ๋ก
2026๋ ์ Flutter ์ฑ์ 60 ๋๋ 120fps๋ก ์์ ์ ์ผ๋ก ์ ์งํ๋ ๋ฐฉ๋ฒ. Impeller, ์ ์ ๋ ์์ ฏ ๋ฆฌ๋น๋, RepaintBoundary, DevTools ํ๋กํ์ผ๋ง์ ๋ค๋ฃน๋๋ค.

2026๋ Flutter ์ฑ๋ฅ ์ต์ ํ๋ ๋ถ๋๋ฌ์ด ์ฑ์ ๋ง๋๋ ๋ฐฉ์์ ๋ฐ๊พผ ๋ ๊ฐ์ง ์ ํ์ ๊ธฐ๋๊ณ ์์ต๋๋ค. ๋ ๋๋ง ์์ง Impeller๊ฐ iOS์ Android์์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ ๊ณต๋๊ธฐ ์์ํ๋ค๋ ์ , ๊ทธ๋ฆฌ๊ณ ํ๋ ์์ํฌ์ ๋ฆฌ๋น๋ ๋ชจ๋ธ์ด ์์ ฏ ํธ๋ฆฌ๋ฅผ ์๊ณ ๋ถ๋ณ์ผ๋ก ์ ์งํ๋ ๊ฐ๋ฐ์์๊ฒ ๋ณด์์ ์ค๋ค๋ ์ ์ ๋๋ค. ์ด ๋ ์ง๋ ๋์ ๊ธฐ๋๋ ํ์ ํ๋ ์๋ง๋ค ์์ผ๋ก ์กฐ์ ํ์ง ์๊ณ ๋ 60 ๋๋ 120fps๋ฅผ ์์ ์ ์ผ๋ก ์ ์งํฉ๋๋ค.
๋๋ถ๋ถ์ Flutter ์ฑ์์ ๊ฐ์ฅ ํฐ ์ด๋์ const ์์ฑ์๋ฅผ ์ถ๊ฐํ๊ณ ํฐ build ๋ฉ์๋๋ฅผ ๋ถํ ํ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ฌ๋ฉด setState ํธ์ถ์ด ํ๋ฉด ์ ์ฒด๊ฐ ์๋๋ผ ๋ฒํผ ํ๋๋ง ๋ค์ ๊ทธ๋ฆฝ๋๋ค.
Impeller๊ฐ Flutter ์ฑ๋ฅ์ ๊ธฐ์ค์ ์ ๋ค์ ์ธ์ฐ๋ ์ด์
Impeller๋ Flutter์ ๋ ๋๋ง ์์ง์ด๋ฉฐ, ๋ชจ๋ฐ์ผ ํ๋ซํผ ์ ๋ฐ์์ Skia๋ฅผ ๋์ ํด ๊ธฐ๋ณธ๊ฐ์ด ๋์์ต๋๋ค. ์ฐจ์ด๊ฐ ๊ฐ์ฅ ํฌ๊ฒ ๋๋ฌ๋๋ ๊ณณ์ ์ ๋๋ฉ์ด์ ์ ์ฒ์ ๋ช ์ด์ ๋๋ค. Skia๋ ์ ฐ์ด๋๋ฅผ ์ ์คํธ ์ธ ํ์์ผ๋ก, ํน์ ํจ๊ณผ๊ฐ ํ๋ฉด์ ์ฒ์ ๋ํ๋ ๋ ์ปดํ์ผํ๊ณ , ์ด๊ฒ์ด Flutter ์ฑ์ด ๋นํ๋ฐ๋ ๊ทธ ์ ๋ช ๋์ ์ฒซ ์คํ ๋๊น์ ๋ง๋ค์ด ๋์ต๋๋ค. Impeller๋ ๊ทธ ์ ฐ์ด๋๋ค์ ๋น๋ ์์ ์ ๋ฏธ๋ฆฌ ์ปดํ์ผํ๋ฏ๋ก, ์ ๋๋ฉ์ด์ ์ ๋งจ ์ฒซ ํ๋ ์์ด 100๋ฒ์งธ ํ๋ ์๋งํผ ๋ถ๋๋ฝ์ต๋๋ค.
Impeller๋ ๋ํ ์ต์ ๊ทธ๋ํฝ API๋ฅผ ์ง์ ๊ฒจ๋ฅํฉ๋๋ค. iOS์์๋ Metal, Android์์๋ Vulkan์ ์ฌ์ฉํ๋ฉฐ, ๋ ์ค๋๋ Android ํ๋์จ์ด๋ฅผ ์ํ OpenGL ํด๋ฐฑ์ ์ ๊ณตํฉ๋๋ค. GitHub์ Impeller ์์ง ๋ฌธ์๋ ๋ ๋๋ง ์ํคํ ์ฒ์ ํ๋ซํผ ์ง์ ๋งคํธ๋ฆญ์ค๋ฅผ ์์ธํ ์ค๋ช ํฉ๋๋ค.
| ์ธก๋ฉด | Skia(๋ ๊ฑฐ์) | Impeller(2026 ๊ธฐ๋ณธ๊ฐ) | |--------|---------------|-------------------------| | ์ ฐ์ด๋ ์ปดํ์ผ | ๋ฐํ์, ์ฒซ ์คํ ๋๊น์ ์ ๋ฐ | ์ฌ์ , ๋น๋ ์์ | | iOS ๋ฐฑ์๋ | OpenGL / Metal | Metal | | Android ๋ฐฑ์๋ | OpenGL | Vulkan, OpenGL ํด๋ฐฑ | | ์ฒซ ์ ๋๋ฉ์ด์ ์ ๋ถ๋๋ฌ์ | ์ด๋ฐ๊ธ ๋๊น | ์ผ๊ด๋จ |
๋๋ถ๋ถ์ ํ๋ก์ ํธ์์ ๋ง์ด๊ทธ๋ ์ด์
์ ๊ณต์ง์
๋๋ค. ์ต๊ทผ Flutter ๋ฆด๋ฆฌ์ค๋ Impeller๋ฅผ ์๋์ผ๋ก ํ์ฑํํ๊ณ , ์ ์
ฐ์ด๋ ์๋ฐ์
์ฐํ์ฑ
(--purge-persistent-cache, ๋ฒ๋ค๋ .sksl ํ์ผ)์ ๋ ์ด์ ํ์ ์์ด ์ญ์ ํ ์ ์์ต๋๋ค. Flutter๊ฐ ์ง์ํ๋ GLSL ํ์ ์งํฉ์ ๋ง์ถฐ ์์ฑ๋ ์ฌ์ฉ์ ์ง์ FragmentProgram ์
ฐ์ด๋๋ ๊ทธ๋๋ก ์ด์ด์ง๋๋ค. Impeller๊ฐ ์ด๋ฅผ ํ๋ ์์ํฌ ์์ฒด ํจ๊ณผ์ ๋์ผํ ์คํ๋ผ์ธ ํ์ดํ๋ผ์ธ์ผ๋ก ์ปดํ์ผํ๊ธฐ ๋๋ฌธ์
๋๋ค.
์ฑ์ด ์ด๋ ์์ง์์ ์คํ๋๋์ง ํ์ธํ๋ ๋ฐ๋ ํ ์ค์ด๋ฉด ๋ฉ๋๋ค. ๋๋ฒ๊ทธ ๋น๋๋ ์์ ์ ํ์ฑ ๋ฐฑ์๋๋ฅผ ์ถ๋ ฅํ๊ณ , DevTools ์ธ์คํํฐ๋ ์ฑ์ ๋ ๋๋ง ์ธ๋ถ ์ ๋ณด์์ ์ด๋ฅผ ๋ณด๊ณ ํฉ๋๋ค. Impeller๊ฐ ํ์ธ๋๋ฉด, ๋จ์ ์ฑ๋ฅ ์์ ์ ์์ ฏ ๊ณ์ธต์ผ๋ก ์ฌ๋ผ๊ฐ๋๋ค. ๊ทธ๊ณณ์์ ํ๋ ์์ํฌ๋ ํ๋ฉด์ด ์ค์ ๋ก ๋ฐ๋๋ ๊ฒ๋ณด๋ค ํจ์ฌ ์์ฃผ ๋ฆฌ๋น๋ํฉ๋๋ค. ์ด ๊ฐ์ด๋์ ๋๋จธ์ง๊ฐ ์ด์ ์ ๋ง์ถ๋ ๊ณณ์ด ๋ฐ๋ก ๊ทธ๊ณณ์ ๋๋ค. ๋น ๋ฅธ ์์ง๋ ์ด๋น ์๋ฐฑ ๋ฒ ์ค์ค๋ก๋ฅผ ๋ค์ ๊ทธ๋ฆฌ๋ ์์ ฏ ํธ๋ฆฌ๋ ๊ตฌํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
const์ ์์ ฏ ๋ถํ ๋ก Flutter ๋ฆฌ๋น๋ ์ค์ด๊ธฐ
๋ชจ๋ setState๋ ์์ ์ ์์ ฏ์ ์ค๋๋ ๊ฒ์ผ๋ก ํ์ํ๊ณ ๊ทธ ์์์ ํ์ ํธ๋ฆฌ์ ๋ํด build๋ฅผ ๋ค์ ์คํํฉ๋๋ค. ์๋ น์ ๊ทธ ํ์ ํธ๋ฆฌ๋ฅผ ๊ฐ๋ฅํ ํ ์ข๊ฒ ์ ์งํ๋ ๊ฒ์
๋๋ค. ๋ ๊ฐ์ง ์ต๊ด์ด ์์
์ ๋๋ถ๋ถ์ ๋ด๋นํฉ๋๋ค. ์ ์ ์์ ฏ์ const๋ก ํ์ํ๋ ๊ฒ, ๊ทธ๋ฆฌ๊ณ ์ํ๋ฅผ ์๋๋ก ๋ฐ์ด ์ํ๋ฅผ ๊ฐ์ง ๋ถ๋ถ์ ์๊ฒ ์ ์งํ๋ ๊ฒ์
๋๋ค.
const ์์ ฏ์ ๋จ์ผ ์ธ์คํด์ค๋ก ์ ๊ทํ๋๋ฏ๋ก, ๋ถ๋ชจ๊ฐ ๋ฆฌ๋น๋๋ ๋ Flutter๋ ๋์ผํ ์ฐธ์กฐ๋ฅผ ๋น๊ตํ๊ณ , ๋ณํ๊ฐ ์๋ค๊ณ ํ๋จํ์ฌ, ๊ทธ ๊ฐ์ง์ ๋ค์ ๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์์ ํ ๊ฑด๋๋๋๋ค.
// A cart update repaints the badge, not the header.
class ProductScreen extends StatefulWidget {
const ProductScreen({super.key});
State<ProductScreen> createState() => _ProductScreenState();
}
class _ProductScreenState extends State<ProductScreen> {
int _cartCount = 0;
Widget build(BuildContext context) {
return Column(
children: [
const ProductHeader(), // const: never rebuilds on setState
CartBadge(count: _cartCount), // only this subtree rebuilds
FilledButton(
onPressed: () => setState(() => _cartCount++),
child: const Text('Add to cart'),
),
],
);
}
}analysis_options.yaml์์ prefer_const_constructors ๋ฆฐํธ๋ฅผ ํ์ฑํํ๋ฉด ์ด ๊ท์จ์ด ์ปดํ์ผ๋ฌ ๊ฒ์ฌ๋ก ๋ฐ๋๋ฏ๋ก, const ๊ธฐํ๊ฐ ์กฐ์ฉํ ์ฌ๋ผ์ง๋ ์ผ์ด ์์ต๋๋ค. ๊ด๋ จ ๊ท์น prefer_const_literals_to_create_immutables๋ ์์ ฏ์ ์ ๋ฌ๋๋ ๋ฆฌ์คํธ์ ๋งต์๋ ๊ฐ์ ๋ณด์ฅ์ ํ์ฅํฉ๋๋ค.
์์ ฏ ํค๋ ์ฌ๊ธฐ์ ์ธ๊ธํ ๊ฐ์น๊ฐ ์์ต๋๋ค. ๋ฆฌ์คํธ์ ์์๊ฐ ๋ฐ๋๊ฑฐ๋ ์์ ฏ์ ํ์
์ ๊ฐ์ง๋ง ์ ์ฒด์ฑ์ด ๋ฐ๋ ๋, ValueKey๋ ObjectKey๋ Flutter๊ฐ ์ํ๋ฅผ ํ๊ณ ๋ค์ ๋ง๋๋ ๋์ ์ด์ ์์๋ฅผ ์ฌ๋ฐ๋ฅธ ์ ์์ ฏ ์ธ์คํด์ค์ ๋งค์นญํ๋๋ก ํด ์ค๋๋ค. ํค์ ๋ถ์ฌ๋ ๋์ ๋ฆฌ์คํธ์์ ์คํฌ๋กค ์์น๊ฐ ์ฌ๋ผ์ง๊ณ ์ ๋๋ฉ์ด์
์ด ๋ฆฌ์
๋๋ ํํ ์์ธ์ด๋ฉฐ, ๊ทธ๋ก ์ธํ ์ถ๊ฐ ๋ฆฌ๋น๋ ์์
์ ๋์น๊ธฐ ์ฝ์ต๋๋ค.
๋ฐ๋ ๋ฐ์ดํฐ๋ก ๋ฆฌ๋น๋ ๋ฒ์ ์ขํ๊ธฐ
์ํ๊ฐ ํธ๋ฆฌ์ ๋์ ๊ณณ์ ์์ผ๋ฉด, ์ ๋ถํ ๋ ๋ ์ด์์์ด๋ผ๋ ์ฌ์ ํ ๋ง์ด ๋ฆฌ๋น๋๋ฉ๋๋ค. ํด๋ฒ์ ๊ฐ์ ๋ธ๋ก๋์บ์คํธํ๊ณ ์์ ฏ ํ๋๋ง ๊ทธ๊ฒ์ ๋ฃ๊ฒ ํ๋ ๊ฒ์
๋๋ค. Flutter๋ ๋ฐ๋ก ์ด๋ฅผ ์ํด ValueNotifier์ ValueListenableBuilder๋ฅผ ์ ๊ณตํ๋ฉฐ, ์ธ๋ถ ํจํค์ง๋ ํ์ ์์ต๋๋ค.
// A scroll-to-top button that rebuilds alone, not the whole page.
class ArticleList extends StatefulWidget {
const ArticleList({super.key});
State<ArticleList> createState() => _ArticleListState();
}
class _ArticleListState extends State<ArticleList> {
final _showFab = ValueNotifier<bool>(false);
final _controller = ScrollController();
void initState() {
super.initState();
// Update the notifier, not setState: the list never rebuilds
_controller.addListener(() => _showFab.value = _controller.offset > 400);
}
void dispose() {
_showFab.dispose();
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: _controller,
itemCount: 200,
itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
),
floatingActionButton: ValueListenableBuilder<bool>(
valueListenable: _showFab,
builder: (_, visible, __) => visible
? FloatingActionButton(
onPressed: () => _controller.jumpTo(0),
child: const Icon(Icons.arrow_upward),
)
: const SizedBox.shrink(),
),
);
}
}์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ฐ์ ์์ด๋์ด๋ฅผ ์ผ๋ฐํํฉ๋๋ค. Riverpod์ ref.watch(provider.select(...))์ Provider์ Selector ์์ ฏ์ ๋ชจ๋ ์ํ์ ์ ํ๋ ์กฐ๊ฐ์ด ๋ฐ๋ ๋๋ง ๋ฆฌ๋น๋ํ๋ฉฐ, ์ด๊ฒ์ด ๋ฐ์ดํฐ๊ฐ ๋ง์ ํ๋ฉด์์ ๋ถ๋๋ฌ์๊ณผ ๋ฒ๋ฒ
์์ ๊ฐ๋ฅด๋ ๊ฒฐ์ ์ ์์ธ์
๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค ์ฌ์ด์ ์ ์ถฉ์ 2026๋
Flutter ์ํ ๊ด๋ฆฌ ๊ฐ์ด๋์์ ๋ค๋ฃน๋๋ค.
ํ๋ฉด์ ๋ฃจํธ ๊ทผ์ฒ์์ setState๋ฅผ ํธ์ถํ๊ฑฐ๋ ํ์ด์ง ์ ์ฒด๋ฅผ ์ต์์ Consumer๋ก ๊ฐ์ธ๋ฉด, ๋ณ๊ฒฝ๋ง๋ค ์กฐ์ฉํ ์๋ฐฑ ๊ฐ์ ์์ ฏ์ด ๋ค์ ๊ทธ๋ ค์ง๋๋ค. ์ด๋ค ํ๋ฉด์ด ์ ๋ ดํ๋ค๊ณ ๋จ์ ํ๊ธฐ ์ ์ DevTools์ ๋ฆฌ๋น๋ ์นด์ดํฐ๋ก ํ๋กํ์ผํ์ญ์์ค.
Flutter ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
RepaintBoundary๋ก ๋น์ผ ๋ค์ ๊ทธ๋ฆฌ๊ธฐ ๊ฒฉ๋ฆฌํ๊ธฐ
๋ฆฌ๋น๋์ ๋ค์ ๊ทธ๋ฆฌ๊ธฐ๋ ๋ณ๊ฐ์ ๋จ๊ณ์
๋๋ค. ์์ ฏ์ ๋ฆฌ๋น๋๋ฅผ ๊ฑด๋๋ฐ๊ณ ๋, ์๋์ค๋ฌ์ด ์ด์์ด ๋ ์ด์ด๋ฅผ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ ๋ค์ ๊ทธ๋ ค์ง ์ ์์ต๋๋ค. RepaintBoundary๋ ํ์ ํธ๋ฆฌ์ ์์ฒด ๋ ์ด์ด๋ฅผ ๋ถ์ฌํ๋ฏ๋ก, ์ ๋๋ฉ์ด์
๋๋ ์ฐจํธ๊ฐ ์ ์ ์นด๋์ ๋งค ํ๋ ์ ๋ค์ ๊ทธ๋ฆฌ๋๋ก ๊ฐ์ํ ์ ์์ต๋๋ค.
// The live chart repaints on its own layer, cards stay untouched.
class Dashboard extends StatelessWidget {
const Dashboard({required this.ticker, super.key});
final Stream<double> ticker;
Widget build(BuildContext context) {
return Column(
children: [
// SummaryCards is static, so isolate it from the animation below
const RepaintBoundary(child: SummaryCards()),
RepaintBoundary(
child: StreamBuilder<double>(
stream: ticker,
builder: (_, snap) => LivePriceChart(value: snap.data ?? 0),
),
),
],
);
}
}RepaintBoundary๋ ๊ณต์ง๊ฐ ์๋๋๋ค. ๊ฐ๊ฐ์ด ๋ ์ด์ด ํ๋๋ฅผ ํ ๋นํ๋ฏ๋ก ์ฌ๊ธฐ์ ๊ธฐ ๋ฟ๋ฆฌ๋ฉด ๋์๋ณด๋ค ํด๊ฐ ํฝ๋๋ค. ์ ๋ง๋ก ์ ๋๋ฉ์ด์ ๋๋ ์์ญ๊ณผ ๊ธด ์คํฌ๋กค ๋ฆฌ์คํธ ์ฃผ์์ ์ฌ์ฉํ๊ณ , ํ์๋ผ์ธ์์ ์ด๋์ ํ์ธํ์ญ์์ค. RepaintBoundary API ๋ ํผ๋ฐ์ค๋ ๋๋ฒ๊ทธ ๋ค์ ๊ทธ๋ฆฌ๊ธฐ ์ค๋ฒ๋ ์ด๊ฐ ์ค์ ๋ก ๋ค์ ๊ทธ๋ ค์ง๋ ๋ ์ด์ด๋ฅผ ์ด๋ป๊ฒ ๊ฐ์กฐํ๋์ง ๋ฌธ์ํํฉ๋๋ค.
build ๋ฉ์๋๋ฅผ ์ ๋ ดํ๊ณ ๋ถ์์ฉ ์์ด ์ ์งํ๊ธฐ
build ๋ฉ์๋๋ ๋ฆฌ๋น๋๋ง๋ค ์คํ๋๋ฏ๋ก, ๊ทธ ์์ ๋น์ผ ๊ฒ์ ๋ฌด์์ด๋ ๋ฐฐ๊ฐ๋ฉ๋๋ค. ๊ท์น์ build๊ฐ ์ํ๋ฅผ ์ฝ๊ณ ์์ ฏ์ ๋ฐํํ๋ ๊ฒ, ๊ทธ ์ด์์ ์๋๋ผ๋ ๊ฒ์
๋๋ค. build ์์์ ScrollController, AnimationController, Future๋ฅผ ํ ๋นํ๋ฉด ๋งค ํ๋ ์ ๋ค์ ์์ฑ๋๊ณ ์ด์ ์ธ์คํด์ค๊ฐ ๋์ถ๋ฉ๋๋ค. ์ด ๊ฐ์ฒด๋ค์ initState๋ dispose์์ ํด์ ๋๋ late final ํ๋์ ์ํฉ๋๋ค.
๋ฌด๊ฑฐ์ด ๋๊ธฐ ์์
์ด ๋ ๋ฒ์งธ ํจ์ ์
๋๋ค. build ์์์ JSON์ ํ์ฑํ๊ฑฐ๋, ํฐ ๋ฆฌ์คํธ๋ฅผ ํํฐ๋งํ๊ฑฐ๋, ๋ ์ง๋ฅผ ํฌ๋งทํ๋ฉด, ๊ธฐ์ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋์ง ์์์ด๋ ๊ทธ ์์
์ด ๋งค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ๋ง๋ค ์ฌ์คํ๋ฉ๋๋ค. ์
๋ ฅ์ด ๋ฐ๋ ๋ ๊ฒฐ๊ณผ๋ฅผ ํ ๋ฒ๋ง ๊ณ์ฐํด ์บ์ํ๊ฑฐ๋, ํ๋ ์์ํฌ๊ฐ ํ ํ๋ ์์ ๋ง๊ณ ๊ธฐ๋ค๋ฆฌ์ง ์๋๋ก FutureBuilder๋ก ์ฎ๊ธฐ์ญ์์ค. ๋ค๋ฅธ ๊ฐ์์ ํ์๋๋ ๊ฐ์ ์์กด์ฑ์ด ๋ฐ๋ ๋๋ง ์ฌ๊ณ์ฐํ๋ ๊ฒํฐ ๋ค์ ๋ฉ๋ชจ์ด์ ์ด์
ํ์ญ์์ค.
Opacity ์์ ฏ์ ์ด๋ฆ์ ๋ฐํ ๋งํ ๊ตฌ์ฒด์ ์ธ ์ํ์
๋๋ค. ํ์ ํธ๋ฆฌ๋ฅผ Opacity๋ก ๊ฐ์ธ๋ฉด ํ๋ฉด ๋ฐ saveLayer๊ฐ ๊ฐ์ ๋๋๋ฐ, ์ด๋ ํ์ดํ๋ผ์ธ์์ ๊ฐ์ฅ ๋น์ผ ์ฐ์ฐ ์ค ํ๋์
๋๋ค. ํ์ด๋ ์ ๋๋ฉ์ด์
์๋ AnimatedOpacity๋ FadeTransition์ด ๋ ์ ๋ ดํ๊ณ , ๊ณ ์ ๋ ์์กฐ์๋ foregroundDecoration์ด๋ ์
ฐ์ด๋๋ฅผ ํตํด ํ์ธํธ ์์ค์์ ์์ ์ ์ฉํ๋ฉด ๋ ์ด์ด๋ฅผ ์์ ํ ํผํ ์ ์์ต๋๋ค. ๊ฐ์ ์ฃผ์๊ฐ ํฐ ํ๋ฉด์์ ์ํฐ์์ผ๋ฆฌ์ด์ฑ์ ์ผ ClipPath์ ClipRRect์๋ ์ ์ฉ๋๋ฉฐ, ์ด๋ค ์ญ์ ๋ค์์ saveLayer๋ฅผ ์ ๋ฐํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก, const ์ปฌ๋ ์
๊ณผ ๋งค ๋น๋๋ง๋ค ์ ํด๋ก์ ๋ฅผ ํฌํํ์ง ์๋ ์ฝ๋ฐฑ์ ์ ํธํ์ญ์์ค. ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ ๋ฌํ const []์ ์ธ๋ผ์ธ ๋๋ค ๋์ ์ ๋ฉ์๋ ์ฐธ์กฐ๋ ๋ชจ๋ ์์ ฏ ๋๋ฑ์ฑ ๊ฒ์ฌ๋ฅผ ์ ๋ ดํ๊ฒ ์ ์งํ๋ฉฐ, ์ด๋ ์์ ์ค๋ช
ํ const ๋จ๋ฝ(็ญ็ตก)์ด ์ค์ ๋ก ๋ฐ๋ํ๋๋ก ํด ์ค๋๋ค.
๊ธด ๋ฆฌ์คํธ์ ์ด๋ฏธ์ง๋ฅผ ํจ์จ์ ์ผ๋ก ๊ตฌ์ฑํ๊ธฐ
๊ธด ํผ๋์ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๋๋ ์์งํ Flutter ์ฝ๋๊ฐ ํ๋ ์์ ๋จ์ด๋จ๋ฆฌ๋ ์ง์ ์ ๋๋ค. ๋ ๊ฐ์ง ๊ท์น์ด ๋๋ถ๋ถ์ ๊ฒฝ์ฐ๋ฅผ ๋ค๋ฃน๋๋ค. ํ์ ์ง์ฐ ๊ตฌ์ฑํ๋ ๊ฒ, ๊ทธ๋ฆฌ๊ณ ์ด๋ฏธ์ง๋ฅผ ์์ค ํด์๋๊ฐ ์๋๋ผ ํ์๋๋ ํฌ๊ธฐ๋ก ๋์ฝ๋ฉํ๋ ๊ฒ์ ๋๋ค.
// Lazy rows plus right-sized image decoding keep scrolling at 60fps.
class Feed extends StatelessWidget {
const Feed({required this.posts, super.key});
final List<Post> posts;
Widget build(BuildContext context) {
return ListView.builder(
itemCount: posts.length, // build on demand, not all at once
cacheExtent: 600, // pre-build past the viewport edge
itemBuilder: (context, index) {
final post = posts[index];
return ListTile(
leading: Image.network(
post.thumbnailUrl,
width: 48,
height: 48,
cacheWidth: 96, // decode at display size, save memory
),
title: Text(post.title),
);
},
);
}
}3000ํฝ์
์ ์ฒด ํด์๋ ์ด๋ฏธ์ง๋ฅผ 48ํฝ์
์๋ฐํ์ ์ฑ์ฌ๋ฃ์ผ๋ฉด, ๊ทธ ํ๋ํ๋๋ง๋ค ๋ฉ๋ชจ๋ฆฌ์ ๋์ฝ๋ฉ ์๊ฐ์ ๋ญ๋นํฉ๋๋ค. cacheWidth๋ฅผ ์ค์ ํ๋ฉด ์์ง์ ๋ชฉํ ํฌ๊ธฐ๋ก ๋์ฝ๋ฉํ๋ผ๊ณ ์ง์ํ๊ฒ ๋๋ฉฐ, ์ด๊ฒ๋ง์ผ๋ก๋ ๋ฆฌ์คํธ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ํ ์๋ฆฟ์ ๊ท๋ชจ๋ก ์ค์ผ ์ ์์ต๋๋ค. Flutter์ ์ฑ๋ฅ ๋ชจ๋ฒ ์ฌ๋ก๋ ๊ฐ์ ์์น์ ๋ฐ๋ฅด๋ Opacity, saveLayer, ํด๋ฆฝ ์ฌ์ฉ์ ๋ํ ์ถ๊ฐ ์ง์นจ์ ๋ชจ์ ๋ก๋๋ค.
2026๋ Flutter ์ต์ ํ๋ฅผ DevTools๋ก ์ธก์ ํ๊ธฐ
์์ ๋ชจ๋ ๊ท์น์ ๊ฐ์ ์ด ์๋๋ผ ๊ฒ์ฆ๋์ด์ผ ํฉ๋๋ค. Flutter DevTools๊ฐ ์ง์ค์ ์์ฒ์
๋๋ค. ์ฑ๋ฅ ์ค๋ฒ๋ ์ด(๋๋ฒ๊ทธ ์ฝ์์ P ๋๋ flutter run --profile)๋ UI์ ๋์คํฐ๋ผ๋ ๋ ๊ทธ๋ํ๋ฅผ ๊ทธ๋ฆฌ๋ฉฐ, ์ด๋ก ์ ์ ๋๋ ๋ง๋๋ ๋ชจ๋ ๋จ์ด์ง ํ๋ ์์
๋๋ค. ์ด์ด์ ํ์๋ผ์ธ ๋ทฐ๊ฐ ๊ฐ ๋๋ฆฐ ํ๋ ์์ ํน์ build, ๋ ์ด์์, ํ์ธํธ ํธ์ถ์ ๊ท์์ํต๋๋ค.
ํญ์ ์ค์ ๊ธฐ๊ธฐ์์ --profile ๋ชจ๋๋ก ํ๋กํ์ผํ์ญ์์ค. ๋๋ฒ๊ทธ ๋น๋๋ ์ต์ ํ๋์ง ์์ Dart๋ฅผ ์คํํ๊ณ ๋ชจ๋ ์ธก์ ์น๋ฅผ ๋ถํ๋ฆฌ๋ฏ๋ก, ๋๋ฒ๊ทธ์์ ๋ฒ๋ฒ
์ด๋ ํ๋ฉด์ด ์ปดํ์ผ ํ์๋ ์๋ฒฝํ๊ฒ ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. DevTools ์ฑ๋ฅ ๊ฐ์ด๋๋ ํ๋ ์ ์ฐจํธ๋ฅผ ์ฝ๊ณ ๋ฒ๋ฒ
์์ ์์ธ์ ์ฐพ์๋ด๋ ๊ณผ์ ์ ์ง์ด ์ฃผ๋ฉฐ, ๊ฐ์ ํ๋กํ์ผ๋ง ๊ทผ์ก์ Flutter ์ ๋๋ฉ์ด์
๊ณผ ๋ ๋๋ง์ ๊ดํ ๋ฉด์ ์์๋ ๋ฑ์ฅํฉ๋๋ค.
ํ๋กํ์ผ ๋ชจ๋๋ก ์คํํ๊ณ , ๋ฒ๋ฒ ์ด๋ ์ํธ์์ฉ์ ์ฌํํ๊ณ , ํ์๋ผ์ธ์ ์ด์ด ํ๋ ์์ ์ง์ ์๊ฐ์ผ๋ก ์ ๋ ฌํ์ญ์์ค. ์ต์ ์ ํ๋ ์์ ๊ฑฐ์ ์ธ์ ๋ ์ง๋์น๊ฒ ํฐ ๋จ์ผ ๋ฆฌ๋น๋๋ ์บ์๋์ง ์์ ์ด๋ฏธ์ง๋ฅผ ๊ฐ๋ฆฌํค๋ฉฐ, ์ด๋ ์ถ์ธกํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋น ๋ฆ ๋๋ค.
๋ ๋์ Flutter ๊ธฐ์ ํ๋ธ๋ ํ๋ก๋์ ์ฑ๋ฅ ์ ๋ต์ ์์ฑํ๋ ์ํ ๊ด๋ฆฌ, ํ ์คํธ, ๋ ๋๋ง ์ฃผ์ ๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค.
๊ฒฐ๋ก
2026๋ Flutter ์ฑ๋ฅ์ ๋ฏธ์ธํ ์๋ น๋ณด๋ค๋, ์ผ๊ด๋๊ฒ ์ ์ฉํ๋ ์งง์ ์ต๊ด ๋ชฉ๋ก์ ๊ดํ ๊ฒ์ ๋๋ค.
- Impeller ์์์ ๋ฐฐํฌํ๊ณ ์ค๋๋ ์ ฐ์ด๋ ์๋ฐ์ ์ฝ๋๋ฅผ ์ญ์ ํ์ญ์์ค. ์ฒซ ํ๋ ์ ๋ฒ๋ฒ ์ ๋ฌธ์ ๋ ์์ง ์์ค์์ ํด๊ฒฐ๋์์ต๋๋ค.
- ์ ์ ์์ ฏ์
const๋ก ํ์ํ๊ณprefer_const_constructors๋ฆฐํธ๋ก ๊ฐ์ ํ์ญ์์ค. - ์ํ๋ฅผ ๊ฐ์ง ์์ ฏ์ ์๊ฒ ๋ถํ ํด, ๊ฐ
setState๊ฐ ๊ฐ๋ฅํ ํ ์ข์ ํ์ ํธ๋ฆฌ๋ฅผ ๋ค์ ๊ทธ๋ฆฌ๊ฒ ํ์ญ์์ค. - ๋ณํ๋ ๊ฐ์ ํ๋ฉด ์ ์ฒด๋ฅผ ๋ฆฌ๋น๋ํ๋ ๋์
ValueListenableBuilder๋select์คํ์ผ API๋ก ๋ธ๋ก๋์บ์คํธํ์ญ์์ค. - ์ ๋ง๋ก ์ ๋๋ฉ์ด์
๋๋ ์์ญ์
RepaintBoundary๋ก ๊ฐ์ธ๋, ๋ ์ถ๊ฐํ๊ธฐ ์ ์ ์ธก์ ํ์ญ์์ค. - ๋ฆฌ์คํธ๋
ListView.builder๋ก ๊ตฌ์ฑํ๊ณ ์ด๋ฏธ์ง๋cacheWidth๋ก ํ์ ํฌ๊ธฐ์ ๋ง์ถฐ ๋์ฝ๋ฉํ์ญ์์ค. - ์ค์ ํ๋์จ์ด์์
--profile๋ชจ๋๋ก ํ๋กํ์ผํ๊ณ , ๋ค์์ ๋ฌด์์ ์ต์ ํํ ์ง ์ง๊ฐ์ด ์๋๋ผ DevTools๊ฐ ๊ฒฐ์ ํ๊ฒ ํ์ญ์์ค.
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
ํ๊ทธ
๊ณต์
๊ด๋ จ ๊ธฐ์ฌ

Flutter ์ํ ๊ด๋ฆฌ: Riverpod vs BLoC - ์์ ๋น๊ต ๊ฐ์ด๋
Flutter ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ Riverpod์ BLoC์ ์ฌ์ธต ๋น๊ต์ ๋๋ค. ์ํคํ ์ฒ, ์ฑ๋ฅ, ํ ์คํธ ์ฉ์ด์ฑ, ์ฌ์ฉ ์ฌ๋ก๋ฅผ ํตํด ์ต์ ์ ์๋ฃจ์ ์ ์ ํํฉ๋๋ค.

๋ชจ๋ฐ์ผ ๊ฐ๋ฐ์๋ฅผ ์ํ Flutter ๋ฉด์ ์ง๋ฌธ 20์
Flutter ๋ฉด์ ์์ ๊ฐ์ฅ ์์ฃผ ์ถ์ ๋๋ 20๊ฐ์ง ์ง๋ฌธ์ ์ค๋นํ์ญ์์ค. Widget, ์ํ ๊ด๋ฆฌ, Dart, ์ํคํ ์ฒ, ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์์ธํ ์ฝ๋ ์์ ์ ํจ๊ป ์ค๋ช ํฉ๋๋ค.

Flutter: ์ฒซ ๋ฒ์งธ ํฌ๋ก์ค ํ๋ซํผ ์ฑ ๊ตฌ์ถํ๊ธฐ
Flutter์ Dart๋ฅผ ์ฌ์ฉํ ํฌ๋ก์ค ํ๋ซํผ ๋ชจ๋ฐ์ผ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์ถ ์์ ๊ฐ์ด๋. Widget, ์ํ ๊ด๋ฆฌ, ๋ด๋น๊ฒ์ด์ , ์ด๋ณด์๋ฅผ ์ํ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ค๋ฃน๋๋ค.