프로그레시브 웹 앱 이해: 모바일 경험 제공 - 2021년 HTML5 및 JavaScript 서비스 작업자
본문
PWA 란 무엇입니까?
PWA(Progressive Web Application)는 웹을 통해 제공되는 일종의 앱 소프트웨어입니다. PWA는 HTML, CSS 및 JavaScript와 같은 일반적인 기술을 사용하여 구축됩니다. 데스크톱 또는 모바일 장치에서 W3C 호환 웹 브라우저를 사용하는 모든 플랫폼에 설치하고 작동할 수 있습니다.
브라우저는 보안 연결(SSL 암호화 기술 사용) 또는 로컬 호스트에서만 웹 애플리케이션이 PWA가 되도록 허용한다는 점에 유의해야 합니다.
누가 PWA를 설치할 수 있습니까?
PWA에 대한 지원은 대부분 모바일 브라우저에 중점을 두고 있지만 일부 데스크톱 브라우저에 대한 지원도 제한적입니다.
2021년 8월 지원:
Mobile:
KaiOS를 제외한 거의 모든 모바일 브라우저는 PWA를 지원합니다.
Desktop:
- Chrome 39 & UP
- Edge 79 & UP
- IOS Safari/Chrome 11.3 & UP - Partial Support
- Firefox - Deprecated as of January 2021
왜 PWA를 개발해야 합니까?
목적:
PWA를 사용하면 모든 장치에 설치할 수 있고 기본 앱과 구별할 수 없는 방식으로 작동하는 웹 응용 프로그램을 제공할 수 있습니다. 특정 기준을 충족하면 웹사이트나 웹 애플리케이션도 쉽게 PWA로 전환될 수 있습니다.
필요한 기술:
- HTML
- CSS
- 자바스크립트
- NodeJS(여기서 사용할 선택적 패키지)
혜택:
- 애플리케이션 스토어 등록
- 오프라인 폴백
- 네트워크 또는 캐시 우선 리소스 가져오기
- 푸시 알림
- 백그라운드 동기화
- 그리고 더
시작하는 데 많은 시간이 필요하지 않습니다!
파일 구조
-root/
-index.html
-manifest.json
-service-worker.js
-logo.[png, jpg, etc...]
index.html
개발은 기본 HTML5 상용구로 시작할 수 있습니다.
HTML5 상용구
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TechSnack Simple PWA</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
<html>
manifest.json
사용자의 장치가 웹 응용 프로그램으로 무엇을 해야 하는지 알 수 있도록 몇 가지 세부 정보를 제공해야 합니다. 이러한 세부 사항은 상당히 자명합니다.
지금은 아이콘 배열을 비워 둡니다. 이에 대한 자세한 내용은 곧.
{
"name": "TechSnack Simple PWA",
"short_name": "TechSnack",
"start_url": "/?home=true",
"icons": [],
"theme_color": "#000000",
"background_color": "#FFFFFF",
"display": "fullscreen",
"orientation": "portrait"
}
이 정보를 사용하여 사용자의 장치는 다음을 수행할 수 있습니다.
- 애플리케이션 설치
- 앱 실행을 위한 사용자 지정 아이콘 적용
- 시작 시 사용자 지정 시작 화면 표시
- 응용 프로그램 창 및 동작 사용자 지정 허용
- 네이티브 애플리케이션 동작 모방
- GPS 및 푸시 알림과 같은 기본 기능에 대한 액세스 허용
- 인기 있는 앱 스토어에 애플리케이션 등록
manifest.json 연결
링크 태그를 사용하여 manifest.json을 앱에 연결합니다.
<head>
...
<link rel="manifest" href="manifest.json">
</head>
노드 패키지(1 - 선택 사항)
앞서 언급했듯이 사용자의 장치는 사용자 지정 아이콘을 적용합니다. 그렇게 하려면 장치가 참조할 이미지를 하나 이상 제공해야 합니다.
여러 화면 크기 또는 해상도는 어떻습니까?
오늘날 전 세계적으로 수많은 다양한 모바일 장치가 사용되고 있습니다. 시각적 자산의 표시를 최적화하기 위해 각 장치는 특정 차원의 로고를 선호합니다.
pwa-asset-generator
다음 MIME 유형의 이미지 파일이 하나 이상 필요합니다.
- PNG
- JPEG/JPG
- SVG
- WebP
설치
$ npm install --global pwa-asset-generator
이제 webroot 디렉토리에서 패키지를 실행하려고 합니다. 다음 스니펫은 우리의 목적을 위해 수행할 것입니다.
npx pwa-asset-generator [path/to/logo] [path/to/output/dir] -i [path/to/index.html] -m [path/to/manifest.json] -f
The -f
flag generates favicon image/meta tag
Execution - From webroot directory
$ npx pwa-asset-generator logo.jpg logos -i index.html -m manifest.json -f
-i, -m 및 -f 플래그를 사용하지 않고 pwa-asset-generator를 실행하면 결과가 대신 콘솔에 출력된다는 점에 주목할 가치가 있습니다.
결과를 복사하여 manifest.json 내의 아이콘 배열에 붙여넣습니다.
출력을 복사하여 index.html의 head 태그에 붙여넣습니다.
새 아이콘/디렉토리
생성된 모든 이미지를 포함합니다.
업데이트된 index.html
index.html 파일은 이제 다음과 같아야 합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#000000">
<link rel="apple-touch-icon" href="icons/apple-icon-180.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2048-2732.jpg" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2732-2048.jpg" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1668-2388.jpg" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2388-1668.jpg" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1536-2048.jpg" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2048-1536.jpg" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1668-2224.jpg" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2224-1668.jpg" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1620-2160.jpg" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2160-1620.jpg" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1284-2778.jpg" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2778-1284.jpg" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1170-2532.jpg" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2532-1170.jpg" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1125-2436.jpg" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2436-1125.jpg" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1242-2688.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2688-1242.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-828-1792.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1792-828.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1242-2208.jpg" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2208-1242.jpg" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-750-1334.jpg" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1334-750.jpg" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-640-1136.jpg" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1136-640.jpg" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<title>TechSnack | Simple PWA</title>
<link id="favicon" rel="sortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="manifest" href="manifest.json">
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
최종 manifest.json
이제 manifest.json 파일이 다음과 같아야 합니다.
{
"name": "TechSnack Simple PWA",
"short_name": "TechSnack",
"start_url": "/?home=true",
"icons": [
{
"src": "icons/manifest-icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "icons/manifest-icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
],
"theme_color": "#000000",
"background_color": "#FFFFFF",
"display": "fullscreen",
"orientation": "portrait"
}
Service Worker
우리의 PWA가 앞에서 언급한 모든 멋진 일을 하기 전에 먼저 서비스 워커를 만들어야 합니다.
서비스 워커는 브라우저가 백그라운드에서 실행되는 리스너 스크립트입니다. 서비스 워커는 웹 페이지와 별도로 실행되어 웹 페이지나 사용자의 상호 작용이나 호출이 필요하지 않은 기능을 구현할 수 있습니다.
서비스 워커는 향후 주기적 동기화 또는 지오펜싱과 같은 기능을 지원할 수 있습니다.
참고: 서비스 워커는 JavaScript 파일이지만 코딩 시 추가 제한 사항이 있습니다. 서비스 워커를 통해 DOM에 액세스하지 못할 수 있습니다.
서비스 워커의 수명 주기
일반적으로 서비스 작업자 수명 주기를 중심으로 구축해야 하는 여러 기능/특징이 있습니다. 내부 구성에 대한 걱정을 피하기 위해 workbox라는 API를 사용할 것입니다.
workbox API
서비스 워커의 경우 workbox라는 API를 사용할 것입니다. 이 API는 자체적으로 설명하기 위해 여러 문서가 필요한 기본 기능을 제공합니다.
핵심적인 내용을 파헤치는 데 관심이 있다면 A Service Workers Lifecycle에 대해 읽을 수 있습니다.
ImportScript
service-worker.js 내부에서 workbox API를 가져올 것입니다.
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.0.2/workbox-sw.js');
Registering Routes
서비스 작업자는 페이지에서 네트워크 요청을 가로챌 수 있습니다. 캐시된 콘텐츠 또는 생성된 콘텐츠가 있는 페이지에 응답할 수 있습니다.
참고: (위에서)
메서드는 기본적으로 GET입니다.
이를 변경하려면 지정해야 합니다.
여러 작업자가 요청을 처리할 수 있는 경우 경로 등록 순서가 중요합니다.
먼저 생성된 작업자가 지정된 요청을 처리하는 데 우선 순위를 가집니다.
service-worker.js
이제 service-worker.js 내에 다음 코드를 추가할 수 있습니다.
...
workbox.routing.registerRoute(
({request}) => request.destination === 'image',
new workbox.strategies.CacheFirst() //to search cache first
//new workbox.strategies.NetworkFirst() //to search server first
);
그게 다야! 위의 코드는 다음을 수행합니다.
- RegisterRoute with workbox
- Intercept all 'image' files at the page's request
여기에서 우리는 전략을 선택합니다. CacheFirst 또는 NetworkFirst에서 페이지를 제공하시겠습니까? 이것은 우리가 관심 있는 특정 리소스가 정적인지 동적인지에 따라 결정됩니다.
- 일반적으로 사용자가 방문하는 페이지에 대해 정적이면 캐시에서 제공하려고 합니다.
- 그러나 어떤 종류의 백엔드에 의해 동적으로 생성되는 경우 네트워크에서 해당 파일을 대부분 가져와야 합니다.
Final service-worker.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.0.2/workbox-sw.js');
workbox.routing.registerRoute(
({request}) => request.destination === 'image',
new workbox.strategies.CacheFirst() //to search cache first
//new workbox.strategies.NetworkFirst() //to search server first
);
Linking service-worker.js
이제 서비스 워커가 있고 이미지 파일에 대한 요청을 가로채므로 index.html 내에서 스크립트를 연결할 수 있습니다.
<body>
...
<script>
if('serviceWorker' in navigator){
navigator.serviceWorker.register('/service-worker.js');
}
</script>
</body>
서비스 워커를 등록하는 것은 간단합니다!
함께 모아서
마침내 PWA 프로젝트의 최종 코드 기반을 살펴볼 수 있습니다.
우리의 파일 구조:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#000000">
<link rel="apple-touch-icon" href="icons/apple-icon-180.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2048-2732.jpg" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2732-2048.jpg" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1668-2388.jpg" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2388-1668.jpg" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1536-2048.jpg" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2048-1536.jpg" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1668-2224.jpg" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2224-1668.jpg" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1620-2160.jpg" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2160-1620.jpg" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1284-2778.jpg" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2778-1284.jpg" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1170-2532.jpg" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2532-1170.jpg" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1125-2436.jpg" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2436-1125.jpg" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1242-2688.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2688-1242.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-828-1792.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1792-828.jpg" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1242-2208.jpg" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-2208-1242.jpg" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-750-1334.jpg" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1334-750.jpg" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-640-1136.jpg" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/apple-splash-1136-640.jpg" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
<title>TechSnack | Simple PWA</title>
<link id="favicon" rel="sortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="manifest" href="manifest.json">
</head>
<body>
<h1>Hello World!</h1>
<script>
if('serviceWorker' in navigator){
navigator.serviceWorker.register('/service-worker.js');
}
</script>
</body>
</html>
manifest.json
{
"name": "TechSnack Simple PWA",
"short_name": "TechSnack",
"start_url": "/?home=true",
"icons": [
{
"src": "icons/manifest-icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "icons/manifest-icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
],
"theme_color": "#000000",
"background_color": "#FFFFFF",
"display": "fullscreen",
"orientation": "portrait"
}
service-worker.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.0.2/workbox-sw.js');
workbox.routing.registerRoute(
({request}) => request.destination === 'image',
new workbox.strategies.CacheFirst() //to search cache first
);
Serving Our Page
이제 브라우저에서 페이지를 볼 수 있습니다.
localhost의 경우 webroot 디렉토리에서 npx serve를 실행합니다. 그런 다음 안전하게 제공된 원격 IP 또는 도메인을 방문할 수 있습니다. 장치에 대한 브라우저 지원을 염두에 두십시오.
주소 표시줄에 새로운 아이콘이 생겼습니다!
브라우저에 PWA를 설치하려면 사용자 작업이 필요합니다. 사용자는 이 아이콘을 클릭하여 프롬프트를 볼 수 있습니다.
이제 장치의 홈페이지에 설치된 아이콘을 클릭할 수 있습니다.
PWA 개발 요약
보시다시피 기본 앱과 동일한 기능을 모방할 수 있는 웹 응용 프로그램을 만드는 것은 매우 쉽습니다. 향후 기사에서는 이 강력한 기술을 통해 현재 액세스할 수 있는 각 기능에 대해 자세히 알아볼 것입니다.
출처 : https://dev.to/techsnack/simple-progressive-web-apps-websites-you-can-install-3k0e
- 이전글초심자를 위한 HTML 21.08.24
- 다음글Ethereum 커뮤니티의 주요 온라인 리소스 21.08.24