refactor: Repository 패턴 적용 및 Clean Architecture 완성
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

## 주요 변경사항

### 🏗️ Architecture
- Repository 패턴 전면 도입 (인터페이스/구현체 분리)
- Domain Layer에 Repository 인터페이스 정의
- Data Layer에 Repository 구현체 배치
- UseCase 의존성을 Service에서 Repository로 전환

### 📦 Dependency Injection
- GetIt 기반 DI Container 재구성 (lib/injection_container.dart)
- Repository 인터페이스와 구현체 등록
- Service와 Repository 공존 (마이그레이션 기간)

### 🔄 Migration Status
완료:
- License 모듈 (6개 UseCase)
- Warehouse Location 모듈 (5개 UseCase)

진행중:
- Auth 모듈 (2/5 UseCase)
- Company 모듈 (1/6 UseCase)

대기:
- User 모듈 (7개 UseCase)
- Equipment 모듈 (4개 UseCase)

### 🎯 Controller 통합
- 중복 Controller 제거 (with_usecase 버전)
- 단일 Controller로 통합
- UseCase 패턴 직접 적용

### 🧹 코드 정리
- 임시 파일 제거 (test_*.md, task.md)
- Node.js 아티팩트 제거 (package.json)
- 불필요한 테스트 파일 정리

###  테스트 개선
- Real API 중심 테스트 구조
- Mock 제거, 실제 API 엔드포인트 사용
- 통합 테스트 프레임워크 강화

## 기술적 영향
- 의존성 역전 원칙 적용
- 레이어 간 결합도 감소
- 테스트 용이성 향상
- 확장성 및 유지보수성 개선

## 다음 단계
1. User/Equipment 모듈 Repository 마이그레이션
2. Service Layer 점진적 제거
3. 캐싱 전략 구현
4. 성능 최적화
This commit is contained in:
JiWoong Sul
2025-08-11 20:14:10 +09:00
parent d64aa26157
commit 731dcd816b
105 changed files with 5225 additions and 3941 deletions

View File

@@ -232,7 +232,7 @@ abstract class ScreenTestFramework {
await testCase.setup?.call(testData);
// 테스트 실행
await testCase.execute(testData);
await testCase.call(testData);
// 검증
await testCase.verify(testData);

View File

@@ -1,7 +1,7 @@
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'package:superport/data/datasources/remote/api_interceptor.dart';
import 'package:superport/data/datasources/interceptors/api_interceptor.dart';
import 'package:superport/data/datasources/remote/auth_remote_datasource.dart';
import 'package:superport/data/datasources/remote/company_remote_datasource.dart';
import 'package:superport/data/datasources/remote/equipment_remote_datasource.dart';
@@ -24,29 +24,25 @@ import 'package:superport/domain/usecases/auth/login_usecase.dart';
import 'package:superport/domain/usecases/company/create_company_usecase.dart';
import 'package:superport/domain/usecases/company/delete_company_usecase.dart';
import 'package:superport/domain/usecases/company/get_companies_usecase.dart';
import 'package:superport/domain/usecases/company/get_company_usecase.dart';
import 'package:superport/domain/usecases/company/get_company_detail_usecase.dart';
import 'package:superport/domain/usecases/company/toggle_company_status_usecase.dart';
import 'package:superport/domain/usecases/company/update_company_usecase.dart';
import 'package:superport/domain/usecases/equipment/create_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/delete_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/equipment_in_usecase.dart';
import 'package:superport/domain/usecases/equipment/equipment_out_usecase.dart';
import 'package:superport/domain/usecases/equipment/get_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/get_equipments_usecase.dart';
import 'package:superport/domain/usecases/equipment/update_equipment_usecase.dart';
import 'package:superport/domain/usecases/license/create_license_usecase.dart';
import 'package:superport/domain/usecases/license/delete_license_usecase.dart';
import 'package:superport/domain/usecases/license/get_license_usecase.dart';
import 'package:superport/domain/usecases/license/get_license_detail_usecase.dart';
import 'package:superport/domain/usecases/license/get_licenses_usecase.dart';
import 'package:superport/domain/usecases/license/update_license_usecase.dart';
import 'package:superport/domain/usecases/user/create_user_usecase.dart';
import 'package:superport/domain/usecases/user/delete_user_usecase.dart';
import 'package:superport/domain/usecases/user/get_user_usecase.dart';
import 'package:superport/domain/usecases/user/get_user_detail_usecase.dart';
import 'package:superport/domain/usecases/user/get_users_usecase.dart';
import 'package:superport/domain/usecases/user/update_user_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/create_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/delete_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_location_detail_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_locations_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/update_warehouse_location_usecase.dart';
import 'package:superport/services/auth_service.dart';
@@ -56,6 +52,8 @@ import 'package:superport/services/license_service.dart';
import 'package:superport/services/user_service.dart';
import 'package:superport/services/warehouse_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:superport/core/storage/secure_storage.dart';
/// Real API 테스트 헬퍼
///
@@ -94,8 +92,11 @@ class RealApiTestHelper {
},
));
// SecureStorage 인스턴스 생성
final secureStorage = SecureStorage();
// API 인터셉터 추가
dio.interceptors.add(ApiInterceptor());
dio.interceptors.add(ApiInterceptor(secureStorage));
// 로깅 인터셉터 추가 (디버그용)
dio.interceptors.add(LogInterceptor(
@@ -108,42 +109,47 @@ class RealApiTestHelper {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
// FlutterSecureStorage
const flutterSecureStorage = FlutterSecureStorage();
// 의존성 등록 - 순서 중요!
// 1. 기본 인프라
_testGetIt!.registerSingleton<Dio>(dio);
_testGetIt!.registerSingleton<SharedPreferences>(prefs);
_testGetIt!.registerSingleton<FlutterSecureStorage>(flutterSecureStorage);
_testGetIt!.registerSingleton<SecureStorage>(secureStorage);
// 2. API Client
_testGetIt!.registerSingleton<ApiClient>(
ApiClient(dio),
ApiClient(),
);
// 3. Remote DataSources
_testGetIt!.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSource(_testGetIt!<ApiClient>()),
() => AuthRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<CompanyRemoteDataSource>(
() => CompanyRemoteDataSource(_testGetIt!<ApiClient>()),
() => CompanyRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<EquipmentRemoteDataSource>(
() => EquipmentRemoteDataSource(_testGetIt!<ApiClient>()),
() => EquipmentRemoteDataSourceImpl(),
);
_testGetIt!.registerLazySingleton<LicenseRemoteDataSource>(
() => LicenseRemoteDataSource(_testGetIt!<ApiClient>()),
() => LicenseRemoteDataSourceImpl(apiClient: _testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<UserRemoteDataSource>(
() => UserRemoteDataSource(_testGetIt!<ApiClient>()),
() => UserRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<WarehouseLocationRemoteDataSource>(
() => WarehouseLocationRemoteDataSource(_testGetIt!<ApiClient>()),
() => WarehouseLocationRemoteDataSourceImpl(apiClient: _testGetIt!<ApiClient>()),
);
// 4. Repositories
_testGetIt!.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(
remoteDataSource: _testGetIt!<AuthRemoteDataSource>(),
prefs: _testGetIt!<SharedPreferences>(),
sharedPreferences: _testGetIt!<SharedPreferences>(),
),
);
_testGetIt!.registerLazySingleton<CompanyRepository>(
@@ -153,7 +159,7 @@ class RealApiTestHelper {
);
_testGetIt!.registerLazySingleton<EquipmentRepository>(
() => EquipmentRepositoryImpl(
remoteDataSource: _testGetIt!<EquipmentRemoteDataSource>(),
_testGetIt!<EquipmentRemoteDataSource>(),
),
);
_testGetIt!.registerLazySingleton<LicenseRepository>(
@@ -172,120 +178,108 @@ class RealApiTestHelper {
),
);
// 5. UseCases - Auth
// 6. UseCases - Auth
_testGetIt!.registerLazySingleton(
() => LoginUseCase(repository: _testGetIt!<AuthRepository>()),
() => LoginUseCase(_testGetIt!<AuthService>()),
);
// 6. UseCases - Company
// 7. UseCases - Company
_testGetIt!.registerLazySingleton(
() => GetCompaniesUseCase(repository: _testGetIt!<CompanyRepository>()),
() => GetCompaniesUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => GetCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => GetCompanyDetailUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => CreateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => CreateCompanyUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => UpdateCompanyUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => DeleteCompanyUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => ToggleCompanyStatusUseCase(repository: _testGetIt!<CompanyRepository>()),
() => ToggleCompanyStatusUseCase(_testGetIt!<CompanyService>()),
);
// 7. UseCases - Equipment
// 8. UseCases - Equipment
_testGetIt!.registerLazySingleton(
() => GetEquipmentsUseCase(repository: _testGetIt!<EquipmentRepository>()),
() => GetEquipmentsUseCase(_testGetIt!<EquipmentService>()),
);
_testGetIt!.registerLazySingleton(
() => GetEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
() => EquipmentInUseCase(_testGetIt!<EquipmentService>()),
);
_testGetIt!.registerLazySingleton(
() => CreateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => EquipmentInUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => EquipmentOutUseCase(repository: _testGetIt!<EquipmentRepository>()),
() => EquipmentOutUseCase(_testGetIt!<EquipmentService>()),
);
// 8. UseCases - License
// 9. UseCases - License
_testGetIt!.registerLazySingleton(
() => GetLicensesUseCase(repository: _testGetIt!<LicenseRepository>()),
() => GetLicensesUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => GetLicenseDetailUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => CreateLicenseUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => UpdateLicenseUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => DeleteLicenseUseCase(_testGetIt!<LicenseRepository>()),
);
// 9. UseCases - User
// 10. UseCases - User
_testGetIt!.registerLazySingleton(
() => GetUsersUseCase(repository: _testGetIt!<UserRepository>()),
() => GetUsersUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => GetUserUseCase(repository: _testGetIt!<UserRepository>()),
() => GetUserDetailUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => CreateUserUseCase(repository: _testGetIt!<UserRepository>()),
() => CreateUserUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateUserUseCase(repository: _testGetIt!<UserRepository>()),
() => UpdateUserUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteUserUseCase(repository: _testGetIt!<UserRepository>()),
() => DeleteUserUseCase(_testGetIt!<UserService>()),
);
// 10. UseCases - Warehouse Location
// 11. UseCases - Warehouse Location
_testGetIt!.registerLazySingleton(
() => GetWarehouseLocationsUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => GetWarehouseLocationsUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => GetWarehouseLocationDetailUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => CreateWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => UpdateWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => DeleteWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
// 11. Services (Legacy - 점진적 마이그레이션을 위해 유지)
// 5. Services (현재 UseCases에서 사용 중)
_testGetIt!.registerLazySingleton<AuthService>(
() => AuthService(),
() => AuthServiceImpl(_testGetIt!<AuthRemoteDataSource>(), _testGetIt!<FlutterSecureStorage>()),
);
_testGetIt!.registerLazySingleton<CompanyService>(
() => CompanyService(),
() => CompanyService(_testGetIt!<CompanyRemoteDataSource>()),
);
_testGetIt!.registerLazySingleton<EquipmentService>(
() => EquipmentService(),
);
_testGetIt!.registerLazySingleton<LicenseService>(
() => LicenseService(),
() => LicenseService(_testGetIt!<LicenseRemoteDataSource>()),
);
_testGetIt!.registerLazySingleton<UserService>(
() => UserService(),
() => UserService(_testGetIt!<UserRemoteDataSource>()),
);
_testGetIt!.registerLazySingleton<WarehouseService>(
() => WarehouseService(),
@@ -303,9 +297,11 @@ class RealApiTestHelper {
final getIt = await setupTestEnvironment();
final loginUseCase = getIt<LoginUseCase>();
final result = await loginUseCase.execute(
email: testEmail,
password: testPassword,
final result = await loginUseCase.call(
LoginParams(
email: testEmail,
password: testPassword,
),
);
return result.fold(

View File

@@ -366,7 +366,7 @@ class CompositeAction extends BaseTestableAction {
continue;
}
final result = await action.execute(tester);
final result = await action.call(tester);
results.add(result);
if (!result.success && stopOnFailure) {
@@ -430,7 +430,7 @@ class ConditionalAction extends BaseTestableAction {
final conditionMet = await condition(tester);
if (conditionMet) {
final result = await trueAction.execute(tester);
final result = await trueAction.call(tester);
return ActionResult(
success: result.success,
message: 'Condition met - ${result.message}',
@@ -441,7 +441,7 @@ class ConditionalAction extends BaseTestableAction {
stackTrace: result.stackTrace,
);
} else if (falseAction != null) {
final result = await falseAction!.execute(tester);
final result = await falseAction!.call(tester);
return ActionResult(
success: result.success,
message: 'Condition not met - ${result.message}',
@@ -497,7 +497,7 @@ class RetryAction extends BaseTestableAction {
continue;
}
lastResult = await action.execute(tester);
lastResult = await action.call(tester);
if (lastResult.success) {
return ActionResult.success(