# πŸ—„οΈ DB 변경사항 둜그 > **μ€‘μš”**: 배포 μ‹œ λ°˜λ“œμ‹œ 이 λ¬Έμ„œλ₯Ό ν™•μΈν•˜μ—¬ DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ μˆœμ„œλŒ€λ‘œ μ‹€ν–‰ν•˜μ„Έμš”. ## πŸ“‹ 변경사항 λͺ©λ‘ ### 2025.10.26 - 관리함 μ™„λ£Œ μ‹ μ²­ 정보 μˆ˜μ • κΈ°λŠ₯ μΆ”κ°€ **🎯 λͺ©μ **: κ΄€λ¦¬ν•¨μ—μ„œ μ™„λ£Œ 사진 μ—…λ‘œλ“œ/ꡐ체 및 μ™„λ£Œ μ½”λ©˜νŠΈ μˆ˜μ • κΈ°λŠ₯ κ΅¬ν˜„ #### πŸ“ 파일 변경사항 **1. λ°±μ—”λ“œ μŠ€ν‚€λ§ˆ μΆ”κ°€** - **파일**: `backend/database/schemas.py` - **λ³€κ²½λ‚΄μš©**: `ManagementUpdateRequest` 클래슀 μΆ”κ°€ - **μ½”λ“œ**: ```python class ManagementUpdateRequest(BaseModel): """κ΄€λ¦¬ν•¨μ—μ„œ 이슈 μ—…λ°μ΄νŠΈ μš”μ²­""" final_description: Optional[str] = None final_category: Optional[IssueCategory] = None solution: Optional[str] = None responsible_department: Optional[DepartmentType] = None responsible_person: Optional[str] = None expected_completion_date: Optional[str] = None cause_department: Optional[DepartmentType] = None management_comment: Optional[str] = None completion_comment: Optional[str] = None completion_photo: Optional[str] = None # Base64 review_status: Optional[ReviewStatus] = None ``` **2. λ°±μ—”λ“œ API κ°œμ„ ** - **파일**: `backend/routers/management.py` - **λ³€κ²½λ‚΄μš©**: `PUT /api/management/{issue_id}` μ—”λ“œν¬μΈνŠΈμ— μ™„λ£Œ 사진 처리 둜직 μΆ”κ°€ - **μ½”λ“œ**: ```python if field == 'completion_photo' and value: # μ™„λ£Œ 사진 Base64 처리 from services.file_service import save_base64_image try: photo_path = save_base64_image(value, "completion_") issue.completion_photo_path = photo_path except Exception as e: print(f"μ™„λ£Œ 사진 μ €μž₯ μ‹€νŒ¨: {e}") continue ``` **3. ν”„λ‘ νŠΈμ—”λ“œ 톡합 λͺ¨λ‹¬** - **파일**: `frontend/issues-management.html` - **λ³€κ²½λ‚΄μš©**: - μ§„ν–‰ 쀑/μ™„λ£Œ λŒ€κΈ° μƒνƒœ λͺ¨λ‘ λ™μΌν•œ μˆ˜μ • κ°€λŠ₯ν•œ λͺ¨λ‹¬ μ‚¬μš© - μ™„λ£Œ 사진 μ—…λ‘œλ“œ/ꡐ체 λ²„νŠΌ μΆ”κ°€ - μ™„λ£Œ μ½”λ©˜νŠΈ ν…μŠ€νŠΈ μ˜μ—­ μˆ˜μ • κ°€λŠ₯ - `loadManagementData()` β†’ `initializeManagement()` ν•¨μˆ˜λͺ… μˆ˜μ • **4. DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜** - **파일**: `backend/migrations/020_add_management_completion_fields.sql` - **λͺ©μ **: μ™„λ£Œ μ‹ μ²­ κ΄€λ ¨ μ»¬λŸΌλ“€μ΄ λˆ„λ½λœ 경우 μΆ”κ°€ (이미 μ‘΄μž¬ν•  수 있음) #### πŸš€ 배포 μ‹œ μ‹€ν–‰ μˆœμ„œ ```bash # 1. λ°μ΄ν„°λ² μ΄μŠ€ λ°±μ—… docker-compose exec postgres pg_dump -U postgres -d m_project > backup_$(date +%Y%m%d_%H%M%S).sql # 2. λ°±μ—”λ“œ μ½”λ“œ 배포 git pull origin master docker-compose down docker-compose build backend docker-compose up -d # 3. DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ docker-compose exec backend python -c " import sys sys.path.append('/app') import psycopg2 import os conn = psycopg2.connect( host=os.getenv('DB_HOST', 'postgres'), database=os.getenv('DB_NAME', 'm_project'), user=os.getenv('DB_USER', 'postgres'), password=os.getenv('DB_PASSWORD', 'password') ) with open('/app/migrations/020_add_management_completion_fields.sql', 'r', encoding='utf-8') as f: migration_sql = f.read() with conn.cursor() as cursor: cursor.execute(migration_sql) conn.commit() print('βœ… λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ™„λ£Œ') conn.close() " # 4. 배포 ν›„ 검증 docker-compose logs backend --tail=20 docker-compose exec postgres psql -U postgres -d m_project -c "SELECT COUNT(*) FROM migration_log WHERE migration_file = '020_add_management_completion_fields.sql';" ``` #### ⚠️ μ£Όμ˜μ‚¬ν•­ - μ™„λ£Œ μ‹ μ²­ κ΄€λ ¨ μ»¬λŸΌλ“€μ΄ 이미 μ‘΄μž¬ν•  수 μžˆμœΌλ―€λ‘œ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μŠ€ν¬λ¦½νŠΈλŠ” μ•ˆμ „ν•˜κ²Œ μž‘μ„±λ¨ - λΈŒλΌμš°μ € μΊμ‹œ 문제둜 인해 κ°•μ œ μƒˆλ‘œκ³ μΉ¨(Ctrl+Shift+F5) ν•„μš”ν•  수 있음 - 422 μ—λŸ¬ λ°œμƒ μ‹œ λ°±μ—”λ“œ μž¬μ‹œμž‘ ν•„μš” --- ### 2025.10.26 - ν”„λ‘œμ νŠΈλ³„ 순번 μžλ™ ν• λ‹Ή κ°œμ„  **🎯 λͺ©μ **: μˆ˜μ‹ ν•¨μ—μ„œ μ§„ν–‰ 쀑/μ™„λ£Œλ‘œ μƒνƒœ λ³€κ²½ μ‹œ ν”„λ‘œμ νŠΈλ³„ 순번이 μžλ™ ν• λ‹Ήλ˜λ„λ‘ κ°œμ„  #### πŸ“ 파일 변경사항 **1. λ°±μ—”λ“œ 둜직 μˆ˜μ •** - **파일**: `backend/routers/inbox.py` - **λ³€κ²½λ‚΄μš©**: `update_issue_status` ν•¨μˆ˜μ— `project_sequence_no` μžλ™ ν• λ‹Ή 둜직 μΆ”κ°€ - **μ½”λ“œ**: ```python # μ§„ν–‰ 쀑 λ˜λŠ” μ™„λ£Œ μƒνƒœλ‘œ λ³€κ²½ μ‹œ ν”„λ‘œμ νŠΈλ³„ 순번 μžλ™ ν• λ‹Ή if status_request.review_status in [ReviewStatus.in_progress, ReviewStatus.completed]: if not issue.project_sequence_no: from sqlalchemy import text result = db.execute( text("SELECT generate_project_sequence_no(:project_id)"), {"project_id": issue.project_id} ) issue.project_sequence_no = result.scalar() ``` **2. 데이터 보정 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜** - **파일**: `backend/migrations/017_fix_project_sequence_no.sql` - **λͺ©μ **: 기쑴에 μ§„ν–‰ 쀑/μ™„λ£Œ μƒνƒœμΈλ° `project_sequence_no`κ°€ λˆ„λ½λœ 데이터 보정 #### πŸš€ 배포 μ‹œ μ‹€ν–‰ μˆœμ„œ ```bash # 1. λ°±μ—”λ“œ μ½”λ“œ 배포 git pull origin master # 2. DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ (Docker ν™˜κ²½) docker-compose exec backend python -c " import psycopg2 import os database_url = os.getenv('DATABASE_URL', 'postgresql://postgres:password@db:5432/mproject') conn = psycopg2.connect(database_url) cur = conn.cursor() with open('migrations/017_fix_project_sequence_no.sql', 'r', encoding='utf-8') as f: cur.execute(f.read()) conn.commit() conn.close() print('βœ… λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ™„λ£Œ') " # 3. μ„œλΉ„μŠ€ μž¬μ‹œμž‘ docker-compose restart backend ``` #### πŸ” 검증 방법 **1. DBμ—μ„œ 직접 확인**: ```sql -- 관리 쀑인 이슈 쀑 순번이 λˆ„λ½λœ 것이 μžˆλŠ”μ§€ 확인 SELECT COUNT(*) as missing_count FROM issues WHERE review_status IN ('in_progress', 'completed') AND project_sequence_no IS NULL; -- κ²°κ³Ό: 0이어야 함 -- ν”„λ‘œμ νŠΈλ³„ 순번이 μ˜¬λ°”λ₯΄κ²Œ ν• λ‹Ήλ˜μ—ˆλŠ”μ§€ 확인 SELECT project_id, COUNT(*) as total_issues, MIN(project_sequence_no) as min_no, MAX(project_sequence_no) as max_no FROM issues WHERE review_status IN ('in_progress', 'completed') GROUP BY project_id ORDER BY project_id; -- κ²°κ³Ό: 각 ν”„λ‘œμ νŠΈλ³„λ‘œ 1λΆ€ν„° μ—°μ†λœ λ²ˆν˜Έκ°€ ν• λ‹Ήλ˜μ–΄μ•Ό 함 ``` **2. ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ 확인**: - μˆ˜μ‹ ν•¨μ—μ„œ μƒˆλ‘œμš΄ 이슈λ₯Ό "μ§„ν–‰ 쀑"으둜 λ³€κ²½ - ν˜„ν™©νŒμ—μ„œ "No.1, No.2..." ν˜•νƒœλ‘œ ν”„λ‘œμ νŠΈλ³„ 순번이 ν‘œμ‹œλ˜λŠ”μ§€ 확인 #### ⚠️ μ£Όμ˜μ‚¬ν•­ 1. **λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ μ „ λ°±μ—…**: μ€‘μš”ν•œ λ°μ΄ν„°μ΄λ―€λ‘œ μ‹€ν–‰ μ „ DB λ°±μ—… ꢌμž₯ 2. **μˆœμ„œ μ€€μˆ˜**: λ°˜λ“œμ‹œ `016_add_management_fields.sql`이 λ¨Όμ € μ‹€ν–‰λœ μƒνƒœμ—¬μ•Ό 함 3. **Docker ν™˜κ²½**: λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ€ Docker μ»¨ν…Œμ΄λ„ˆ λ‚΄μ—μ„œ μ‹€ν–‰ν•΄μ•Ό 함 4. **migration_log ν…Œμ΄λΈ” ꡬ쑰**: - 컬럼λͺ…: `migration_file` (migration_name μ•„λ‹˜) - ν•„μˆ˜ 컬럼: `migration_file`, `executed_at`, `status`, `notes` 5. **λ‘€λ°± 방법**: 문제 λ°œμƒ μ‹œ `project_sequence_no` μ»¬λŸΌμ„ NULL둜 μ„€μ • ν›„ μž¬μ‹€ν–‰ #### πŸ“Š μ˜ˆμƒ κ²°κ³Ό **Before**: ``` ν˜„ν™©νŒ: No. (λΉˆκ°’) ν”„λ‘œμ νŠΈ A: μ΄μŠˆλ“€μ΄ 전체 톡합 번호둜 ν‘œμ‹œ (No.2, No.5, No.8...) ``` **After**: ``` ν˜„ν™©νŒ: No.1, No.2, No.3... ν”„λ‘œμ νŠΈ A: No.1, No.2, No.3 ν”„λ‘œμ νŠΈ B: No.1, No.2, No.3 각 ν”„λ‘œμ νŠΈλ³„λ‘œ 1λΆ€ν„° μ‹œμž‘ν•˜λŠ” κΉ”λ”ν•œ 순번 ``` --- ## πŸ“ 이전 변경사항 ### 2025.10.25 - 관리함 ν•„λ“œ μΆ”κ°€ - **파일**: `backend/migrations/016_add_management_fields.sql` - **λ‚΄μš©**: κ΄€λ¦¬ν•¨μ—μ„œ μ‚¬μš©ν•  μΆ”κ°€ ν•„λ“œλ“€ 및 `generate_project_sequence_no()` ν•¨μˆ˜ 생성 ### 2025.10.24 - μ‚¬μš©μž λΆ€μ„œ 정보 μΆ”κ°€ - **파일**: `backend/migrations/015_add_user_department.sql` - **λ‚΄μš©**: μ‚¬μš©μž ν…Œμ΄λΈ”μ— λΆ€μ„œ 정보 컬럼 μΆ”κ°€ --- ## πŸ”§ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ 도ꡬ **μžλ™ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 슀크립트** (μΆ”ν›„ 개발 μ˜ˆμ •): ```bash #!/bin/bash # run_migrations.sh # λͺ¨λ“  λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ μˆœμ„œλŒ€λ‘œ μ‹€ν–‰ν•˜λŠ” 슀크립트 ``` **λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μƒνƒœ 확인**: ```sql -- 졜근 μ‹€ν–‰λœ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 확인 SELECT migration_file, executed_at, status, notes FROM migration_log ORDER BY executed_at DESC LIMIT 10; -- νŠΉμ • λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 확인 SELECT * FROM migration_log WHERE migration_file = '017_fix_project_sequence_no.sql'; ``` #### πŸ“‹ μ‹€ν–‰ μ™„λ£Œ μƒνƒœ (2025.10.26) βœ… **λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ¨** - **μ‹€ν–‰ μ‹œκ°„**: 2025-10-26 11:15:44+09:00 - **μƒνƒœ**: SUCCESS - **확인사항**: - `generate_project_sequence_no()` ν•¨μˆ˜ 쑴재 βœ… - `issues.project_sequence_no` 컬럼 쑴재 βœ… - 순번이 λˆ„λ½λœ 관리 쀑인 이슈: 0개 βœ… - λ°±μ—”λ“œ μ„œλΉ„μŠ€ μž¬μ‹œμž‘ μ™„λ£Œ βœ