📌 Day 11: 상태 관리 개념 및 setState() 이해

2025. 3. 4. 13:38같이 공부합시다 - Flutter/Dart & Flutter 기초부터 실전까지

728x90

📃 개요

⚠️ Flutter에서 상태(State)를 관리하는 방법을 모르고 개발하면, 앱의 UI가 예상대로 동작하지 않을 수 있습니다.
⚠️ 잘못된 상태 관리는 코드 복잡도를 증가시키고, 유지보수를 어렵게 만듭니다.
📌 Flutter 개발에서 "상태(State)"를 관리하는 것은 필수적인 개념입니다.
📌 Flutter에서 가장 기본적인 상태 관리 방법은 setState()를 활용하는 것입니다.
 
상태(State)란?UI에 영향을 주는 데이터
setState()란?상태가 변경될 때 화면을 다시 그려주는 역할
setState()를 사용하면 StatefulWidget에서 UI를 업데이트할 수 있습니다.
상태 관리는 앱의 동적인 데이터(예: 버튼 클릭, API 호출 결과)를 UI에 반영하는 과정입니다.
Flutter의 다양한 상태 관리 기법(Provider, Riverpod, Bloc 등)의 기초가 setState()입니다.
 
 
 
 
 


🔔 주제

🔸 Flutter에서 상태(State)란?
🔸 setState()를 사용하여 UI 업데이트하기
🔸 StatelessWidget vs StatefulWidget 차이
🔸 StatefulWidget에서 상태를 변경하는 방법
🔸 올바른 상태 관리 패턴과 주의할 점
 
 
 
 
 


1️⃣ Flutter에서 상태(State)란?

🔸 Flutter 앱의 UI는 상태(State)에 따라 변합니다.
🔸 Flutter에서 상태는 "변경 가능한 데이터"이며, 이 데이터를 효과적으로 관리하는 것이 중요합니다.
🔸 상태(State)는 사용자의 입력, API 데이터, 애니메이션 등으로 인해 변경되는 값을 의미합니다.
🔸 예를 들어, 사용자가 버튼을 눌러 숫자를 증가시키면, 증가된 숫자가 UI에 반영되어야 합니다.
 
 
 
 
 


2️⃣ StatelessWidget vs StatefulWidget 차이

📌 Flutter에는 두 가지 기본 위젯이 있습니다.

위젯유형특징 예제
StatelessWidget상태가 없는 정적인 UI텍스트, 아이콘, 버튼
StatefulWidget상태가 변경되는 UI카운터, 입력 필드, 체크박스

 
 
📌 StatelessWidget 예제

class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("StatelessWidget 예제")),
      body: Center(child: Text("나는 변경되지 않는 UI입니다.")),
    );
  }
}

StatelessWidget은 한 번 생성되면 변경되지 않는 UI를 정의할 때 사용
 
 
📌 StatefulWidget 예제

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'setState 예제',
      home: const CounterScreen(),
    );
  }
}

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

  @override
  _CounterScreenState createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int _counter = 0; // 상태 변수

  void _incrementCounter() {
    setState(() {
      _counter++; // 상태 변경 후 UI 업데이트
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("setState 예제")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text("버튼 클릭 횟수:", style: TextStyle(fontSize: 20)),
            Text("$_counter", style: const TextStyle(fontSize: 40, fontWeight: FontWeight.bold)),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _incrementCounter,
              child: const Text("숫자 증가"),
            ),
          ],
        ),
      ),
    );
  }
}

🔔 코드 설명
🔸 setState()는 StatefulWidget에서 상태가 변경될 때 UI를 업데이트하는 역할을 합니다.
🔸 setState()를 호출하면 build() 메서드가 다시 실행되면서 화면이 새로 그려집니다.
✅ setState()를 호출하면 _counter 값이 변경되고 UI가 다시 그려짐
✅ StatefulWidget을 사용하여 상태가 변경되는 화면을 구현
 
 
 
 
 


3️⃣ setState()란?

🔸 Flutter에서 상태를 변경하려면 반드시 setState()를 호출해야 합니다.
🔸 setState()는 UI를 다시 그려(rebuild) 주는 역할을 합니다.
🔸 StatefulWidget에서만 사용할 수 있으며, StatelessWidget에서는 동작하지 않습니다.
📌 setState() 기본 사용법

setState(() {
  _counter++;
});

이 코드가 실행되면 _counter 값이 증가하고, UI가 다시 렌더링됨
 
 
 
 
 


4️⃣ setState()를 사용할 때 주의해야 할 점

⚠️ setState()를 남발하면 성능이 저하될 수 있음
🔸 필요한 부분만 다시 그리도록 최소한의 setState() 사용 권장
 
⚠️ setState()는 StatefulWidget 내부에서만 사용 가능
🔸 상태 관리가 복잡해질 경우 Provider, Riverpod, Bloc 같은 상태 관리 패턴을 활용
 
 
📌 setState()를 사용할 때 주의할 점
 
1. 불필요한 setState() 호출을 피하기
 
📌 잘못된 예제 (불필요한 setState() 호출)

setState(() {
  _counter++;
  print("setState() 호출됨");
});

⚠️ setState() 안에서 print()를 실행하는 것은 불필요한 렌더링을 초래할 수 있음
 
📌 올바른 예제 (setState() 최소화)

void _increment() {
  _counter++;
  setState(() {}); // 상태 업데이트 최소화
}

🔸 setState()를 최소한으로 호출하여 성능을 유지하는 것이 중요!
 
 
2. setState() 내부에서 API 호출 금지
⚠️ setState() 안에서 비동기 작업을 직접 실행하면 앱이 멈출 수 있음

void fetchData() async {
  String data = await fetchFromApi();
  setState(() {
    result = data; // ✅ setState() 내부에서 UI 업데이트만 수행
  });
}

 
 
 
 
 
 


5️⃣ StatefulWidget , setState() 가 포함된 기능 예제

📌 할 일 관리 앱 예제

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "할 일 관리 앱",
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
      ),
      home: TodoListScreen(),
    );
  }
}

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

  @override
  TodoListScreenState createState() => TodoListScreenState();
}

class TodoListScreenState extends State<TodoListScreen> {
  final TextEditingController _controller = TextEditingController();
  final List<String> _tasks = [];

  void _addTask() {
    if (_controller.text.isNotEmpty) {
      setState(() {
        _tasks.add(_controller.text);
        _controller.clear();
      });
    }
  }

  void _removeTask(int index) {
    setState(() {
      _tasks.removeAt(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("할 일 목록")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      labelText: "새로운 할 일",
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 10),
                ElevatedButton(
                  onPressed: _addTask,
                  child: const Text("추가"),
                ),
              ],
            ),
            const SizedBox(height: 20),
            Expanded(
              child: _tasks.isEmpty
                  ? const Center(child: Text("할 일이 없습니다."))
                  : ListView.builder(
                      itemCount: _tasks.length,
                      itemBuilder: (context, index) {
                        return Card(
                          child: ListTile(
                            title: Text(_tasks[index]),
                            trailing: IconButton(
                              icon: Icon(Icons.delete, color: Colors.red),
                              onPressed: () => _removeTask(index),
                            ),
                          ),
                        );
                      },
                    ),
            ),
          ],
        ),
      ),
    );
  }
}

 
🔻 예제 코드 테스트
 


 
 
 
 


📌 내용 요약

Flutter에서 "상태(State)"는 UI에 영향을 주는 데이터
StatelessWidget은 변경되지 않는 UI, StatefulWidget은 상태가 변하는 UI에 사용
setState()는 상태가 변경될 때 화면을 다시 렌더링하는 역할
setState()를 최소한으로 사용하여 성능을 유지하는 것이 중요
 

728x90