이번 포스팅에서는 Feature-based Clean Architecture에서 API 통신을 구현하는 방법을 다루겠습니다.
먼저, 데이터 모델을 정의합니다. PairingModel이라는 이름으로 데이터를 처리하며, freezed를 통해 간결하고 안전하게 코드를 생성합니다.
import 'package:freezed_annotation/freezed_annotation.dart';
part 'pairing_model.freezed.dart';
part 'pairing_model.g.dart';
@Freezed()
class PairingModel with _$PairingModel {
factory PairingModel({
@JsonKey(name: "ID") String? id,
@JsonKey(name: "NAME") String? name,
}) = _PairingModel;
factory PairingModel.fromJson(Map<String, dynamic> json) =>
_$PairingModelFromJson(json);
}
위 코드에서는 PairingModel 클래스에 id와 name 필드를 정의했으며, freezed 패키지가 제공하는 JSON 직렬화/역직렬화 기능을 사용하여 API와의 데이터 매핑을 자동화했습니다.
PairingRepository는 API 호출 로직을 담고 있습니다. NetWorkRepository 의존성을 주입하여 네트워크 통신을 수행합니다.
import '../../../../../common/network/network_manager.dart';
class PairingRepository {
final NetWorkRepository netWorkManagerRepository;
PairingRepository(this.netWorkManagerRepository);
Future<dynamic> postPairing(String telNo, String gender, String birthYear,
String residentialArea) async {
return await netWorkManagerRepository.postResponse(
domainUrl: "~",
url: '~',
data: {
"TEL_NO": telNo,
},
headers: {
"Content-Type": "application/json",
},
);
}
위의 postPairing 메서드는 전화번호 정보를 서버로 전송하며, 네트워크 레이어와의 결합을 최소화하여 깔끔하게 유지합니다.
비즈니스 로직을 분리하기 위해 UseCase를 정의합니다. PostParingUseCase는 PairingRepository를 호출해 데이터를 받아오고, 이를 PairingModel로 변환합니다.
import '../../data/models/pairing_model.dart';
import '../repositories/pairing_repository.dart';
class PostParingUseCase {
final PairingRepository repository;
PostParingUseCase(this.repository);
Future<PairingModel> call(String telNo) async {
final response =
await repository.postPairing(telNo);
return PairingModel.fromJson(response);
}
}
PairingNotifier는 StateNotifier를 사용하여 PairingModel의 상태를 관리합니다. postPairing 메서드는 API 호출 후 상태를 업데이트하며, 예외 처리도 포함합니다.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:homechoice/features/pairing_feature/domain/usecases/set_stb_nikename_use_case.dart';
import '../../data/models/pairing_model.dart';
import '../../domain/repositories/pairing_repository.dart';
import '../../domain/usecases/disconnect_pairing_use_case.dart';
import '../../domain/usecases/fetch_pairing_state_use_case.dart';
import '../../domain/usecases/fetch_stb_pairing_devices_use_case.dart';
import '../../domain/usecases/fetch_tv_apps_use_case.dart';
import '../../domain/usecases/post_pairing_use_case.dart';
class PairingNotifier extends StateNotifier<PairingModel> {
PairingNotifier(this.repository) : super(PairingModel());
final PairingRepository repository;
///페어링 신규
Future<void> postPairing(String telNo) async {
try {
final datas = await PostParingUseCase(repository)(
telNo);
state = state.copyWith(
id: datas.id,
name: datas.name,
);
} catch (error) {
state = state.copyWith(isLoading: false);
}
}
}
PairingProvider는 PairingNotifier를 제공하여, UI와 상태 관리를 연결해줍니다.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:homechoice/features/pairing_feature/domain/repositories/pairing_repository.dart';
import '../../../../../common/network/network_manager.dart';
import '../../data/models/pairing_model.dart';
import 'pairing_notifier.dart';
final PairingProvider =
StateNotifierProvider<PairingNotifier, PairingModel>((ref) {
final netWorkManagerRepository = ref.read(netWorkManagerProvider);
final repository = PairingRepository(netWorkManagerRepository);
return PairingNotifier(repository);
});
위의 구조는 Feature-based Clean Architecture에서 각각의 책임을 명확히 분리하여 확장성과 테스트 가능성을 높인 예제입니다.