✅ 백엔드 구조 개선: - DatabaseService: 공통 DB 쿼리 로직 통합 - FileUploadService: 파일 업로드 로직 모듈화 및 트랜잭션 관리 개선 - 서비스 레이어 패턴 도입으로 코드 재사용성 향상 ✅ 프론트엔드 컴포넌트 개선: - LoadingSpinner, ErrorMessage, ConfirmDialog 공통 컴포넌트 생성 - 재사용 가능한 컴포넌트 라이브러리 구축 - deprecated/backup 파일들 완전 제거 ✅ 성능 최적화: - optimize_database.py: 핵심 DB 인덱스 자동 생성 - 쿼리 최적화 및 통계 업데이트 자동화 - VACUUM ANALYZE 자동 실행 ✅ 코드 정리: - 개별 SQL 마이그레이션 파일들을 legacy/ 폴더로 정리 - 중복된 마이그레이션 스크립트 정리 - 깔끔하고 체계적인 프로젝트 구조 완성 ✅ 자동 마이그레이션 시스템 강화: - complete_migrate.py: SQLAlchemy 기반 완전한 마이그레이션 - analyze_and_fix_schema.py: 백엔드 코드 분석 기반 스키마 수정 - fix_missing_tables.py: 누락된 테이블/컬럼 자동 생성 - start.sh: 배포 시 자동 실행 순서 최적화
This commit is contained in:
126
backend/scripts/simple_migrate.py
Normal file
126
backend/scripts/simple_migrate.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TK-MP-Project 간단하고 안정적인 DB 마이그레이션 스크립트
|
||||
macOS Docker와 Synology Container Manager 모두 지원
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import psycopg2
|
||||
from datetime import datetime
|
||||
|
||||
def get_db_config():
|
||||
"""환경변수에서 DB 설정 가져오기"""
|
||||
return {
|
||||
'host': os.getenv('DB_HOST', 'postgres'),
|
||||
'port': int(os.getenv('DB_PORT', 5432)),
|
||||
'database': os.getenv('DB_NAME', 'tk_mp_bom'),
|
||||
'user': os.getenv('DB_USER', 'tkmp_user'),
|
||||
'password': os.getenv('DB_PASSWORD', 'tkmp_password')
|
||||
}
|
||||
|
||||
def wait_for_database(max_retries=60, retry_interval=2):
|
||||
"""데이터베이스 연결 대기 (더 긴 대기시간과 짧은 간격)"""
|
||||
db_config = get_db_config()
|
||||
|
||||
for attempt in range(1, max_retries + 1):
|
||||
try:
|
||||
conn = psycopg2.connect(**db_config)
|
||||
conn.close()
|
||||
print(f"✅ 데이터베이스 연결 성공 (시도 {attempt}/{max_retries})")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"⏳ DB 연결 대기 중... ({attempt}/{max_retries}) - {str(e)[:50]}")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
print("❌ 데이터베이스 연결 실패")
|
||||
return False
|
||||
|
||||
def execute_sql(sql_commands):
|
||||
"""SQL 명령어 실행"""
|
||||
db_config = get_db_config()
|
||||
|
||||
try:
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cursor = conn.cursor()
|
||||
|
||||
for sql in sql_commands:
|
||||
if sql.strip():
|
||||
cursor.execute(sql)
|
||||
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ SQL 실행 실패: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_migration_sql():
|
||||
"""필요한 마이그레이션 SQL 반환"""
|
||||
return [
|
||||
# materials 테이블 컬럼 추가
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS row_number INTEGER;",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS main_nom VARCHAR(50);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS red_nom VARCHAR(50);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS full_material_grade TEXT;",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS length NUMERIC(10,3);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS purchase_confirmed BOOLEAN DEFAULT FALSE;",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS confirmed_quantity NUMERIC(10,3);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS purchase_status VARCHAR(20);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS purchase_confirmed_by VARCHAR(100);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS purchase_confirmed_at TIMESTAMP;",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS revision_status VARCHAR(20);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS material_hash VARCHAR(64);",
|
||||
"ALTER TABLE materials ADD COLUMN IF NOT EXISTS normalized_description TEXT;",
|
||||
|
||||
# users 테이블 status 컬럼 확인 및 추가
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'active';",
|
||||
"UPDATE users SET status = 'active' WHERE status IS NULL AND is_active = TRUE;",
|
||||
"UPDATE users SET status = 'inactive' WHERE status IS NULL AND is_active = FALSE;",
|
||||
|
||||
# 기본 관리자 계정 확인 및 생성
|
||||
"""
|
||||
INSERT INTO users (username, password, name, email, role, access_level, department, position, status)
|
||||
VALUES ('admin', '$2b$12$ld4LDOW5mxkiRQEkXfMUIep/aIzFleQZ4yoL10ZQkUxGqnkYuhNMW', '시스템 관리자', 'admin@tkmp.com', 'admin', 'admin', 'IT', '시스템 관리자', 'active')
|
||||
ON CONFLICT (username) DO UPDATE SET
|
||||
password = EXCLUDED.password,
|
||||
status = 'active';
|
||||
""",
|
||||
|
||||
# 인덱스 생성
|
||||
"CREATE INDEX IF NOT EXISTS idx_materials_main_nom ON materials(main_nom);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_materials_red_nom ON materials(red_nom);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_materials_revision_status ON materials(revision_status);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_materials_purchase_status ON materials(purchase_status);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);",
|
||||
]
|
||||
|
||||
def main():
|
||||
"""메인 실행 함수"""
|
||||
print("🚀 TK-MP-Project 간단 DB 마이그레이션 시작")
|
||||
print(f"⏰ 시작 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
# 1. 데이터베이스 연결 대기
|
||||
print("🔄 데이터베이스 연결 확인 중...")
|
||||
if not wait_for_database():
|
||||
print("❌ 데이터베이스 연결 실패. 마이그레이션을 중단합니다.")
|
||||
sys.exit(1)
|
||||
|
||||
# 2. 마이그레이션 실행
|
||||
print("🔄 데이터베이스 마이그레이션 실행 중...")
|
||||
migration_sql = get_migration_sql()
|
||||
|
||||
if execute_sql(migration_sql):
|
||||
print("✅ 데이터베이스 마이그레이션 완료")
|
||||
else:
|
||||
print("❌ 데이터베이스 마이그레이션 실패")
|
||||
sys.exit(1)
|
||||
|
||||
print("🎉 간단 DB 마이그레이션 완료!")
|
||||
print(f"⏰ 완료 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("👤 기본 계정: admin/admin123")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user