🔄 전반적인 시스템 리팩토링 완료
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

 백엔드 구조 개선:
- 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:
Hyungi Ahn
2025-10-20 08:41:06 +09:00
parent 0c99697a6f
commit 3398f71b80
61 changed files with 3370 additions and 4512 deletions

View 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()