Flutter를 처음 접하면 가장 먼저 마주치는 개념이 바로 위젯이에요. Flutter는 모든 것이 위젯이라고 할 만큼 위젯 중심으로 앱을 만들게 되어있어요. 버튼, 텍스트, 이미지뿐만 아니라 화면 전체도 위젯으로 이루어져 있답니다.
처음에는 “위젯이 뭘까?” 하고 막막했는데, 직접 사용해보니 위젯을 이해하면 앱 개발이 훨씬 재미있어지더라고요. 이번 글에서는 Flutter의 위젯 시스템을 제대로 이해할 수 있도록 StatelessWidget과 StatefulWidget의 차이, 그리고 커스텀 위젯 만드는 방법까지 정리해봤어요.
📌 위젯이란 무엇인가요?
- Flutter에서 앱 화면을 구성하는 모든 요소가 위젯이에요.
- 버튼, 이미지, 리스트, 심지어 앱바, 전체 화면까지 전부 위젯으로 만들어져 있어요.
- 위젯은 계층 구조로 이루어져서 **위젯 트리(Widget Tree)**라고 불러요.
예를 들어, 화면에 버튼 하나를 띄우면 버튼 위젯이 생기고, 그 버튼은 부모 위젯인 Column이나 Scaffold 같은 위젯 안에 들어가 있어요. 이렇게 부모-자식 관계로 이어져서 앱 화면이 만들어진답니다.
✨ StatelessWidget vs StatefulWidget
Flutter를 하다 보면 가장 많이 듣는 질문 중 하나가 “StatelessWidget과 StatefulWidget의 차이가 뭐예요?”인데요. 정말 중요한 부분이라서 꼼꼼하게 정리해봤어요.
상태(state)란 무엇일까요?
앱을 개발할 때 화면이 바뀌는 부분이 있잖아요?
예를 들어:
- 버튼을 눌렀더니 숫자가 올라간다거나
- 체크박스를 눌러서 체크 표시가 바뀐다거나
- 사용자가 입력한 텍스트가 실시간으로 화면에 반영된다거나
이렇게 사용자의 상호작용이나 데이터 변경에 따라 바뀌는 정보들을 바로 **상태(state)**라고 불러요.
Flutter에서는 이 상태가 변하면 UI를 새로 그려줘야 해서, 상태 관리가 아주 중요하답니다.
🗂️ StatelessWidget — 상태가 없는 고정된 화면
StatelessWidget은 이름처럼 상태가 없는 위젯이에요.
앱 실행 중에 값이 바뀌지 않고 고정된 UI를 보여줄 때 사용해요.
- 앱 타이틀이나 로고처럼 화면이 변하지 않는 부분
- 단순히 텍스트, 이미지 등 고정된 정보만 보여주는 부분
특징
- 상태가 없기 때문에 코드가 간단해요.
- UI가 변하지 않아서 개발자가 상태 관리를 따로 해줄 필요가 없어요.
예시 코드
class MyTitleWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
'Welcome to My App!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
);
}
}
앱의 제목, 회사 로고, 안내 문구 등을 보여줄 때 주로 사용했어요.
이렇게 고정된 UI는 StatelessWidget으로 만들면 코드도 깔끔하고 유지보수도 쉽답니다.
🛠️ StatefulWidget — 상태가 바뀌는 동적인 화면
StatefulWidget은 앱을 실행하는 동안 값이 바뀌는 UI를 만들 때 사용해요.
예를 들어 버튼을 눌렀을 때 숫자가 증가하거나, 서버에서 데이터를 받아와서 리스트가 바뀌는 등의 경우예요.
- 상태(state)를 State 클래스에서 관리해요.
- 상태가 변할 때마다 setState()를 호출해서 Flutter에게 “상태가 바뀌었어요!”라고 알려주면 Flutter가 UI를 다시 그려줘요.
특징
- 동적인 UI를 만들 수 있어요.
- 사용자와의 상호작용에 따라 화면이 바뀌도록 코드를 작성할 수 있어요.
예시 코드
class MyCounterWidget extends StatefulWidget {
@override
_MyCounterWidgetState createState() => _MyCounterWidgetState();
}
class _MyCounterWidgetState extends State<MyCounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('$_counter'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('증가'),
),
],
);
}
}
위 코드는 버튼을 누를 때마다 _counter 값이 증가하고, setState()를 호출하면
Flutter가 build()를 다시 실행해서 숫자가 화면에 업데이트돼요.

StatelessWidget과의 가장 큰 차이는 상태(멤버 변수)를 가질 수 있는 State<T> generic 클래스가 있다는 점이에요.
💡 기본 Widget 간단 비교
항목 | StatelessWidget | StatefulWidget |
상태(state) | 없음 | 있음 (State 클래스에서 관리) |
UI 변경 | 앱 실행 중 변경 없음 | 앱 실행 중 변경 가능 |
코드 구조 | 간단함 | State 클래스와 분리 |
사용 예시 | 앱 타이틀, 로고, 안내 문구 | 카운터, 체크박스, 입력폼 |
StatelessWidget과 StatefulWidget을 구분하는 가장 중요한 기준은 화면이 바뀌느냐, 바뀌지 않느냐예요.
아주 쉽게 구분하자면, 화면을 그리기 위한 변수가 필요한가? 로 나눌 수 있어요.
1개라도 "상태" 로 사용될 변수가 필요하다면 StatefulWidget을 사용하면 돼요.
- 화면이 고정돼서 상태가 필요 없는 경우 → StatelessWidget
- 사용자와 상호작용하거나 데이터가 바뀌어서 UI가 변해야 하는 경우 → StatefulWidget
Flutter 앱을 만들면서 StatelessWidget부터 차근차근 시작해보고,
필요할 때 StatefulWidget으로 확장해보시면 좋아요.
앱의 복잡도가 올라갈수록 상태 관리가 정말 중요하다는 걸 느끼게 될 거예요! 😊
🛠️ 커스텀 위젯 만들기
Flutter의 강점 중 하나는 커스텀 위젯을 만들어서 재사용할 수 있다는 점이에요. 앱을 만들다 보면 비슷한 디자인이나 버튼 등을 여러 번 써야 할 때가 많거든요. 이럴 때 커스텀 위젯으로 만들어두면 유지보수도 쉽고 코드도 훨씬 깔끔해져요.
- 커스텀 위젯을 사용하면 같은 UI를 반복해서 작성하지 않아도 돼요.
- 디자인 변경이 필요할 때 한 군데만 수정하면 전체 앱에 적용돼서 정말 편리했어요.
예를 들어 앱에서 공통으로 쓰는 버튼을 만들고 싶다면 이렇게 만들어 볼 수 있어요.
class MyCustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
MyCustomButton({required this.text, required this.onPressed});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(text),
);
}
}
위처럼 커스텀 위젯으로 만들어두면 버튼을 반복해서 쓸 수 있고, 스타일이나 기능을 바꿔야 할 때도 코드 한 군데만 고치면 돼서 정말 효율적이었어요.
💡 마무리하며
Flutter 앱 개발을 하면서 StatelessWidget과 StatefulWidget을 잘 이해하는 건 정말 중요하다고 느꼈어요.
처음에는 StatelessWidget으로 간단한 UI부터 만들어보고,
앱이 점점 복잡해지면 StatefulWidget으로 상태를 관리하면서 동적인 UI도 만들어보면 좋을 것 같아요.
그리고 커스텀 위젯을 적절히 활용하면 코드도 깔끔해지고, 유지보수도 쉬워져서 더 좋은 앱을 만들 수 있었어요.
Flutter의 위젯 시스템을 이해하고 나면 앱 개발이 한층 더 재미있어질 거예요!
앱 개발하면서 StatelessWidget과 StatefulWidget을 직접 만들어보고,
커스텀 위젯도 만들어보면서 Flutter의 매력을 한껏 느껴보시길 추천드려요.
혹시 더 궁금한 게 있다면 언제든 물어봐 주세요! 😊
'Flutter 개발' 카테고리의 다른 글
플러터 화면 이동 완전 정복 – push부터 pushAndRemoveUntil까지 Navigator 함수 총정리! (0) | 2025.06.30 |
---|---|
플러터 pubspec.yaml 완전 기초 설명 (0) | 2025.06.30 |
Flutter의 Dart 언어, 왜 사용할까? JavaScript, Java, Swift와 비교 (2) | 2025.06.08 |
Riverpod 심화편2: Firestore 데이터 연동하기 – get과 stream 연결 쉽게 관리하기 (전체 코드 예제 포함) (0) | 2025.06.01 |
Riverpod 심화편: FutureProvider, StreamProvider, NotifierProvider 완벽 가이드 (0) | 2025.05.25 |