Flutter Widgets — 개념, 위젯 트리 및 build

저자: IT Sectr 게시일: 2026-02-21 읽는 시간: 8 분

Flutter Widgets — Google Flutter 프레임워크(2018년 12월 안정 버전 1.0 출시)의 사용자 인터페이스 기본 구성 요소입니다. Flutter에서는 화면과 버튼부터 패딩과 정렬에 이르기까지 모든 것이 위젯입니다. 위젯은 트리(widget tree)를 형성하며, Flutter는 자체 Skia/Impeller 엔진에서 이를 렌더링하여 플랫폼의 네이티브 구성 요소를 우회합니다. Google I/O(2025)에 따르면, Flutter는 Google Play와 App Store에서 50만 개 이상의 앱에서 사용됩니다. Flutter 아키텍처는 위젯을 StatelessWidget(불변)과 StatefulWidget(State 객체를 통한 가변 상태)으로 나눕니다.

주요 포인트

  • Flutter Widgets — Flutter의 모든 UI 요소: 화면과 버튼부터 패딩과 정렬까지; 프레임워크는 플랫폼의 네이티브 구성 요소를 사용하지 않습니다.
  • StatelessWidget — 가변 상태가 없는 위젯; 생성자를 통해 전달된 데이터를 표시하며 build 후에 변경되지 않습니다.
  • StatefulWidget — 리빌드 후에도 유지되는 별도의 State 객체에 저장된 가변 상태를 가진 위젯입니다.
  • 위젯 트리 (widget tree) — UI를 설명하는 위젯의 계층 구조; Flutter는 렌더링을 위해 이를 Element Tree와 Render Tree로 변환합니다.
  • BuildContext — 트리에서 위젯의 위치, 테마, MediaQuery 및 InheritedWidget에 대한 액세스를 제공하는 빌드 컨텍스트입니다.

Flutter Widgets란?

Flutter Widgets — Flutter 프레임워크의 모든 UI 요소로, 불변(immutable) Dart 클래스로 구현됩니다. 위젯은 UI 요소의 구성입니다: 크기, 색상, 위치, 이벤트 핸들러. Android(XML + View)나 iOS(Storyboard + UIView)와 달리, Flutter는 네이티브 구성 요소를 사용하지 않습니다 — 모든 것이 Canvas에서 Skia(Android/iOS/Linux) 또는 Impeller(iOS/macOS) 엔진으로 그려집니다. 이는 플랫폼 차이 없이 모든 플랫폼에서 일관된 모양을 보장합니다. Flutter는 두 가지 위젯 세트를 제공합니다: Material Widgets(Android/웹용 Google Material Design 3)와 Cupertino Widgets(iOS/macOS용 Apple HIG). 개발자는 단일 애플리케이션에서 두 세트를 모두 결합할 수 있습니다. 각 위젯에는 자식 위젯 또는 그 구성을 반환하는 build() 메서드가 있습니다 — 이것이 위젯 트리가 구축되는 방식입니다.

Flutter의 세 가지 트리

Flutter는 세 가지 트리로 작동합니다: Widget Tree(개발자가 생성한 구성), Element Tree(트리에서 위젯 위치에 매핑, 프레임워크가 관리), Render Tree(레이아웃을 계산하고 페인팅을 수행하는 객체). Widget은 Element를 생성하고, Element는 RenderObject를 생성합니다. 구성이 변경(리빌드)되면 Element가 재사용됩니다 — 이것이 Flutter의 최적화 메커니즘입니다: 위젯 유형과 key가 일치하면 Element가 다시 생성되지 않고 새 구성으로 업데이트됩니다. 세 가지 트리를 이해하는 것이 Flutter 애플리케이션 성능 최적화의 핵심입니다.

StatelessWidget: 불변 위젯

StatelessWidget — 내부 상태를 저장하지 않는 위젯입니다. 생성자를 통해 매개변수를 받아들이고 build 후에 변경할 수 없이 표시합니다. StatelessWidget은 정적 요소에 이상적입니다: 헤더, 아이콘, 아바타, 고정 데이터 목록. build() 메서드는 생성 시 한 번과 부모 위젯이 변경될 때 호출됩니다. StatelessWidget에는 상태가 없으므로 별도의 State 객체가 필요하지 않습니다 — build()는 위젯 클래스에서 직접 정의됩니다.

dart
import 'package:flutter/material.dart';

class ProfileHeader extends StatelessWidget {
  final String name;
  final String avatarUrl;

  const ProfileHeader({super.key, required this.name, required this.avatarUrl});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        CircleAvatar(
          radius: 24,
          backgroundImage: NetworkImage(avatarUrl),
        ),
        const SizedBox(width: 12),
        Text(
          name,
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
      ],
    );
  }
}

ProfileHeader — 생성자를 통해 name과 avatarUrl을 받는 StatelessWidget입니다. CircleAvatar는 아바타를 표시하고, Text는 이름을 표시합니다. 위젯은 생성 후 이러한 값을 변경할 수 없습니다. const 생성자(const ProfileHeader)를 사용하면 Flutter가 부모 리빌드 중에 위젯 인스턴스를 재사용할 수 있습니다 — 모든 StatelessWidget에 권장되는 미세 최적화입니다.

StatefulWidget: 상태가 있는 위젯

StatefulWidget — 별도의 State 객체에 저장된 가변 상태를 가진 위젯입니다. StatefulWidget 자체는 StatelessWidget처럼 불변입니다. 가변 부분은 State에 위임됩니다: State는 한 번 생성되어 여러 위젯 리빌드 후에도 유지되는 객체입니다. 상태가 변경되면(setState()), Flutter는 State에서 build()를 호출하여 UI를 업데이트합니다. StatefulWidget은 대화형 요소에 사용됩니다: 카운터, 텍스트 필드, 체크박스, 애니메이션, 타이머.

dart
import 'package:flutter/material.dart';

class LikeButton extends StatefulWidget {
  const LikeButton({super.key});

  @override
  State<LikeButton> createState() => _LikeButtonState();
}

class _LikeButtonState extends State<LikeButton> {
  bool liked = false;
  int count = 42;

  void _toggleLike() {
    setState(() {
      liked = !liked;
      count += liked ? 1 : -1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _toggleLike,
      child: Text(liked ? '♥ $count' : '♡ $count'),
    );
  }
}

LikeButton — State 클래스 _LikeButtonState를 가진 StatefulWidget입니다. liked와 count 필드는 State에 저장된 상태이며 리빌드 시 재설정되지 않습니다. setState()는 Flutter에 UI 재구축이 필요함을 알립니다. Flutter는 모든 부모 리빌드에서 State를 다시 생성하지 않습니다 — 구성(key 또는 위젯 유형)이 변경될 때만 그렇습니다. IT Sectr에서는 표시에는 StatelessWidget을, 대화형 구성 요소에는 StatefulWidget을 사용하며, 변경 범위까지 StatefulWidget을 최소화하는 원칙을 따릅니다.

위젯 트리: Widget에서 RenderObject까지

Widget Tree — 애플리케이션 UI를 설명하는 위젯의 계층 구조입니다. Flutter는 Widget Tree를 Element Tree로 변환합니다: 각 Widget은 Element를 생성하고, Element는 트리에서의 위치와 RenderObject와의 연결을 추적합니다. Render Tree는 레이아웃(크기 및 위치 결정)과 페인트(렌더링)를 수행합니다. Element는 중요한 최적화 계층입니다: 리빌드 중에 위젯 유형과 key가 변경되지 않은 경우 Element가 재사용되어 RenderObject를 다시 생성하지 않고 구성을 업데이트합니다. 이는 높은 프레임 속도(60/120 FPS)에서 Flutter의 성능을 보장합니다.

dart
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter 위젯')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.flutter_dash, size: 80),
            const SizedBox(height: 16),
            const Text(
              '안녕하세요, Flutter!',
              style: TextStyle(fontSize: 24),
            ),
          ],
        ),
      ),
    );
  }
}

HomeScreen 위젯 트리: Scaffold → AppBar + Center → Column → [Icon, SizedBox, Text]. Scaffold는 루트 Material Design 레이아웃 위젯입니다. Center는 자식 Column을 중앙에 배치합니다. Column은 자식을 중앙 정렬(mainAxisAlignment.center)로 세로로 배열합니다. Flutter는 레이아웃을 위에서 아래로(제약 조건은 부모에서 전달), 크기는 아래에서 위로(sizedByParent 또는 자식에서 크기 조정) 계산합니다.

BuildContext: 빌드 컨텍스트 및 트리 액세스

BuildContext — Element Tree에서 위젯 위치의 설명자입니다. 각 build() 메서드는 BuildContext를 받으며, 위젯은 이를 통해 다음에 액세스할 수 있습니다: MediaQuery(화면 크기, 방향, 픽셀 밀도, 안전 영역), Theme(MaterialThemeData 또는 CupertinoThemeData), Navigator(탐색 스택), ScaffoldMessenger(SnackBar), InheritedWidget(트리 아래로 데이터 전달). BuildContext는 build() 완료 후 저장하거나 동기 컨텍스트 외부에서 사용해서는 안 됩니다 — 이로 인해 “Scaffold.of() called with a context that does not contain a Scaffold”와 같은 오류가 발생합니다. 컨텍스트를 저장하는 대신 데이터 액세스를 위해 Builder 위젯 또는 InheritedWidget 패턴을 사용하세요.

Key: 트리에서 위젯 식별

Key — 각 위젯 생성자의 선택적 매개변수(super.key)입니다. Key는 목록에서 항목의 순서나 개수가 변경될 때 Flutter가 위젯을 식별하는 데 도움을 줍니다. Key가 없으면 Flutter는 유형과 위치로 위젯을 비교합니다. Key 없이 목록 중간에 항목을 추가하면 Flutter는 잘못된 위젯에 Element를 재사용하여 상태 버그(StatefulWidget) 및 애니메이션 문제를 일으킵니다. 목록에는 ValueKey(고유 식별자), ObjectKey(데이터 객체 기반), UniqueKey(리빌드마다 고유 보장 — 강제 재생성용)를 사용하세요.

Flutter 위젯 카탈로그: Material, Cupertino, Layout

Flutter SDK(안정 버전 3.3+, Dart 3+)에는 200개 이상의 내장 위젯이 카테고리로 나뉘어 포함되어 있습니다: Layout(Row, Column, Stack, Container, Expanded, Flexible, Wrap, GridView, ListView, CustomScrollView), Material Design(Scaffold, AppBar, Card, Drawer, BottomNavigationBar, FloatingActionButton, TabBar, Chip, DataTable), Cupertino(CupertinoPageScaffold, CupertinoNavigationBar, CupertinoButton, CupertinoTabBar), Painting & Effects(Opacity, DecoratedBox, ClipRect, Transform, BackdropFilter, ShaderMask), Async(FutureBuilder, StreamBuilder) 및 Accessibility(Semantics, MergeSemantics, ExcludeSemantics). 레이아웃 위젯은 모든 Flutter UI의 기초입니다: Row는 수평 시퀀스, Column은 수직, Stack은 겹치는 요소, Container와 BoxDecoration은 장식용입니다.

카테고리주요 위젯목적
LayoutRow, Column, Stack, Container, Expanded, Flexible자식 요소의 위치 및 크기 조정
MaterialScaffold, AppBar, Card, Drawer, FAB, TabBarMaterial Design 3 UI 구성 요소
CupertinoCupertinoPageScaffold, CupertinoNavigationBar, CupertinoButtoniOS 스타일 구성 요소
PaintingOpacity, ClipRect, Transform, DecoratedBox, BackdropFilter시각 효과 및 클리핑
AsyncFutureBuilder, StreamBuilderUI 상태를 사용한 비동기 데이터 로딩
AccessibilitySemantics, MergeSemantics스크린 리더용 시맨틱 마크업

자주 묻는 질문

StatelessWidget과 StatefulWidget의 차이점은 무엇인가요?

StatelessWidget은 내부 상태가 없습니다 — 모든 데이터는 생성자를 통해 전달되며 변경되지 않습니다. StatefulWidget은 리빌드 후에도 유지되는 별도의 State 객체에 가변 상태를 저장합니다. 상태 변경은 setState()를 호출하고 Flutter가 UI를 재구축합니다. 불변 요소에는 StatelessWidget을, 대화형 요소에는 StatefulWidget을 사용하세요.

Flutter에서 BuildContext란 무엇인가요?

BuildContext는 build() 메서드에 전달되는 Element Tree에서 위젯 위치의 설명자입니다. 컨텍스트를 통해 위젯은 MediaQuery, Theme, Navigator, ScaffoldMessenger 및 InheritedWidget에 액세스합니다. BuildContext는 build() 완료 후 저장해서는 안 됩니다. 데이터 액세스를 위해서는 Builder 위젯 또는 InheritedWidget을 사용하세요.

Flutter를 사용하려면 Dart를 알아야 하나요?

네, Flutter는 Dart를 유일한 프로그래밍 언어로 사용합니다. Dart는 null safety, sound null safety, extension methods 및 pattern matching을 갖춘 정적 타입 언어입니다. 시작하려면 기본적인 Dart 지식(클래스, 함수, async/await, 컬렉션)으로 충분합니다. Dart는 Dart Native를 통해 네이티브 코드(AOT)로, Dart2JS를 통해 JavaScript(웹용)로 컴파일됩니다.

Flutter는 모바일 플랫폼 전용인가요?

아니요, Flutter는 6개의 플랫폼을 지원합니다: iOS, Android, Web, macOS, Windows, Linux. 코드의 90% 이상이 모든 플랫폼에서 공유됩니다. 플랫폼 적응(카메라, 파일 시스템)은 플러그인 또는 플랫폼 채널을 통해 구현됩니다. 데스크톱의 경우 Flutter는 Flutter Desktop 윈도우 셸을 사용하고, 웹의 경우 CanvasKit(WebGL) 또는 HTML 렌더러를 사용합니다.

Flutter는 어떻게 60 FPS 성능을 달성하나요?

Flutter는 60/120 FPS를 달성합니다: 자체 2D 엔진(Skia/Impeller), 네이티브 브리지 부재(React Native와 달리), 유형/key가 변경되지 않을 때 Element 재사용을 통한 세 가지 트리(Widget → Element → Render), 단방향 패스(제약 조건은 아래로, 크기는 위로)의 레이아웃 알고리즘, 그리고 화면의 개별 영역 다시 그리기를 분리하는 RepaintBoundary를 통해.

요약

  • Flutter Widgets — Scaffold부터 SizedBox까지 모든 UI 요소; Flutter는 Skia/Impeller에서 자체 렌더링하며 네이티브 구성 요소를 사용하지 않습니다.
  • StatelessWidget — 상태가 없는 위젯; build()는 불변 생성자 매개변수를 기반으로 UI를 반환합니다.
  • StatefulWidget — State에 가변 상태를 가진 위젯; setState()가 UI 리빌드를 트리거합니다.
  • Flutter의 세 가지 트리: Widget(구성) → Element(위치) → Render(레이아웃 + 페인트); 성능을 위해 Element가 재사용됩니다.
  • BuildContext — 트리에서 위젯의 위치; MediaQuery, Theme, Navigator, InheritedWidget에 액세스.
  • Material 및 Cupertino — 각각 Google Material Design 3 및 Apple HIG를 위한 두 가지 위젯 세트.
  • Key(ValueKey, ObjectKey, UniqueKey) — 목록에서 순서나 개수 변경 시 위젯 식별.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기