[flutter] 플러터 앱의 디자인 패턴 (Design Patterns) 소개

플러터는 Google에서 개발한 UI 프레임워크로, 다양한 플랫폼의 앱을 만들 수 있습니다. 플러터를 사용하여 앱을 개발할 때, 효율적이고 구조적인 방식으로 코드를 작성하기 위해 디자인 패턴을 활용하는 것이 좋습니다. 디자인 패턴은 특정 문제를 해결하기 위한 일련의 방법론이며, 개발자들이 더욱 효과적으로 코드를 구성할 수 있도록 도와줍니다.

이번 글에서는 플러터 앱 개발에서 자주 사용되는 네 가지 디자인 패턴을 소개하겠습니다.

1. MVC (Model-View-Controller)

MVC는 가장 기본적인 디자인 패턴 중 하나로, 애플리케이션을 세 가지 구성 요소로 구분합니다. 모델(Model)은 데이터와 비즈니스 로직을 처리하며, 뷰(View)는 사용자 인터페이스를 담당하고, 컨트롤러(Controller)는 모델과 뷰를 연결하여 사용자의 입력을 처리합니다. MVC 패턴은 코드를 재사용하고 유지보수하기 쉽게 만들어주며, 개발자들 사이에서 매우 인기있는 패턴입니다.

예시:

class CounterModel {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
  }

  void decrement() {
    _count--;
  }
}

class CounterController {
  CounterModel model;

  CounterController() {
    model = CounterModel();
  }

  void increment() {
    model.increment();
  }

  void decrement() {
    model.decrement();
  }
}

class CounterView extends StatelessWidget {
  final CounterController controller;

  CounterView({required this.controller});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: ${controller.model.count}'),
        ElevatedButton(
          onPressed: controller.increment,
          child: Text('Increment'),
        ),
        ElevatedButton(
          onPressed: controller.decrement,
          child: Text('Decrement'),
        ),
      ],
    );
  }
}

2. Provider

Provider는 플러터에서 상태 관리를 위한 디자인 패턴입니다. 이 패턴은 데이터의 제공자(Provider)가 이용자(Consumer)에게 필요한 데이터를 제공하는 방식으로 동작합니다. Provider는 앱의 다양한 부분에서 상태 관리를 담당하고, 각 부분에서 필요한 데이터를 제공하며, 앱의 성능과 확장성을 향상시키는 데 도움을 줍니다.

예시:

class CounterModel extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void decrement() {
    _count--;
    notifyListeners();
  }
}

class CounterView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterModel = Provider.of<CounterModel>(context);

    return Column(
      children: [
        Text('Count: ${counterModel.count}'),
        ElevatedButton(
          onPressed: counterModel.increment,
          child: Text('Increment'),
        ),
        ElevatedButton(
          onPressed: counterModel.decrement,
          child: Text('Decrement'),
        ),
      ],
    );
  }
}

3. BLoC (Business Logic Component)

BLoC는 플러터에서 대표적인 상태 관리 패턴 중 하나입니다. BLoC 패턴은 비즈니스 로직을 독립적인 컴포넌트로 추출하여 효과적으로 관리하는 방식입니다. BLoC는 데이터 스트림을 이용하여 UI와 비즈니스 로직 간의 분리를 가능하게 하며, 앱의 복잡한 상태 관리를 쉽게 구현할 수 있습니다.

예시:

class CounterEvent {}

class IncrementCounter extends CounterEvent {}

class DecrementCounter extends CounterEvent {}

class CounterBloc {
  final _countController = StreamController<int>.broadcast();
  int _count = 0;

  Stream<int> get countStream => _countController.stream;

  void dispatch(CounterEvent event) {
    if (event is IncrementCounter) {
      _count++;
    } else if (event is DecrementCounter) {
      _count--;
    }

    _countController.sink.add(_count);
  }

  void dispose() {
    _countController.close();
  }
}

class CounterView extends StatelessWidget {
  final CounterBloc bloc;

  CounterView({required this.bloc});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        StreamBuilder(
          stream: bloc.countStream,
          builder: (context, snapshot) {
            return Text('Count: ${snapshot.data ?? 0}');
          },
        ),
        ElevatedButton(
          onPressed: () => bloc.dispatch(IncrementCounter()),
          child: Text('Increment'),
        ),
        ElevatedButton(
          onPressed: () => bloc.dispatch(DecrementCounter()),
          child: Text('Decrement'),
        ),
      ],
    );
  }
}

4. MVVM (Model-View-ViewModel)

MVVM은 플러터 앱의 디자인 패턴으로, 앱의 로직을 세 가지 구성 요소로 분리합니다. 모델(Model)은 데이터와 비즈니스 로직을 처리하며, 뷰(View)는 사용자 인터페이스를 관리합니다. 그리고 뷰 모델(ViewModel)은 모델과 뷰 사이에서 데이터 바인딩 및 비즈니스 로직을 처리합니다. MVVM 패턴은 플러터 앱에서 데이터와 UI의 관계를 쉽게 관리할 수 있게 해주며, 코드의 재사용성과 테스트 용이성을 향상시킵니다.

예시:

class CounterModel {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
  }

  void decrement() {
    _count--;
  }
}

class CounterViewModel {
  final CounterModel model;

  CounterViewModel({required this.model});

  int get count => model.count;

  void increment() {
    model.increment();
  }

  void decrement() {
    model.decrement();
  }
}

class CounterView extends StatelessWidget {
  final CounterViewModel viewModel;

  CounterView({required this.viewModel});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: ${viewModel.count}'),
        ElevatedButton(
          onPressed: viewModel.increment,
          child: Text('Increment'),
        ),
        ElevatedButton(
          onPressed: viewModel.decrement,
          child: Text('Decrement'),
        ),
      ],
    );
  }
}

디자인 패턴은 플러터 앱 개발에서 구조적이고 효율적인 방식으로 코드를 작성하기 위해 큰 도움을 줍니다. 각 패턴을 적절히 활용하여 앱을 개발하면 보다 효율적이고 확장 가능한 앱을 만들 수 있습니다.