일시적 상태(ephemeral state)와 앱 상태(app state)의 차이점을 설명해주세요.
질문
Flutter에서 일시적 상태(ephemeral state)와 앱 상태(app state)의 차이점을 설명해주세요.
답변
Flutter에서 상태는 크게 일시적 상태(ephemeral state)와 앱 상태(app state)로 나눌 수 있습니다. 이 두 유형의 상태는 관리 방식과 사용 목적에서 중요한 차이가 있습니다.
일시적 상태(Ephemeral State)
일시적 상태는 다음과 같은 특징이 있습니다:
범위: 단일 위젯이나 작은 위젯 그룹 내에서만 관련이 있는 로컬 상태입니다.
생명주기: 위젯이 화면에 존재하는 동안만 필요한 임시적인 상태입니다.
관리 방법: 주로
StatefulWidget
의setState()
메서드를 사용하여 관리합니다.예시:
- 페이지 스크롤 위치
- 애니메이션 진행 상태
- 폼 필드의 현재 값
- 체크박스, 라디오 버튼의 선택 상태
- 탭 컨트롤러의 현재 탭 인덱스
복잡성: 비교적 단순하고 관리가 쉽습니다.
일시적 상태 예시 코드
class ExpandableCard extends StatefulWidget {
@override
_ExpandableCardState createState() => _ExpandableCardState();
}
class _ExpandableCardState extends State<ExpandableCard> {
// 일시적 상태: 카드가 확장되었는지 여부
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
// 상태 업데이트
setState(() {
_isExpanded = !_isExpanded;
});
},
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: _isExpanded ? 200 : 100,
color: Colors.blue,
child: Center(
child: Text(
_isExpanded ? '접기' : '펼치기',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}
앱 상태(App State)
앱 상태는 다음과 같은 특징이 있습니다:
범위: 앱의 여러 부분에서 공유되는 데이터로, 앱 전체 또는 여러 위젯에 영향을 미칩니다.
생명주기: 일반적으로 앱 세션 전체 또는 여러 화면에 걸쳐 유지되어야 하는 상태입니다.
관리 방법:
Provider
,Riverpod
,BLoC
,Redux
등과 같은 상태 관리 솔루션을 사용합니다.예시:
- 사용자 로그인 정보
- 장바구니 내용
- 앱 설정 및 환경 설정
- 네트워크에서 가져온 데이터
- 여러 화면에서 필요한 상품 목록
복잡성: 일반적으로 더 복잡하며, 구조화된 관리 방법이 필요합니다.
앱 상태 예시 코드 (Provider 사용)
// 상태 모델
class CartModel extends ChangeNotifier {
final List<Product> _items = [];
List<Product> get items => _items;
int get totalItems => _items.length;
void addProduct(Product product) {
_items.add(product);
notifyListeners();
}
void removeProduct(Product product) {
_items.remove(product);
notifyListeners();
}
}
// 메인 앱
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CartModel(),
child: MyApp(),
),
);
}
// 제품 목록 화면
class ProductListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('제품 목록'),
actions: [
// 장바구니 아이콘과 아이템 개수 표시
Consumer<CartModel>(
builder: (context, cart, child) {
return Badge(
label: Text('${cart.totalItems}'),
child: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () {
Navigator.pushNamed(context, '/cart');
},
),
);
},
),
],
),
body: ListView.builder(
itemBuilder: (context, index) {
return ProductTile(product: availableProducts[index]);
},
itemCount: availableProducts.length,
),
);
}
}
// 제품 타일 위젯
class ProductTile extends StatelessWidget {
final Product product;
ProductTile({required this.product});
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(product.name),
subtitle: Text('${product.price}원'),
trailing: IconButton(
icon: Icon(Icons.add_shopping_cart),
onPressed: () {
// 앱 상태 업데이트
context.read<CartModel>().addProduct(product);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('${product.name} 장바구니에 추가됨')),
);
},
),
);
}
}
두 상태 유형의 주요 차이점
특성 | 일시적 상태 (Ephemeral State) | 앱 상태 (App State) |
---|---|---|
범위 | 단일 위젯 또는 작은 위젯 그룹 | 앱 전체 또는 여러 위젯 |
지속성 | 위젯 생명주기 동안만 유지 | 앱 세션 또는 영구 저장 가능 |
관리 방법 | setState() | Provider, BLoC, Redux 등 |
복잡성 | 단순, 적은 코드 | 복잡, 구조화된 코드 필요 |
데이터 흐름 | 지역적, 상향식 | 전역적, 하향식 |
디버깅 | 간단 | 더 복잡하지만 구조화됨 |
테스트 | 위젯 테스트만으로 충분 | 단위 테스트, 통합 테스트 필요 |
선택 기준
어떤 유형의 상태를 사용할지 결정할 때 고려할 사항:
데이터의 범위: 데이터가 단일 위젯이나 화면에만 필요하다면 일시적 상태, 여러 위젯이나 화면에서 필요하다면 앱 상태로 관리합니다.
데이터의 지속성: 화면이 재구성되거나 다른 화면으로 이동할 때 데이터가 유지되어야 한다면 앱 상태로 관리합니다.
데이터의 복잡성: 단순한 플래그나 값은 일시적 상태로, 복잡한 객체나 컬렉션은 앱 상태로 관리하는 것이 효율적입니다.
실제 앱에서의 상태 분리 예시
실제 애플리케이션에서는 두 가지 상태 유형을 함께 사용하는 것이 일반적입니다:
class ProductDetailScreen extends StatefulWidget {
final int productId;
ProductDetailScreen({required this.productId});
@override
_ProductDetailScreenState createState() => _ProductDetailScreenState();
}
class _ProductDetailScreenState extends State<ProductDetailScreen> {
// 일시적 상태: 현재 선택된 이미지 인덱스
int _selectedImageIndex = 0;
@override
Widget build(BuildContext context) {
// 앱 상태: 제품 데이터와 장바구니
final productRepository = context.watch<ProductRepository>();
final cartModel = context.read<CartModel>();
// 앱 상태에서 제품 정보 가져오기
final product = productRepository.getProductById(widget.productId);
return Scaffold(
appBar: AppBar(title: Text(product.name)),
body: Column(
children: [
// 이미지 슬라이더 (일시적 상태 사용)
ImageSlider(
images: product.images,
selectedIndex: _selectedImageIndex,
onIndexChanged: (index) {
setState(() {
_selectedImageIndex = index;
});
},
),
// 제품 정보 표시 (앱 상태 사용)
ProductInfo(product: product),
// 장바구니 추가 버튼 (앱 상태 업데이트)
ElevatedButton(
onPressed: () => cartModel.addProduct(product),
child: Text('장바구니에 추가'),
),
],
),
);
}
}
결론
적절한 상태 관리 방식을 선택하는 것은 앱의 구조와 확장성에 큰 영향을 미치므로, 앱의 요구사항을 고려하여 신중하게 결정해야 합니다. 대부분의 실제 애플리케이션은 두 가지 상태 유형을 모두 사용하며, 각 상태의 특성과 요구사항에 맞는 접근 방식을 선택하는 것이 중요합니다.