# 🌐 Flask + Jinja2 Headplane 고도화 κ΄€λ¦¬μž νŽ˜μ΄μ§€ 개발 κ³„νš ## πŸ“‹ ν”„λ‘œμ νŠΈ κ°œμš” ### 개발 λͺ©ν‘œ κΈ°μ‘΄ Headplane UIλ₯Ό ν¬ν¬ν•˜μ§€ μ•Šκ³ , **Flask + Jinja2**둜 별도 κ΄€λ¦¬μž νŽ˜μ΄μ§€λ₯Ό κ΅¬μΆ•ν•˜μ—¬ Headscale λ°μ΄ν„°λ² μ΄μŠ€μ™€ 직접 μ—°λ™ν•˜λŠ” κ³ λ„ν™”λœ 관리 μ‹œμŠ€ν…œ 개발 ### 핡심 컨셉 - **κΈ°μ‘΄ Headplane**: κΈ°λ³Έ κΈ°λŠ₯ μœ μ§€ (3000번 포트) - **Flask Admin**: κ³ λ„ν™”λœ 관리 κΈ°λŠ₯ (5000번 포트) - **데이터 톡합**: λ™μΌν•œ SQLite DB 곡유둜 μ‹€μ‹œκ°„ 동기화 - **팜큐 νŠΉν™”**: μ•½κ΅­ 관리에 μ΅œμ ν™”λœ UI/UX ## πŸ—οΈ μ•„ν‚€ν…μ²˜ 섀계 ### μ‹œμŠ€ν…œ ꡬ쑰 ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Headplane UI β”‚ β”‚ Flask Admin β”‚ β”‚ Headscale API β”‚ β”‚ (포트: 3000) β”‚ β”‚ (포트: 5000) β”‚ β”‚ (포트: 8070) β”‚ β”‚ κΈ°λ³Έ κΈ°λŠ₯ β”‚ β”‚ 고도화 κΈ°λŠ₯ β”‚ β”‚ λ°±μ—”λ“œ API β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ SQLite Database β”‚ β”‚ (곡유 데이터) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### 포트 ꡬ성 - **Headscale API**: 8070 (κΈ°μ‘΄ μœ μ§€) - **Headplane UI**: 3000 (κΈ°μ‘΄ μœ μ§€) - **Flask Admin**: 5000 (μ‹ κ·œ μΆ”κ°€) ## πŸ“‚ Flask ν”„λ‘œμ νŠΈ ꡬ쑰 ``` farmq-admin/ β”œβ”€β”€ app.py # Flask μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 메인 β”œβ”€β”€ config.py # μ„€μ • 파일 β”œβ”€β”€ requirements.txt # Python μ˜μ‘΄μ„± β”œβ”€β”€ models/ β”‚ β”œβ”€β”€ __init__.py β”‚ β”œβ”€β”€ headscale_models.py # SQLAlchemy λͺ¨λΈ (μž¬μ‚¬μš©) β”‚ └── pharmacy_models.py # 팜큐 ν™•μž₯ λͺ¨λΈ β”œβ”€β”€ routes/ β”‚ β”œβ”€β”€ __init__.py β”‚ β”œβ”€β”€ dashboard.py # 메인 λŒ€μ‹œλ³΄λ“œ β”‚ β”œβ”€β”€ pharmacy.py # μ•½κ΅­ 관리 β”‚ β”œβ”€β”€ machines.py # λ¨Έμ‹  관리 (고도화) β”‚ β”œβ”€β”€ users.py # μ‚¬μš©μž 관리 (고도화) β”‚ β”œβ”€β”€ monitoring.py # μ‹€μ‹œκ°„ λͺ¨λ‹ˆν„°λ§ β”‚ └── api.py # REST API μ—”λ“œν¬μΈνŠΈ β”œβ”€β”€ templates/ β”‚ β”œβ”€β”€ base.html # κΈ°λ³Έ λ ˆμ΄μ•„μ›ƒ β”‚ β”œβ”€β”€ dashboard/ β”‚ β”‚ β”œβ”€β”€ index.html # 메인 λŒ€μ‹œλ³΄λ“œ β”‚ β”‚ └── stats.html # 톡계 λŒ€μ‹œλ³΄λ“œ β”‚ β”œβ”€β”€ pharmacy/ β”‚ β”‚ β”œβ”€β”€ list.html # μ•½κ΅­ λͺ©λ‘ β”‚ β”‚ β”œβ”€β”€ detail.html # μ•½κ΅­ 상세 β”‚ β”‚ β”œβ”€β”€ create.html # μ•½κ΅­ 등둝 β”‚ β”‚ └── edit.html # μ•½κ΅­ μˆ˜μ • β”‚ β”œβ”€β”€ machines/ β”‚ β”‚ β”œβ”€β”€ list.html # λ¨Έμ‹  λͺ©λ‘ (고도화) β”‚ β”‚ β”œβ”€β”€ detail.html # λ¨Έμ‹  상세 (ν•˜λ“œμ›¨μ–΄ 정보) β”‚ β”‚ └── monitoring.html # μ‹€μ‹œκ°„ λͺ¨λ‹ˆν„°λ§ β”‚ └── users/ β”‚ β”œβ”€β”€ list.html # μ‚¬μš©μž λͺ©λ‘ (μ•½κ΅­ 정보 포함) β”‚ └── detail.html # μ‚¬μš©μž 상세 β”œβ”€β”€ static/ β”‚ β”œβ”€β”€ css/ β”‚ β”‚ β”œβ”€β”€ bootstrap.min.css # Bootstrap 5 β”‚ β”‚ β”œβ”€β”€ custom.css # μ»€μŠ€ν…€ μŠ€νƒ€μΌ β”‚ β”‚ └── dashboard.css # λŒ€μ‹œλ³΄λ“œ μ „μš© μŠ€νƒ€μΌ β”‚ β”œβ”€β”€ js/ β”‚ β”‚ β”œβ”€β”€ bootstrap.min.js # Bootstrap JS β”‚ β”‚ β”œβ”€β”€ chart.min.js # Chart.js 라이브러리 β”‚ β”‚ β”œβ”€β”€ dashboard.js # λŒ€μ‹œλ³΄λ“œ JS β”‚ β”‚ └── monitoring.js # μ‹€μ‹œκ°„ λͺ¨λ‹ˆν„°λ§ JS β”‚ └── img/ β”‚ β”œβ”€β”€ logo.png # 팜큐 둜고 β”‚ └── icons/ # μ•„μ΄μ½˜λ“€ β”œβ”€β”€ utils/ β”‚ β”œβ”€β”€ __init__.py β”‚ β”œβ”€β”€ database.py # DB μ—°κ²° μœ ν‹Έλ¦¬ν‹° β”‚ β”œβ”€β”€ auth.py # 인증 κ΄€λ ¨ β”‚ β”œβ”€β”€ monitoring.py # λͺ¨λ‹ˆν„°λ§ 데이터 μˆ˜μ§‘ β”‚ └── proxmox.py # Proxmox API 연동 └── docker/ β”œβ”€β”€ Dockerfile # Flask μ•±μš© λ„μ»€νŒŒμΌ └── docker-compose.yml # 톡합 μ»¨ν…Œμ΄λ„ˆ ꡬ성 ``` ## 🎨 UI/UX 섀계 ### λ””μžμΈ 컨셉 - **Modern Dashboard**: Bootstrap 5 기반 λ°˜μ‘ν˜• λ””μžμΈ - **팜큐 λΈŒλžœλ”©**: μ•½κ΅­ 관리에 νŠΉν™”λœ 색상/μ•„μ΄μ½˜ μ‚¬μš© - **Korean-First**: ν•œκ΅­μ–΄ μš°μ„  μΈν„°νŽ˜μ΄μŠ€ - **Mobile Responsive**: λͺ¨λ°”일/νƒœλΈ”λ¦Ώ μ™„λ²½ 지원 ### 메인 λŒ€μ‹œλ³΄λ“œ λ ˆμ΄μ•„μ›ƒ ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ πŸ₯ 팜큐 μ•½κ΅­ 관리 μ‹œμŠ€ν…œ [κ΄€λ¦¬μž: admin] [λ‘œκ·Έμ•„μ›ƒ] β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ [λŒ€μ‹œλ³΄λ“œ] [약ꡭ관리] [머신관리] [μ‚¬μš©μžκ΄€λ¦¬] [λͺ¨λ‹ˆν„°λ§] [μ„€μ •] β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ πŸ“Š 전체 ν˜„ν™© β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ │총 μ•½κ΅­ 수 β”‚μ˜¨λΌμΈ β”‚μ˜€ν”„λΌμΈ │평균 CPU μ˜¨λ„ β”‚ β”‚ β”‚ β”‚ 100 β”‚ 95 β”‚ 5 β”‚ 62Β°C β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ 🚨 μ‹€μ‹œκ°„ μ•Œλ¦Ό πŸ“ˆ μ„±λŠ₯ 차트 β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚β€’ λΆ€μ‚°ν•΄μš΄μ•½κ΅­: CPU 85Β°C β”‚ β”‚ [CPU μ‚¬μš©λ₯  차트] β”‚ β”‚ β”‚ β”‚β€’ λŒ€κ΅¬μ€‘μ•™μ•½κ΅­: λ””μŠ€ν¬95% β”‚ β”‚ [λ©”λͺ¨λ¦¬ μ‚¬μš©λ₯ ] β”‚ β”‚ β”‚ β”‚β€’ μ„œμšΈμ•½κ΅­: μ—°κ²° λŠκΉ€ β”‚ β”‚ [λ„€νŠΈμ›Œν¬ νŠΈλž˜ν”½] β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ πŸ“‹ 약ꡭ별 μƒνƒœ (μ‹€μ‹œκ°„) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚μ•½κ΅­λͺ… β”‚μƒνƒœ β”‚CPUμ˜¨λ„ β”‚λ©”λͺ¨λ¦¬ β”‚λ§ˆμ§€λ§‰ 접속 β”‚ β”‚ β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚μ„œμšΈμ€‘μ•™μ•½κ΅­ β”‚πŸŸ’ μ˜¨λΌμΈβ”‚ 65Β°C β”‚ 80% β”‚ 2λΆ„ μ „ β”‚ β”‚ β”‚ β”‚λΆ€μ‚°ν•΄μš΄μ•½κ΅­ β”‚πŸŸ‘ κ²½κ³  β”‚ 85Β°C β”‚ 60% β”‚ 5λΆ„ μ „ β”‚ β”‚ β”‚ β”‚λŒ€κ΅¬μ€‘μ•™μ•½κ΅­ β”‚πŸ”΄ μœ„ν—˜ β”‚ 70Β°C β”‚ 95% β”‚ 10λΆ„ μ „ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## 🎯 핡심 κΈ°λŠ₯ λͺ…μ„Έ ### 1. 톡합 λŒ€μ‹œλ³΄λ“œ ```python # routes/dashboard.py @app.route('/') def dashboard(): stats = { 'total_pharmacies': get_pharmacy_count(), 'online_machines': get_online_machines_count(), 'offline_machines': get_offline_machines_count(), 'avg_cpu_temp': get_average_cpu_temperature(), 'alerts': get_active_alerts(), 'recent_activities': get_recent_activities() } return render_template('dashboard/index.html', stats=stats) ``` ### 2. μ•½κ΅­ 관리 μ‹œμŠ€ν…œ #### 2-1. μ•½κ΅­ λͺ©λ‘ νŽ˜μ΄μ§€ ```html
πŸ₯ μ•½κ΅­ 관리
{% for pharmacy in pharmacies %} {% endfor %}
μ•½κ΅­λͺ… μ‚¬μ—…μžλ²ˆν˜Έ λ‹΄λ‹Ήμž μ—°κ²°λœ λ¨Έμ‹  μƒνƒœ λ§ˆμ§€λ§‰ 접속 μ•‘μ…˜
{{ pharmacy.pharmacy_name }}
{{ pharmacy.address }}
{{ pharmacy.business_number }} {{ pharmacy.manager_name }}
{{ pharmacy.phone }}
{{ pharmacy.machine_count }}λŒ€ {% if pharmacy.is_online %} 🟒 온라인 {% else %} πŸ”΄ μ˜€ν”„λΌμΈ {% endif %} {{ pharmacy.last_seen_humanized }}
``` ### 3. κ³ λ„ν™”λœ λ¨Έμ‹  관리 #### 3-1. λ¨Έμ‹  상세 νŽ˜μ΄μ§€ (ν•˜λ“œμ›¨μ–΄ 정보 포함) ```html
πŸ–₯️ λ¨Έμ‹  κΈ°λ³Έ 정보
λ¨Έμ‹ λͺ…:
{{ machine.given_name }}
호슀트λͺ…:
{{ machine.hostname }}
IP μ£Όμ†Œ:
{{ machine.ipv4 }}
μ†Œμ† μ•½κ΅­:
{{ machine.pharmacy.pharmacy_name }}
λ§ˆμ§€λ§‰ 접속:
{% if machine.is_online() %} 🟒 온라인 {% else %} πŸ”΄ {{ machine.last_seen_humanized }} {% endif %}
βš™οΈ ν•˜λ“œμ›¨μ–΄ 사양
{% if machine.specs %}
CPU:
{{ machine.specs.cpu_model }} ({{ machine.specs.cpu_cores }}μ½”μ–΄)
RAM:
{{ machine.specs.ram_gb }}GB
Storage:
{{ machine.specs.storage_gb }}GB
GPU:
{{ machine.specs.gpu_model or 'μ—†μŒ' }}
{% else %}

ν•˜λ“œμ›¨μ–΄ 정보가 λ“±λ‘λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

ν•˜λ“œμ›¨μ–΄ 정보 등둝 {% endif %}
πŸ“Š μ‹€μ‹œκ°„ λͺ¨λ‹ˆν„°λ§
{% if machine.latest_monitoring %}
CPU μ‚¬μš©λ₯ 
{{ machine.latest_monitoring.cpu_usage }}%
λ©”λͺ¨λ¦¬ μ‚¬μš©λ₯ 
{{ machine.latest_monitoring.memory_usage }}%
🌑️
CPU μ˜¨λ„
{{ machine.latest_monitoring.cpu_temperature }}Β°C
πŸ’Ύ
λ””μŠ€ν¬ μ‚¬μš©λ₯ 
{{ machine.latest_monitoring.disk_usage }}%
{% else %}
아직 λͺ¨λ‹ˆν„°λ§ 데이터가 μ—†μŠ΅λ‹ˆλ‹€. μž μ‹œ ν›„ λ‹€μ‹œ ν™•μΈν•΄μ£Όμ„Έμš”.
{% endif %}
``` ### 4. μ‹€μ‹œκ°„ λͺ¨λ‹ˆν„°λ§ μ‹œμŠ€ν…œ ```python # routes/monitoring.py from flask import Blueprint, jsonify from utils.monitoring import collect_monitoring_data from utils.proxmox import ProxmoxAPI monitoring_bp = Blueprint('monitoring', __name__) @monitoring_bp.route('/api/monitoring/realtime') def realtime_monitoring(): """μ‹€μ‹œκ°„ λͺ¨λ‹ˆν„°λ§ 데이터 API""" data = { 'total_machines': get_total_machines(), 'online_count': get_online_machines_count(), 'alerts': get_active_alerts(), 'performance': get_performance_summary() } return jsonify(data) @monitoring_bp.route('/api/machines//monitoring') def machine_monitoring(machine_id): """νŠΉμ • λ¨Έμ‹  λͺ¨λ‹ˆν„°λ§ 데이터""" monitoring_data = collect_monitoring_data(machine_id) return jsonify(monitoring_data) ``` ## πŸ”§ 기술 μŠ€νƒ ### Backend - **Flask 3.0**: μ›Ή ν”„λ ˆμž„μ›Œν¬ - **SQLAlchemy 2.0**: ORM (κΈ°μ‘΄ λͺ¨λΈ μž¬μ‚¬μš©) - **Jinja2**: ν…œν”Œλ¦Ώ μ—”μ§„ - **Flask-Login**: μ‚¬μš©μž 인증 - **APScheduler**: λ°±κ·ΈλΌμš΄λ“œ μž‘μ—… (λͺ¨λ‹ˆν„°λ§ 데이터 μˆ˜μ§‘) ### Frontend - **Bootstrap 5**: CSS ν”„λ ˆμž„μ›Œν¬ - **Chart.js**: 차트 라이브러리 - **Font Awesome**: μ•„μ΄μ½˜ - **jQuery**: DOM μ‘°μž‘ - **Socket.io**: μ‹€μ‹œκ°„ 톡신 ### λ°μ΄ν„°λ² μ΄μŠ€ - **SQLite**: κΈ°μ‘΄ Headscale DB 곡유 - **ν™•μž₯ ν…Œμ΄λΈ”**: PharmacyInfo, MachineSpecs, MonitoringData ### 배포 - **Docker**: μ»¨ν…Œμ΄λ„ˆν™” - **Nginx**: λ¦¬λ²„μŠ€ ν”„λ‘μ‹œ (μ˜΅μ…˜) ## πŸ“… 개발 λ‘œλ“œλ§΅ ### Phase 1: κΈ°λ³Έ ν”„λ ˆμž„μ›Œν¬ ꡬ좕 (1-2일) - [ ] Flask μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κΈ°λ³Έ ꡬ쑰 생성 - [ ] SQLAlchemy λͺ¨λΈ 연동 (κΈ°μ‘΄ λͺ¨λΈ μž¬μ‚¬μš©) - [ ] Bootstrap 기반 κΈ°λ³Έ ν…œν”Œλ¦Ώ ꡬ성 - [ ] λΌμš°νŒ… ꡬ쑰 섀계 ### Phase 2: 핡심 κΈ°λŠ₯ κ΅¬ν˜„ (3-4일) - [ ] 메인 λŒ€μ‹œλ³΄λ“œ κ΅¬ν˜„ - [ ] μ•½κ΅­ 관리 CRUD κΈ°λŠ₯ - [ ] λ¨Έμ‹  관리 고도화 (ν•˜λ“œμ›¨μ–΄ 정보 포함) - [ ] μ‚¬μš©μž 관리 ν™•μž₯ (μ•½κ΅­ 정보 연동) ### Phase 3: μ‹€μ‹œκ°„ κΈ°λŠ₯ (2-3일) - [ ] λͺ¨λ‹ˆν„°λ§ 데이터 μˆ˜μ§‘ μ‹œμŠ€ν…œ - [ ] μ‹€μ‹œκ°„ 차트 및 μ•Œλ¦Ό - [ ] WebSocket 기반 라이브 μ—…λ°μ΄νŠΈ - [ ] Proxmox API 연동 ### Phase 4: 톡합 및 μ΅œμ ν™” (2-3일) - [ ] κΈ°μ‘΄ Headplaneκ³Ό 데이터 동기화 ν…ŒμŠ€νŠΈ - [ ] Docker μ»¨ν…Œμ΄λ„ˆν™” - [ ] μ„±λŠ₯ μ΅œμ ν™” - [ ] μ‚¬μš©μž ν…ŒμŠ€νŠΈ 및 ν”Όλ“œλ°± 반영 ### Phase 5: 배포 및 운영 (1-2일) - [ ] Docker Compose 톡합 ꡬ성 - [ ] ν”„λ‘œλ•μ…˜ 배포 - [ ] λͺ¨λ‹ˆν„°λ§ 및 λ‘œκΉ… μ„€μ • - [ ] μ‚¬μš©μž ꡐ윑 자료 μž‘μ„± ## πŸ’° μ˜ˆμƒ λ¦¬μ†ŒμŠ€ ### 개발 μ‹œκ°„ - **총 개발 κΈ°κ°„**: 8-12일 - **개발자**: 1λͺ… (ν’€νƒ€μž„) - **일일 μž‘μ—…λŸ‰**: 6-8μ‹œκ°„ ### 기술적 μš”κ΅¬μ‚¬ν•­ - **Python 3.8+** - **λ©”λͺ¨λ¦¬**: μ΅œμ†Œ 512MB (Flask μ•±) - **λ””μŠ€ν¬**: μΆ”κ°€ 100MB (정적 파일 포함) ## πŸš€ μ‹œμž‘ν•˜κΈ° ### 1단계: 개발 ν™˜κ²½ μ€€λΉ„ ```bash # Flask ν”„λ‘œμ νŠΈ 디렉터리 생성 mkdir farmq-admin cd farmq-admin # Python κ°€μƒν™˜κ²½ 생성 python3 -m venv flask-venv source flask-venv/bin/activate # ν•„μˆ˜ νŒ¨ν‚€μ§€ μ„€μΉ˜ pip install flask sqlalchemy jinja2 flask-login apscheduler ``` ### 2단계: κΈ°λ³Έ ꡬ쑰 생성 ```bash # ν”„λ‘œμ νŠΈ ꡬ쑰 생성 mkdir -p {routes,templates,static/{css,js,img},utils,models} touch app.py config.py requirements.txt ``` ### 3단계: 첫 번째 κ΅¬ν˜„ - κΈ°λ³Έ Flask μ•± 생성 - SQLAlchemy 연동 - κ°„λ‹¨ν•œ λŒ€μ‹œλ³΄λ“œ νŽ˜μ΄μ§€ ## 🎯 성곡 μ§€ν‘œ ### κΈ°λŠ₯적 λͺ©ν‘œ - [ ] 100개 μ•½κ΅­ 데이터 μ™„λ²½ 관리 - [ ] μ‹€μ‹œκ°„ λͺ¨λ‹ˆν„°λ§ 정확도 95% 이상 - [ ] κΈ°μ‘΄ Headplaneκ³Ό 데이터 100% 동기화 - [ ] νŽ˜μ΄μ§€ λ‘œλ”© μ‹œκ°„ 2초 이내 ### μ‚¬μš©μ„± λͺ©ν‘œ - [ ] 관리 업무 νš¨μœ¨μ„± 70% ν–₯상 - [ ] λͺ¨λ°”일 μ ‘κ·Όμ„± μ™„λ²½ 지원 - [ ] ν•œκ΅­μ–΄ UI 100% μ™„μ„± - [ ] μ‚¬μš©μž λ§Œμ‘±λ„ 4.8/5.0 이상 이제 이 κ³„νšμ„ λ°”νƒ•μœΌλ‘œ Flask κ΄€λ¦¬μž νŽ˜μ΄μ§€ κ°œλ°œμ„ μ‹œμž‘ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ? --- **πŸ“… μž‘μ„±μΌ**: 2025-09-09 **πŸ‘€ μž‘μ„±μž**: Claude Code Assistant **🎯 λͺ©ν‘œ**: Headplane UI 고도화λ₯Ό μœ„ν•œ Flask 기반 κ΄€λ¦¬μž μ‹œμŠ€ν…œ