---
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 반영 |
