플러터에서 위젯은 크게 두가지 종류 StatlessWidget 과 StatefulWidget이 있다. 많은 블로그에서는 단순히 두 차이에 대해서만 기술해놓고 있을 뿐 어느상황에서 어떤 위젯을 선택해야하는지는 다루는 글이 없는 것 같아 적어보았다.
“상태가 변화하는 위젯이라면 무조건 StatefulWidget을 써야 하는 것 아닌가?"
사실, Flutter의 페인팅 시스템과 Diffing(Reconciliation) 메커니즘 덕분에, 외부에서 상태가 주입되는 경우(예: Provider, Bloc 등)에도 StatelessWidget만으로 충분히 UI를 업데이트할 수 있다. 즉, 위젯 자체는 불변성을 유지하며, 부모나 글로벌 상태 관리에서 전달받은 값이 변경되면, 전체 트리의 일부만 효율적으로 다시 그려지게 된다.
“모든 위젯을 StatelessWidget으로 구현하면 될 텐데, 왜 굳이 StatefulWidget을 사용해야 할까?"
상태가 변화하는 위젯을 StatelessWidget으로 구현하려면, 상태 관리를 외부로 끌어내야 한다. 이는 UI 로직과 상태 관리 로직이 분리되어, 재사용성과 테스트 용이성은 높아지지만, 복잡한 사용자 상호작용이나 애니메이션, 입력 폼 등 국소 상태를 직접 관리해야 하는 경우에는 불편하고 코드 복잡도가 증가한다. StatefulWidget은 이러한 경우에 내부 State 객체를 통해 로컬 상태를 직접 관리할 수 있게 해주며, 부모 위젯이 재빌드되더라도 상태가 보존된다는 장점이 있다.
내가 생각하는 선택기준
StatelessWidget은 외부에서 주입된 데이터만으로 UI를 구성하며, 선언적 방식의 최적화(Liner Building, Reconciliation)를 통해 효율적인 업데이트가 가능하다. StatefulWidget은 내부 상태를 직접 관리할 필요가 있는 경우에 사용되어, 사용자 상호작용, 애니메이션, 입력 등의 동적 변화에 따른 UI 갱신과 상태 보존을 책임진다. 이를 기반으로 체크리스트를 만들어보자면
앱 내부 상태가 필요할 때
예시상황: 사용자 상호작용(클릭, 드래그), 애니메이션, 타이머 등
-> StatefulWidget
외부에서 전달받은 데이터만으로 상태가 변할때
예시상황: 부모나 글로벌 상태 관리(Provider, Bloc 등)에서 데이터를 공급받음
-> StatelessWidget
국소적 상태(해당 위젯 내에서만 변경 및 유지)가 필요할 때
-> StatefulWidget
전역 혹은 상위 컴포넌트에서 관리하는 상태를 단순히 보여주는 역할인가?
-> StatelessWidget
해당 위젯만 독립적으로 리빌드되어야 하는가?
리스트 아이템, 독립적 입력폼
-> StatefulWidget (국소적으로 상태를 관리)
부모 위젯의 리빌드에 의해 자동 갱신되어도 문제가 없는가?
-> StatelessWidget
위젯의 역할이 단순 UI 표현에 집중되어 있는가?
-> StatelessWidget
UI와 상태 관리 로직이 함께 있어야 직관적인가?
폼, 탭 전환, 체크박스 등
-> StatefulWidget
다양한 컨텍스트에서 재사용해야 하는가?
-> StatelessWidget (외부 데이터를 주입받아 동작하므로 테스트와 재사용이 용이)
특정 시나리오나 환경에 묶여있는 로직이 있는가?
-> StatefulWidget
이를 정리하자면 StatefulWidget 은 위젯 내부에서 직접 관리해야 할 동적 상태가 존재할 때, 사용자와의 Interaction, 애니메이션, 국소 상태 관리 상황에 선택하고 StatelessWidget 은 모든 상태가 외부에서 주입되며, 위젯은 단순 UI 표현에 집중할 때, 재사용성과 테스트 용이성이 중요한 경우에 사용한다.
'Flutter' 카테고리의 다른 글
Flutter에서 Stomp로 소켓통신하기 (1) | 2025.02.05 |
---|---|
어떻게 플러터는 변경된 위젯만 콕찝어서 다시 페인팅할 수 있는걸까? (0) | 2025.02.02 |
Package 버전관리 전략 (0) | 2025.01.27 |
Expanded 과 Flexible 의 차이 (0) | 2025.01.25 |
Segment 버튼에 애니메이션 적용하기 (0) | 2025.01.25 |