blog
vite-plugin-pwa로 Vite 앱을 PWA로 만들기
Front-end / Tooling#Vite#PWA#Service Worker#Workbox
vite-plugin-pwa는 웹 앱 매니페스트와 서비스 워커(Workbox)를 빌드 시점에 함께 생성한다. 매니페스트 파일과 서비스 워커를 따로 손으로 만들어 등록할 필요가 없다.
1. 설치
npm install -D vite-plugin-pwa @vite-pwa/assets-generator
2. 아이콘 생성
로고 SVG 하나로 PWA에 필요한 아이콘 세트(64·192·512, maskable, apple-touch-icon)를 만든다.
// package.json
{
"scripts": {
"generate-pwa-assets": "pwa-assets-generator --preset minimal-2023 public/logo.svg"
}
}
3. 플러그인 설정
매니페스트는 별도 manifest.json 대신 플러그인 옵션에 적는다. 빌드하면 manifest.webmanifest가 생성되고 index.html에 링크가 자동으로 들어간다.
// vite.config.ts
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'apple-touch-icon-180x180.png'],
manifest: {
name: 'PROJECT_NAME',
short_name: 'PROJECT_SHORT_NAME',
description: 'PROJECT_DESCRIPTION',
lang: 'ko',
start_url: '/',
scope: '/',
display: 'standalone',
orientation: 'portrait',
background_color: '#ffffff',
theme_color: '#ffffff',
icons: [
{ src: 'pwa-64x64.png', sizes: '64x64', type: 'image/png' },
{ src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' },
{ src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
{ src: 'maskable-icon-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
],
},
workbox: {
// 오프라인 상태에서 페이지 이동 시 보여줄 문서
navigateFallback: '/index.html',
},
}),
],
});
prefer_related_applications: true는 넣지 않는다. 이 값은 "네이티브 앱을 우선 설치하라"는 뜻이라 PWA 설치 안내가 뜨지 않을 수 있다.
4. 서비스 워커 등록
registerType: 'autoUpdate'이면 플러그인이 등록 스크립트를 자동으로 주입한다. 등록 시점이나 업데이트 알림을 직접 제어하고 싶을 때만 가상 모듈을 사용한다.
// main.ts
import { registerSW } from 'virtual:pwa-register';
registerSW({ immediate: true });
5. HTTPS
서비스 워커는 보안 컨텍스트에서만 동작한다. localhost는 예외로 허용되지만, 같은 네트워크의 모바일 기기에서 IP로 접속해 테스트하려면 HTTPS가 필요하다. 로컬 인증서는 mkcert로 발급하는 방법이 가장 간단하다.
참고로 <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">는 페이지 안의 http 요청을 https로 바꿔 Mixed Content를 막는 설정일 뿐, 사이트를 HTTPS로 만들어 주지는 않는다.