Repository Design Pattern

이재영·2024년 11월 26일
0

MVVM 학습 중, Repository Pattern이 많이 쓰이기도 하고 처음 접해보아 정리해본다..

Repository Layer

우선 Repository을 짧게 요약하자면, 데이터 접근 로직을 몰아 넣은 곳이다.

위 사진을 보면 도메인 계층(ViewModel)데이터 계층(Data)사이에서 Repository을 추가하여 상호작용 하게 한다. Repository를 데이터 접근만을 (비즈니스 로직X!) 다루므로 데이터 계층으로 볼 수도 있다.

Repository layer 덕분에 추후 단위 테스트를 작성할 때 
실제 DB나 API에 의존하지 않고 모의 객체를 사용하여 테스트가 가능해진다고 한다!

또한, DBAPI와의 작업을 Repository에서 처리하여 비즈니스 로직(=데이터를 어떻게 사용할까?)에 더욱 집중할 수 있다.

데이터 접근만을 다루고, 비즈니스 로직은 서비스 계층에서 다루는게 바람직하다.

일반적인 Repository

인터페이스 - 데이터를 어떻게 조회하고 저장할 것인지에 대한 메소드 정의

abstract class UserRepository {
  Future<User> getUser(int id);
  Future<void> saveUser(User user);
}

구현체 - 실제 데이터베이스나 외부 API와 소통하는 구현

class UserRepositoryImpl implements UserRepository {
  final Map<int, User> _userDatabase = {};  // 임시 로컬 데이터베이스

  
  Future<User> getUser(int userId) async {
    // 사용자 정보를 로컬 데이터베이스에서 찾아 반환
    if (_userDatabase.containsKey(userId)) {
      return _userDatabase[userId]!;
    } else {
      throw Exception('User not found');
    }
  }

  
  Future<void> saveUser(User user) async {
    // 사용자 정보를 로컬 데이터베이스에 저장
    _userDatabase[user.id] = user;
  }
}


사용 예제

위의 데이터 관련 로직을 통해 ViewModelView에 어떻게 사용되는지 알아보자

Model

class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});
}

ViewModel

class UserViewModel {
  final UserRepository userRepository;

  // UserViewModel이 UserRepositoryImpl을 사용하도록 의존성 주입
  UserViewModel(this.userRepository);

  Future<User> fetchUser(int userId) async {
    try {
      // 사용자 ID가 유효한지 확인
      if (userId <= 0) {
        throw Exception('Invalid user ID');
      }
    
      // Repository를 통해 사용자 정보 가져오기
      return await userRepository.getUser(userId);
      
    } catch (e) {
      throw Exception('Failed to fetch user: $e');
    }
  }

  Future<void> saveUser(User user) async {
    await userRepository.saveUser(user);
  }
}

UserRepositoryImpl을 이용해 데이터를 관리하고, UI에 표시할 데이터를 준비하며, 데이터를 가져올 때 비즈니스 로직(사용자 ID 확인, 에러 핸들링)도 처리한다.


View

import 'package:flutter/material.dart';

class UserView extends StatelessWidget {
  final UserViewModel userViewModel = UserViewModel(UserRepositoryImpl());

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('User Information')),
      body: Center(
        child: FutureBuilder<User>(
          future: userViewModel.fetchUser(1),  // 사용자 ID 1로 데이터를 불러옴
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator();  // 로딩 중 표시
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');  // 에러 메시지
            } else if (snapshot.hasData) {
              final user = snapshot.data!;
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('ID: ${user.id}'),
                  Text('Name: ${user.name}'),
                  Text('Email: ${user.email}'),
                ],
              );
            } else {
              return Text('No data available');
            }
          },
        ),
      ),
    );
  }
}

++

이런식으로 디자인 패턴을 통해 layer를 나누는 것 만으로는 클래스 간의 결합도를 최소화하는데 완벽할 수 없다. 더 효과적으로 나아가, 의존성 주입과 같은 방법으로 클래스 간의 결합도 최소화를 실현할 수 있음!



https://f-lab.kr/insight/repository-pattern-20240626
https://medium.com/@pererikbergman/repository-design-pattern-e28c0f3e4a30
https://velog.io/@ilil1/Repository-Pattern-%EC%9D%B4%EB%9E%80

profile
chmod 000

0개의 댓글