IMCAT.DEV
로그인 가입
Dart 고급 디자인패턴

Dart 상태 관리 패턴

Dart 상태 관리 패턴 — 고급

상태 관리 패턴

1. 도입 (왜 배워야 하는가)

상태 관리 패턴은 Flutter와 같은 UI 프레임워크에서 매우 중요한 개념입니다. 상태란 애플리케이션의 비즈니스 로직에서 데이터의 현재 상태를 의미하며, 사용자가 인터페이스와 상호작용할 때 이 상태가 어떻게 변화할지를 관리하는 것이 필수적입니다.

상태 관리를 하지 않으면, 데이터의 일관성이 깨지거나 사용자 인터페이스가 예기치 않게 동작하는 등의 문제가 발생할 수 있습니다. 예를 들어, 쇼핑몰 앱에서 장바구니에 상품을 추가했지만, UI가 업데이트되지 않으면 사용자는 상품이 제대로 추가되지 않았다고 생각할 수 있습니다. 이러한 문제는 사용자 경험을 저하시킬 수 있습니다.

2. 핵심 개념 설명

상태 관리에는 여러 가지 패턴이 있으며, 각 패턴은 특정 요구 사항에 따라 다르게 적용됩니다. 여기서는 세 가지 주요 패턴을 설명하겠습니다.

패턴설명
Provider간단하고 직관적이며, Flutter의 공식 라이브러리입니다.
BLoC비즈니스 로직을 분리하여 테스트 용이성을 높입니다.
Redux전역 상태를 관리하며, 앱의 상태를 예측 가능한 방식으로 처리합니다.

비유하자면, 상태 관리는 마치 음식점의 주방과 같습니다. 주방에서는 재료를 준비하고 요리를 하지만, 손님에게 제공되는 음식(상태)은 항상 일정해야 합니다. 주방에서 어떤 요리가 만들어지든, 손님이 보는 것은 항상 일관된 결과여야 합니다.

3. 기본 코드 예제

아래는 Provider를 이용한 상태 관리의 기본 예제입니다.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

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

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 상태가 변경되었음을 알림
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('Counter')),
          body: Center(child: CounterWidget()),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Provider.of<Counter>(context, listen: false).increment();
            },
            child: Icon(Icons.add),
          ),
        ),
      ),
    );
  }
}

class CounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final count = Provider.of<Counter>(context).count;
    return Text('Count: $count', style: TextStyle(fontSize: 24));
  }
}

실행 결과: 앱을 실행하면 화면 중앙에 "Count: 0"이라는 텍스트가 나타나고, 버튼을 클릭할 때마다 숫자가 증가합니다.

코드 한 줄씩 동작 설명:

  1. import 구문을 통해 필요한 패키지를 가져옵니다.
  2. Counter 클래스는 상태를 유지하고 변화를 알리는 기능을 합니다.
  3. MyApp 클래스는 Provider를 설정하고 기본 UI를 구성합니다.
  4. CounterWidget은 현재 카운트를 화면에 출력합니다.
  5. 플로팅 버튼을 클릭하면 increment() 메서드가 호출되어 카운트가 증가합니다.
  6. 4. 실전 활용 예제

    예제 1: 쇼핑몰에서 장바구니 구현

    장바구니에 상품을 추가하고, 총 가격을 계산하는 로직을 Provider를 사용하여 구현할 수 있습니다.

    class Cart with ChangeNotifier {
      List<String> _items = [];
    
      List<String> get items => _items;
    
      void addItem(String item) {
        _items.add(item);
        notifyListeners();
      }
    }

    장바구니에 아이템을 추가할 때마다 UI가 자동으로 업데이트됩니다.

    예제 2: BLoC 패턴 사용하기

    BLoC 패턴을 사용하여 상태와 비즈니스 로직을 분리할 수 있습니다.

    class CounterBloc {
      final _countController = StreamController<int>();
      int _count = 0;
    
      Stream<int> get count => _countController.stream;
    
      void increment() {
        _count++;
        _countController.sink.add(_count);
      }
    
      void dispose() {
        _countController.close();
      }
    }

    이 코드는 카운터의 상태를 스트림으로 관리하여, UI에서 쉽게 변경 사항을 구독할 수 있도록 합니다.

    5. 흔한 실수와 주의사항

    1. 상태를 직접 변경하는 실수

    잘못된 코드:

    Provider.of<Counter>(context).count++; // 직접 접근

    올바른 코드:

    Provider.of<Counter>(context, listen: false).increment(); // 메서드 호출

    상태를 직접 변경하면 UI가 업데이트되지 않습니다.

    2. Provider를 잘못 사용

    잘못된 코드:

    // Provider를 사용하지 않고 전역 변수로 상태를 관리
    int globalCount = 0;

    올바른 코드:

    // Provider를 통해 상태를 관리
    ChangeNotifierProvider(
      create: (context) => Counter(),
    );

    전역 변수를 사용하면 상태 관리의 장점을 누릴 수 없습니다.

    6. 정리

  7. 상태 관리는 사용자 경험에 큰 영향을 미치며, 적절한 패턴을 선택하는 것이 중요합니다.
  8. Provider, BLoC, Redux 등의 다양한 패턴을 이해하고 활용할 수 있어야 합니다.
  9. 상태를 직접 수정하지 않고, 항상 메서드를 통해 변경해야 합니다.
  10. 실용적인 예제를 통해 각 패턴의 필요성을 느끼고, 구현할 수 있어야 합니다.
  11. 다음에 학습하면 좋은 주제:

    • Flutter의 비동기 처리
    • 애니메이션과 상태 관리 통합
    • 고급 BLoC 패턴 및 테스트 방법