From d1a5964bb7508e7610722b8750b249a07b8fa454 Mon Sep 17 00:00:00 2001 From: thug0bin Date: Wed, 25 Feb 2026 08:51:23 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20PWA=20=EA=B8=B0=EB=B3=B8=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(manifest,=20service=20worker,=20=EC=95=B1=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EC=BD=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - manifest.json: 앱 이름 "청춘약국 마일리지", standalone 모드, 보라색 테마 - sw.js: 정적 자산 캐싱 (동적 페이지 제외) - 앱 아이콘 192x192, 512x512 PNG 생성 - /sw.js 루트 라우트로 서비스 워커 scope 허용 Co-Authored-By: Claude Opus 4.6 --- backend/static/icons/icon-192.png | Bin 0 -> 4734 bytes backend/static/icons/icon-512.png | Bin 0 -> 12330 bytes backend/static/manifest.json | 25 ++++++++++++ backend/static/sw.js | 63 ++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 backend/static/icons/icon-192.png create mode 100644 backend/static/icons/icon-512.png create mode 100644 backend/static/manifest.json create mode 100644 backend/static/sw.js diff --git a/backend/static/icons/icon-192.png b/backend/static/icons/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..9a734afac6705ebd7fb6ffeb055c80b2554bd75f GIT binary patch literal 4734 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 literal 0 HcmV?d00001 diff --git a/backend/static/manifest.json b/backend/static/manifest.json new file mode 100644 index 0000000..c75994d --- /dev/null +++ b/backend/static/manifest.json @@ -0,0 +1,25 @@ +{ + "name": "청춘약국 마일리지", + "short_name": "청춘약국", + "description": "청춘약국 QR 마일리지 적립 서비스", + "start_url": "/my-page", + "display": "standalone", + "background_color": "#f5f7fa", + "theme_color": "#6366f1", + "orientation": "portrait", + "lang": "ko", + "icons": [ + { + "src": "/static/icons/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/static/icons/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ] +} diff --git a/backend/static/sw.js b/backend/static/sw.js new file mode 100644 index 0000000..ceff4cc --- /dev/null +++ b/backend/static/sw.js @@ -0,0 +1,63 @@ +const CACHE_NAME = 'chungchun-pharmacy-v1'; +const STATIC_ASSETS = [ + '/static/js/lottie.min.js', + '/static/animations/ai-loading.json', + '/static/icons/icon-192.png', + '/static/icons/icon-512.png' +]; + +// Install: pre-cache static assets +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS)) + ); + self.skipWaiting(); +}); + +// Activate: clean old caches +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((keys) => + Promise.all( + keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)) + ) + ) + ); + self.clients.claim(); +}); + +// Fetch: cache-first for static, network-only for dynamic +self.addEventListener('fetch', (event) => { + const url = new URL(event.request.url); + + // Skip non-GET requests + if (event.request.method !== 'GET') return; + + // Skip dynamic routes entirely + if (url.pathname.startsWith('/api/') || + url.pathname.startsWith('/admin') || + url.pathname.startsWith('/claim') || + url.pathname.startsWith('/my-page') || + url.pathname === '/privacy' || + url.pathname === '/logout' || + url.pathname === '/') { + return; + } + + // Cache-first for static assets and fonts + if (url.pathname.startsWith('/static/') || + url.hostname === 'fonts.googleapis.com' || + url.hostname === 'fonts.gstatic.com') { + event.respondWith( + caches.match(event.request).then((cached) => { + return cached || fetch(event.request).then((response) => { + if (response.ok) { + const clone = response.clone(); + caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone)); + } + return response; + }); + }) + ); + } +});