diff --git a/nginx.conf b/nginx.conf index 2a12dc8..2f7415a 100644 --- a/nginx.conf +++ b/nginx.conf @@ -3,6 +3,12 @@ server { root /usr/share/nginx/html; index index.html; + # index.html은 캐싱 금지 (배포 후 즉시 반영) + location = /index.html { + try_files $uri =404; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } + # SPA fallback (react-router 사용 시 필요) location / { try_files $uri $uri/ /index.html; diff --git a/src/components/admin/AdminCommon.css b/src/components/admin/AdminCommon.css index 1d0ef75..8a0bf0d 100644 --- a/src/components/admin/AdminCommon.css +++ b/src/components/admin/AdminCommon.css @@ -67,6 +67,12 @@ border-color: rgba(186, 205, 176, 0.5); } +.admin-error { + font-size: 0.85rem; + color: rgba(229, 115, 115, 0.9); + margin: 0; +} + .admin-form-actions { display: flex; gap: 8px; diff --git a/src/components/admin/DownloadAdmin.jsx b/src/components/admin/DownloadAdmin.jsx index 415478c..103169d 100644 --- a/src/components/admin/DownloadAdmin.jsx +++ b/src/components/admin/DownloadAdmin.jsx @@ -7,6 +7,7 @@ export default function DownloadAdmin() { const [form, setForm] = useState({ url: '', version: '', fileName: '', fileSize: '', fileHash: '' }); const [loading, setLoading] = useState(false); const [saved, setSaved] = useState(false); + const [error, setError] = useState(''); useEffect(() => { getDownloadInfo() @@ -23,6 +24,7 @@ export default function DownloadAdmin() { const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); + setError(''); try { await apiFetch('/api/download/info', { method: 'PUT', @@ -30,6 +32,8 @@ export default function DownloadAdmin() { }); setSaved(true); setTimeout(() => setSaved(false), 2000); + } catch (err) { + setError(err.message || '저장에 실패했습니다.'); } finally { setLoading(false); } @@ -56,6 +60,7 @@ export default function DownloadAdmin() { {field('파일명', 'fileName', 'A301_Launcher.zip')} {field('파일 크기', 'fileSize', '1.2 GB')} {field('A301.exe SHA256 해시', 'fileHash', 'sha256 해시값 (certutil -hashfile A301.exe SHA256)')} + {error &&
{error}
}