#!/usr/bin/env python3 """ 자동 마이그레이션 실행기 - 스키마 분석 결과를 바탕으로 자동으로 DB를 업데이트 """ import os import sys import psycopg2 from psycopg2.extras import RealDictCursor from datetime import datetime from schema_analyzer import SchemaAnalyzer class AutoMigrator: def __init__(self): self.db_config = { 'host': 'localhost', 'port': 5432, 'database': 'tk_mp_bom', 'user': 'tkmp_user', 'password': 'tkmp_password' } self.analyzer = SchemaAnalyzer() self.migration_log = [] def run_full_analysis_and_migration(self): """전체 분석 및 마이그레이션 실행""" print("🚀 자동 마이그레이션 시작") # 1. 스키마 분석 print("\n1️⃣ 스키마 분석 중...") self.analyzer.analyze_code_models() self.analyzer.analyze_db_schema() analysis_result = self.analyzer.compare_schemas() # 2. 분석 결과 확인 if not analysis_result['missing_tables'] and not analysis_result['missing_columns']: print("✅ 스키마가 이미 최신 상태입니다!") return True # 3. 마이그레이션 실행 print("\n2️⃣ 마이그레이션 실행 중...") success = self._execute_migrations(analysis_result) # 4. 결과 로깅 self._log_migration_result(success, analysis_result) return success def _execute_migrations(self, analysis_result) -> bool: """마이그레이션 실행""" try: conn = psycopg2.connect(**self.db_config) cursor = conn.cursor() # 트랜잭션 시작 conn.autocommit = False # 1. 누락된 테이블 생성 for table_name in analysis_result['missing_tables']: success = self._create_missing_table(cursor, table_name) if not success: conn.rollback() return False # 2. 누락된 컬럼 추가 for missing_col in analysis_result['missing_columns']: success = self._add_missing_column(cursor, missing_col) if not success: conn.rollback() return False # 커밋 conn.commit() cursor.close() conn.close() print("✅ 마이그레이션 성공!") return True except Exception as e: print(f"❌ 마이그레이션 실패: {e}") if 'conn' in locals(): conn.rollback() return False def _create_missing_table(self, cursor, table_name: str) -> bool: """누락된 테이블 생성""" try: create_sql = self.analyzer._generate_create_table_sql(table_name) print(f" 📋 테이블 생성: {table_name}") cursor.execute(create_sql) self.migration_log.append({ 'type': 'CREATE_TABLE', 'table': table_name, 'sql': create_sql, 'timestamp': datetime.now().isoformat() }) return True except Exception as e: print(f" ❌ 테이블 생성 실패 {table_name}: {e}") return False def _add_missing_column(self, cursor, missing_col: dict) -> bool: """누락된 컬럼 추가""" try: alter_sql = self.analyzer._generate_add_column_sql(missing_col) print(f" 🔧 컬럼 추가: {missing_col['table']}.{missing_col['column']}") cursor.execute(alter_sql) self.migration_log.append({ 'type': 'ADD_COLUMN', 'table': missing_col['table'], 'column': missing_col['column'], 'sql': alter_sql, 'timestamp': datetime.now().isoformat() }) return True except Exception as e: print(f" ❌ 컬럼 추가 실패 {missing_col['table']}.{missing_col['column']}: {e}") return False def _log_migration_result(self, success: bool, analysis_result: dict): """마이그레이션 결과 로깅""" log_entry = { 'timestamp': datetime.now().isoformat(), 'success': success, 'analysis_result': analysis_result, 'migration_log': self.migration_log } # 로그 파일에 저장 import json log_filename = f"migration_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" with open(log_filename, 'w', encoding='utf-8') as f: json.dump(log_entry, f, indent=2, ensure_ascii=False) print(f"📄 마이그레이션 로그 저장: {log_filename}") def fix_immediate_issues(self): """즉시 해결이 필요한 문제들 수정""" print("🔧 즉시 해결 필요한 문제들 수정 중...") immediate_fixes = [ { 'description': 'users 테이블에 status 컬럼 추가', 'sql': "ALTER TABLE users ADD COLUMN status VARCHAR(20) DEFAULT 'active'", 'check_sql': "SELECT column_name FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'status'" } ] try: conn = psycopg2.connect(**self.db_config) cursor = conn.cursor(cursor_factory=RealDictCursor) for fix in immediate_fixes: # 이미 존재하는지 확인 cursor.execute(fix['check_sql']) if cursor.fetchone(): print(f" ✅ 이미 존재: {fix['description']}") continue # 수정 실행 cursor.execute(fix['sql']) conn.commit() print(f" 🔧 수정 완료: {fix['description']}") cursor.close() conn.close() return True except Exception as e: print(f"❌ 즉시 수정 실패: {e}") return False def main(): migrator = AutoMigrator() # 1. 즉시 해결 필요한 문제들 수정 migrator.fix_immediate_issues() # 2. 전체 분석 및 마이그레이션 success = migrator.run_full_analysis_and_migration() if success: print("\n🎉 모든 마이그레이션이 성공적으로 완료되었습니다!") else: print("\n💥 마이그레이션 중 오류가 발생했습니다. 로그를 확인해주세요.") return success if __name__ == "__main__": main()