refactor: Repository 패턴 적용 및 Clean Architecture 완성
## 주요 변경사항 ### 🏗️ 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:
@@ -221,7 +221,7 @@ class WarehouseAutomatedTest extends BaseScreenTest {
|
||||
perPage: 20,
|
||||
);
|
||||
// PaginatedResponse의 items를 반환하여 List처럼 사용할 수 있도록 함
|
||||
return result.items;
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -312,9 +312,9 @@ class WarehouseTestData {
|
||||
final purposes = ['물류', '보관', '배송', '집하', '분류', '냉동', '냉장', '특수', '일반', '대형'];
|
||||
final suffixes = ['창고', '센터', '물류센터', '보관소', '집하장'];
|
||||
|
||||
final type = types[random.nextInt(types.items.length)];
|
||||
final purpose = purposes[random.nextInt(purposes.items.length)];
|
||||
final suffix = suffixes[random.nextInt(suffixes.items.length)];
|
||||
final type = types[random.nextInt(types.length)];
|
||||
final purpose = purposes[random.nextInt(purposes.length)];
|
||||
final suffix = suffixes[random.nextInt(suffixes.length)];
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
return '$type $purpose$suffix - TEST$timestamp';
|
||||
@@ -331,9 +331,9 @@ class WarehouseTestData {
|
||||
'산업단지', '물류단지', '유통단지', '첨단산업단지', '일반산업단지', '국가산업단지'
|
||||
];
|
||||
|
||||
final city = cities[random.nextInt(cities.items.length)];
|
||||
final district = districts[random.nextInt(districts.items.length)];
|
||||
final industrial = industrialAreas[random.nextInt(industrialAreas.items.length)];
|
||||
final city = cities[random.nextInt(cities.length)];
|
||||
final district = districts[random.nextInt(districts.length)];
|
||||
final industrial = industrialAreas[random.nextInt(industrialAreas.length)];
|
||||
final number = random.nextInt(500) + 1;
|
||||
final detail = '$industrial $number블록 ${random.nextInt(10) + 1}호';
|
||||
|
||||
@@ -362,7 +362,7 @@ class WarehouseTestData {
|
||||
final featureCount = random.nextInt(3) + 1; // 1-3개 특징
|
||||
|
||||
for (int i = 0; i < featureCount; i++) {
|
||||
final feature = features[random.nextInt(features.items.length)];
|
||||
final feature = features[random.nextInt(features.length)];
|
||||
if (!selectedFeatures.contains(feature)) {
|
||||
selectedFeatures.add(feature);
|
||||
}
|
||||
@@ -376,8 +376,8 @@ class WarehouseTestData {
|
||||
final lastNames = ['김', '이', '박', '최', '정', '강', '조', '윤', '장', '임'];
|
||||
final firstNames = ['창고장', '소장', '센터장', '팀장', '과장', '부장', '이사', '실장'];
|
||||
|
||||
final lastName = lastNames[random.nextInt(lastNames.items.length)];
|
||||
final firstName = firstNames[random.nextInt(firstNames.items.length)];
|
||||
final lastName = lastNames[random.nextInt(lastNames.length)];
|
||||
final firstName = firstNames[random.nextInt(firstNames.length)];
|
||||
|
||||
return '$lastName$firstName';
|
||||
}
|
||||
@@ -385,7 +385,7 @@ class WarehouseTestData {
|
||||
// 연락처 생성기
|
||||
static String generateContact() {
|
||||
final areaCodes = ['02', '031', '032', '033', '041', '042', '043', '051', '052', '053'];
|
||||
final areaCode = areaCodes[random.nextInt(areaCodes.items.length)];
|
||||
final areaCode = areaCodes[random.nextInt(areaCodes.length)];
|
||||
final middle = random.nextInt(9000) + 1000;
|
||||
final last = random.nextInt(9000) + 1000;
|
||||
return '$areaCode-$middle-$last';
|
||||
@@ -394,7 +394,7 @@ class WarehouseTestData {
|
||||
// 창고 용량 생성기 (평방미터)
|
||||
static int generateCapacity() {
|
||||
final capacities = [500, 1000, 1500, 2000, 3000, 5000, 10000, 15000, 20000];
|
||||
return capacities[random.nextInt(capacities.items.length)];
|
||||
return capacities[random.nextInt(capacities.length)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ extension on WarehouseAutomatedTest {
|
||||
await _testWarehouseUpdate(createdWarehouse.id);
|
||||
|
||||
// 6. 창고 검색 테스트
|
||||
await _testWarehouseSearch(createdWarehouse.name.split(' ').items.first);
|
||||
await _testWarehouseSearch(createdWarehouse.name.split(' ').first);
|
||||
|
||||
// 7. 활성/비활성 필터링 테스트
|
||||
await _testActiveFiltering();
|
||||
@@ -603,12 +603,12 @@ extension on WarehouseAutomatedTest {
|
||||
try {
|
||||
// search 파라미터가 지원되는지 확인
|
||||
final searchResults = await warehouseService.searchWarehouseLocations(
|
||||
keyword: searchKeyword.split(' ').items.first, // 첫 단어만 사용
|
||||
keyword: searchKeyword.split(' ').first, // 첫 단어만 사용
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
);
|
||||
|
||||
_log('검색 결과: ${searchResults.items.length}개 창고');
|
||||
_log('검색 결과: ${searchResults.length}개 창고');
|
||||
testContext.setData('searchResults', searchResults);
|
||||
testContext.setData('searchSuccess', true);
|
||||
} catch (e) {
|
||||
@@ -620,13 +620,13 @@ extension on WarehouseAutomatedTest {
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
);
|
||||
final allWarehouses = allWarehousesResult.items;
|
||||
final allWarehouses = allWarehousesResult;
|
||||
|
||||
final filtered = allWarehouses.items.where((w) =>
|
||||
final filtered = allWarehouses.where((w) =>
|
||||
w.name.toLowerCase().contains(searchKeyword.toLowerCase())
|
||||
).toList();
|
||||
|
||||
_log('필터링 결과: ${filtered.items.length}개 창고');
|
||||
_log('필터링 결과: ${filtered.length}개 창고');
|
||||
testContext.setData('searchResults', filtered);
|
||||
testContext.setData('searchSuccess', true);
|
||||
} catch (e2) {
|
||||
@@ -647,8 +647,8 @@ extension on WarehouseAutomatedTest {
|
||||
perPage: 10,
|
||||
isActive: true,
|
||||
);
|
||||
final activeWarehouses = activeWarehousesResult.items;
|
||||
_log('활성 창고: ${activeWarehouses.items.length}개');
|
||||
final activeWarehouses = activeWarehousesResult;
|
||||
_log('활성 창고: ${activeWarehouses.length}개');
|
||||
|
||||
// 비활성 창고만 조회
|
||||
_log('비활성 창고 조회 중...');
|
||||
@@ -657,8 +657,8 @@ extension on WarehouseAutomatedTest {
|
||||
perPage: 10,
|
||||
isActive: false,
|
||||
);
|
||||
final inactiveWarehouses = inactiveWarehousesResult.items;
|
||||
_log('비활성 창고: ${inactiveWarehouses.items.length}개');
|
||||
final inactiveWarehouses = inactiveWarehousesResult;
|
||||
_log('비활성 창고: ${inactiveWarehouses.length}개');
|
||||
|
||||
testContext.setData('activeWarehouses', activeWarehouses);
|
||||
testContext.setData('inactiveWarehouses', inactiveWarehouses);
|
||||
@@ -856,7 +856,7 @@ extension on WarehouseAutomatedTest {
|
||||
);
|
||||
|
||||
// expect(diagnosis.errorType, equals(ErrorType.missingRequiredField));
|
||||
_log('진단 결과: ${diagnosis.missingFields?.items.length ?? 0}개 필드 누락');
|
||||
_log('진단 결과: ${diagnosis.missingFields?.length ?? 0}개 필드 누락');
|
||||
|
||||
// 자동 수정
|
||||
final fixResult = await autoFixer.attemptAutoFix(diagnosis);
|
||||
@@ -903,7 +903,7 @@ extension on WarehouseAutomatedTest {
|
||||
// 1. 창고별 장비 목록 조회 (초기 상태)
|
||||
_log('창고별 장비 목록 조회 중...');
|
||||
final initialEquipment = await warehouseService.getWarehouseEquipment(warehouse.id);
|
||||
_log('초기 장비 수: ${initialEquipment.items.length}개');
|
||||
_log('초기 장비 수: ${initialEquipment.length}개');
|
||||
|
||||
// 2. 장비 입고 시뮬레이션 (실제로는 Equipment 서비스를 통해 수행)
|
||||
_log('장비 입고 프로세스는 Equipment 서비스에서 처리됩니다');
|
||||
@@ -911,17 +911,17 @@ extension on WarehouseAutomatedTest {
|
||||
// 3. 사용 중인 창고 목록 조회
|
||||
_log('사용 중인 창고 목록 조회 중...');
|
||||
final inUseWarehouses = await warehouseService.getInUseWarehouseLocations();
|
||||
_log('사용 중인 창고 수: ${inUseWarehouses.items.length}개');
|
||||
_log('사용 중인 창고 수: ${inUseWarehouses.length}개');
|
||||
|
||||
// 장비가 있는 창고는 사용 중으로 표시되어야 함
|
||||
if (initialEquipment.items.isNotEmpty) {
|
||||
final isInUse = inUseWarehouses.items.any((w) => w.id == warehouse.id);
|
||||
if (initialEquipment.isNotEmpty) {
|
||||
final isInUse = inUseWarehouses.any((w) => w.id == warehouse.id);
|
||||
// expect(isInUse, isTrue, reason: '장비가 있는 창고가 사용 중으로 표시되지 않았습니다');
|
||||
}
|
||||
|
||||
testContext.setData('equipmentIntegrationSuccess', true);
|
||||
testContext.setData('initialEquipmentCount', initialEquipment.items.length);
|
||||
testContext.setData('inUseWarehouseCount', inUseWarehouses.items.length);
|
||||
testContext.setData('initialEquipmentCount', initialEquipment.length);
|
||||
testContext.setData('inUseWarehouseCount', inUseWarehouses.length);
|
||||
|
||||
} catch (e) {
|
||||
_log('장비 연동 중 오류 발생: $e');
|
||||
@@ -952,7 +952,7 @@ extension on WarehouseAutomatedTest {
|
||||
page: 1,
|
||||
perPage: 100,
|
||||
);
|
||||
_log('전체 창고 수: ${allWarehouses.items.length}개');
|
||||
_log('전체 창고 수: ${allWarehouses.length}개');
|
||||
|
||||
// 2. 활성 창고만 필터링
|
||||
_log('활성 창고만 필터링...');
|
||||
@@ -961,7 +961,7 @@ extension on WarehouseAutomatedTest {
|
||||
perPage: 100,
|
||||
isActive: true,
|
||||
);
|
||||
_log('활성 창고 수: ${activeWarehouses.items.length}개');
|
||||
_log('활성 창고 수: ${activeWarehouses.length}개');
|
||||
|
||||
// 3. 비활성 창고 필터링
|
||||
_log('비활성 창고 필터링...');
|
||||
@@ -970,21 +970,21 @@ extension on WarehouseAutomatedTest {
|
||||
perPage: 100,
|
||||
isActive: false,
|
||||
);
|
||||
_log('비활성 창고 수: ${inactiveWarehouses.items.length}개');
|
||||
_log('비활성 창고 수: ${inactiveWarehouses.length}개');
|
||||
|
||||
// 4. 사용 중인 창고 목록
|
||||
_log('사용 중인 창고 목록 조회...');
|
||||
final inUseWarehouses = await warehouseService.getInUseWarehouseLocations();
|
||||
_log('사용 중인 창고 수: ${inUseWarehouses.items.length}개');
|
||||
_log('사용 중인 창고 수: ${inUseWarehouses.length}개');
|
||||
|
||||
// 검증: 활성 + 비활성 = 전체 (대략적으로)
|
||||
// 페이지네이션 때문에 정확히 일치하지 않을 수 있음
|
||||
|
||||
testContext.setData('inUseManagementSuccess', true);
|
||||
testContext.setData('totalWarehouses', allWarehouses.items.length);
|
||||
testContext.setData('activeWarehouses', activeWarehouses.items.length);
|
||||
testContext.setData('inactiveWarehouses', inactiveWarehouses.items.length);
|
||||
testContext.setData('inUseWarehouses', inUseWarehouses.items.length);
|
||||
testContext.setData('totalWarehouses', allWarehouses.length);
|
||||
testContext.setData('activeWarehouses', activeWarehouses.length);
|
||||
testContext.setData('inactiveWarehouses', inactiveWarehouses.length);
|
||||
testContext.setData('inUseWarehouses', inUseWarehouses.length);
|
||||
|
||||
} catch (e) {
|
||||
_log('사용 중인 창고 관리 중 오류 발생: $e');
|
||||
@@ -1024,8 +1024,8 @@ extension WarehouseServiceExtension on WarehouseService {
|
||||
// 실제 검색 API가 있다면 사용
|
||||
// 없다면 전체 목록을 가져와서 필터링
|
||||
final allResult = await getWarehouseLocations(page: page, perPage: perPage * 5);
|
||||
final all = allResult.items;
|
||||
return all.where((w) =>
|
||||
final all = allResult;
|
||||
return all.items.where((w) =>
|
||||
w.name.toLowerCase().contains(keyword.toLowerCase()) ||
|
||||
(w.address.toString().toLowerCase().contains(keyword.toLowerCase()))
|
||||
).toList();
|
||||
|
||||
Reference in New Issue
Block a user