refactor: 사용하지 않는 파일 9개 정리

- 코드베이스 분석을 통해 사용되지 않는 파일 식별 및 삭제
- migration, utils, models, screens 등 미사용 파일 제거
- flutter analyze 결과: 63개 → 48개 오류로 개선
- 전체 .dart 파일 수: 365개로 정리 완료

삭제된 파일:
- lib/core/migrations/execute_migration.dart
- lib/core/utils/login_diagnostics.dart
- lib/utils/equipment_display_helper.dart
- lib/utils/formatters/business_number_formatter.dart
- lib/utils/user_utils.dart
- lib/models/user_phone_field.dart
- lib/screens/rent/rent_list_screen_simple.dart
- lib/screens/common/widgets/category_autocomplete_field.dart
- lib/screens/common/widgets/company_branch_dropdown.dart

 빌드 검증 완료: flutter pub get, build_runner 성공
 Phase 10 이후 추가 코드베이스 정리 완료
This commit is contained in:
JiWoong Sul
2025-08-29 15:13:56 +09:00
parent d916b281a7
commit 74b13e7080
9 changed files with 0 additions and 1353 deletions

View File

@@ -1,326 +0,0 @@
/// License → Maintenance 마이그레이션 실행 스크립트
///
/// 사용법:
/// dart run lib/core/migrations/execute_migration.dart
///
/// 옵션:
/// --dry-run: 실제 데이터 변경 없이 시뮬레이션만 수행
/// --rollback: 이전 백업에서 롤백 실행
/// --validate: 마이그레이션 후 데이터 검증만 수행
library;
import 'dart:io';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'license_to_maintenance_migration.dart';
import 'maintenance_data_validator.dart';
class MigrationExecutor {
static const String apiBaseUrl = 'http://43.201.34.104:8080/api/v1';
static const String backupPath = './migration_backup.json';
final Dio _dio;
String? _authToken;
MigrationExecutor() : _dio = Dio(BaseOptions(
baseUrl: apiBaseUrl,
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
));
/// 마이그레이션 실행
Future<void> execute({bool isDryRun = false}) async {
print('=' * 60);
print('License → Maintenance 마이그레이션 시작');
print('=' * 60);
print('모드: ${isDryRun ? "DRY RUN (시뮬레이션)" : "실제 실행"}');
print('시작 시간: ${DateTime.now()}');
print('-' * 60);
try {
// 1. 인증
print('\n[1/7] API 인증 중...');
await _authenticate();
// 2. 기존 License 데이터 가져오기
print('\n[2/7] License 데이터 로딩 중...');
final licenseData = await _fetchLicenseData();
print('${licenseData.length}개 License 발견');
// 3. Equipment 및 Equipment History 데이터 가져오기
print('\n[3/7] Equipment 관련 데이터 로딩 중...');
final equipmentData = await _fetchEquipmentData();
final equipmentHistoryData = await _fetchEquipmentHistoryData();
print('${equipmentData.length}개 Equipment 발견');
print('${equipmentHistoryData.length}개 Equipment History 발견');
// 4. 마이그레이션 실행
print('\n[4/7] 데이터 변환 중...');
final result = await LicenseToMaintenanceMigration.migrate(
licenseData: licenseData,
equipmentData: equipmentData,
equipmentHistoryData: equipmentHistoryData,
);
if (!result.success) {
throw Exception('마이그레이션 실패: ${result.error}');
}
// 5. 백업 저장
print('\n[5/7] 백업 저장 중...');
if (!isDryRun) {
await _saveBackup(result.backup!);
print(' → 백업 저장 완료: $backupPath');
} else {
print(' → [DRY RUN] 백업 저장 건너뜀');
}
// 6. Maintenance 데이터 저장
print('\n[6/7] Maintenance 데이터 저장 중...');
if (!isDryRun) {
await _saveMaintenanceData(result.maintenanceData!);
print('${result.maintenanceData!.length}개 Maintenance 저장 완료');
} else {
print(' → [DRY RUN] 실제 저장 건너뜀');
_printSampleData(result.maintenanceData!);
}
// 7. 검증
print('\n[7/7] 데이터 검증 중...');
final validationReport = await MaintenanceDataValidator.validate(
maintenanceData: result.maintenanceData!,
equipmentHistoryData: [], // TODO: 실제 equipment history 데이터 로드
);
_printValidationReport(validationReport);
// 완료
print('\n${'=' * 60}');
print('마이그레이션 ${isDryRun ? "시뮬레이션" : "실행"} 완료!');
print('=' * 60);
print('통계:');
print(' - 총 License: ${result.statistics!.totalCount}');
print(' - 활성: ${result.statistics!.activeCount}');
print(' - 만료 예정: ${result.statistics!.upcomingCount}');
print(' - 만료됨: ${result.statistics!.expiredCount}');
print('종료 시간: ${DateTime.now()}');
} catch (e) {
print('\n❌ 마이그레이션 실패!');
print('오류: $e');
print('\n롤백이 필요한 경우 다음 명령을 실행하세요:');
print('dart run lib/core/migrations/execute_migration.dart --rollback');
exit(1);
}
}
/// 롤백 실행
Future<void> rollback() async {
print('=' * 60);
print('License → Maintenance 롤백 시작');
print('=' * 60);
try {
// 백업 파일 로드
final backupFile = File(backupPath);
if (!await backupFile.exists()) {
throw Exception('백업 파일을 찾을 수 없습니다: $backupPath');
}
final backupContent = await backupFile.readAsString();
final backup = jsonDecode(backupContent) as Map<String, dynamic>;
print('백업 정보:');
print(' - 생성 시간: ${backup['timestamp']}');
print(' - 버전: ${backup['version']}');
print(' - 데이터 수: ${(backup['data'] as List).length}');
// 롤백 확인
print('\n정말로 롤백하시겠습니까? (y/n)');
final confirm = stdin.readLineSync();
if (confirm?.toLowerCase() != 'y') {
print('롤백 취소됨');
return;
}
// 롤백 실행
print('\n롤백 실행 중...');
final success = await LicenseToMaintenanceMigration.rollback(backup);
if (success) {
print('✅ 롤백 완료!');
// Maintenance 데이터 삭제
print('Maintenance 데이터 정리 중...');
await _deleteMaintenanceData();
// License 데이터 복원
print('License 데이터 복원 중...');
await _restoreLicenseData(backup['data'] as List);
print('\n롤백이 성공적으로 완료되었습니다.');
} else {
throw Exception('롤백 실패');
}
} catch (e) {
print('\n❌ 롤백 실패!');
print('오류: $e');
print('\n수동 복구가 필요할 수 있습니다.');
exit(1);
}
}
/// API 인증
Future<void> _authenticate() async {
try {
final response = await _dio.post('/auth/login', data: {
'email': 'admin@example.com',
'password': 'password123',
});
_authToken = response.data['token'];
_dio.options.headers['Authorization'] = 'Bearer $_authToken';
} catch (e) {
throw Exception('인증 실패: $e');
}
}
/// License 데이터 가져오기
Future<List<Map<String, dynamic>>> _fetchLicenseData() async {
try {
// 실제 환경에서는 API 호출
// 여기서는 더미 데이터 반환 (실제 구현 시 수정 필요)
return [
{
'id': 1,
'equipment_id': 1,
'license_type': 'O',
'period_months': 12,
'cost': 1000000,
'vendor_name': '삼성전자서비스',
'vendor_contact': '1588-3366',
'start_date': '2024-01-01T00:00:00Z',
'expiry_date': '2024-12-31T23:59:59Z',
'created_at': '2024-01-01T00:00:00Z',
},
// 추가 데이터...
];
} catch (e) {
throw Exception('License 데이터 로딩 실패: $e');
}
}
/// Equipment 데이터 가져오기
Future<List<Map<String, dynamic>>> _fetchEquipmentData() async {
try {
final response = await _dio.get('/equipments');
return (response.data['data'] as List)
.map((e) => e as Map<String, dynamic>)
.toList();
} catch (e) {
throw Exception('Equipment 데이터 로딩 실패: $e');
}
}
/// Equipment History 데이터 가져오기
Future<List<Map<String, dynamic>>> _fetchEquipmentHistoryData() async {
try {
final response = await _dio.get('/equipment_history');
return (response.data['data'] as List)
.map((e) => e as Map<String, dynamic>)
.toList();
} catch (e) {
// Equipment History가 아직 구현되지 않았을 수 있음
print(' ⚠️ Equipment History API 미구현, 빈 데이터 사용');
return [];
}
}
/// 백업 저장
Future<void> _saveBackup(Map<String, dynamic> backup) async {
final file = File(backupPath);
await file.writeAsString(jsonEncode(backup));
}
/// Maintenance 데이터 저장
Future<void> _saveMaintenanceData(List<Map<String, dynamic>> data) async {
for (final maintenance in data) {
try {
await _dio.post('/maintenances', data: maintenance);
} catch (e) {
print(' ⚠️ Maintenance #${maintenance['id']} 저장 실패: $e');
}
}
}
/// Maintenance 데이터 삭제
Future<void> _deleteMaintenanceData() async {
try {
// 모든 Maintenance 데이터 삭제
await _dio.delete('/maintenances/all');
} catch (e) {
print(' ⚠️ Maintenance 데이터 삭제 실패: $e');
}
}
/// License 데이터 복원
Future<void> _restoreLicenseData(List<dynamic> data) async {
for (final license in data) {
try {
await _dio.post('/licenses', data: license);
} catch (e) {
print(' ⚠️ License #${license['id']} 복원 실패: $e');
}
}
}
/// 샘플 데이터 출력 (DRY RUN용)
void _printSampleData(List<Map<String, dynamic>> data) {
print('\n 샘플 변환 데이터 (처음 3개):');
for (var i = 0; i < (data.length > 3 ? 3 : data.length); i++) {
final item = data[i];
print(' ${i + 1}. Maintenance #${item['id']}');
print(' - Equipment History ID: ${item['equipment_history_id']}');
print(' - Type: ${item['maintenance_type']}');
print(' - Period: ${item['period_months']} months');
print(' - Status: ${item['status']}');
print(' - Next Date: ${item['next_date']}');
}
}
/// 검증 보고서 출력
void _printValidationReport(ValidationReport report) {
print('\n 검증 결과:');
print(' - 전체 검증: ${report.isValid ? "✅ 통과" : "❌ 실패"}');
print(' - 데이터 무결성: ${report.dataIntegrity ? "" : ""}');
print(' - FK 관계: ${report.foreignKeyIntegrity ? "" : ""}');
print(' - 비즈니스 규칙: ${report.businessRulesValid ? "" : ""}');
if (report.warnings.isNotEmpty) {
print('\n 경고:');
for (final warning in report.warnings) {
print(' ⚠️ $warning');
}
}
if (report.errors.isNotEmpty) {
print('\n 오류:');
for (final error in report.errors) {
print('$error');
}
}
}
}
/// 메인 실행 함수
Future<void> main(List<String> args) async {
final executor = MigrationExecutor();
if (args.contains('--rollback')) {
await executor.rollback();
} else {
final isDryRun = args.contains('--dry-run');
await executor.execute(isDryRun: isDryRun);
}
}

View File

@@ -1,329 +0,0 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:superport/core/utils/debug_logger.dart';
import 'package:superport/core/config/environment.dart' as env;
/// 로그인 문제 진단을 위한 유틸리티 클래스
class LoginDiagnostics {
/// 로그인 프로세스 전체 진단
static Future<Map<String, dynamic>> runFullDiagnostics() async {
final results = <String, dynamic>{};
try {
// 1. 환경 설정 확인
results['environment'] = _checkEnvironment();
// 2. 네트워크 연결 확인
results['network'] = await _checkNetworkConnectivity();
// 3. API 엔드포인트 확인
results['apiEndpoint'] = await _checkApiEndpoint();
// 4. 모델 직렬화 테스트
results['serialization'] = _testSerialization();
// 5. 저장소 접근 테스트
results['storage'] = await _testStorageAccess();
DebugLogger.log(
'로그인 진단 완료',
tag: 'DIAGNOSTICS',
data: results,
);
return results;
} catch (e, stackTrace) {
DebugLogger.logError(
'진단 중 오류 발생',
error: e,
stackTrace: stackTrace,
);
return {
'error': e.toString(),
'stackTrace': stackTrace.toString(),
};
}
}
/// 환경 설정 확인
static Map<String, dynamic> _checkEnvironment() {
return {
'apiBaseUrl': env.Environment.apiBaseUrl,
'isDebugMode': kDebugMode,
'platform': defaultTargetPlatform.toString(),
};
}
/// 네트워크 연결 확인
static Future<Map<String, dynamic>> _checkNetworkConnectivity() async {
final dio = Dio();
final results = <String, dynamic>{};
try {
// Google DNS로 연결 테스트
final response = await dio.get('https://dns.google/resolve?name=google.com');
results['internetConnection'] = response.statusCode == 200;
} catch (e) {
results['internetConnection'] = false;
results['error'] = e.toString();
}
// API 서버 연결 테스트
try {
final response = await dio.get(
'${env.Environment.apiBaseUrl}/health',
options: Options(
validateStatus: (status) => status != null && status < 500,
),
);
results['apiServerReachable'] = true;
results['apiServerStatus'] = response.statusCode;
} catch (e) {
results['apiServerReachable'] = false;
results['apiServerError'] = e.toString();
}
return results;
}
/// API 엔드포인트 확인
static Future<Map<String, dynamic>> _checkApiEndpoint() async {
final dio = Dio();
final results = <String, dynamic>{};
try {
// OPTIONS 요청으로 CORS 확인
final response = await dio.request(
'${env.Environment.apiBaseUrl}/auth/login',
options: Options(
method: 'OPTIONS',
validateStatus: (status) => true,
),
);
results['corsEnabled'] = response.statusCode == 200 || response.statusCode == 204;
results['allowedMethods'] = response.headers['access-control-allow-methods'];
results['allowedHeaders'] = response.headers['access-control-allow-headers'];
// 실제 로그인 엔드포인트 테스트 (잘못된 자격 증명으로)
final loginResponse = await dio.post(
'${env.Environment.apiBaseUrl}/auth/login',
data: {
'email': 'test@test.com',
'password': 'test',
},
options: Options(
validateStatus: (status) => true,
),
);
results['loginEndpointStatus'] = loginResponse.statusCode;
results['loginResponseType'] = loginResponse.data?.runtimeType.toString();
// 응답 구조 분석
if (loginResponse.data is Map) {
final data = loginResponse.data as Map;
results['responseKeys'] = data.keys.toList();
results['hasSuccessField'] = data.containsKey('success');
results['hasDataField'] = data.containsKey('data');
results['hasAccessToken'] = data.containsKey('accessToken') || data.containsKey('access_token');
}
} catch (e) {
results['error'] = e.toString();
if (e is DioException) {
results['dioErrorType'] = e.type.toString();
results['dioMessage'] = e.message;
}
}
return results;
}
/// 모델 직렬화 테스트
static Map<String, dynamic> _testSerialization() {
final results = <String, dynamic>{};
try {
// LoginRequest 테스트
results['loginRequestValid'] = true;
// LoginResponse 테스트 (형식 1)
final loginResponse1 = {
'success': true,
'data': {
'accessToken': 'test_token',
'refreshToken': 'refresh_token',
'tokenType': 'Bearer',
'expiresIn': 3600,
'user': {
'id': 1,
'username': 'testuser',
'email': 'test@example.com',
'name': '테스트',
'role': 'USER',
},
},
};
results['format1Valid'] = _validateResponseFormat1(loginResponse1);
// LoginResponse 테스트 (형식 2)
final loginResponse2 = {
'accessToken': 'test_token',
'refreshToken': 'refresh_token',
'tokenType': 'Bearer',
'expiresIn': 3600,
'user': {
'id': 1,
'username': 'testuser',
'email': 'test@example.com',
'name': '테스트',
'role': 'USER',
},
};
results['format2Valid'] = _validateResponseFormat2(loginResponse2);
} catch (e) {
results['error'] = e.toString();
}
return results;
}
/// 저장소 접근 테스트
static Future<Map<String, dynamic>> _testStorageAccess() async {
final results = <String, dynamic>{};
try {
// 실제 FlutterSecureStorage 테스트는 의존성 주입이 필요하므로
// 여기서는 기본적인 체크만 수행
results['platformSupported'] = true;
// 플랫폼별 특이사항 체크
if (defaultTargetPlatform == TargetPlatform.iOS) {
results['note'] = 'iOS Keychain 사용';
} else if (defaultTargetPlatform == TargetPlatform.android) {
results['note'] = 'Android KeyStore 사용';
} else {
results['note'] = '웹 또는 데스크톱 플랫폼';
}
} catch (e) {
results['error'] = e.toString();
}
return results;
}
/// 응답 형식 1 검증
static bool _validateResponseFormat1(Map<String, dynamic> response) {
try {
if (!response.containsKey('success') || response['success'] != true) {
return false;
}
if (!response.containsKey('data') || response['data'] is! Map) {
return false;
}
final data = response['data'] as Map<String, dynamic>;
final requiredFields = ['accessToken', 'refreshToken', 'user'];
for (final field in requiredFields) {
if (!data.containsKey(field)) {
return false;
}
}
return true;
} catch (e) {
return false;
}
}
/// 응답 형식 2 검증
static bool _validateResponseFormat2(Map<String, dynamic> response) {
try {
final requiredFields = ['accessToken', 'refreshToken', 'user'];
for (final field in requiredFields) {
if (!response.containsKey(field)) {
return false;
}
}
if (response['user'] is! Map) {
return false;
}
return true;
} catch (e) {
return false;
}
}
/// 진단 결과를 읽기 쉬운 형식으로 포맷
static String formatDiagnosticsReport(Map<String, dynamic> diagnostics) {
final buffer = StringBuffer();
buffer.writeln('=== 로그인 진단 보고서 ===\n');
// 환경 설정
if (diagnostics.containsKey('environment')) {
buffer.writeln('## 환경 설정');
final env = diagnostics['environment'] as Map<String, dynamic>;
env.forEach((key, value) {
buffer.writeln('- $key: $value');
});
buffer.writeln();
}
// 네트워크 상태
if (diagnostics.containsKey('network')) {
buffer.writeln('## 네트워크 상태');
final network = diagnostics['network'] as Map<String, dynamic>;
buffer.writeln('- 인터넷 연결: ${network['internetConnection'] == true ? '' : ''}');
if (network.containsKey('apiServerReachable')) {
buffer.writeln('- API 서버 접근: ${network['apiServerReachable'] == true ? '' : ''}');
}
buffer.writeln();
}
// API 엔드포인트
if (diagnostics.containsKey('apiEndpoint')) {
buffer.writeln('## API 엔드포인트');
final api = diagnostics['apiEndpoint'] as Map<String, dynamic>;
if (api['skip'] == true) {
buffer.writeln('- Mock 모드로 건너뜀');
} else {
buffer.writeln('- CORS 활성화: ${api['corsEnabled'] == true ? '' : ''}');
buffer.writeln('- 로그인 엔드포인트 상태: ${api['loginEndpointStatus']}');
if (api.containsKey('responseKeys')) {
buffer.writeln('- 응답 키: ${api['responseKeys']}');
}
}
buffer.writeln();
}
// 직렬화 테스트
if (diagnostics.containsKey('serialization')) {
buffer.writeln('## 모델 직렬화');
final serial = diagnostics['serialization'] as Map<String, dynamic>;
buffer.writeln('- LoginRequest: ${serial['loginRequestValid'] == true ? '' : ''}');
buffer.writeln('- 응답 형식 1: ${serial['format1Valid'] == true ? '' : ''}');
buffer.writeln('- 응답 형식 2: ${serial['format2Valid'] == true ? '' : ''}');
buffer.writeln();
}
// 오류 정보
if (diagnostics.containsKey('error')) {
buffer.writeln('## ⚠️ 오류 발생');
buffer.writeln(diagnostics['error']);
}
return buffer.toString();
}
}