PWA는 서비스 워커, 매니페스트, HTTPS를 사용해 설치할 수 있고 오프라인에서 동작하며 푸시 알림을 받을 수 있는 웹 앱입니다. 2015년 Alex Russell과 Frances Berriman이 만든 이 개념은 웹을 떠나지 않고도 웹사이트와 설치된 앱 사이의 간극을 메웁니다. 하나의 코드베이스, 홈 화면 아이콘, 독립 창, 그리고 기차가 터널에 들어가도 계속 동작하는 화면입니다. 이 용어집이 더하는 관점이자 프론트엔드 설명 글들이 건너뛰는 지점은 이것입니다. PWA도 결국 클라이언트입니다. 서비스 워커는 캐시할 뿐이고, 원본 데이터(source of truth)는 백엔드에 있습니다.
핵심 요약
| 질문 | 답변 |
|---|---|
| 세 가지 기둥 | 서비스 워커 · 웹 앱 매니페스트 · HTTPS |
| 더해 주는 것 | 설치 가능 · 오프라인 동작 · 푸시 지원, 모두 하나의 코드베이스 위에서 |
| 반응형 사이트와 비교 | 서비스 워커 + 매니페스트를 더함. 레이아웃만으로는 PWA가 아님 |
| 네이티브와 비교 | 도달 범위와 속도 vs. 하드웨어 접근과 스토어 노출 |
| 백엔드의 진실 | 서비스 워커 캐시는 사본이고, API/BaaS가 원본 |
세 가지 기둥과 그 뒤의 백엔드
// JavaScript — the PWA's three pillars + the backend behind them
// 1 · Service worker: caches the shell, buffers writes when offline
navigator.serviceWorker.register('/sw.js');
// 2 · Manifest (public/manifest.json) makes it installable:
// { "name": "Notes", "display": "standalone", "start_url": "/", "icons": [...] }
// 3 · HTTPS is required — service workers refuse to run otherwise.
// The catch: the SW cache is a MIRROR, not the source of truth.
// saveEventually() stores the write offline and sends it when back online:
const note = new Parse.Object('Note').set('text', draft);
await note.saveEventually(); // the BaaS is authoritative; the cache was a buffer // Flutter / Dart — Back4app Flutter SDK (Flutter web compiles to a PWA)
// The service worker caches the app shell; data still lives on the backend.
final note = ParseObject('Note')..set('text', draft);
await note.save();
// Offline-first pattern: write locally, queue, and sync when connectivity
// returns — the API/BaaS is the source of truth, the cache is a local mirror.
// Push notifications need a BACKEND to send them; the client only subscribes. // Swift — a native shell can host a PWA/WebView; the backend is shared
// The point of parity: whatever the client (PWA, native, web), the
// source of truth is the same backend API.
var note = Note()
note.text = draft
_ = try await note.save()
// Offline-first: cache locally, sync when online — the API is authoritative.
// Web push (PWA) and native push (APNs) both need a backend to SEND them. // Kotlin — a native shell can host a PWA/WebView; the backend is shared
// The point of parity: whatever the client (PWA, native, web), the
// source of truth is the same backend API.
val note = ParseObject("Note")
note.put("text", draft)
note.save()
// Offline-first: cache locally, sync when online — the API is authoritative.
// Web push (PWA) and native push (APNs/FCM) both need a backend to SEND them. 서비스 워커는 앱과 네트워크 사이에 프록시로 자리 잡는 백그라운드 스크립트로, 앱 셸을 캐시해 오프라인에서도 제공합니다. 웹 앱 매니페스트는 앱을 설치 가능하게 만드는 JSON 메타데이터(이름, 아이콘, display: standalone, 시작 URL)입니다. 그리고 HTTPS는 필수입니다. 서비스 워커는 보안 컨텍스트 밖에서는 실행을 거부하기 때문입니다. 이 셋이 앱의 앞단을 이룹니다. 코드 탭의 save()는 그 뒤에 있는 요점을 보여 줍니다. 캐시는 사본이고, 네트워크가 돌아오면 쓰기는 원본인 백엔드로 동기화됩니다.
PWA가 오프라인에서 페이지를 제공하는 방식
오프라인 캐싱 전략
| 전략 | 제공 출처 | 적합한 대상 |
|---|---|---|
| Cache-first | 캐시, 그다음 네트워크 | 정적 에셋: 앱 셸, 아이콘, 폰트 |
| Network-first | 네트워크, 실패 시 캐시 | 항상 최신이어야 하는 데이터 |
| Stale-while-revalidate | 지금은 캐시, 백그라운드에서 갱신 | 잠시 오래되어도 괜찮은 콘텐츠 |
| Cache-only / network-only | 한 출처만 사용 | 미리 캐시한 필수 리소스 / 절대 캐시하지 않는 호출 |
결정은 앱 단위가 아니라 리소스 단위로 내립니다. 앱 셸은 즉시 로드를 위해 cache-first가 맞고, 사용자의 실시간 데이터는 최신성을 위해 network-first가 맞습니다. 피드는 stale-while-revalidate로 즉각적인 체감을 주면서 스스로 바로잡을 수 있습니다. 어떤 전략을 고르든 캐시는 API 위의 읽기/쓰기 버퍼입니다. 오프라인 쓰기는 로컬 대기열에 쌓였다가 재연결 시 조정되며, 이는 모든 오프라인 우선 클라이언트에 필요한 동일한 따라잡기 규율입니다.
푸시 알림은 실제로 어디에서 오는가
거의 어떤 용어집도 설명하지 않는 메커니즘이자, PWA가 알림 하나를 보내는 데도 백엔드가 필요한 이유입니다. 클라이언트는 구독만 합니다. 권한을 요청하고 구독 엔드포인트를 받을 뿐입니다. 발송은 서버의 일입니다. 백엔드 → 브라우저의 푸시 서비스 → 서비스 워커의 push 이벤트 → 표시된 알림. 서비스 워커는 앱이 닫혀 있어도 깨어나 알림을 표시할 수 있지만, 백엔드가 보내지 않으면 아무것도 도착하지 않습니다. 그래서 “PWA에는 푸시가 있다”는 말은 서버 쪽 절반이 없으면 기능의 절반에 불과합니다. 전달 체인은 푸시 알림 항목에서 다룹니다. 여기서의 요점은 웹 푸시든 네이티브 푸시든 결국 발송을 결정하는 것은 개발자 자신의 코드라는 점입니다.
PWA vs. 네이티브 앱
| PWA | 네이티브 | |
|---|---|---|
| 코드베이스 | 웹 기반 하나 | 플랫폼마다 하나(또는 크로스 플랫폼) |
| 배포 | URL 하나, 스토어 불필요 | 심사를 거치는 앱 스토어 |
| 업데이트 | 서버 측에서 즉시 | 스토어 출시 주기 |
| 발견 가능성 | 검색 엔진에 색인됨 | 스토어 검색만 |
| 하드웨어 접근 | 제한적(OS마다 다름) | 전체 |
| 푸시 안정성 | Android에서는 양호, iOS에서는 조건부 | APNs/FCM으로 안정적 |
| iOS의 현실 | 공유 메뉴를 통한 설치만 가능, 16.4부터 푸시 | 정식 지원 |
솔직한 비대칭은 마지막 행에 있습니다. iOS는 PWA가 가장 약한 곳입니다. 설치는 공유 메뉴를 통해서만 가능하고, 자동 설치 프롬프트가 없으며, 푸시는 설치된 경우에만 그리고 최신 버전에서만 동작하고, 백그라운드 동기화는 불완전합니다. Android와 데스크톱에서는 네이티브와의 격차가 작지만, iOS에서는 그것이 많은 제품의 결정 요인이 됩니다.
일반적인 사용 사례
- 콘텐츠와 커머스: 새 사용자에게 다가가는 데는 도달 범위, SEO, 낮은 설치 마찰이 스토어 다운로드보다 유리합니다.
- 사내 도구: 스토어 배포 없이 알려진 사용자에게 제공하는, 설치 가능하고 오프라인에 강한 앱입니다.
- 오프라인 우선 현장 앱: 신호가 없을 때 데이터를 수집하고 신호가 돌아오면 동기화합니다. 버퍼로서의 캐시 패턴입니다.
- 적은 예산으로 얻는 크로스 플랫폼 도달 범위: 네이티브로 만들면 작업량이 세 배가 될 여러 플랫폼을 하나의 웹 코드베이스로 커버합니다.
- 네이티브 앱의 동반자: 같은 백엔드가 도달 범위를 위한 PWA와 깊이를 위한 네이티브 앱을 함께 지원합니다.
PWA를 선택해야 할까? 결정 매트릭스
| 상황 | 기울어지는 쪽 |
|---|---|
| 도달 범위, SEO, 낮은 마찰, 하나의 코드베이스 | PWA |
| 주요 사용자층이 Android/데스크톱 | PWA. 네이티브와의 격차가 작음 |
| iOS가 핵심이고 푸시 의존도가 높음 | 네이티브, 또는 iOS의 제약을 감수 |
| 무거운 그래픽, AR, 깊은 하드웨어 통합 | 네이티브 |
| 오프라인 우선 데이터 수집 | 동기화 백엔드를 갖춘 PWA |
| 스토어 노출이 필요함 | 네이티브(또는 허용되는 곳에서는 스토어에 올린 PWA) |
한계와 트레이드오프
- iOS가 상한선을 정합니다. 공유 메뉴를 통한 수동 설치, 조건부 푸시, 백그라운드 동기화의 공백 때문에 iOS는 PWA냐 네이티브냐를 가장 자주 결정하는 제약이 됩니다.
- 하드웨어 접근은 부분적입니다. Bluetooth, NFC, 연락처, 고급 카메라 API는 OS에 따라 제한되거나 아예 없습니다. 기기 깊숙이 들어가는 앱에는 여전히 네이티브가 필요합니다.
- 캐시는 데이터베이스가 아닙니다. 서비스 워커 캐시를 원본 데이터로 취급하면 쓰기 유실과 오래된 데이터 충돌을 부릅니다. 캐시는 원본인 백엔드 위의 버퍼입니다.
- 푸시는 절반은 클라이언트, 절반은 서버입니다. 구독은 쉽지만 PWA에도 발송할 백엔드가 필요합니다. 따라서 “오프라인과 푸시”는 프론트엔드 배지를 단 백엔드 기능입니다.
- 발견 가능성은 양날의 검입니다. 스토어의 관문이 없다는 것은 스토어 진열대도 없다는 뜻입니다. 큐레이션과 설치 절차를 URL 하나와 맞바꾸는 셈입니다.
Back4app에서의 PWA
Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. 프론트엔드의 기둥들만으로는 완성되지 않는 기능의 백엔드 쪽 절반입니다. 서비스 워커는 앱 셸을 캐시하지만, 캐시가 비추는 원본 데이터는 Back4app입니다. 인증된 사용자, 재연결 시 오프라인 쓰기를 조정하는 동기화된 데이터, 그리고 PWA가 구독만 할 수 있는 웹 푸시를 실제로 보내는 서버까지 포함됩니다. 하나의 백엔드가 플랫폼별 SDK를 통해 PWA, 일반 웹 앱, 네이티브 앱을 똑같이 지원하므로, PWA가 프론트엔드에서 약속하는 크로스 플랫폼 도달 범위는 백엔드에서도 한 번 작성으로 모든 곳에 닿는 구조와 짝을 이룹니다. 클라이언트는 캐시하고 플랫폼은 기억하며, “설치 가능, 오프라인, 푸시”의 두 절반이 마침내 만납니다.
자주 묻는 질문
PWA란 무엇입니까?
표준 웹 기술로 만든 웹 앱으로, 최신 브라우저 기능을 활용해 설치된 앱처럼 동작합니다. 홈 화면 아이콘, 독립 창, 오프라인 지원, 푸시 알림을 모두 웹으로 전달되는 하나의 코드베이스에서 제공합니다. 이 용어는 2015년 Alex Russell과 Frances Berriman이 만들었습니다.
어떤 조건을 갖추면 PWA가 됩니까?
세 가지가 함께 동작해야 합니다. 서비스 워커(에셋을 캐시하고 오프라인 사용과 푸시를 가능하게 하는 백그라운드 스크립트), 웹 앱 매니페스트(앱을 설치 가능하게 만드는 JSON 메타데이터), HTTPS(서비스 워커는 보안 컨텍스트가 아니면 실행되지 않습니다)입니다. 실무적인 기준은 네트워크 상태와 관계없이 설치할 수 있고 안정적으로 동작하는가입니다.
서비스 워커란 무엇입니까?
자체 스레드에서 백그라운드로 실행되며 앱과 네트워크 사이에서 네트워크 프록시 역할을 하는 JavaScript 파일입니다. 요청을 가로채 캐시나 네트워크에서 응답하며, 이것이 오프라인 사용, 백그라운드 동기화, 앱이 닫혀 있을 때의 푸시 메시지 수신을 가능하게 합니다.
PWA와 네이티브 앱 중 어느 쪽이 더 낫습니까?
PWA는 더 빠르고 저렴하게 만들 수 있고, 하나의 코드베이스로 배포되며, 즉시 업데이트되고, 앱 스토어가 필요 없으며, 검색으로 발견됩니다. 네이티브 앱은 하드웨어 전체에 접근할 수 있고, 가장 매끄러운 UX와 더 안정적인 푸시, 스토어 노출을 얻습니다. 무거운 그래픽이나 깊은 기기 통합이 필요하면 네이티브를, 도달 범위와 출시 속도가 중요하면 PWA를 선택하는 것이 좋습니다.
PWA와 반응형 웹사이트의 차이는 무엇입니까?
반응형 사이트는 화면 크기에 맞춰 레이아웃만 조정하며 연결이 필요합니다. PWA는 그 위에 설치 가능성, 오프라인 캐싱, 푸시 알림을 더합니다. 모든 PWA는 반응형이지만, 반응형 사이트는 서비스 워커와 매니페스트를 추가해야 비로소 PWA가 됩니다.
PWA는 푸시 알림을 보낼 수 있습니까?
Push API와 Notification API로 웹 푸시를 받을 수는 있지만, 클라이언트는 구독만 할 뿐이고 실제 발송은 백엔드가 해야 합니다. iOS 지원은 16.4에서 도입되었고 PWA가 홈 화면에 설치된 경우에만 동작하므로, 웹 푸시가 닿는 사용자층은 네이티브 푸시보다 좁습니다.
PWA는 오프라인에서 동작합니까?
그렇습니다. 서비스 워커가 에셋과 데이터를 캐시하고 네트워크가 끊기면 캐시에서 제공합니다. 일반적인 전략은 정적 에셋에는 cache-first, 최신 데이터에는 network-first, 즉시 표시하면서 백그라운드에서 갱신하려면 stale-while-revalidate입니다. 오프라인에서 발생한 쓰기는 대기열에 쌓였다가 연결이 돌아오면 동기화됩니다.
PWA를 앱 스토어에 올릴 수 있습니까?
부분적으로 가능합니다. Android의 스토어는 Trusted Web Activity로 패키징된 PWA를 받아 주고, 일부 데스크톱 스토어는 PWA를 정식 앱으로 취급합니다. Apple의 App Store는 사실상 받아 주지 않습니다. 웹사이트를 다시 포장한 PWA는 일상적으로 거절되며, 이것이 iOS가 여전히 PWA에 가장 까다로운 플랫폼인 이유 중 하나입니다.