Provider MVVM architecture

샤워실의 바보·2024년 2월 16일
0
post-thumbnail

Model-View-ViewModel (MVVM)은 소프트웨어 개발에서 널리 사용되는 아키텍처 패턴 중 하나로, 데이터 모델(Model), 사용자 인터페이스(View), 그리고 이 둘을 연결하는 비즈니스 로직과 데이터 처리 로직을 담당하는 뷰모델(ViewModel)로 구성됩니다. Flutter에서 Provider 패키지를 사용하여 MVVM 아키텍처를 구현할 수 있습니다. 이를 통해 데이터 관리와 UI 로직을 분리하여 앱의 유지보수성, 테스트 용이성, 그리고 확장성을 향상시킬 수 있습니다.

MVVM 아키텍처의 주요 구성 요소:

  • Model: 앱의 데이터와 비즈니스 로직을 포함합니다. API 호출 결과, 데이터베이스 쿼리 결과 등이 여기에 해당됩니다.
  • View: 사용자에게 보여지는 UI 부분입니다. Flutter에서는 위젯이 이 역할을 합니다.
  • ViewModel: View와 Model 사이의 중개자 역할을 합니다. View에 데이터를 제공하고, 사용자 입력에 따라 모델을 업데이트합니다. ViewModel은 일반적으로 ChangeNotifier를 상속받아 구현됩니다.

Provider를 사용한 MVVM 아키텍처 구현 방법:

  1. 모델 정의: 데이터 구조와 관련 비즈니스 로직을 포함하는 모델 클래스를 정의합니다.

  2. 뷰모델 생성: ChangeNotifier를 상속받는 클래스를 생성하고, 이 클래스 내에서 모델을 관리하며, View로부터의 사용자 입력을 처리하는 메서드를 정의합니다. 데이터가 변경되면 notifyListeners()를 호출하여 View를 업데이트합니다.

  3. Provider 설정: main.dart 또는 앱의 최상위에 있는 파일에서, ChangeNotifierProvider를 사용하여 앱 전역에서 뷰모델 인스턴스에 접근할 수 있도록 설정합니다.

  4. 뷰 구현: Consumer 또는 context.watch()와 같은 메서드를 사용하여 뷰모델로부터 데이터를 받아 UI를 구성합니다. 사용자 입력은 뷰모델로 전달되어 처리됩니다.

예시 코드:

// Model
class UserData {
  final String name;
  final int age;
  UserData(this.name, this.age);
}

// ViewModel
class UserViewModel extends ChangeNotifier {
  UserData _user = UserData('John Doe', 30);
  UserData get user => _user;

  void updateUser(String name, int age) {
    _user = UserData(name, age);
    notifyListeners();
  }
}

// main.dart
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => UserViewModel(),
      child: MyApp(),
    ),
  );
}

// View (A Flutter Widget)
class UserView extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final viewModel = context.watch<UserViewModel>();

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Name: ${viewModel.user.name}'),
            Text('Age: ${viewModel.user.age}'),
            ElevatedButton(
              onPressed: () => viewModel.updateUser('Jane Doe', 28),
              child: Text('Update User'),
            ),
          ],
        ),
      ),
    );
  }
}

이 예시에서는 간단한 사용자 데이터를 관리하는 MVVM 아키텍처를 구현하고 있습니다. UserViewModel은 사용자 데이터를 업데이트하는 로직을 포함하며, UI에서는 context.watch()를 사용하여 모델의 변경사항을 감지하고 화면을 갱신합니다.

Provider와 MVVM 아키텍처를 함께 사용하면, 상태 관리를 효율적으로 수행하고, 앱의 구조를 더 명확하게 구분할 수 있습니다. 이는 큰 규모의 앱 개발과 팀 작업에 특히 유용합니다.

MVVM 아키텍처에 Repository를 포함하여 Flutter 앱을 구성할 때, 데이터 레이어와 비즈니스 로직 레이어 사이에 추상화 레이어를 추가함으로써, 앱의 유지보수성과 테스트 용이성을 더욱 향상시킬 수 있습니다. Repository는 데이터 소스(예: 웹 API, 로컬 데이터베이스)로부터 데이터를 가져오는 역할을 담당하며, 이 데이터를 ViewModel에 제공합니다. ViewModel은 이 데이터를 사용하여 View에 표시할 정보를 준비합니다.

아래 예제 코드는 Repository를 포함한 MVVM 아키텍처를 구현하는 방법을 보여줍니다. 코드에는 MVVM 구성요소와 관련된 주석이 포함되어 있습니다.

// Model
class UserData {
  final String name;
  final int age;
  UserData(this.name, this.age);
}

// Repository
class UserRepository {
  // 데이터 소스로부터 사용자 데이터를 가져오는 메서드
  Future<UserData> fetchUserData() async {
    // 여기서는 가상의 API 호출이라고 가정합니다.
    // 실제 앱에서는 여기서 웹 API를 호출하거나 로컬 데이터베이스를 쿼리할 수 있습니다.
    return UserData('John Doe', 30); // 가상의 사용자 데이터 반환
  }
}

// ViewModel
class UserViewModel extends ChangeNotifier {
  final UserRepository userRepository;

  // ViewModel이 생성될 때 UserRepository 인스턴스를 주입받습니다.
  UserViewModel({required this.userRepository});

  UserData _user = UserData('', 0);
  UserData get user => _user;

  // 사용자 데이터를 업데이트하는 메서드
  Future<void> updateUser() async {
    _user = await userRepository.fetchUserData();
    notifyListeners(); // 데이터가 변경되었음을 리스너들에게 알림
  }
}

// main.dart
void main() {
  runApp(
    ChangeNotifierProvider(
      // UserViewModel을 생성하고 UserRepository 인스턴스를 주입합니다.
      create: (context) => UserViewModel(userRepository: UserRepository()),
      child: MyApp(),
    ),
  );
}

// View
class UserView extends StatelessWidget {
  
  Widget build(BuildContext context) {
    // ViewModel에서 제공하는 사용자 데이터를 구독합니다.
    final viewModel = context.watch<UserViewModel>();

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Name: ${viewModel.user.name}'),
            Text('Age: ${viewModel.user.age}'),
            ElevatedButton(
              // 버튼을 누르면 ViewModel을 통해 사용자 데이터를 업데이트합니다.
              onPressed: () => viewModel.updateUser(),
              child: Text('Update User'),
            ),
          ],
        ),
      ),
    );
  }
}

이 코드는 UserRepository를 통해 데이터를 가져오고, UserViewModel에서 이 데이터를 관리하며, UserView에서 사용자에게 데이터를 표시하는 방식으로 구성됩니다. ViewModel은 ChangeNotifier를 상속받아 데이터 변경 시 View를 자동으로 업데이트할 수 있도록 합니다. ChangeNotifierProvider는 앱의 최상위에서 ViewModel을 제공하며, context.watch<UserViewModel>()을 사용하여 ViewModel의 데이터 변화를 구독합니다.

이 예시는 MVVM 아키텍처와 Repository 패턴을 통합하여 Flutter 앱에서 데이터 관리와 비즈니스 로직을 효과적으로 분리하는 방법을 보여줍니다. 이 구조는 앱의 확장성, 유지보수성, 그리고 테스트 용이성을 향상시키는 데 도움이 됩니다.

이 코드 예제는 Flutter에서 MVVM 아키텍처와 Repository 패턴을 사용하여 비디오 재생 설정(음소거, 자동 재생 등)을 관리하는 방식을 보여줍니다. MVVM 아키텍처는 Model, View, ViewModel의 세 가지 주요 구성 요소로 이루어져 있으며, Repository 패턴은 데이터 소스를 추상화하여 ViewModel과 데이터 소스 사이의 결합도를 낮춥니다. 이를 통해 유지보수성과 테스트 용이성이 향상됩니다.

구성 요소 설명:

  • Model (PlaybackConfigModel): 애플리케이션의 데이터 구조를 정의합니다. 이 경우, 비디오의 음소거 및 자동 재생 설정을 나타냅니다.

  • View (VideoPost, SettingsScreen): 사용자 인터페이스를 구성하며, 사용자 입력을 ViewModel에 전달하고, ViewModel로부터 데이터 변화를 구독하여 UI를 업데이트합니다.

  • ViewModel (PlaybackConfigViewModel): View와 Model 사이의 연결 고리 역할을 하며, 비즈니스 로직을 수행합니다. ChangeNotifier를 상속받아 데이터 변경 시 View에 알립니다.

  • Repository (PlaybackConfigRepository): 데이터 소스를 추상화하며, 여기서는 SharedPreferences를 통해 음소거 및 자동 재생 설정을 로컬에 저장하고 불러옵니다. ViewModel은 Repository를 통해 이러한 설정을 관리합니다.

주요 코드 설명 및 주석:

// PlaybackConfigModel: 설정 데이터 모델 정의.
class PlaybackConfigModel {
  bool muted;
  bool autoplay;

  PlaybackConfigModel({
    required this.muted,
    required this.autoplay,
  });
}

// PlaybackConfigRepository: 설정 데이터 관리를 위한 레포지토리.
// SharedPreferences를 사용해 설정을 로컬 저장소에 저장 및 로드.
class PlaybackConfigRepository {
  final SharedPreferences _preferences;
  
  PlaybackConfigRepository(this._preferences);

  // 음소거 설정 저장 및 로드 메서드
  Future<void> setMuted(bool value) async {
    _preferences.setBool(_muted, value);
  }

  bool isMuted() {
    return _preferences.getBool(_muted) ?? false;
  }

  // 자동 재생 설정 저장 및 로드 메서드
  Future<void> setAutoplay(bool value) async {
    _preferences.setBool(_autoplay, value);
  }

  bool isAutoplay() {
    return _preferences.getBool(_autoplay) ?? false;
  }
}

// PlaybackConfigViewModel: 비즈니스 로직과 View 업데이트 로직을 포함하는 ViewModel.
// Repository로부터 설정을 관리하고, 변경사항을 View에 알림.
class PlaybackConfigViewModel extends ChangeNotifier {
  final PlaybackConfigRepository _repository;
  late final PlaybackConfigModel _model;

  PlaybackConfigViewModel(this._repository) {
    _model = PlaybackConfigModel(
      muted: _repository.isMuted(),
      autoplay: _repository.isAutoplay(),
    );
  }

  // 음소거 및 자동 재생 설정에 대한 getter와 setter.
  bool get muted => _model.muted;
  void setMuted(bool value) {
    _repository.setMuted(value);
    _model.muted = value;
    notifyListeners(); // 변경 사항을 View에 알림.
  }

  bool get autoplay => _model.autoplay;
  void setAutoplay(bool value) {
    _repository.setAutoplay(value);
    _model.autoplay = value;
    notifyListeners();
  }
}

이 코드는 SharedPreferences를 사용하여 사용자의 비디오 재생 설정을 로컬 저장소에 저장하고, 이 설정을 앱 전체에 걸쳐 관리하는 방법을 보여줍니다. PlaybackConfigViewModel은 설정의 변경을 처리하고, 이 변경 사항을 View에 알려 UI를 업데이트합니다. PlaybackConfigRepository는 데이터의 지속성을 관리하며, ViewModel과 Model 사이의 결합도를 낮춥니다.

이러한 구조를 통해, 앱의 데이터 관리와 비즈니스 로직을 UI 로직으로부터 분리하여, 앱의 유지보수성과 확장성을 향상시킬 수 있습니다.

profile
공부하는 개발자

0개의 댓글