๋ฉ”์ธ ์ปจํ…์ธ ๋กœ ์ด๋™
๋ฒ„์ „: 2.x

๐Ÿ“ฆ plugin-pwa

Docusaurus Plugin to add PWA support using Workbox. This plugin generates a Service Worker in production build only, and allows you to create fully PWA-compliant documentation site with offline and installation support.

Installationโ€‹

npm install --save @docusaurus/plugin-pwa

Configurationโ€‹

Create a PWA manifest at ./static/manifest.json.

Modify docusaurus.config.js with a minimal PWA config, like:

docusaurus.config.js
module.exports = {
plugins: [
[
'@docusaurus/plugin-pwa',
{
debug: true,
offlineModeActivationStrategies: [
'appInstalled',
'standalone',
'queryString',
],
pwaHead: [
{
tagName: 'link',
rel: 'icon',
href: '/img/docusaurus.png',
},
{
tagName: 'link',
rel: 'manifest',
href: '/manifest.json', // your PWA manifest
},
{
tagName: 'meta',
name: 'theme-color',
content: 'rgb(37, 194, 160)',
},
],
},
],
],
};

Progressive Web Appโ€‹

์„œ๋น„์Šค ์›Œ์ปค๋ฅผ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด PWA๊ฐ€ ๋˜๋Š” ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. You'll need to at least include a Web App Manifest and have the correct tags in <head> (Options > pwaHead).

After deployment, you can use Lighthouse to run an audit on your site.

For a more exhaustive list of what it takes for your site to be a PWA, refer to the PWA Checklist

App installation supportโ€‹

์—ฌ๋Ÿฌ๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ง€์›ํ•œ๋‹ค๋ฉด ๋„ํ์‚ฌ์šฐ๋ฃจ์Šค ์‚ฌ์ดํŠธ๋ฅผ ์•ฑ์ฒ˜๋Ÿผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

A screen recording of the installation process. A button appears in the address bar of the browser, which displays a dialog asking &quot;install this application?&quot; when clicked. After clicking the &quot;Install&quot; button, a new application is opened in the operating system, opening to the Docusaurus homepage.

์ฐธ๊ณ 

์•ฑ ์„ค์น˜๋ฅผ ์ง€์›ํ•˜๋ ค๋ฉด HTTPS ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฉฐ ์œ ํšจํ•œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Offline mode (precaching)โ€‹

์„œ๋น„์Šค ์›Œ์ปค ์‚ฌ์ „์บ์‹ฑ์„ ์‚ฌ์šฉํ•ด ๋„ํ์‚ฌ์šฐ๋ฃจ์Šค ์‚ฌ์ดํŠธ๋ฅผ ์˜คํ”„๋ผ์ธ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

The workbox-precaching page explains the idea:

์„œ๋น„์Šค ์›Œ์ปค์˜ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋กœ ์„œ๋น„์Šค ์›Œ์ปค ์„ค์น˜ ์‹œ ์บ์‹œ ์ฒ˜๋ฆฌํ•  ํŒŒ์ผ ๋ฌถ์Œ์„ ์ €์žฅํ•ด๋†“์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค ์›Œ์ปค๊ฐ€ ์‚ฌ์šฉ๋˜๊ธฐ ์ „์— ์ฝ˜ํ…์ธ ๋ฅผ ์บ์‹œ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— "์‚ฌ์ „์บ์‹ฑ"์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ์ด์œ ๋Š” ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์บ์‹œ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ์ฃผ๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ์บ์‹œ๋ฅผ ์–ธ์ œ ๋งŒ๋“ค๊ณ  ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ๋ณด๊ด€ํ• ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์„ ๋ฟ ์•„๋‹ˆ๋ผ ๋„คํŠธ์›Œํฌ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ”๋กœ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰ ์˜คํ”„๋ผ์ธ์—์„œ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์›น ์•ฑ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒ๋‹ˆ๋‹ค.

Workbox๋Š” API๋ฅผ ๋‹จ์ˆœํ™”ํ•˜๊ณ  ํšจ์œจ์ ์œผ๋กœ ํ•„์š”ํ•œ ํŒŒ์ผ์„ ๋‚ด๋ ค๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด ์‚ฌ์ „์บ์‹ฑ ์ž‘์—…์„ ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ์˜คํ”„๋ผ์ธ ๋ชจ๋“œ๋Š” ์‚ฌ์ดํŠธ๊ฐ€ ์•ฑ์œผ๋กœ ์„ค์น˜๋˜๋ฉด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค. See the offlineModeActivationStrategies option for details.

์‚ฌ์ดํŠธ ์‚ฌ์ „์บ์‹ฑ ์ดํ›„์— ์„œ๋น„์Šค ์›Œ์ปค๋Š” ๋ฐฉ๋ฌธ์ž์—๊ฒŒ ์บ์‹œ๋œ ์‘๋‹ต์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ๋นŒ๋“œ๊ฐ€ ์ƒˆ๋กœ์šด ์„œ๋น„์Šค ์›Œ์ปค์™€ ํ•จ๊ป˜ ๋ฐฐํฌ๋˜๋ฉด ์ƒˆ๋กœ์šด ์•ฑ์ด ์„ค์น˜๋˜๊ณ  ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ์ด๋™ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋Œ€๊ธฐ ์ƒํƒœ์— ๋“ค์–ด๊ฐ€๋ฉด ๋ฆฌ๋กœ๋“œ ํŒ์—…์ด ํ‘œ์‹œ๋˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ƒˆ๋กœ์šด ์ฝ˜ํ…์ธ ๋ฅผ ์œ„ํ•ด ํŽ˜์ด์ง€๋ฅผ ๋ฆฌ๋กœ๋“œํ•  ๊ฒƒ์ธ์ง€ ๋ฌผ์–ด๋ด…๋‹ˆ๋‹ค. Until the user either clears the application cache or clicks the reload button on the popup, the service worker will continue serving the old content.

warning

์˜คํ”„๋ผ์ธ ๋ชจ๋“œ๋‚˜ ์‚ฌ์ „์บ์‹ฑ์€ ์‚ฌ์ดํŠธ์˜ ๋ชจ๋“  ์• ์…‹์„ ๋ฏธ๋ฆฌ ๋‚ด๋ ค๋ฐ›์•„์•ผ ํ•˜๋ฉฐ ๋ถˆํ•„์š”ํ•œ ๋Œ€์—ญํญ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์ข…๋ฅ˜์˜ ์‚ฌ์ดํŠธ์—์„œ ๋ฌด์กฐ๊ฑด ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฑด ์ข‹์€ ์ƒ๊ฐ์€ ์•„๋‹™๋‹ˆ๋‹ค.

Optionsโ€‹

debugโ€‹

  • Type: boolean
  • Default: false

์•„๋ž˜์™€ ๊ฐ™์€ ๋””๋ฒ„๊ทธ ๋ชจ๋“œ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

  • Workbox ๋กœ๊ทธ
  • ์ถ”๊ฐ€์ ์ธ ๋„ํ์‚ฌ์šฐ๋ฃจ์Šค ๋กœ๊ทธ
  • ์ตœ์ ํ™”๋˜์ง€ ๋ชปํ•œ SW ํŒŒ์ผ ์ถœ๋ ฅ
  • ์†Œ์Šค๋งต

offlineModeActivationStrategiesโ€‹

  • Type: ('appInstalled' | 'mobile' | 'saveData'| 'queryString' | 'always')[]
  • Default: ['appInstalled', 'queryString', 'standalone']

์˜คํ”„๋ผ์ธ ๋ชจ๋“œ๋ฅผ ์ ์šฉํ•  ์ƒํƒœ์— ๋Œ€ํ•œ ์˜ต์…˜์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

  • appInstalled: activates for users having installed the site as an app (not 100% reliable)
  • standalone: activates for users running the app as standalone (often the case once a PWA is installed)
  • queryString: activates if queryString contains offlineMode=true (convenient for PWA debugging)
  • mobile: activates for mobile users (width <= 996px)
  • saveData: activates for users with navigator.connection.saveData === true
  • always: activates for all users
warning

์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”. ์ผ๋ถ€ ์‚ฌ์šฉ์ž๋Š” ์˜คํ”„๋ผ์ธ ๋ชจ๋“œ๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐ•์ œ๋˜๋Š” ๊ฒƒ์„ ์‹ซ์–ดํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„ํ—˜

๋งค์šฐ ์•ˆ์ •์ ์ธ ๋ฐฉ์‹์œผ๋กœ PWA๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

The appinstalled event has been removed from the specification, and the navigator.getInstalledRelatedApps() API is only supported in recent Chrome versions and require related_applications declared in the manifest.

The standalone strategy is a nice fallback to activate the offline mode (at least when running the installed app).

injectManifestConfigโ€‹

Workbox options to pass to workbox.injectManifest(). ํ”„๋ฆฌ์บ์‹ฑ์„ ์ ์šฉํ•˜๊ณ  ์˜คํ”„๋ผ์ธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์• ์…‹์„ ์ง์ ‘ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Type: InjectManifestOptions
  • Default: {}
docusaurus.config.js
module.exports = {
plugins: [
[
'@docusaurus/plugin-pwa',
{
injectManifestConfig: {
manifestTransforms: [
//...
],
modifyURLPrefix: {
//...
},
// We already add regular static assets (HTML, images...) to be available offline
// You can add more files according to your needs
globPatterns: ['**/*.{pdf,docx,xlsx}'],
// ...
},
},
],
],
};

pwaHeadโ€‹

  • Type: ({ tagName: string; [attributeName: string]: string })[]
  • Default: []

Array of objects containing tagName and key-value pairs for attributes to inject into the <head> tag. ๊ธฐ์ˆ ์ ์œผ๋กœ ์–ด๋–ค head ํƒœ๊ทธ ๋“  ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์—ฌ๋Ÿฌ๋ถ„์˜ ์‚ฌ์ดํŠธ PWA์— ์ ํ•ฉํ•œ ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ชฉ๋ก์€ ์—ฌ๋Ÿฌ๋ถ„์˜ ์•ฑ์— ์ ํ•ฉํ•œ ํƒœ๊ทธ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

module.exports = {
plugins: [
[
'@docusaurus/plugin-pwa',
{
pwaHead: [
{
tagName: 'link',
rel: 'icon',
href: '/img/docusaurus.png',
},
{
tagName: 'link',
rel: 'manifest',
href: '/manifest.json',
},
{
tagName: 'meta',
name: 'theme-color',
content: 'rgb(37, 194, 160)',
},
{
tagName: 'meta',
name: 'apple-mobile-web-app-capable',
content: 'yes',
},
{
tagName: 'meta',
name: 'apple-mobile-web-app-status-bar-style',
content: '#000',
},
{
tagName: 'link',
rel: 'apple-touch-icon',
href: '/img/docusaurus.png',
},
{
tagName: 'link',
rel: 'mask-icon',
href: '/img/docusaurus.svg',
color: 'rgb(37, 194, 160)',
},
{
tagName: 'meta',
name: 'msapplication-TileImage',
content: '/img/docusaurus.png',
},
{
tagName: 'meta',
name: 'msapplication-TileColor',
content: '#000',
},
],
},
],
],
};

swCustomโ€‹

  • Type: string | undefined
  • Default: undefined

Workbox์— ์ ์šฉํ•  ๊ทœ์น™ ์ถ”๊ฐ€ ์‹œ ์œ ์šฉํ•œ ์„ค์ •์ž…๋‹ˆ๋‹ค. ์„œ๋น„์Šค ์›Œ์ปค์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ๊ณผ ํ•จ๊ป˜ Workbox ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฐ•๋ ฅํ•œ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠธ๋žœ์ŠคํŒŒ์ผ๋œ ์ฝ”๋“œ์ด๋ฉฐ ์ตœ์‹  ES6+ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์™ธ๋ถ€ ๊ฒฝ๋กœ์— ์žˆ๋Š” ํŒŒ์ผ์„ ์บ์‹œํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';

// default fn export receiving some useful params
export default function swCustom(params) {
const {
debug, // :boolean
offlineMode, // :boolean
} = params;

// Cache responses from external resources
registerRoute((context) => {
return [
/graph\.facebook\.com\/.*\/picture/,
/netlify\.com\/img/,
/avatars1\.githubusercontent/,
].some((regex) => context.url.href.match(regex));
}, new StaleWhileRevalidate());
}

The module should have a default function export, and receives some params.

swRegisterโ€‹

  • Type: string | false
  • Default: 'docusaurus-plugin-pwa/src/registerSW.js'

์•ฑ์ด ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ๋“ฑ๋ก์ด ์ฒ˜๋ฆฌ๋˜๋„๋ก ๋„ํ์‚ฌ์šฐ๋ฃจ์Šค ์•ฑ ์•ž์— ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. The default registerSW.js file is enough for simple registration.

Passing false will disable registration entirely.

Manifest exampleโ€‹

๋„ํ์‚ฌ์šฐ๋ฃจ์Šค ์‚ฌ์ดํŠธ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

{
"name": "Docusaurus v2",
"short_name": "Docusaurus",
"theme_color": "#2196f3",
"background_color": "#424242",
"display": "standalone",
"scope": "./",
"start_url": "./index.html",
"related_applications": [
{
"platform": "webapp",
"url": "https://docusaurus.io/manifest.json"
}
],
"icons": [
{
"src": "img/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "img/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "img/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "img/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "img/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "img/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "img/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "img/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

Customizing reload popupโ€‹

The @theme/PwaReloadPopup component is rendered when a new service worker is waiting to be installed, and we suggest a reload to the user. You can swizzle this component and implement your own UI. It will receive an onReload callback as props, which should be called when the reload button is clicked. ๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ๋Š” ์„œ๋น„์Šค ์›Œ์ปค์—๊ฒŒ ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ํŽ˜์ด์ง€๋ฅผ ๋ฆฌ๋กœ๋“œํ•˜๋„๋ก ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

The default theme includes an implementation for the reload popup and uses Infima Alerts.

A screen recording of the reload process. An alert box shows in the bottom right of the window, saying &quot;New content available&quot;. After clicking the &quot;Refresh&quot; button, the page&#39;s main heading changes from &quot;Introduction&quot; to &quot;PWA :))&quot;.

Your component can render null, but this is not recommended: users won't have a way to get up-to-date content.