--- id: wiki-2026-0508-progressive-web-apps-pwas title: Progressive Web Apps (PWAs) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [PWA, Installable Web Apps, Web App Manifest Apps] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [web, frontend, mobile, service-worker, offline-first] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: javascript framework: web-platform --- # Progressive Web Apps (PWAs) ## 매 한 줄 > **"매 the web with installability, offline, and push — without the app store tax."**. 매 Alex Russell 이 2015년 정립한 PWA 는 매 service worker + manifest + HTTPS 의 조합으로 매 web app 을 매 native-feel 으로 격상. 매 2026: iOS 17+/Safari 17+ 가 push notifications 와 매 home-screen install 을 정식 지원, 매 Project Fugu API 들 (file system, USB, Bluetooth) 까지 가능. ## 매 핵심 ### 매 3 pillars - **Service Worker**: 매 background script — 매 caching, 매 offline, 매 push. - **Web App Manifest**: 매 JSON — 매 install metadata (icon, name, display). - **HTTPS**: 매 mandatory transport (loopback exception for dev). ### 매 capabilities (2026) - 매 offline first via Cache + IndexedDB. - 매 background sync (eventual consistency). - 매 push notifications (iOS 17+ supports). - 매 file system access (Origin Private File System, OPFS). - 매 share target (receive shares from native). - 매 periodic background sync. - 매 WebGPU / WebTransport / WebCodecs. ### 매 응용 1. Twitter, Pinterest, Starbucks Lite — 매 60-80% bundle reduction. 2. 매 conference apps — 매 offline schedule + push. 3. 매 internal enterprise tools — 매 install via QR, no MDM. 4. 매 emerging-market apps — 매 low-bandwidth-friendly. ## 💻 패턴 ### Pattern 1: Minimal manifest.json ```json { "name": "Wiki Cleanup Tool", "short_name": "WikiClean", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#0066cc", "icons": [ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" } ] } ``` ### Pattern 2: Service worker (cache-first for assets, network-first for API) ```javascript // sw.js const CACHE = 'app-v3'; const ASSETS = ['/', '/index.html', '/app.js', '/styles.css']; self.addEventListener('install', (e) => { e.waitUntil(caches.open(CACHE).then((c) => c.addAll(ASSETS))); }); self.addEventListener('activate', (e) => { e.waitUntil(caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))) )); }); self.addEventListener('fetch', (e) => { const url = new URL(e.request.url); if (url.pathname.startsWith('/api/')) { // network-first e.respondWith(fetch(e.request).catch(() => caches.match(e.request))); } else { // cache-first e.respondWith(caches.match(e.request).then((r) => r || fetch(e.request))); } }); ``` ### Pattern 3: Registration with update flow ```javascript // app.js if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then((reg) => { reg.addEventListener('updatefound', () => { const sw = reg.installing; sw.addEventListener('statechange', () => { if (sw.state === 'installed' && navigator.serviceWorker.controller) { showUpdateToast(() => sw.postMessage({ type: 'SKIP_WAITING' })); } }); }); }); } ``` ### Pattern 4: Push notifications (2026 cross-platform) ```javascript async function subscribe() { const reg = await navigator.serviceWorker.ready; const sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: VAPID_PUBLIC_KEY, }); await fetch('/api/save-sub', { method: 'POST', body: JSON.stringify(sub) }); } // sw.js self.addEventListener('push', (e) => { const { title, body, url } = e.data.json(); e.waitUntil(self.registration.showNotification(title, { body, data: { url } })); }); self.addEventListener('notificationclick', (e) => { e.notification.close(); e.waitUntil(clients.openWindow(e.notification.data.url)); }); ``` ### Pattern 5: Background sync for offline writes ```javascript // queue write when offline async function postWithSync(url, payload) { try { return await fetch(url, { method: 'POST', body: JSON.stringify(payload) }); } catch { const reg = await navigator.serviceWorker.ready; await idbAdd('outbox', { url, payload }); await reg.sync.register('flush-outbox'); } } // sw.js self.addEventListener('sync', (e) => { if (e.tag === 'flush-outbox') e.waitUntil(flushOutbox()); }); ``` ### Pattern 6: File System Access (2026 Fugu) ```javascript async function saveDoc(content) { const handle = await window.showSaveFilePicker({ types: [{ description: 'Markdown', accept: { 'text/markdown': ['.md'] } }], }); const writable = await handle.createWritable(); await writable.write(content); await writable.close(); } ``` ### Pattern 7: Install prompt UX ```javascript let deferred; window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferred = e; showCustomInstallButton(); }); async function onInstallClick() { if (!deferred) return; deferred.prompt(); const { outcome } = await deferred.userChoice; analytics.track('pwa_install', { outcome }); deferred = null; } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 매 content-heavy site | PWA, no native | | 매 needs deep OS integration (BLE, NFC, telephony) | Native + Capacitor / native shell | | 매 cross-platform productivity | PWA with Fugu APIs | | 매 emerging-market reach | PWA — 매 small bundle, offline | | 매 game (heavy GPU) | Native or WebGPU PWA | | 매 enterprise behind SSO | PWA — 매 no app store distribution | **기본값**: 매 start with PWA, 매 fall back to native shell only when blocked APIs needed. ## 🔗 Graph - 부모: [[Large_Frontend_Projects|Frontend Architecture]] - 변형: [[Service Worker]] - 응용: [[Offline-First]] · [[Push Notifications]] - Adjacent: [[WebGPU]] · [[Workbox]] ## 🤖 LLM 활용 **언제**: 매 caching strategy design, 매 SW boilerplate, 매 update-flow patterns. **언제 X**: 매 native-only API needs (full BLE stack, deep telephony) — 매 use Capacitor / native. ## ❌ 안티패턴 - **Cache-everything**: 매 stale stale stale — 매 use versioned cache + activate cleanup. - **No update flow**: 매 user stuck on old SW forever. - **HTTPS skipped in staging**: 매 SW won't register — 매 broken parity. - **Manifest without maskable icons**: 매 ugly cropped icons on Android adaptive. - **Push spam**: 매 user opt-out + iOS revoke + reputation damage. ## 🧪 검증 / 중복 - Verified (W3C Service Worker spec, Web App Manifest spec, web.dev/learn/pwa). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — 3 pillars, 7 패턴, 2026 Fugu/iOS push 반영 |