#!/usr/bin/env python3 import os import re from pathlib import Path from typing import Set, Dict, List, Tuple import subprocess class FinalDartFileAnalyzer: def __init__(self, project_root: str): self.project_root = Path(project_root) self.lib_path = self.project_root / "lib" # 확실히 사용되지 않는 파일들 (분석 결과 기반) self.definitely_unused = [ # Models - Equipment 관련 (백엔드 스키마 변경으로 미사용) "lib/data/models/equipment/equipment_in_request.dart", "lib/data/models/equipment/equipment_io_response.dart", "lib/data/models/equipment/equipment_list_dto.dart", "lib/data/models/equipment/equipment_out_request.dart", "lib/data/models/equipment/equipment_request.dart", "lib/data/models/equipment/equipment_response.dart", "lib/data/models/equipment_history_companies_link_dto.dart", "lib/models/user_phone_field.dart", # Administrator (UI에서 연결되지 않음) "lib/screens/administrator/administrator_list.dart", # Deprecated UI Components "lib/screens/common/custom_widgets/autocomplete_dropdown.dart", "lib/screens/common/custom_widgets/category_data.dart", "lib/screens/common/custom_widgets/category_selection_field.dart", "lib/screens/common/custom_widgets/date_picker_field.dart", "lib/screens/common/custom_widgets/highlight_text.dart", "lib/screens/common/widgets/address_input.dart", "lib/screens/common/widgets/autocomplete_dropdown_field.dart", "lib/screens/common/widgets/category_autocomplete_field.dart", "lib/screens/common/widgets/company_branch_dropdown.dart", # Company widgets (복잡한 UI에서 단순화됨) "lib/screens/company/widgets/company_branch_dialog.dart", "lib/screens/company/widgets/company_form_header.dart", "lib/screens/company/widgets/company_info_card.dart", "lib/screens/company/widgets/company_name_autocomplete.dart", "lib/screens/company/widgets/duplicate_company_dialog.dart", "lib/screens/company/widgets/map_dialog.dart", # Equipment widgets (Phase 4에서 단순화됨) "lib/screens/equipment/widgets/autocomplete_text_field.dart", "lib/screens/equipment/widgets/custom_dropdown_field.dart", "lib/screens/equipment/widgets/equipment_basic_info_section.dart", "lib/screens/equipment/widgets/equipment_history_panel.dart", "lib/screens/equipment/widgets/equipment_out_info.dart", "lib/screens/equipment/widgets/equipment_status_chip.dart", # Inventory components "lib/screens/inventory/components/stock_level_indicator.dart", "lib/screens/inventory/controllers/equipment_history_controller.dart", # Model components (단순화됨) "lib/screens/model/components/model_grouped_table.dart", "lib/screens/model/components/model_vendor_cascade.dart", # Rent (단순화됨) "lib/screens/rent/rent_list_screen_simple.dart", # Deprecated services "lib/services/health_check_service_web.dart", # Unused repositories "lib/data/repositories/lookups_repository_impl.dart", "lib/data/repositories/rent_repository.dart", "lib/domain/repositories/lookups_repository.dart", # Shadcn widgets (별도 컴포넌트로 대체됨) "lib/widgets/shadcn/shad_date_picker.dart", "lib/widgets/shadcn/shad_dialog.dart", "lib/widgets/shadcn/shad_select.dart", "lib/widgets/shadcn/shad_table.dart", # Utils (안전함) "lib/utils/address_constants.dart", "lib/utils/equipment_display_helper.dart", "lib/utils/formatters/business_number_formatter.dart", "lib/utils/user_utils.dart", # UseCase 통합 파일들 (개별 파일로 분리됨) "lib/domain/usecases/auth/auth_usecases.dart", "lib/domain/usecases/company/company_usecases.dart", "lib/domain/usecases/equipment/equipment_usecases.dart", "lib/domain/usecases/lookups/get_lookups_by_type.dart", "lib/domain/usecases/lookups/initialize_lookups.dart", "lib/domain/usecases/user/delete_user_usecase.dart", "lib/domain/usecases/user/get_user_detail_usecase.dart", "lib/domain/usecases/user/reset_password_usecase.dart", "lib/domain/usecases/user/toggle_user_status_usecase.dart", "lib/domain/usecases/user/update_user_usecase.dart", "lib/domain/usecases/user/user_usecases.dart", "lib/domain/usecases/warehouse_location/warehouse_location_usecases.dart", ] # 검토가 필요한 파일들 self.needs_review = [ # Core utils (사용될 수 있음) "lib/core/utils/formatters.dart", "lib/core/utils/login_diagnostics.dart", "lib/core/utils/validators.dart", # Migration 관련 (일회성이지만 중요) "lib/core/migrations/execute_migration.dart", "lib/core/migrations/license_to_maintenance_migration.dart", "lib/core/migrations/maintenance_data_validator.dart", # Theme (사용될 수 있음) "lib/core/theme/shadcn_theme.dart", ] def check_git_status(self) -> List[str]: """Git에서 삭제된 dart 파일들 확인""" try: result = subprocess.run(['git', 'status', '--porcelain'], cwd=self.project_root, capture_output=True, text=True) git_status = result.stdout deleted_files = [] for line in git_status.split('\n'): if line.startswith('D ') and line.endswith('.dart'): deleted_files.append(line[3:]) # Remove 'D ' prefix return deleted_files except Exception as e: print(f"Git status 확인 실패: {e}") return [] def verify_file_usage(self, file_path: str) -> Tuple[bool, List[str]]: """파일이 실제로 사용되는지 grep으로 확인""" path_obj = Path(file_path) if not path_obj.exists(): return False, ["파일이 존재하지 않음"] # 파일명에서 클래스/타입 이름 추출 filename = path_obj.stem possible_names = [ filename, ''.join(word.capitalize() for word in filename.split('_')), filename.replace('_', ''), ] references = [] try: for name in possible_names: result = subprocess.run( ['grep', '-r', '-l', name, 'lib/'], cwd=self.project_root, capture_output=True, text=True ) if result.returncode == 0: found_files = result.stdout.strip().split('\n') # 자기 자신과 생성 파일 제외 filtered_files = [f for f in found_files if f != file_path and not f.endswith('.g.dart') and not f.endswith('.freezed.dart')] references.extend(filtered_files) except Exception: pass return len(references) > 0, list(set(references)) def generate_final_report(self) -> None: """최종 삭제 권장 보고서 생성""" print("\n" + "="*80) print("FLUTTER 프로젝트 사용되지 않는 파일 최종 분석 보고서") print("="*80) print(f"📂 프로젝트: {self.project_root}") print(f"📅 분석 일시: 2025-08-29 (Phase 10 완료 후)") # Git 상태 확인 deleted_files = self.check_git_status() print(f"\n📋 Git Status:") if deleted_files: print(f" 🗑️ 이미 삭제된 파일: {len(deleted_files)}개") for deleted in deleted_files[:5]: print(f" - {deleted}") if len(deleted_files) > 5: print(f" ... 및 {len(deleted_files) - 5}개 더") else: print(" ✅ 삭제된 dart 파일 없음") print(f"\n🎯 삭제 권장 파일 분석:") print(f" ✅ 안전 삭제 가능: {len(self.definitely_unused)}개") print(f" ⚠️ 검토 후 삭제: {len(self.needs_review)}개") # 카테고리별 안전 삭제 가능 파일들 categories = { 'Equipment Models (백엔드 스키마 변경)': [f for f in self.definitely_unused if 'equipment/' in f or 'equipment_' in f], 'Administrator (UI 미연결)': [f for f in self.definitely_unused if 'administrator' in f], 'Deprecated UI Components': [f for f in self.definitely_unused if 'widgets/' in f or 'custom_widgets/' in f], 'Company Widgets (단순화됨)': [f for f in self.definitely_unused if 'company/widgets/' in f], 'Equipment Widgets (Phase 4 단순화)': [f for f in self.definitely_unused if 'equipment/widgets/' in f], 'Shadcn Components (대체됨)': [f for f in self.definitely_unused if 'widgets/shadcn/' in f], 'UseCase 통합 파일들': [f for f in self.definitely_unused if 'usecases/' in f and ('_usecases.dart' in f or 'lookups/' in f)], '기타 Utils 및 Services': [f for f in self.definitely_unused if f not in sum([ [f for f in self.definitely_unused if 'equipment/' in f or 'equipment_' in f], [f for f in self.definitely_unused if 'administrator' in f], [f for f in self.definitely_unused if 'widgets/' in f or 'custom_widgets/' in f], [f for f in self.definitely_unused if 'company/widgets/' in f], [f for f in self.definitely_unused if 'equipment/widgets/' in f], [f for f in self.definitely_unused if 'widgets/shadcn/' in f], [f for f in self.definitely_unused if 'usecases/' in f and ('_usecases.dart' in f or 'lookups/' in f)], ], [])], } print(f"\n📂 카테고리별 안전 삭제 가능 파일:") total_safe = 0 for category, files in categories.items(): if files: print(f"\n📁 {category} ({len(files)}개):") for file_path in sorted(files)[:5]: # 처음 5개만 표시 print(f" ✅ {file_path}") if len(files) > 5: print(f" ... 및 {len(files) - 5}개 더") total_safe += len(files) print(f"\n⚠️ 검토 필요 파일들 ({len(self.needs_review)}개):") for file_path in self.needs_review: print(f" ⚠️ {file_path}") if 'formatters.dart' in file_path: print(f" → 유틸리티 함수들이 다른 곳에서 사용될 수 있음") elif 'migration' in file_path: print(f" → 마이그레이션 완료 후 삭제 가능") elif 'theme' in file_path: print(f" → 테마 정의 파일, 실제 사용 여부 확인 필요") print(f"\n🚀 즉시 실행 가능한 삭제 명령어:") print(f"# 1단계: 안전 삭제 가능 파일들 (백업 권장)") # 10개씩 나누어 삭제 명령 생성 chunks = [self.definitely_unused[i:i+10] for i in range(0, len(self.definitely_unused), 10)] for i, chunk in enumerate(chunks, 1): print(f"\n# 1단계-{i}: Equipment/UI 관련 파일들 ({len(chunk)}개)") files_str = ' \\\n '.join(f'"{f}"' for f in chunk) print(f"rm {files_str}") print(f"\n# 2단계: 생성 파일들 정리 (자동 재생성됨)") print(f"flutter packages pub run build_runner clean") print(f"flutter packages pub run build_runner build --delete-conflicting-outputs") print(f"\n# 3단계: 분석 재실행으로 효과 확인") print(f"flutter analyze") print(f"\n💡 삭제 후 예상 효과:") print(f" 📊 파일 수: 290개 → {290 - len(self.definitely_unused)}개 (약 {len(self.definitely_unused)}개 감소)") print(f" 🧹 코드 정리: 사용되지 않는 Equipment 모델, UI 컴포넌트, UseCase 파일 제거") print(f" 🚀 빌드 성능: 불필요한 파일 컴파일 시간 단축") print(f" 📦 앱 크기: 미사용 코드 제거로 번들 크기 최적화") print(f"\n⚠️ 중요 주의사항:") print(f" 1. 삭제 전 git commit으로 백업 생성 필수") print(f" 2. 삭제 후 flutter analyze와 flutter test 실행") print(f" 3. 문제 발생 시 git reset --hard HEAD~1로 복원") print(f" 4. Phase 10 완료 상태에서 안전한 파일들만 선별됨") print(f"\n🎯 권장 삭제 순서:") print(f" 1️⃣ Equipment 관련 모델 파일 (8개) - 백엔드 스키마 변경으로 확실히 미사용") print(f" 2️⃣ UI 위젯 컴포넌트 (20+ 개) - Phase 4-7에서 단순화로 미사용 확인") print(f" 3️⃣ UseCase 통합 파일 (10개) - 개별 파일로 분리되어 미사용") print(f" 4️⃣ 기타 Utils 및 Services (나머지) - import 없음 확인됨") print("\n" + "="*80) def main(): project_root = "/Users/maximilian.j.sul/Documents/flutter/superport" analyzer = FinalDartFileAnalyzer(project_root) analyzer.generate_final_report() if __name__ == "__main__": main()