결론은 레퍼지토리와 같이 해야한다 그래야 다른곳에서도 끌어올 수 있다.
파이어베이스 데이터 접근을 Repository vs Bloc에서 직접 처리하는 것의 주요 차이점을 설명하겠습니다:
1. 관심사의 분리 (Separation of Concerns)
```dart
// Repository 패턴 사용
class TodaySaleRepository {
Future<List<ProductModel>> getTodaySaleProducts() async {
final snapshot = await _firestore
.collection('products')
.where('discountPercent', isGreaterThanOrEqualTo: 40)
.get();
return snapshot.docs.map((doc) => ProductModel.fromMap(doc.data())).toList();
}
}
// Bloc은 비즈니스 로직에만 집중
class TodaySaleBloc extends Bloc<TodaySaleEvent, TodaySaleState> {
final TodaySaleRepository _repository;
on<LoadTodaySaleProducts>((event, emit) async {
try {
final products = await _repository.getTodaySaleProducts();
emit(TodaySaleLoaded(products));
} catch (e) {
emit(TodaySaleError('로드 실패'));
}
});
}
// Bloc에서 직접 처리 (좋지 않은 예)
class TodaySaleBloc extends Bloc<TodaySaleEvent, TodaySaleState> {
final FirebaseFirestore _firestore;
on<LoadTodaySaleProducts>((event, emit) async {
try {
final snapshot = await _firestore
.collection('products')
.where('discountPercent', isGreaterThanOrEqualTo: 40)
.get();
final products = snapshot.docs.map((doc) =>
ProductModel.fromMap(doc.data())).toList();
emit(TodaySaleLoaded(products));
} catch (e) {
emit(TodaySaleError('로드 실패'));
}
});
}
```
2. 재사용성
```dart
// Repository 사용 시 다른 Bloc에서도 재사용 가능
class OtherBloc extends Bloc<OtherEvent, OtherState> {
final TodaySaleRepository _repository;
on<LoadProducts>((event, emit) async {
final products = await _repository.getTodaySaleProducts();
// 다른 로직...
});
}
```
3. 테스트 용이성
```dart
// Repository Mock 생성이 쉬움
class MockTodaySaleRepository implements TodaySaleRepository {
@override
Future<List<ProductModel>> getTodaySaleProducts() async {
return [/* test data */];
}
}
// 테스트 코드
test('should load products', () async {
final repository = MockTodaySaleRepository();
final bloc = TodaySaleBloc(repository: repository);
bloc.add(LoadTodaySaleProducts());
await expectLater(
bloc.stream,
emitsInOrder([
isA<TodaySaleLoading>(),
isA<TodaySaleLoaded>(),
]),
);
});
```
4. 데이터 소스 변경 용이성
```dart
// Repository를 통해 데이터 소스 변경이 쉬움
class TodaySaleRepository {
Future<List<ProductModel>> getTodaySaleProducts() async {
// Firebase에서 REST API로 변경하더라도
// Bloc의 코드는 변경할 필요 없음
final response = await http.get('api/products');
return ProductModel.fromJson(response.body);
}
}
```
5. 오류 처리와 데이터 변환
```dart
class TodaySaleRepository {
Future<List<ProductModel>> getTodaySaleProducts() async {
try {
final snapshot = await _firestore.collection('products').get();
// 데이터 변환 및 필터링 로직을 repository에서 처리
return snapshot.docs
.map((doc) => ProductModel.fromMap(doc.data()))
.where((product) => product.discountPercent >= 40)
.toList();
} catch (e) {
// 구체적인 에러 처리
if (e is FirebaseException) {
throw RepositoryException('Firebase 연결 실패');
}
throw RepositoryException('알 수 없는 에러');
}
}
}
```
Repository 패턴을 사용하는 것이 더 좋은 이유:
1. **유지보수성**: 데이터 액세스 로직이 한 곳에 집중되어 있어 관리가 쉬움
2. **테스트 용이성**: Repository를 Mock으로 대체하여 테스트하기 쉬움
3. **코드 재사용**: 여러 Bloc에서 동일한 Repository 사용 가능
4. **관심사 분리**:
- Repository: 데이터 액세스 담당
- Bloc: 비즈니스 로직 담당
- UI: 화면 표시 담당
5. **유연성**: 데이터 소스 변경이 용이함 (Firebase → REST API 등)
6. **일관성**: 데이터 처리 방식의 일관성 유지
따라서 클린 아키텍처와 SOLID 원칙을 따르기 위해서는 Repository 패턴을 사용하는 것이 권장됩니다.
'★★★트러블 슈팅' 카테고리의 다른 글
닉네임, 비밀번호 등등 오류해결 (0) | 2024.11.13 |
---|---|
파베 블록만 써서가져오지말고, 레퍼지토리 거쳐야하는 이유: 수정 (2) | 2024.11.06 |
Map<String, dynamic> 방식 (0) | 2024.11.04 |
date time 타입변화 에러 (0) | 2024.11.04 |
호출순서에 따라 구현이 안되는 문제 (0) | 2024.11.03 |