🔧 완전한 스키마 자동화 시스템 구축
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

 주요 기능:
- 완전한 데이터베이스 스키마 분석 및 자동 마이그레이션 시스템
- 44개 테이블 완전 지원 (운영 서버 43개 + 1개 추가)
- 누락된 테이블/컬럼 자동 감지 및 생성

🔧 해결된 스키마 문제:
- users.status 컬럼 누락 → 자동 추가
- files 테이블 4개 컬럼 누락 → 자동 추가
- materials 테이블 22개 컬럼 누락 → 자동 추가
- support_details, purchase_requests, purchase_request_items 테이블 누락 → 자동 생성
- material_purchase_tracking.description, purchase_status 컬럼 누락 → 자동 추가

🚀 자동화 도구:
- schema_analyzer.py: 코드와 DB 스키마 비교 분석
- auto_migrator.py: 자동 마이그레이션 실행
- docker_migrator.py: Docker 환경용 간편 마이그레이션
- schema_monitor.py: 실시간 스키마 모니터링

📋 리비전 관리 시스템:
- 8개 카테고리별 리비전 페이지 구현
- PIPE Cutting Plan 관리 시스템
- PIPE Issue Management 시스템
- 완전한 리비전 비교 및 추적 기능

🎯 사용법:
docker exec tk-mp-backend python3 scripts/docker_migrator.py

앞으로 스키마 문제가 발생하면 위 명령 하나로 자동 해결!
This commit is contained in:
Hyungi Ahn
2025-10-21 10:34:45 +09:00
parent 9d7165bbf9
commit 8f42a1054e
55 changed files with 22443 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
#!/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()