From 1cfd4da8baf722d5c9b454feb9bda34041472f09 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Wed, 1 Apr 2026 15:36:36 +0900 Subject: [PATCH] =?UTF-8?q?feat(pwa):=20Network-First=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EC=9B=8C=EC=BB=A4=20=E2=80=94=20=ED=99=88=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=95=B1=20=EC=9E=90=EB=8F=99=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sw.js: Network-First 캐시 전략 (GET + same-origin + res.ok만 캐시) - tkfb-core.js: SW 등록 + 업데이트 감지 시 자동 새로고침 (최초 설치 시 토스트 방지: controller 체크) - manifest.json: start_url → dashboard-new.html - nginx: sw.js, manifest.json no-cache 헤더 - 배포 시 sw.js의 APP_VERSION만 변경하면 전 사용자 자동 갱신 Co-Authored-By: Claude Opus 4.6 (1M context) --- system1-factory/web/manifest.json | 2 +- system1-factory/web/nginx.conf | 8 ++++ .../web/pages/admin/attendance-report.html | 2 +- .../web/pages/admin/equipment-detail.html | 2 +- .../web/pages/admin/equipments.html | 2 +- .../web/pages/admin/purchase-analysis.html | 2 +- .../web/pages/admin/repair-management.html | 2 +- .../web/pages/attendance/annual-overview.html | 2 +- .../web/pages/attendance/checkin.html | 2 +- .../web/pages/attendance/daily.html | 2 +- .../pages/attendance/monthly-comparison.html | 2 +- .../web/pages/attendance/monthly.html | 2 +- .../pages/attendance/my-monthly-confirm.html | 2 +- .../pages/attendance/my-vacation-info.html | 2 +- .../pages/attendance/vacation-allocation.html | 2 +- .../pages/attendance/vacation-approval.html | 2 +- .../web/pages/attendance/vacation-input.html | 2 +- .../pages/attendance/vacation-management.html | 2 +- .../pages/attendance/vacation-request.html | 2 +- .../web/pages/attendance/work-status.html | 2 +- system1-factory/web/pages/dashboard-new.html | 2 +- system1-factory/web/pages/dashboard.html | 2 +- .../web/pages/inspection/daily-patrol.html | 2 +- .../web/pages/inspection/zone-detail.html | 2 +- system1-factory/web/pages/profile/info.html | 2 +- .../web/pages/profile/password.html | 2 +- .../web/pages/purchase/request-mobile.html | 2 +- .../web/pages/purchase/request.html | 2 +- system1-factory/web/pages/work/analysis.html | 2 +- .../web/pages/work/daily-status.html | 2 +- .../web/pages/work/meeting-detail.html | 2 +- system1-factory/web/pages/work/meetings.html | 2 +- .../web/pages/work/proxy-input.html | 2 +- .../web/pages/work/report-create-mobile.html | 2 +- .../web/pages/work/report-create.html | 2 +- system1-factory/web/pages/work/schedule.html | 2 +- .../web/pages/work/tbm-create.html | 2 +- .../web/pages/work/tbm-mobile.html | 2 +- system1-factory/web/pages/work/tbm.html | 2 +- system1-factory/web/static/js/tkfb-core.js | 19 +++++--- system1-factory/web/sw.js | 43 ++++++++++++------- 41 files changed, 88 insertions(+), 58 deletions(-) diff --git a/system1-factory/web/manifest.json b/system1-factory/web/manifest.json index 7f7ffe5..0cf8a41 100644 --- a/system1-factory/web/manifest.json +++ b/system1-factory/web/manifest.json @@ -2,7 +2,7 @@ "name": "TK 공장관리 - 테크니컬코리아", "short_name": "TK공장", "description": "테크니컬코리아 공장관리 시스템", - "start_url": "/pages/dashboard.html", + "start_url": "/pages/dashboard-new.html", "display": "standalone", "background_color": "#ffffff", "theme_color": "#1e40af", diff --git a/system1-factory/web/nginx.conf b/system1-factory/web/nginx.conf index 365f2cf..0394ded 100644 --- a/system1-factory/web/nginx.conf +++ b/system1-factory/web/nginx.conf @@ -18,6 +18,14 @@ server { add_header Cache-Control "no-store, no-cache, must-revalidate"; } + # SW, manifest 캐시 비활성화 (PWA 업데이트 즉시 반영) + location = /sw.js { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + location = /manifest.json { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + # 정적 파일 캐시 (JS, CSS, 이미지 등) location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf)$ { expires 1h; diff --git a/system1-factory/web/pages/admin/attendance-report.html b/system1-factory/web/pages/admin/attendance-report.html index b4bd31c..c572f04 100644 --- a/system1-factory/web/pages/admin/attendance-report.html +++ b/system1-factory/web/pages/admin/attendance-report.html @@ -190,7 +190,7 @@ - + + + + diff --git a/system1-factory/web/pages/admin/repair-management.html b/system1-factory/web/pages/admin/repair-management.html index 71d1c55..1ab55a7 100644 --- a/system1-factory/web/pages/admin/repair-management.html +++ b/system1-factory/web/pages/admin/repair-management.html @@ -507,7 +507,7 @@ - + + + + + diff --git a/system1-factory/web/pages/attendance/monthly.html b/system1-factory/web/pages/attendance/monthly.html index 727114f..1518c20 100644 --- a/system1-factory/web/pages/attendance/monthly.html +++ b/system1-factory/web/pages/attendance/monthly.html @@ -489,7 +489,7 @@ - + + diff --git a/system1-factory/web/pages/attendance/my-vacation-info.html b/system1-factory/web/pages/attendance/my-vacation-info.html index 6e877ca..0d8b570 100644 --- a/system1-factory/web/pages/attendance/my-vacation-info.html +++ b/system1-factory/web/pages/attendance/my-vacation-info.html @@ -267,7 +267,7 @@ - + + diff --git a/system1-factory/web/pages/attendance/vacation-approval.html b/system1-factory/web/pages/attendance/vacation-approval.html index 7f1d399..45890ab 100644 --- a/system1-factory/web/pages/attendance/vacation-approval.html +++ b/system1-factory/web/pages/attendance/vacation-approval.html @@ -130,7 +130,7 @@ - + diff --git a/system1-factory/web/pages/attendance/vacation-input.html b/system1-factory/web/pages/attendance/vacation-input.html index 24d935d..0ddc45a 100644 --- a/system1-factory/web/pages/attendance/vacation-input.html +++ b/system1-factory/web/pages/attendance/vacation-input.html @@ -123,7 +123,7 @@ - + diff --git a/system1-factory/web/pages/attendance/vacation-management.html b/system1-factory/web/pages/attendance/vacation-management.html index b83959a..9c417f9 100644 --- a/system1-factory/web/pages/attendance/vacation-management.html +++ b/system1-factory/web/pages/attendance/vacation-management.html @@ -215,7 +215,7 @@ - + diff --git a/system1-factory/web/pages/attendance/vacation-request.html b/system1-factory/web/pages/attendance/vacation-request.html index c365eb2..91fe46e 100644 --- a/system1-factory/web/pages/attendance/vacation-request.html +++ b/system1-factory/web/pages/attendance/vacation-request.html @@ -117,7 +117,7 @@ - + diff --git a/system1-factory/web/pages/attendance/work-status.html b/system1-factory/web/pages/attendance/work-status.html index 16210f8..57c3a50 100644 --- a/system1-factory/web/pages/attendance/work-status.html +++ b/system1-factory/web/pages/attendance/work-status.html @@ -286,7 +286,7 @@ - + + diff --git a/system1-factory/web/pages/dashboard.html b/system1-factory/web/pages/dashboard.html index 63a64df..c966878 100644 --- a/system1-factory/web/pages/dashboard.html +++ b/system1-factory/web/pages/dashboard.html @@ -324,7 +324,7 @@ - + diff --git a/system1-factory/web/pages/inspection/daily-patrol.html b/system1-factory/web/pages/inspection/daily-patrol.html index 6e0add9..69aad0e 100644 --- a/system1-factory/web/pages/inspection/daily-patrol.html +++ b/system1-factory/web/pages/inspection/daily-patrol.html @@ -209,7 +209,7 @@ }, 50); })(); - + diff --git a/system1-factory/web/pages/inspection/zone-detail.html b/system1-factory/web/pages/inspection/zone-detail.html index 927439b..a22c728 100644 --- a/system1-factory/web/pages/inspection/zone-detail.html +++ b/system1-factory/web/pages/inspection/zone-detail.html @@ -304,7 +304,7 @@ }, 50); })(); - + diff --git a/system1-factory/web/pages/profile/info.html b/system1-factory/web/pages/profile/info.html index 5cb5657..a10175e 100644 --- a/system1-factory/web/pages/profile/info.html +++ b/system1-factory/web/pages/profile/info.html @@ -320,7 +320,7 @@ - + diff --git a/system1-factory/web/pages/profile/password.html b/system1-factory/web/pages/profile/password.html index d9347fa..b8e2a7b 100644 --- a/system1-factory/web/pages/profile/password.html +++ b/system1-factory/web/pages/profile/password.html @@ -390,7 +390,7 @@ - + diff --git a/system1-factory/web/pages/purchase/request-mobile.html b/system1-factory/web/pages/purchase/request-mobile.html index 40e4991..a145c13 100644 --- a/system1-factory/web/pages/purchase/request-mobile.html +++ b/system1-factory/web/pages/purchase/request-mobile.html @@ -97,7 +97,7 @@
- + diff --git a/system1-factory/web/pages/purchase/request.html b/system1-factory/web/pages/purchase/request.html index d88ac6f..8eee40e 100644 --- a/system1-factory/web/pages/purchase/request.html +++ b/system1-factory/web/pages/purchase/request.html @@ -312,7 +312,7 @@ - + diff --git a/system1-factory/web/pages/work/analysis.html b/system1-factory/web/pages/work/analysis.html index 7b44995..85e44dc 100644 --- a/system1-factory/web/pages/work/analysis.html +++ b/system1-factory/web/pages/work/analysis.html @@ -277,7 +277,7 @@ - + diff --git a/system1-factory/web/pages/work/daily-status.html b/system1-factory/web/pages/work/daily-status.html index c398af3..d652637 100644 --- a/system1-factory/web/pages/work/daily-status.html +++ b/system1-factory/web/pages/work/daily-status.html @@ -163,7 +163,7 @@ - + diff --git a/system1-factory/web/pages/work/meeting-detail.html b/system1-factory/web/pages/work/meeting-detail.html index b073abb..cf464d4 100644 --- a/system1-factory/web/pages/work/meeting-detail.html +++ b/system1-factory/web/pages/work/meeting-detail.html @@ -193,7 +193,7 @@ - + diff --git a/system1-factory/web/pages/work/meetings.html b/system1-factory/web/pages/work/meetings.html index e6cb45a..68a9567 100644 --- a/system1-factory/web/pages/work/meetings.html +++ b/system1-factory/web/pages/work/meetings.html @@ -80,7 +80,7 @@ - + diff --git a/system1-factory/web/pages/work/proxy-input.html b/system1-factory/web/pages/work/proxy-input.html index 387a795..ca43b84 100644 --- a/system1-factory/web/pages/work/proxy-input.html +++ b/system1-factory/web/pages/work/proxy-input.html @@ -114,7 +114,7 @@ - + diff --git a/system1-factory/web/pages/work/report-create-mobile.html b/system1-factory/web/pages/work/report-create-mobile.html index 6351b4f..2f7f94f 100644 --- a/system1-factory/web/pages/work/report-create-mobile.html +++ b/system1-factory/web/pages/work/report-create-mobile.html @@ -190,7 +190,7 @@ - + diff --git a/system1-factory/web/pages/work/report-create.html b/system1-factory/web/pages/work/report-create.html index a28c677..70f1afd 100644 --- a/system1-factory/web/pages/work/report-create.html +++ b/system1-factory/web/pages/work/report-create.html @@ -149,7 +149,7 @@ - + diff --git a/system1-factory/web/pages/work/schedule.html b/system1-factory/web/pages/work/schedule.html index 872c06e..c087027 100644 --- a/system1-factory/web/pages/work/schedule.html +++ b/system1-factory/web/pages/work/schedule.html @@ -347,7 +347,7 @@ - + + diff --git a/system1-factory/web/pages/work/tbm-mobile.html b/system1-factory/web/pages/work/tbm-mobile.html index 6323a02..ad831dd 100644 --- a/system1-factory/web/pages/work/tbm-mobile.html +++ b/system1-factory/web/pages/work/tbm-mobile.html @@ -264,7 +264,7 @@ - + diff --git a/system1-factory/web/pages/work/tbm.html b/system1-factory/web/pages/work/tbm.html index ca4af35..7074352 100644 --- a/system1-factory/web/pages/work/tbm.html +++ b/system1-factory/web/pages/work/tbm.html @@ -573,7 +573,7 @@
- + diff --git a/system1-factory/web/static/js/tkfb-core.js b/system1-factory/web/static/js/tkfb-core.js index 09c4de3..f246d75 100644 --- a/system1-factory/web/static/js/tkfb-core.js +++ b/system1-factory/web/static/js/tkfb-core.js @@ -1,9 +1,18 @@ -/* ===== 서비스 워커 해제 (push-sw.js 제외) ===== */ +/* ===== 서비스 워커 등록 (Network-First 캐시) ===== */ if ('serviceWorker' in navigator) { - navigator.serviceWorker.getRegistrations().then(function(regs) { regs.forEach(function(r) { - if (!r.active || !r.active.scriptURL.includes('push-sw.js')) { r.unregister(); } - }); }); - if (typeof caches !== 'undefined') { caches.keys().then(function(ns) { ns.forEach(function(n) { caches.delete(n); }); }); } + navigator.serviceWorker.register('/sw.js').then(function(reg) { + // 기존 SW가 있을 때만 업데이트 감지 (최초 설치 시 토스트 방지) + if (!navigator.serviceWorker.controller) return; + reg.addEventListener('updatefound', function() { + var newSW = reg.installing; + newSW.addEventListener('statechange', function() { + if (newSW.state === 'activated') { + showToast('새 버전이 적용되었습니다.', 'info'); + setTimeout(function() { location.reload(); }, 1500); + } + }); + }); + }); } /* ===== Config ===== */ diff --git a/system1-factory/web/sw.js b/system1-factory/web/sw.js index c8dbe5b..e66115b 100644 --- a/system1-factory/web/sw.js +++ b/system1-factory/web/sw.js @@ -1,7 +1,8 @@ -// sw.js - Service Worker 자체 해제 (캐시 초기화) -// 기존 SW를 교체하여 모든 캐시를 삭제하고 자체 해제합니다. +// sw.js — Network-First 서비스 워커 +const APP_VERSION = 'v2026040101'; +const CACHE_NAME = 'tkfb-' + APP_VERSION; -self.addEventListener('install', function(event) { +self.addEventListener('install', function() { self.skipWaiting(); }); @@ -9,20 +10,32 @@ self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(keys) { return Promise.all( - keys.map(function(key) { - console.log('SW: 캐시 삭제:', key); - return caches.delete(key); - }) + keys.filter(function(k) { return k !== CACHE_NAME; }) + .map(function(k) { return caches.delete(k); }) ); }).then(function() { - console.log('SW: 모든 캐시 삭제 완료, 자체 해제'); - return self.registration.unregister(); - }).then(function() { - return self.clients.matchAll(); - }).then(function(clients) { - clients.forEach(function(client) { - client.postMessage({ type: 'SW_CLEARED' }); - }); + return self.clients.claim(); }) ); }); + +self.addEventListener('fetch', function(event) { + // API, POST, cross-origin 요청은 캐싱하지 않음 + if (event.request.url.includes('/api/')) return; + if (event.request.method !== 'GET') return; + if (!event.request.url.startsWith(self.location.origin)) return; + + event.respondWith( + fetch(event.request) + .then(function(res) { + if (res.ok) { + var clone = res.clone(); + caches.open(CACHE_NAME).then(function(c) { c.put(event.request, clone); }); + } + return res; + }) + .catch(function() { + return caches.match(event.request); + }) + ); +});