[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -1,122 +1,219 @@
|
||||
---
|
||||
id: wiki-2026-0508-progressive-web-apps-pwas
|
||||
title: Progressive Web Apps PWAs
|
||||
title: Progressive Web Apps (PWAs)
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: []
|
||||
aliases: [PWA, Installable Web Apps, Web App Manifest Apps]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.92
|
||||
tags: [auto-wikified, technical-documentation]
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [web, frontend, mobile, service-worker, offline-first]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-08
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: javascript
|
||||
framework: web-platform
|
||||
---
|
||||
|
||||
# Progressive Web Apps (PWAs)
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
프로그레시브 웹 앱(PWA)은 전통적인 네이티브 앱을 대체할 수 있는 비용 효율적이고 성능이 뛰어난 웹 애플리케이션 아키텍처이다 [1]. Google, Apple, Microsoft 등 주요 기술 기업들의 표준 지원에 힘입어 주류 개발 트렌드로 자리 잡았다 [1]. 단일 코드베이스를 통해 다양한 플랫폼에서 실행되며, 오프라인 환경에서도 네이티브 앱과 유사한 강력한 사용자 경험을 제공하는 것이 특징이다 [1].
|
||||
## 매 한 줄
|
||||
> **"매 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) 까지 가능.
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
* **빠른 로딩 및 이탈률 감소:** 기존 모바일 웹사이트와 비교하여 페이지 로딩 시간을 크게 단축시키며, 사용자의 이탈률(Bounce rates)을 최대 42%까지 감소시킬 수 있다 [1].
|
||||
* **오프라인 환경 지원:** 서비스 워커(Service workers) 기술을 활용하여 인터넷 연결이 불안정하거나 완전히 끊긴 상황에서도 애플리케이션이 정상적으로 작동하도록 지원한다 [1].
|
||||
* **개발 및 운영 비용 절감:** iOS와 Android용 네이티브 앱을 별도로 구축할 필요 없이 단일 PWA만 배포하면 되므로, 개발 비용을 30~50%가량 절감할 수 있다 [1].
|
||||
* **경량화 및 비즈니스 성과 향상 사례:** 스타벅스(Starbucks)는 PWA를 도입하여 기존 148MB에 달하던 모바일 앱의 용량을 1MB 미만으로 대폭 줄였으며(99.84% 감소), 동시에 일일 활성 사용자 수를 두 배로 늘리는 성과를 달성했다 [1].
|
||||
## 매 핵심
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
PWA는 오프라인 지원, 비용 절감, 용량 경량화 등 강력한 장점을 제공하지만, 완성된 애플리케이션을 기존 앱 스토어에 출시(Publishing)하는 과정에서 특정한 한계(limits)와 고려해야 할 배포 옵션들이 존재할 수 있다 [2]. 이 외에 PWA와 관련된 구체적인 기술적 부작용, 제약 사항 및 기타 성능적 반대 급부(Trade-off)에 대해서는 소스에 관련 정보가 부족합니다.
|
||||
### 매 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).
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
### Related Concepts
|
||||
### 매 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.
|
||||
|
||||
#### [관계 유형 A: 아키텍처/기반 기술]
|
||||
- [[Service Workers]]
|
||||
- 연결 이유: 인터넷 연결 없이도 PWA가 오프라인에서 동작할 수 있게 해주는 핵심 기반 기술이다 [1].
|
||||
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 웹 브라우저의 백그라운드 환경에서 오프라인 데이터 캐싱과 리소스 처리를 통해 네이티브 앱과 유사한 환경을 구현하는 원리 [1].
|
||||
### 매 응용
|
||||
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.
|
||||
|
||||
#### [관계 유형 B: 구현/비교 대상]
|
||||
- [[Native Apps]]
|
||||
- 연결 이유: PWA가 기술적으로 대체하고자 하는 전통적인 플랫폼 종속적 모바일 애플리케이션 모델이다 [1].
|
||||
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 다중 코드베이스를 유지해야 하는 네이티브 앱 대비 단일 코드베이스를 사용하는 PWA가 제공하는 30~50%의 비용 절감 메커니즘 [1].
|
||||
## 💻 패턴
|
||||
|
||||
- [[Cross-Platform Development]]
|
||||
- 연결 이유: 단일 코드베이스로 다중 운영체제를 지원하여 배포 시간과 비용을 줄인다는 점에서 PWA와 개발 목표를 공유하는 접근 방식이다 [1, 3].
|
||||
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 웹 표준 기반의 PWA 생태계와 Flutter나 React Native와 같은 프레임워크 기반 크로스 플랫폼 개발 방식 간의 전략적 차이 [1, 3].
|
||||
|
||||
### Deeper Research Questions
|
||||
- PWA의 서비스 워커를 통한 데이터 캐싱은 복잡한 동적 데이터를 실시간으로 처리할 때 어떠한 기술적 한계와 오버헤드를 가지는가?
|
||||
- 애플리케이션을 Apple App Store나 Google Play와 같은 기존 앱 스토어에 PWA 형태로 등록하여 배포할 때 발생하는 구체적인 제약 사항(Limits)과 우회 전략은 무엇인가?
|
||||
- 단일 코드베이스를 사용하는 PWA가 React Native 및 Flutter와 같은 크로스 플랫폼 프레임워크와 비교했을 때, 디바이스의 네이티브 하드웨어 API(카메라, 센서, 블루투스 등)에 접근하는 권한과 성능 차이는 어떠한가?
|
||||
- 스타벅스 사례처럼 148MB의 네이티브 앱을 1MB 이하의 PWA로 경량화하는 아키텍처 개편 과정에서 웹 기술로 완전히 대체할 수 없어 포기해야 했던 기능적 트레이드오프는 무엇이었는가?
|
||||
- PWA의 오프라인 지원 아키텍처를 탈중앙화 애플리케이션(dApps) 및 Web3 환경과 결합했을 때 얻을 수 있는 시스템적 시너지 및 보안 취약점은 무엇인가?
|
||||
|
||||
### Practical Application Contexts
|
||||
- **Implementation:** 웹 프로젝트에 서비스 워커를 등록하여 오프라인 캐싱 로직을 구현하고, 네트워크 단절 상황에서도 화면이 렌더링되도록 코드를 작성한다 [1].
|
||||
- **System Design:** iOS, Android, 웹 환경을 별도의 인프라로 설계하지 않고, 단일 코드베이스 기반의 PWA 배포 파이프라인 하나로 아키텍처를 통합하여 복잡성을 줄인다 [1].
|
||||
- **Operation / Maintenance:** 네이티브 앱 버전 파편화로 인한 운영 부담을 줄이고, 단일 웹 스택을 유지보수하여 전체 운영 및 개발 비용을 30~50% 감축한다 [1].
|
||||
- **Learning Path:** PWA 표준 규격, 오프라인 데이터 캐싱, 그리고 반응형 모바일 UI 디자인을 학습하여 기존 웹사이트를 고성능 앱 환경으로 전환하는 기술 역량을 강화한다 [1].
|
||||
- **My Project Relevance:** 모바일 앱 개발에 한정된 예산과 짧은 기간(Time-to-market)이 주어졌을 때, 비용 효율적으로 네이티브 수준의 사용자 경험을 제공하기 위한 최우선 모바일 전략으로 PWA를 채택한다 [1].
|
||||
|
||||
### Adjacent Topics
|
||||
- [[Cloud Native & Microservices Architectures]]
|
||||
- 확장 방향: 프론트엔드를 PWA로 경량화하는 전략과 연계하여, 백엔드 역시 마이크로서비스 및 서버리스 컴퓨팅(Serverless computing)을 도입하여 시스템 전체의 확장성과 로딩 성능을 최적화하는 아키텍처 연구로 확장 [4].
|
||||
- [[No Code & Low Code Development]]
|
||||
- 확장 방향: 제품 출시 주기 단축 및 개발 비용 절감이라는 PWA의 장점과 맞물려, 전문 지식 없이 애플리케이션을 빠르게 배포하기 위한 노코드/로우코드 플랫폼과의 결합 시나리오로 확장 [1, 5].
|
||||
|
||||
---
|
||||
*Last updated: 2026-05-02*
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
### 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" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### 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'];
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
self.addEventListener('install', (e) => {
|
||||
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(ASSETS)));
|
||||
});
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
self.addEventListener('activate', (e) => {
|
||||
e.waitUntil(caches.keys().then((keys) =>
|
||||
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
|
||||
));
|
||||
});
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
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)));
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
### 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' }));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
### 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
|
||||
- 부모: [[Web Platform]] · [[Frontend Architecture]]
|
||||
- 변형: [[Service Worker]] · [[Web App Manifest]] · [[Project Fugu]]
|
||||
- 응용: [[Offline-First]] · [[Push Notifications]] · [[Installable Web]]
|
||||
- Adjacent: [[IndexedDB]] · [[Cache API]] · [[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 반영 |
|
||||
|
||||
Reference in New Issue
Block a user