반응형
결제 화면 초기화 과정에서
여러 API를 순차적으로 호출해야 하는 로직이 Bloc 내부에 집중되면서 가독성과 책임 분리가 점점 어려워졌다.
기존 로직에서 프로모션 확인 여부를 추가했어야하는데, bloc 파일을 보니 init 할때 api 를 여러개 호출해야했다. 따라서 UseCase 파일을 만들어 분리하였다.
기존 문제점 :
초기 이벤트 처리 시 Bloc 안에서 여러 작업을 직접 수행하고 있었다.
- 상품 정보 조회
- 할인 정보 조회
- 프로모션 여부 확인
개선 방향 -> 여러 API 호출을 하나의 “초기화 유스케이스”로 묶고 Bloc은 결과를 받아 상태만 갱신하도록 단순화
개선 전에는 Bloc 안에서 getIt 3개를 직접 사용했었다.
- Bloc이 API 호출 흐름까지 다 알고 있음
- getIt 의존성 많음
- 재사용 어려움
on<InitEvent>((event, emit) async {
emit(state.copyWith(isLoading: true));
final productRes = await _fetchProductUseCase(
linkKey: productLinkKey,
);
final product = productRes.tryGetSuccess();
if (product != null) {
final discountRes = await _fetchDiscountInfoUseCase(
amount: product.saleAmount!,
);
final promoRes = await _fetchPromotionUseCase(
productBundleId: product.bundleId.toString(),
);
emit(state.copyWith(
product: product,
discountInfo: discountRes.tryGetSuccess(),
promotion: promoRes.isSuccess()
? promoRes.tryGetSuccess()
: null,
totalPrice: product.saleAmount!,
quantity: 1,
));
} else {
produceSideEffect(
BlocEffect.showNotFoundData(
message: '상품을 찾을 수 없습니다',
),
);
}
emit(state.copyWith(isLoading: false));
});
개선 후 : Init 전용 UseCase 로 분리
class InitPurchaseUseCase {
final FetchProductUseCase _fetchProduct;
final FetchDiscountInfoUseCase _fetchDiscount;
final FetchPromotionUseCase _fetchPromotion;
InitPurchaseUseCase(
this._fetchProduct,
this._fetchDiscount,
this._fetchPromotion,
);
Future<PurchaseInitData?> call({
required String productLinkKey,
}) async {
final productRes = await _fetchProduct(
linkKey: productLinkKey,
);
final product = productRes.tryGetSuccess();
if (product == null) return null;
final discountRes = await _fetchDiscount(
amount: product.saleAmount!,
);
final promoRes = await _fetchPromotion(
productBundleId: product.bundleId.toString(),
);
return PurchaseInitData(
product: product,
discountInfo: discountRes.tryGetSuccess(),
promotion:
promoRes.isSuccess() ? promoRes.tryGetSuccess() : null,
);
}
}
==> Bloc 안에서는,
on<InitEvent>((event, emit) async {
emit(state.copyWith(isLoading: true));
final initData = await initPurchaseUseCase(
productLinkKey: productLinkKey,
);
if (initData == null) {
produceSideEffect(
BlocEffect.showNotFoundData(
message: '상품을 찾을 수 없습니다',
),
);
emit(state.copyWith(isLoading: false));
return;
}
emit(state.copyWith(
product: initData.product,
discountInfo: initData.discountInfo,
promotion: initData.promotion,
totalPrice: initData.product.saleAmount!,
quantity: 1,
isLoading: false,
));
});
| on<InitEvent>((event, emit) async { emit(state.copyWith(isLoading: true)); final productRes = await _fetchProductUseCase( linkKey: productLinkKey, ); final product = productRes.tryGetSuccess(); if (product != null) { final discountRes = await _fetchDiscountInfoUseCase( amount: product.saleAmount!, ); final promoRes = await _fetchPromotionUseCase( productBundleId: product.bundleId.toString(), ); emit(state.copyWith( product: product, discountInfo: discountRes.tryGetSuccess(), promotion: promoRes.isSuccess() ? promoRes.tryGetSuccess() : null, totalPrice: product.saleAmount!, quantity: 1, )); } else { produceSideEffect( BlocEffect.showNotFoundData( message: '상품을 찾을 수 없습니다', ), ); } emit(state.copyWith(isLoading: false)); }); |
on<InitEvent>((event, emit) async { emit(state.copyWith(isLoading: true)); final initData = await initPurchaseUseCase( productLinkKey: productLinkKey, ); if (initData == null) { produceSideEffect( BlocEffect.showNotFoundData( message: '상품을 찾을 수 없습니다', ), ); emit(state.copyWith(isLoading: false)); return; } emit(state.copyWith( product: initData.product, discountInfo: initData.discountInfo, promotion: initData.promotion, totalPrice: initData.product.saleAmount!, quantity: 1, isLoading: false, )); }); |
Bloc에서는 getIt 사용을 최소화하고, 의존성은 UseCase 생성 시 주입하도록 변경했슴.니ㄷ ㅏ ..
같은 on<InitEvent>인데도 코드 양 확 줄었어요.. 보기 편하고.. 좋슨~~ !! 🤣
