Flutter 튜토리얼 4편: Widget 개념과 기본 위젯

요약#

핵심 요지#

  • 문제 정의: UI 요소마다 다른 방식으로 다루면 코드가 일관성 없이 복잡해진다.
  • 핵심 주장: Flutter는 Widget1이라는 단일 개념으로 모든 UI를 표현한다.
  • 주요 근거: StatelessWidget2StatefulWidget3으로 상태 유무를 명확히 구분하고, 위젯 조합으로 복잡한 UI를 구성한다.
  • 실무 기준: 대부분의 경우 StatelessWidget을 사용하고, 상태가 필요할 때만 StatefulWidget을 선택한다.
  • 한계: 모든 것이 Widget이라는 개념이 처음에는 직관적이지 않을 수 있다.

문서가 설명하는 범위#

  • Widget이 무엇이고 왜 모든 것이 Widget인지
  • StatelessWidget과 StatefulWidget의 차이와 사용 기준
  • 기본 위젯들의 역할과 사용법

읽는 시간: 15분 | 난이도: 초급


참고 자료#


문제 상황#

전통적인 UI 프레임워크에서는 버튼, 텍스트, 레이아웃, 애니메이션 등을 각각 다른 방식으로 다룹니다.
HTML/CSS에서는 요소와 스타일이 분리되고, 네이티브 앱에서는 뷰와 레이아웃 매니저가 별도로 존재합니다.
이게 왜 문제일까요?

기존 방식의 한계#

웹: HTML(구조) + CSS(스타일) + JavaScript(동작)
Android: View + LayoutManager + Adapter
iOS: UIView + AutoLayout + Delegate

문제는 다음과 같습니다.

  • UI 요소마다 다른 API와 패턴을 익혀야 한다.
  • 구조, 스타일, 동작이 분리되어 코드가 흩어진다.
  • 재사용 가능한 컴포넌트를 만들기 어렵다.

해결 방법#

Flutter는 “모든 것이 Widget”이라는 단순한 원칙으로 UI를 구성합니다.
텍스트, 버튼, 패딩, 정렬, 애니메이션 모두 Widget입니다.
하나의 개념으로 모든 UI를 표현하는 거죠.

챕터 1: Widget이란 무엇인가#

Why#

NOTE

UI 요소마다 다른 클래스와 패턴을 사용하면 어떻게 될까요?
배워야 할 것이 너무 많아집니다.
그래서 Flutter는 “모든 것을 Widget 하나로 표현”합니다.

// 전통적 방식: 요소마다 다른 클래스
TextView textView = new TextView();
LinearLayout layout = new LinearLayout();
Animation animation = new Animation();

What#

NOTE

Widget은 Flutter에서 UI를 표현하는 기본 단위입니다.
화면에 보이는 모든 것이 Widget입니다.
심지어 보이지 않는 레이아웃과 패딩도 Widget이에요.

How#

TIP

Widget은 UI의 설계도라고 생각하면 됩니다.
한번 만든 Widget은 변경할 수 없습니다.
상태가 바뀌면 새로운 Widget을 만들어야 합니다.

// 모든 것이 Widget
Text('Hello') // 텍스트도 Widget
Icon(Icons.star) // 아이콘도 Widget
Padding(...) // 패딩도 Widget
Center(...) // 정렬도 Widget
ElevatedButton(...) // 버튼도 Widget

Widget은 계층 구조를 이룹니다.

graph TD A[MaterialApp] --> B[Scaffold] B --> C[AppBar] B --> D[body: Center] C --> E[Text: 제목] D --> F[Column] F --> G[Text: 내용] F --> H[ElevatedButton]

Watch out#

WARNING

Widget은 한번 만들면 변경할 수 없습니다.
그래서 UI를 바꾸고 싶으면 새로운 Widget을 만들어야 합니다.
이게 Flutter의 핵심 개념입니다.

// 잘못된 방식: Widget 속성 직접 변경 시도
// text.value = '새 텍스트'; // 불가능
// 올바른 방식: 새 Widget 생성
Text('새 텍스트')

결론: Flutter에서 모든 UI 요소는 Widget이며, 불변 객체로 UI를 선언합니다.


챕터 2: StatelessWidget 이해하기#

Why#

NOTE

대부분의 UI는 한 번 그려지면 변경되지 않습니다.
예를 들어 앱 이름이나 아이콘은 바뀌지 않죠.
이런 경우에는 상태 관리가 필요 없습니다.

정적 텍스트, 아이콘, 레이아웃 → 상태 불필요
카운터, 입력 필드, 체크박스 → 상태 필요

What#

NOTE

StatelessWidget은 상태가 없는 Widget입니다.
생성자로 받은 값만으로 UI가 결정됩니다.
시간이 지나도 스스로 변하지 않아요.

How#

TIP

StatelessWidget은 build 메서드만 구현하면 됩니다.

class Greeting extends StatelessWidget {
final String name;
const Greeting({super.key, required this.name});
@override
Widget build(BuildContext context) {
return Text('안녕하세요, $name님!');
}
}
// 사용
Greeting(name: 'Flutter')

build 메서드가 호출되는 시점은 다음과 같습니다.

시점설명
최초 렌더링Widget이 화면에 처음 그려질 때
부모 재빌드부모 Widget이 재빌드될 때
의존성 변경InheritedWidget 등 의존성이 바뀔 때

Watch out#

WARNING

build 메서드는 자주 호출될 수 있으므로 부작용(side effect)이 없어야 합니다.

@override
Widget build(BuildContext context) {
// 잘못된 예: build에서 네트워크 호출
// fetchData(); // 매번 호출됨!
// 올바른 예: 순수하게 Widget만 반환
return Text('Hello');
}

결론: 변경되지 않는 UI는 StatelessWidget으로 간단하게 구현합니다.


챕터 3: StatefulWidget 이해하기#

Why#

NOTE

카운터, 체크박스, 텍스트 입력 필드를 생각해보세요.
사용자가 뭔가를 하면 UI가 바뀌어야 합니다.
이런 경우에는 상태를 저장하고 관리해야 합니다.

// 버튼을 누르면 숫자가 증가해야 함
int count = 0; // 이 상태를 어디에 저장?

What#

NOTE

StatefulWidget은 상태를 가지는 Widget입니다.
Widget과 State가 따로 분리되어 있습니다.
Widget이 다시 만들어져도 State는 그대로 유지됩니다.

How#

TIP

StatefulWidget은 두 개의 클래스로 구성됩니다.

// 1. StatefulWidget 클래스 (불변)
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
// 2. State 클래스 (상태 보관)
class _CounterState extends State<Counter> {
int _count = 0; // 상태 변수
void _increment() {
setState(() {
_count++; // 상태 변경
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('카운트: $_count'),
ElevatedButton(
onPressed: _increment,
child: Text('증가'),
),
],
);
}
}

setState()4의 역할은 다음과 같습니다.

sequenceDiagram participant User as 사용자 participant Button as 버튼 participant State as State participant Framework as Flutter User->>Button: 클릭 Button->>State: _increment() State->>State: setState(() { _count++ }) State->>Framework: 재빌드 요청 Framework->>State: build() 호출 State->>Framework: 새 Widget 반환

Watch out#

WARNING

setState() 없이 상태를 변경하면 UI가 업데이트되지 않습니다.

void _increment() {
// 잘못된 예: setState 없이 변경
_count++; // UI 업데이트 안 됨!
// 올바른 예: setState로 감싸기
setState(() {
_count++;
});
}

결론: 변경되는 상태가 필요하면 StatefulWidget을 사용하고, 반드시 setState()로 변경을 알립니다.


챕터 4: Widget 선택 기준#

Why#

NOTE

StatelessWidget과 StatefulWidget 중 뭘 선택해야 할까요?
명확한 기준이 필요합니다.
잘못 선택하면 코드가 복잡해지거나 버그가 생길 수 있습니다.

What#

NOTE

선택 기준은 간단합니다. “시간이 지나면서 변하는 데이터가 있는가?”입니다.

How#

TIP
상황선택예시
고정된 텍스트, 아이콘StatelessWidget라벨, 로고, 헤더
사용자 입력 반응StatefulWidget버튼 클릭 카운터, 폼
애니메이션StatefulWidget로딩 스피너, 전환 효과
외부 데이터 표시만StatelessWidgetAPI 데이터 표시 (상태는 부모에서)
내부 상태 관리StatefulWidget탭 선택, 확장/축소

결정 흐름도

flowchart TD A[Widget 생성] --> B{내부에서 변하는 데이터가 있는가?} B -->|없음| C[StatelessWidget] B -->|있음| D{상태를 이 Widget이 관리해야 하는가?} D -->|예| E[StatefulWidget] D -->|아니오| F[StatelessWidget + 부모에서 상태 전달]

Watch out#

WARNING

무조건 StatefulWidget을 사용하면 안 됩니다.
성능과 유지보수에 불리합니다.
가능하면 StatelessWidget을 사용하세요.

// 권장: 상태를 부모에서 관리
class Parent extends StatefulWidget { ... }
class Child extends StatelessWidget {
final int count; // 부모에서 전달받음
const Child({required this.count});
...
}

결론: 기본적으로 StatelessWidget을 선택하고, 내부 상태가 필요할 때만 StatefulWidget을 사용합니다.


챕터 5: 기본 위젯 살펴보기#

Why#

NOTE

Flutter에는 수백 개의 Widget이 있습니다.
하지만 자주 사용하는 기본 Widget만 알면 됩니다.
이것들을 조합하면 대부분의 UI를 만들 수 있습니다.

What#

NOTE

기본 Widget은 구조, 레이아웃, 표시, 입력의 네 가지 역할로 분류할 수 있습니다.

How#

TIP

구조 Widget

Widget용도주요 속성
Scaffold5Material Design 기본 구조appBar, body, drawer
AppBar상단 앱 바title, actions, leading
Container6다목적 컨테이너padding, margin, color, decoration
Scaffold(
appBar: AppBar(title: Text('제목')),
body: Container(
padding: EdgeInsets.all(16),
color: Colors.white,
child: Text('내용'),
),
)

레이아웃 Widget

Widget용도주요 속성
Row7가로 배치mainAxisAlignment, children
Column8세로 배치crossAxisAlignment, children
Center중앙 정렬child
Padding여백 추가padding, child
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(Icons.star),
Icon(Icons.star),
Icon(Icons.star),
],
),
Padding(
padding: EdgeInsets.all(8),
child: Text('별점'),
),
],
)

표시 Widget

Widget용도주요 속성
Text텍스트 표시style, textAlign, maxLines
Icon아이콘 표시size, color
Image이미지 표시fit, width, height
Column(
children: [
Image.network('https://example.com/image.png'),
Text(
'제목',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
Icon(Icons.favorite, color: Colors.red, size: 32),
],
)

입력 Widget

Widget용도주요 속성
ElevatedButton돌출 버튼onPressed, child
TextButton텍스트 버튼onPressed, child
IconButton아이콘 버튼onPressed, icon
TextField텍스트 입력controller, decoration
Column(
children: [
ElevatedButton(
onPressed: () { print('클릭!'); },
child: Text('확인'),
),
TextField(
decoration: InputDecoration(labelText: '이름'),
),
],
)

Watch out#

WARNING

Widget 카탈로그에는 수백 개의 Widget이 있습니다.
처음부터 다 외우려고 하지 마세요.
필요할 때 공식 문서를 찾아보면 됩니다.

자주 쓰는 Widget → 먼저 익히기
특수한 Widget → 필요할 때 찾아보기

결론: Scaffold, Container, Row, Column, Text 등 기본 Widget을 먼저 익히고, 나머지는 필요할 때 학습합니다.


챕터 6: Widget 카탈로그 활용하기#

Why#

NOTE

Flutter는 공식적으로 수백 개의 Widget을 제공합니다.
어떤 Widget이 있는지 알아야 직접 만들 필요가 없습니다.
카탈로그를 잘 활용하면 개발 시간을 크게 줄일 수 있습니다.

What#

NOTE

Widget 카탈로그는 용도별로 분류된 공식 Widget 목록입니다.
Material Design(Android)과 Cupertino(iOS) 스타일로 나뉘어 있습니다.
각각의 기능별로 세분화되어 있어서 찾기 쉽습니다.

How#

TIP

디자인 시스템별 분류

시스템설명예시
MaterialGoogle Material Design 3AppBar, FloatingActionButton
CupertinoApple iOS 스타일CupertinoButton, CupertinoNavigationBar

기능별 분류

카테고리설명주요 Widget
Basics필수 기본 WidgetContainer, Row, Column, Text
Layout배치와 정렬Stack, GridView, ListView
Input사용자 입력TextField, Checkbox, Slider
Scrolling스크롤 기능ListView, SingleChildScrollView
Animation애니메이션AnimatedContainer, Hero
Styling테마와 스타일Theme, MediaQuery

Widget 찾는 방법

  1. 공식 문서의 Widget catalog 검색
  2. Widget of the Week 영상 시리즈 시청
  3. Flutter DevTools의 Widget Inspector 활용

Watch out#

WARNING

같은 기능을 하는 Widget이 여러 개 있을 수 있습니다.
Android용과 iOS용이 따로 있기 때문입니다.
플랫폼에 맞는 Widget을 선택해야 일관된 UX를 제공할 수 있습니다.

// Android 스타일
ElevatedButton(onPressed: () {}, child: Text('확인'))
// iOS 스타일
CupertinoButton(onPressed: () {}, child: Text('확인'))

결론: Widget 카탈로그를 활용해서 필요한 Widget을 찾고, 플랫폼에 맞게 선택합니다.


한계#

Widget 개념은 강력하지만 몇 가지 주의점이 있습니다.
처음에는 낯설 수 있지만 익숙해지면 편해집니다.

  • 학습 곡선: “모든 것이 Widget”이라는 개념이 처음에는 낯설 수 있습니다.
  • 깊은 중첩: Widget을 조합하다 보면 들여쓰기가 깊어질 수 있습니다.
  • 상태 관리 복잡성: 앱이 커지면 StatefulWidget만으로는 상태 관리가 어려워집니다.

Footnotes#

  1. Widget(위젯): Flutter에서 UI를 구성하는 기본 단위다. 모든 것이 Widget이며, 불변 객체로 UI를 선언한다.

  2. StatelessWidget(스테이트리스 위젯): 상태를 가지지 않는 Widget이다. 입력만으로 UI가 결정된다.

  3. StatefulWidget(스테이트풀 위젯): 변경 가능한 상태를 가지는 Widget이다. Widget과 State 클래스로 구성된다.

  4. setState(): StatefulWidget에서 상태 변경을 Flutter에 알리는 메서드다. 호출하면 build()가 다시 실행된다.

  5. Scaffold(스캐폴드): Material Design의 기본 레이아웃 구조를 제공하는 Widget이다. AppBar, body, drawer 등을 포함한다.

  6. Container(컨테이너): 패딩, 마진, 배경색, 테두리 등 다양한 스타일을 적용할 수 있는 다목적 Widget이다.

  7. Row(로우): 자식 Widget들을 가로 방향으로 배치하는 레이아웃 Widget이다.

  8. Column(컬럼): 자식 Widget들을 세로 방향으로 배치하는 레이아웃 Widget이다.

공유

이 글이 도움이 되었다면 다른 사람과 공유해주세요!

Flutter 튜토리얼 4편: Widget 개념과 기본 위젯
https://moodturnpost.net/posts/flutter/flutter-widget-basics/
작성자
Moodturn
게시일
2026-01-08
Moodturn

목차