
최종 업데이트: 2019년 4월 30일
웹 앱, 프로그레시브 웹 앱의 특징은 무엇인가요?
프로그레시브 웹 앱은 데스크톱 및 모바일에서 설치 가능하고 웹을 통해 직접 제공되는 앱 같은 환경을 제공합니다. 빠르고 안정적인 웹 앱입니다. 그리고 가장 중요한 것은 모든 브라우저에서 작동하는 웹 앱이라는 것입니다. 현재 웹 앱을 빌드 중이라면 프로그레시브 웹 앱을 빌드하기 위한 과정이 이미 진행 중입니다.
빠르고 안정적인
모든 웹 환경은 빠른 속도여야 하며, 프로그레시브 웹 앱의 경우 특히 그렇습니다. '신속'이란 의미 있는 콘텐츠를 화면에 표시하고 대화형 환경을 제공하는 데 걸리는 시간을 의미합니다.
안정적인 속도여야 합니다. 스트레스를 완화하는 것은 얼마나 믿을 수 있는 성능인가? 앱 스토어와 대용량 다운로드는 통제되지만, 앱이 설치된 지점에 도달하면 모든 앱 시작 선불 요금이 비례 배분되며 시작 시 변동이 발생하지 않습니다. 각 애플리케이션 시작은 변이 없이 마지막만큼 빨라집니다. 프로그레시브 웹 앱은 이 안정적인 성능을 제공하여 사용자가 설치된 환경에서 기대하는 성능을 제공해야 합니다.
설치 가능
프로그레시브 웹 앱은 브라우저 탭에서 실행할 수 있지만 설치할 수도 있습니다. 사이트를 북마크하면 바로가기를 추가할 수 있지만, 설치된 프로그레시브 웹 앱 (PWA)은 다른 모든 앱과 마찬가지로 표시되고 작동합니다. 다른 앱과 동일한 위치에서 실행됩니다. 맞춤설정된 스플래시 화면, 아이콘 등을 포함해 시작 환경을 제어할 수 있습니다. 주소 표시줄이나 다른 브라우저 UI가 없는 앱 창에서 앱으로 실행됩니다. 설치된 다른 앱과 마찬가지로 작업 전환기의 최상위 앱입니다.
설치 가능한 PWA가 빠르고 안정적입니다. PWA를 설치하는 사용자는 앱이 어떤 종류의 네트워크 연결이든 상관없이 작동할 것이라고 기대합니다. 설치된 모든 앱에서 충족해야 하는 기준 기대치입니다.
모바일 및 데스크톱
PWA는 반응형 디자인 기법을 사용하여 모바일 및 데스크톱 모두에서 작동하며 플랫폼 간에 단일 코드베이스를 사용합니다. 네이티브 앱 작성을 고려하고 있다면 PWA가 제공하는 이점을 살펴보세요.
빌드할 항목
이 Codelab에서는 PWA 기법을 사용하여 날씨 웹 앱을 빌드합니다. 이 앱에는 아래의 기능이 있습니다.
- 데스크톱 또는 모바일에서 작동하도록 반응형 디자인을 사용하세요.
- 서비스 워커를 사용하여 실행하는 데 필요한 앱 리소스 (HTML, CSS, 자바스크립트, 이미지)를 사전 캐시하고, 런타임에 날씨 데이터를 캐시하여 성능을 향상하는 속도.
- 웹 앱 매니페스트와
beforeinstallprompt이벤트를 사용하여 설치 가능임을 사용자에게 알리고 설치 가능합니다.

학습할 내용
- 웹 앱 매니페스트를 만들고 추가하는 방법
- 간단한 오프라인 환경을 제공하는 방법
- 완전한 오프라인 환경을 제공하는 방법
- 앱을 설치할 수 있도록 하는 방법
이 Codelab에서는 프로그레시브 웹 앱에 중점을 둡니다. 따라서 이와 관련 없는 개념과 코드 블록은 그냥 넘어가겠습니다. 단, 필요할 때 복사해서 붙여넣을 수 있도록 다른 설명 없이 제공만 해드리겠습니다.
필요한 항목
- 최신 버전의 Chrome (74 이상) PWA는 모든 브라우저에서 작동하지만, Chrome DevTools의 몇 가지 기능을 사용하여 브라우저 수준에서 어떤 일이 발생하는지 더 잘 파악하고 설치 환경을 테스트하는 데 사용할 수 있습니다.
- HTML, CSS, 자바스크립트, Chrome DevTools 관련 지식
Dark Sky API 키 가져오기
Google의 날씨 데이터는 Dark Sky API에서 가져옵니다. 이 API를 사용하려면 API 키를 요청해야 합니다. 사용하기 쉽고 비상업용 프로젝트에서 무료로 사용할 수 있습니다.
API 키가 제대로 작동하는지 확인하기
API 키가 제대로 작동하는지 테스트하려면 DarkSky API에 HTTP를 요청합니다. 아래 URL을 업데이트하여 DARKSKY_API_KEY를 API 키로 변경합니다. 모든 것이 제대로 작동한다면 뉴욕의 최신 일기예보가 표시됩니다.
https://api.darksky.net/forecast/DARKSKY_API_KEY/40.7720232,-73.9732319
코드 가져오기
이 프로젝트에 필요한 모든 것을 Git 저장소에 넣었습니다. 시작하려면 코드를 가져온 후 원하는 개발 환경에서 열어야 합니다. 이 Codelab에서는 Glitch를 사용하는 것이 좋습니다.
적극 권장: Glitch를 사용하여 저장소 가져오기
이 Codelab에서는 Glitch를 사용하는 것이 좋습니다.
- 새 브라우저 탭을 열고 https://glitch.com으로 이동합니다.
- 계정이 없다면 가입해야 합니다.
- 새 프로젝트를 클릭한 다음 Git 저장소에서 클론을 클릭합니다.
- https://github.com/googlecodelabs/your-first-pwapp.git을 클론하고 OK를 클릭합니다.
- 저장소가 로드되면
.env파일을 수정하고 DarkSky API 키로 업데이트합니다. - 표시 버튼을 클릭한 다음 새 창에서를 선택하여 PWA가 작동하는 모습을 확인합니다.
대안: 코드 다운로드 및 로컬에서 작업
코드를 다운로드하여 로컬에서 실행하려면 최신 버전의 Node.js와 코드 편집기 설정이 있어야 하며 사용할 준비가 되어 있어야 합니다.
- 다운로드한 ZIP 파일의 압축을 해제합니다.
npm install을 실행하여 서버를 실행하는 데 필요한 종속 항목을 설치합니다.server.js를 수정하고 DarkSky API 키를 설정합니다.node server.js를 실행하여 포트 8000에서 서버를 시작합니다.- 브라우저 탭을 열고 http://localhost:8000으로 이동합니다.
시작 지점
시작점은 이 Codelab을 위해 설계된 기본 날씨 앱입니다. 이 Codelab의 개념을 보여주기 위해 코드를 단순화했으며 오류 처리는 거의 없습니다. 프로덕션 앱에서 이러한 코드를 재사용하도록 하려면 오류를 처리하고 모든 코드를 완전히 테스트해야 합니다.
시도해 볼 만한 작업
- 오른쪽 하단의 파란색 + 버튼이 있는 새 도시를 추가합니다.
- 오른쪽 상단의 새로고침 버튼을 사용하여 데이터를 새로고침합니다.
- 각 도시 카드의 오른쪽 상단에 있는 x를 사용하여 도시를 삭제합니다.
- Chrome DevTools의 기기 전환 툴바를 사용하여 데스크톱과 휴대기기에서 어떻게 작동하는지 확인하세요.
- Chrome DevTools의 네트워크 패널을 사용하여 오프라인으로 전환 시 발생하는 결과를 확인하세요.
- Chrome DevTools의 네트워크 패널을 사용하여 네트워크가 느린 3G로 제한되면 어떻게 되는지 확인합니다.
server.js에서FORECAST_DELAY값을 변경하여 예측 서버에 지연을 추가합니다.
Lighthouse 감사
Lighthouse는 사이트 및 페이지의 품질을 개선하는 데 도움이 되는 사용하기 쉬운 도구입니다. Lighthouse는 성능, 접근성, 프로그레시브 웹 앱 등에 대한 감사를 실행합니다. 각 감사에는 감사가 중요한 이유와 문제 해결 방법을 설명하는 참조 문서가 있습니다.

Lighthouse를 사용해 날씨 앱을 감사하고 사용자가 변경한 사항을 확인합니다.
Lighthouse 실행
- 새 탭에서 프로젝트를 엽니다.
- Chrome DevTools를 열고 감사 패널로 전환합니다. 모든 감사 유형을 사용 설정된 상태로 둡니다.
- 감사 실행을 클릭합니다. 시간이 지나면 Lighthouse에서 페이지에 관한 보고서를 제공합니다.
프로그레시브 웹 앱 감사
프로그레시브 웹 앱 감사 결과에 초점을 맞출 예정입니다.

여기서 집중해야 할 빨간색도 많습니다.
- ❗FAILED: 오프라인 시 현재 페이지가 200으로 응답하지 않습니다.
- ❗FAILED: 오프라인 시
start_url가 200으로 응답하지 않습니다. - ❗FAILED: 페이지 및
start_url.를 제어하는 서비스 워커를 등록하지 않습니다. - ❗FAILED: 웹 앱 매니페스트가 설치 가능 요구사항을 충족하지 않습니다.
- ❗FAILED: 맞춤 스플래시 화면에 구성되지 않았습니다.
- ❗FAILED: 주소 표시줄 테마 색상을 설정하지 않습니다.
지금 바로 문제 해결을 시작해 보세요.
이 섹션이 끝나면 날씨 앱이 다음 감사를 통과하게 됩니다.
- 웹 앱 매니페스트가 설치 가능 요건을 충족하지 않습니다.
- 맞춤 스플래시 화면에 구성되지 않았습니다.
- 주소 표시줄 테마 색상을 설정하지 않습니다.
웹 앱 매니페스트 만들기
웹 앱 매니페스트는 개발자가 앱을 사용자에게 표시하는 방식을 제어할 수 있는 간단한 JSON 파일입니다.
웹 앱 매니페스트를 사용하면 웹 앱에서 다음 작업을 할 수 있습니다.
- 독립형 창 (
display)에서 앱을 열 브라우저를 지정합니다. - 앱이 처음 실행될 때 열리는 페이지(
start_url)를 정의합니다. - 앱이 도크와 앱 런처 (
short_name,icons)에서 어떻게 표시될지 정의합니다. - 스플래시 화면(
name,icons,colors)을 만듭니다. - 가로 모드 또는 세로 모드 (
orientation)에서 창을 열도록 브라우저에 지시합니다. - 그 외 다수
프로젝트에 public/manifest.json라는 파일을 만듭니다. 다음 콘텐츠를 복사하여 붙여넣습니다.
public/manifest.json
{
"name": "Weather",
"short_name": "Weather",
"icons": [{
"src": "/images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "/images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "/images/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "/images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}, {
"src": "/images/icons/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
}, {
"src": "/images/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#3E4EB8",
"theme_color": "#2F3BA2"
}
매니페스트는 다양한 화면 크기에 맞게 설계된 아이콘 배열을 지원합니다. 이 Codelab에서는 iOS 통합을 위한 다른 요소를 몇 가지 포함했습니다.
웹 앱 매니페스트에 링크 추가하기
다음으로, 앱의 각 페이지에 <link rel="manifest"...를 추가하여 브라우저에 매니페스트에 알립니다. 다음 줄을 index.html 파일의 <head> 요소에 추가합니다.
public/index.html
<!-- CODELAB: Add link rel manifest -->
<link rel="manifest" href="/manifest.json">
DevTools Detour
DevTools를 사용하면 manifest.json 파일을 빠르고 쉽게 확인할 수 있습니다. Application 패널에서 Manifest 창을 엽니다. 매니페스트 정보를 올바르게 추가했다면 이 창에서 사람이 읽을 수 있는 형식으로 파싱되고 표시되는 것을 볼 수 있습니다.

iOS 메타 태그 및 아이콘 추가
iOS의 Safari는 웹 앱 매니페스트 (아직)를 지원하지 않으므로 기존 meta 태그를 index.html 파일의 <head>에 추가해야 합니다.
public/index.html
<!-- CODELAB: Add iOS meta tags and icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Weather PWA">
<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png">
보너스: 간단한 Lighthouse 수정
Lighthouse 감사에서는 해결하기 쉬운 몇 가지 다른 문제가 있었으므로 여기에 있는 동안 이 문제에 대해 살펴보겠습니다.
메타 설명 설정
검색엔진 최적화 감사에 따라 Lighthouse는 '문서에 메타 설명이 없습니다.'라고 언급했습니다. 설명은 Google 검색결과에 표시될 수 있습니다. 고품질 고유 설명은 검색 사용자와의 관련성을 높이고 검색 트래픽을 늘릴 수 있습니다.
설명을 추가하려면 문서의 meta 태그를 <head>에 추가하세요.
public/index.html
<!-- CODELAB: Add description here -->
<meta name="description" content="A sample weather app">
주소 표시줄 테마 색상 설정
LightPWA 감사에서 Lighthouse는 앱에 '주소 표시줄 테마 색상을 설정하지 않습니다'라고 언급했습니다. 브라우저의 색상에 맞춰 브라우저의 주소 표시줄에 테마를 지정하여 사용자 경험을 더욱 몰입시킬 수 있습니다.
모바일에서 테마 색상을 설정하려면 문서의 meta 태그를 <head>에 추가하세요.
public/index.html
<!-- CODELAB: Add meta theme-color -->
<meta name="theme-color" content="#2F3BA2" />
Lighthouse로 변경사항 확인
Audits 창의 왼쪽 상단에서 + 기호를 클릭하여 Lighthouse를 다시 실행하고 변경사항을 확인합니다.
검색엔진 최적화 감사
- ✅ 통과: 문서에 메타 설명이 있습니다.
프로그레시브 웹 앱 감사
- ❗FAILED: 오프라인 시 현재 페이지가 200으로 응답하지 않습니다.
- ❗FAILED: 오프라인 시
start_url가 200으로 응답하지 않습니다. - ❗FAILED: 페이지 및
start_url.를 제어하는 서비스 워커를 등록하지 않습니다. - ✅ 통과: 웹 앱 매니페스트가 설치 요구사항을 충족합니다.
- ✅ 통과: 맞춤 스플래시 화면에 맞게 구성됨
- ✅ 통과: 주소 표시줄 테마 색상을 설정합니다.
앱을 설치한 사용자는 오프라인 상태라면 항상 기준 환경을 경험하게 됩니다. 따라서 설치 가능한 웹 앱이 Chrome의 오프라인 공룡 게임을 표시하지 않는 것이 중요합니다. 오프라인 환경은 간단한 오프라인 페이지부터 이전에 캐시된 데이터가 포함된 읽기 전용 환경부터 네트워크 연결이 복원될 때 자동으로 동기화되는 완벽하게 작동하는 오프라인 환경까지 다양합니다.
이 섹션에서는 날씨 앱에 간단한 오프라인 페이지를 추가합니다. 사용자가 오프라인 상태에서 앱을 로드하려고 하면 브라우저에 표시되는 일반적인 오프라인 페이지 대신 맞춤 페이지가 표시됩니다. 이 섹션이 끝나면 날씨 앱이 다음 감사를 통과하게 됩니다.
- 오프라인 시 현재 페이지가 200으로 응답하지 않습니다.
start_url는 오프라인 시 200으로 응답하지 않습니다.- 페이지 및
start_url.을(를) 제어하는 서비스 워커를 등록하지 않습니다.
다음 섹션에서는 전체 오프라인 환경을 맞춤 오프라인 페이지로 교체하겠습니다. 이렇게 하면 오프라인 환경이 개선되지만, 더 중요한 점은 대부분의 애셋 (HTML, CSS, 자바스크립트)이 로컬에 저장 및 게재되어 네트워크를 잠재적인 병목 현상으로 제거하기 때문에 성능이 크게 향상됩니다.
서비스 워커의 도움
서비스 워커에 익숙하지 않은 경우 서비스 워커 소개를 읽어보고 작업 워커의 역할에 대한 기본적인 이해와 수명 주기 등의 원리를 이해할 수 있습니다.
서비스 워커를 통해 제공되는 기능은 점진적 개선으로 간주되어야 하며, 브라우저에서 지원하는 경우에만 추가해야 합니다. 예를 들어, 서비스 워커를 사용하면 네트워크가 사용 가능하더라도 앱에서 사용할 수 있도록 앱 셸 및 앱의 데이터를 캐시할 수 있습니다. 서비스 워커가 지원되지 않는 경우 오프라인 코드가 호출되지 않으며 사용자에게 기본 환경이 제공됩니다. 기능 감지를 사용하여 점진적 개선을 제공하면 오버헤드가 거의 발생하지 않으며 기능을 지원하지 않는 이전 브라우저에서는 손상되지 않습니다.
서비스 워커 등록
첫 번째 단계는 서비스 워커를 등록하는 것입니다. 다음 코드를 index.html 파일에 추가하세요.
public/index.html
// CODELAB: Register service worker.
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then((reg) => {
console.log('Service worker registered.', reg);
});
});
}
이 코드는 서비스 워커 API를 사용할 수 있는지 확인하고 사용할 수 있다면 페이지가 로드되면 /service-worker.js의 서비스 워커가 등록됩니다.
서비스 워커는 /scripts/ 디렉터리가 아닌 루트 디렉터리에서 제공됩니다. 이는 서비스 워커의 scope를 설정하는 가장 쉬운 방법입니다. 서비스 워커의 scope는 서비스 워커가 제어하는 파일, 즉 서비스 워커가 요청을 가로채는 경로에서 결정됩니다. 기본 scope는 서비스 워커 파일의 위치이며 아래의 모든 디렉터리로 확장됩니다. 따라서, service-worker.js가 루트 디렉터리에 있는 경우 서비스 워커는 이 도메인의 모든 웹페이지의 요청을 제어합니다.
오프라인 페이지 사전 캐시
먼저 서비스 워커에 캐시할 항목을 알려야 합니다. 네트워크 연결 없이 항상 표시될 수 있는 간단한 오프라인 페이지(public/offline.html)를 이미 만들었습니다.
service-worker.js에서 '/offline.html',를 FILES_TO_CACHE 배열에 추가하면 최종 결과는 다음과 같습니다.
public/service-worker.js
// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
'/offline.html',
];
다음으로 install 이벤트에 다음 코드를 추가하여 서비스 워커에 오프라인 페이지를 미리 캐시하도록 합니다.
public/service-worker.js
// CODELAB: Precache static resources here.
evt.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('[ServiceWorker] Pre-caching offline page');
return cache.addAll(FILES_TO_CACHE);
})
);
이제 install 이벤트가 caches.open()로 캐시를 열고 캐시 이름을 제공합니다. 캐시 이름을 제공하면 파일의 버전을 관리하거나 캐시된 리소스와 데이터를 분리할 수 있기 때문에 쉽게 업데이트할 수 있지만 다른 하나는 영향을 받지 않습니다.
캐시가 열리면 cache.addAll()을 호출할 수 있습니다. 이 메서드는 URL 목록을 가져와 서버에서 가져와 응답을 캐시에 추가합니다. 개별 요청 중 하나라도 실패하면 cache.addAll()이 실패합니다. 즉, 설치 단계가 성공하면 캐시가 일관된 상태가 됩니다. 그러나 어떤 이유로든 실패할 경우 다음에 서비스 워커를 시작할 때 자동으로 다시 시도합니다.
DevTools Detour
DevTools를 사용하여 서비스 워커를 이해하고 디버깅하는 방법을 살펴보겠습니다. 페이지를 새로고침하기 전에 DevTools를 열고 Application 패널의 Service Workers 창으로 이동합니다. 다음과 같이 나타납니다.

이처럼 빈 페이지가 표시된다면 현재 열려 있는 페이지에 등록된 서비스 워커가 없다는 의미입니다.
이제 페이지를 새로고침하세요. 이제 서비스 워커 창이 다음과 같이 표시됩니다.

이와 같은 정보가 표시되면 페이지에 서비스 워커가 실행 중인 것입니다.
상태 라벨 옆에 숫자 (이 경우 34251)가 있습니다. 서비스 워커를 사용하면서 이 수치를 주시하세요. 서비스 워커의 업데이트 여부를 쉽게 확인할 수 있는 방법입니다.
이전 오프라인 페이지 정리하기
activate 이벤트를 사용하여 캐시의 이전 데이터를 정리합니다. 이 코드는 앱 셸 파일이 변경될 때마다 서비스 워커가 캐시를 업데이트하도록 합니다. 이 작업을 실행하려면 서비스 워커 파일의 맨 위에서 CACHE_NAME 변수를 증가해야 합니다.
activate 이벤트에 다음 코드를 추가합니다.
public/service-worker.js
// CODELAB: Remove previous cached data from disk.
evt.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(keyList.map((key) => {
if (key !== CACHE_NAME) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
DevTools Detour
서비스 워커 창이 열리면 페이지를 새로고침합니다. 새 서비스 워커가 설치되고 상태 번호가 증가하는 것을 볼 수 있습니다.

install 이벤트는 self.skipWaiting()로 완료되고 activate 이벤트는 self.clients.claim()로 완료되므로 업데이트된 서비스 워커가 즉시 제어됩니다. 그렇게 하지 않으면 이전 서비스 워커는 페이지가 열려 있는 한 계속해서 페이지를 제어합니다.
실패한 네트워크 요청 처리
마지막으로 fetch 이벤트를 처리해야 합니다. 캐시 대체 네트워크 전략을 사용할 것입니다. 서비스 워커가 먼저 네트워크에서 리소스를 가져오려고 시도합니다. 실패하면 서비스 워커가 캐시에서 오프라인 페이지를 반환합니다.

public/service-worker.js
// CODELAB: Add fetch event handler here.
if (evt.request.mode !== 'navigate') {
// Not a page navigation, bail.
return;
}
evt.respondWith(
fetch(evt.request)
.catch(() => {
return caches.open(CACHE_NAME)
.then((cache) => {
return cache.match('offline.html');
});
})
);
fetch 핸들러는 페이지 탐색만 처리하면 되므로 다른 요청을 핸들러에서 덤프하고 브라우저에서 정상적으로 처리할 수 있습니다. 그러나 요청 .mode가 navigate이면 fetch을 사용하여 네트워크에서 항목을 가져오려고 시도합니다. 실패하면 catch 핸들러가 caches.open(CACHE_NAME)로 캐시를 열고 cache.match('offline.html')를 사용하여 미리 캐시된 오프라인 페이지를 가져옵니다. 결과는 evt.respondWith()를 사용하여 브라우저에 다시 전달됩니다.
DevTools Detour
모든 것이 예상대로 작동하는지 확인해 보겠습니다. Service Workers 서비스 창이 열리면 페이지를 새로고침합니다. 새 서비스 워커가 설치되고 상태 번호가 증가하는 것을 볼 수 있습니다.
또한 캐시된 항목을 확인할 수도 있습니다. DevTools의 Application 패널에서 Cache Storage 창으로 이동합니다. 캐시 저장소를 마우스 오른쪽 버튼으로 클릭하고 캐시 새로고침을 선택한 다음 섹션을 펼칩니다. 왼쪽에 정적 캐시의 이름이 표시됩니다. 캐시 이름을 클릭하면 캐시된 모든 파일이 표시됩니다.

이제 오프라인 모드를 테스트해 보세요. DevTools의 애플리케이션 패널에 있는 서비스 워커 창으로 돌아가서 오프라인 체크박스를 선택합니다. 확인하면 Network 패널 탭 옆에 작은 경고 경고 아이콘이 표시됩니다. 이 아이콘은 오프라인 상태를 나타냅니다.

페이지를 새로고침하면 됩니다. Chrome의 오프라인 공룡이 아닌 우리의 오프라인 판다를 만나 보세요!
서비스 워커 테스트를 위한 팁
서비스 워커를 디버깅하는 것은 쉬운 일이 아니며, 캐싱이 요구될 경우 캐시가 예상대로 업데이트되지 않으면 더욱 악몽이 될 수 있습니다. 일반적인 서비스 워커 수명 주기와 코드의 버그 사이에서 좌절할 수 있습니다. 그러나
DevTools 사용
Application 패널의 Service Workers 창에는 여러분의 생활을 훨씬 더 편리하게 만들어 주는 체크박스가 몇 개 있습니다.

- 오프라인 - 이 옵션을 선택하면 오프라인 경험을 시뮬레이션하고 요청이 네트워크로 전송되는 것을 방지합니다.
- 새로고침 시 - 이 체크박스를 선택하면 최신 서비스 워커를 설치하고 즉시 활성화합니다.
- 네트워크 우회 - 이 옵션을 선택하면 요청이 서비스 워커를 우회하고 네트워크로 직접 전송됩니다.
새로 시작하기
캐시된 데이터를 로드하거나 예상대로 업데이트되지 않을 수도 있습니다. 저장된 모든 데이터 (localStorage, indexDB 데이터, 캐시된 파일)를 지우고 서비스 워커를 삭제하려면 애플리케이션 패널의 스토리지 비우기 창을 사용합니다. 또는 시크릿 창에서 작업할 수도 있습니다.

기타 도움말:
- 서비스 워커가 등록 취소되면 포함된 브라우저 창이 닫힐 때까지 서비스 워커가 목록에 남아 있을 수 있습니다.
- 앱에 여러 개의 창이 열려 있는 경우 모든 창을 새로고침하고 최신 서비스 워커로 업데이트할 때까지 새 서비스 워커가 적용되지 않습니다.
- 서비스 워커를 등록 취소해도 캐시는 삭제되지 않습니다.
- 서비스 워커가 있고 새 서비스 워커가 등록된 경우, 즉시 관리를 받기 전에는 페이지를 새로고침할 때까지 새 서비스 워커가 제어하지 못합니다.
Lighthouse로 변경사항 확인
Lighthouse를 다시 실행하고 변경사항을 확인합니다. 변경사항을 확인하기 전에 오프라인 체크박스를 선택 해제해야 합니다.
검색엔진 최적화 감사
- ✅ 통과: 문서에 메타 설명이 있습니다.
프로그레시브 웹 앱 감사
- ✅ 통과: 오프라인 시 현재 페이지가 200으로 응답합니다.
- ✅ 통과: 오프라인 시
start_url가 200으로 응답함 - ✅ 통과: 페이지 및
start_url.를 제어하는 서비스 워커를 등록합니다. - ✅ 통과: 웹 앱 매니페스트가 설치 요구사항을 충족합니다.
- ✅ 통과: 맞춤 스플래시 화면에 맞게 구성됨
- ✅ 통과: 주소 표시줄 테마 색상을 설정합니다.
잠시 시간을 내어 휴대전화를 비행기 모드로 설정하고 자주 사용하는 앱을 실행해 보세요. 거의 모든 경우 상당히 강력한 오프라인 환경을 제공합니다. 사용자는 앱의 강력한 환경을 기대합니다. 웹도 다르지 않아야 합니다. 프로그레시브 웹 앱은 오프라인에서 핵심 시나리오로 설계되어야 합니다.
서비스 워커 수명 주기
서비스 워커의 수명 주기는 가장 복잡한 부분입니다. 무엇을 하려고 하는지, 얻게 된 이점은 무엇인지 모른다면, 여러분을 싸우는 듯한 느낌이 들 수 있습니다. 그러나 작동 원리를 알고 나면 사용자에게 원활하고 방해가 되지 않는 업데이트를 제공할 수 있습니다. 즉, 웹과 네이티브 패턴의 장점을 결합하여
install 이벤트
서비스 워커가 가져오는 첫 번째 이벤트는 install입니다. 작업자가 실행되는 즉시 트리거되며 서비스 워커당 한 번만 호출됩니다. 서비스 워커 스크립트를 변경하면 브라우저에서 다른 서비스 워커로 간주하여 자체 install 이벤트를 가져옵니다.

일반적으로 install 이벤트는 앱을 실행하는 데 필요한 모든 것을 캐시하는 데 사용됩니다.
activate 이벤트
서비스 워커는 시작될 때마다 activate 이벤트를 수신합니다. activate 이벤트의 주요 목적은 서비스 워커의 동작을 구성하고, 이전 실행에서 남은 리소스 (예: 이전 캐시)를 정리하고, 서비스 워커가 네트워크 요청을 처리할 준비를 하도록 하는 것입니다 (예: 아래 설명된 fetch 이벤트).
fetch 이벤트
가져오기 이벤트를 사용하면 서비스 워커가 네트워크 요청을 가로채고 요청을 처리할 수 있습니다. 네트워크로 이동하여 리소스를 가져오거나, 자체 캐시에서 리소스를 가져오거나, 커스텀 응답을 생성하거나, 여러 옵션을 실행할 수 있습니다. 사용할 수 있는 다양한 전략을 보려면 오프라인 설명서를 확인하세요.
서비스 워커 업데이트
브라우저가 페이지를 로드할 때마다 새로운 버전의 서비스 워커가 있는지 확인합니다. 새 버전이 발견되면 새 버전이 다운로드되어 백그라운드에서 설치되지만 활성화되지는 않습니다. 새 버전의 서비스 워커는 이전 서비스 워커를 사용하는 페이지가 더 이상 열리지 않을 때까지 대기 상태가 됩니다. 이전 서비스 워커를 사용하는 모든 창이 닫히면 새 서비스 워커가 활성화되며 제어할 수 있습니다. 자세한 내용은 서비스 워커 수명 주기 문서의 서비스 워커 업데이트 섹션을 참조하세요.
올바른 캐싱 전략 선택
올바른 캐싱 전략 선택은 캐시하려는 리소스 유형과 나중에 액세스해야 하는 방법에 따라 다릅니다. 날씨 앱의 경우 캐시해야 하는 리소스를 사전 캐시하려는 리소스와 런타임 시 캐시하는 데이터라는 두 가지 카테고리로 나누어 보겠습니다.
정적 리소스 캐싱
리소스 사전 준비는 사용자가 데스크톱 또는 모바일 앱을 설치할 때 발생하는 것과 유사한 개념입니다. 앱이 실행되는 데 필요한 핵심 리소스는 기기에 설치되거나 캐시되어 나중에 네트워크 연결 여부와 관계없이 로드될 수 있습니다.
앱의 경우, 서비스 워커가 설치되면 모든 정적 리소스를 사전 캐시하므로 앱을 실행하는 데 필요한 모든 것이 사용자의 기기에 저장됩니다. 앱이 매우 빠르게 로드되도록 하기 위해 네트워크로 이동하는 대신 리소스를 캐시에서 가져오는 대신 캐시 우선 전략을 사용합니다. 사용할 수 없는 경우에만 네트워크에서 캐시에서 가져옵니다.

로컬 캐시에서 가져오면 모든 네트워크 가변성이 사라집니다. 사용자가 어떤 종류의 네트워크를 사용하든 (Wi-Fi, 5G, 3G 또는 2G) 거의 실시간으로 실행할 수 있는 주요 리소스입니다.
앱 데이터 캐싱
비활성 기간 재검증 전략은 특정 유형의 데이터에 이상적이며 Google 앱에서 효과적입니다. 최대한 빨리 데이터를 화면에 표시한 다음 네트워크에서 최신 데이터를 반환하면 업데이트됩니다. 비활성 상태 재검증은 두 개의 비동기 요청, 즉 캐시와 네트워크 요청을 각각 시작해야 한다는 것을 의미합니다.

일반적인 상황에서는 캐시된 데이터가 거의 즉시 반환되어 사용할 수 있는 최신 데이터가 앱에 제공됩니다. 그런 다음 네트워크 요청이 반환되면 앱이 네트워크의 최신 데이터를 사용하여 업데이트됩니다.
이 앱의 경우 네트워크보다 나은 환경을 제공하여 사용자가 네트워크 요청이 타임아웃되어 화면에 표시되는 것을 기다리지 않아도 되므로 캐시 전략으로 대체합니다. 처음에는 오래된 데이터가 표시될 수 있지만 네트워크 요청이 반환되면 앱이 최신 데이터로 업데이트됩니다.
앱 로직 업데이트
앞서 언급했듯이 앱은 두 개의 비동기 요청(캐시에 대한 요청, 네트워크에 대한 요청)을 시작해야 합니다. 앱은 window에서 사용 가능한 caches 객체를 사용하여 캐시에 액세스하고 최신 데이터를 검색합니다. 이는 caches 객체가 일부 브라우저에서 지원되지 않을 수 있으며 점진적 개선을 보여주는 좋은 예입니다. 그렇지 않으면 네트워크 요청이 계속 작동해야 합니다.
getForecastFromCache() 함수를 업데이트하여 caches 객체를 전역 window 객체에서 사용할 수 있는지 확인하고, 사용할 수 있는 경우 캐시에서 데이터를 요청합니다.
public/scripts/app.js
// CODELAB: Add code to get weather forecast from the caches object.
if (!('caches' in window)) {
return null;
}
const url = `${window.location.origin}/forecast/${coords}`;
return caches.match(url)
.then((response) => {
if (response) {
return response.json();
}
return null;
})
.catch((err) => {
console.error('Error getting data from cache', err);
return null;
});
그런 다음 네트워크 updateData()를 수정하여 두 번 호출(네트워크에서 예측을 얻으려면 getForecastFromNetwork(), 다른 하나는 캐시된 최신 예측을 가져오기 위한 getForecastFromCache())을 호출해야 합니다.
public/scripts/app.js
// CODELAB: Add code to call getForecastFromCache.
getForecastFromCache(location.geo)
.then((forecast) => {
renderForecast(card, forecast);
});
이제 날씨 앱이 캐시와 fetch를 통해 각각 하나씩 총 2개의 비동기 데이터 요청을 합니다. 캐시에 있는 데이터가 있는 경우 아주 빠르게 반환되고 렌더링됩니다 (수십 밀리초). 그런 다음 fetch가 응답하면 날씨 API에서 직접 최신 데이터로 카드가 업데이트됩니다.
캐시 요청과 fetch 요청이 모두 예측 카드를 업데이트하는 호출로 끝나는 방식을 확인합니다. 앱은 최신 데이터를 표시하는지 어떻게 알 수 있나요? renderForecast()의 다음 코드에서 처리됩니다.
public/scripts/app.js
// If the data on the element is newer, skip the update.
if (lastUpdated >= data.currently.time) {
return;
}
카드가 업데이트될 때마다 앱은 카드의 숨겨진 속성에 데이터의 타임스탬프를 저장합니다. 카드는 카드에 이미 존재하는 타임스탬프가 함수에 전달된 데이터보다 최신인 경우에만 적용됩니다.
앱 리소스 사전 캐시
서비스 워커에서 애플리케이션 데이터를 앱 셸에서 분리할 수 있도록 DATA_CACHE_NAME를 추가해 보겠습니다. 앱 셸이 업데이트되고 이전 캐시가 제거되면 데이터는 그대로 유지되어 초고속 로드를 위한 준비가 됩니다. 향후 데이터 형식이 변경되면 이를 처리하고 앱 셸 및 콘텐츠의 동기화를 유지할 방법이 필요합니다.
public/service-worker.js
// CODELAB: Update cache names any time any of the cached files change.
const CACHE_NAME = 'static-cache-v2';
const DATA_CACHE_NAME = 'data-cache-v1';
CACHE_NAME도 업데이트해야 합니다. 모든 정적 리소스도 변경됩니다.
앱이 오프라인으로 작동하기 위해 필요한 모든 리소스를 사전 캐시해야 합니다. 이는 실적 개선에도 도움이 됩니다. 앱은 네트워크에서 모든 리소스를 가져오는 대신 모든 리소스를 로컬 캐시에서 로드하여 네트워크 불안정성을 제거할 수 있습니다.
파일 목록으로 FILES_TO_CACHE 배열을 업데이트합니다.
public/service-worker.js
// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
'/',
'/index.html',
'/scripts/app.js',
'/scripts/install.js',
'/scripts/luxon-1.11.4.js',
'/styles/inline.css',
'/images/add.svg',
'/images/clear-day.svg',
'/images/clear-night.svg',
'/images/cloudy.svg',
'/images/fog.svg',
'/images/hail.svg',
'/images/install.svg',
'/images/partly-cloudy-day.svg',
'/images/partly-cloudy-night.svg',
'/images/rain.svg',
'/images/refresh.svg',
'/images/sleet.svg',
'/images/snow.svg',
'/images/thunderstorm.svg',
'/images/tornado.svg',
'/images/wind.svg',
];
캐시할 파일 목록을 수동으로 생성하므로 파일을 업데이트할 때마다 CACHE_NAME를 업데이트해야 합니다. 이제 앱이 오프라인에서 작동하는 데 필요한 모든 리소스를 보유하고 있으므로 오프라인 페이지를 다시 표시하지 않을 것이므로 캐시된 파일 목록에서 offline.html을(를) 삭제할 수 있었습니다.
활성화 이벤트 핸들러 업데이트
activate 이벤트가 실수로 데이터를 삭제하지 않도록 하려면 service-worker.js의 activate 이벤트에서 if (key !== CACHE_NAME) {를 다음으로 바꿉니다.
public/service-worker.js
if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {
가져오기 이벤트 핸들러 업데이트
나중에 쉽게 액세스할 수 있도록 날씨 API에 대한 요청을 가로채고 응답을 캐시에 저장하도록 서비스 워커를 수정해야 합니다. 비활성 중 재검증 전략에서는 네트워크 응답이 항상 최신 정보를 제공하기를 기대합니다. 네트워크에서 충족할 수 없는 경우 이미 앱이 최근에 캐시된 데이터를 검색했기 때문에 실패해도 됩니다.
Data API에 대한 요청을 다른 요청과 별도로 처리하도록 fetch 이벤트 핸들러를 업데이트합니다.
public/service-worker.js
// CODELAB: Add fetch event handler here.
if (evt.request.url.includes('/forecast/')) {
console.log('[Service Worker] Fetch (data)', evt.request.url);
evt.respondWith(
caches.open(DATA_CACHE_NAME).then((cache) => {
return fetch(evt.request)
.then((response) => {
// If the response was good, clone it and store it in the cache.
if (response.status === 200) {
cache.put(evt.request.url, response.clone());
}
return response;
}).catch((err) => {
// Network request failed, try to get it from the cache.
return cache.match(evt.request);
});
}));
return;
}
evt.respondWith(
caches.open(CACHE_NAME).then((cache) => {
return cache.match(evt.request)
.then((response) => {
return response || fetch(evt.request);
});
})
);
이 코드는 요청을 가로채고 일기 예보용인지 확인합니다. 지원되는 경우 fetch를 사용하여 요청합니다. 응답이 반환되면 캐시를 열고 응답을 클론하여 캐시에 저장한 다음 응답을 원래 요청자에게 반환합니다.
서비스 워커가 이미지뿐만 아니라 스크립트, CSS 파일 등을 포함한 모든 요청을 처리하도록 해야 하므로 evt.request.mode !== 'navigate' 검사를 삭제해야 합니다. 해당 체크인을 남기면 서비스 워커 캐시에서 HTML만 제공됩니다. 그 외 모든 것은 네트워크에서 요청됩니다.
사용해 보기
이제 앱이 완전히 오프라인에서 작동해야 합니다. 페이지를 새로고침하여 최신 서비스 워커를 설치했는지 확인합니다. 그런 다음 몇 개의 도시를 저장하고 앱에서 새로고침 버튼을 눌러 최신 날씨 데이터를 확인하세요.
다음으로 DevTools의 Application 패널에 있는 Cache Storage 창으로 이동합니다. 섹션을 펼치면 왼쪽에 정적 캐시와 데이터 캐시의 이름이 표시됩니다. 데이터 캐시를 열면 각 도시에 저장된 데이터가 표시됩니다.

서비스 워커 창으로 전환하고 오프라인 체크박스를 선택합니다. 페이지를 새로고침한 후 오프라인으로 전환하여 페이지를 새로고침하세요.
빠른 네트워크를 사용 중인 경우 저속 연결 상태에서 일기 예보 데이터가 어떻게 업데이트되는지 확인하려면 server.js의 FORECAST_DELAY 속성을 5000로 설정합니다. 예측 API에 대한 모든 요청이 5,000밀리초 지연됩니다.
Lighthouse로 변경사항 확인
Lighthouse를 다시 실행하는 것도 좋은 방법입니다.
검색엔진 최적화 감사
- ✅ 통과: 문서에 메타 설명이 있습니다.
프로그레시브 웹 앱 감사
- ✅ 통과: 오프라인 시 현재 페이지가 200으로 응답합니다.
- ✅ 통과: 오프라인 시
start_url가 200으로 응답함 - ✅ 통과: 페이지 및
start_url.를 제어하는 서비스 워커를 등록합니다. - ✅ 통과: 웹 앱 매니페스트가 설치 요구사항을 충족합니다.
- ✅ 통과: 맞춤 스플래시 화면에 맞게 구성됨
- ✅ 통과: 주소 표시줄 테마 색상을 설정합니다.
프로그레시브 웹 앱이 설치되면 설치된 다른 모든 앱처럼 보이고 동작합니다. 다른 앱과 동일한 위치에서 실행됩니다. 주소 표시줄이나 다른 브라우저 UI가 없는 앱에서 실행됩니다. 설치된 다른 앱과 마찬가지로 작업 전환기의 최상위 앱입니다.

Chrome에서 프로그레시브 웹 앱은 점 3개로 된 컨텍스트 메뉴를 통해 설치하거나 사용자에게 앱을 설치하라는 메시지를 표시하는 버튼 또는 기타 UI 구성요소를 제공할 수 있습니다.
Lighthouse로 감사
사용자가 프로그레시브 웹 앱을 설치할 수 있으려면 앱이 특정 기준을 충족해야 합니다. Lighthouse를 확인하는 가장 쉬운 방법은 Lighthouse를 사용하고 설치 가능한 기준을 충족하는지 확인하는 것입니다.

이 Codelab을 완료했다면 PWA가 이미 이 기준을 충족해야 합니다.
index.html에 install.js 추가
먼저 index.html 파일에 install.js를 추가해 보겠습니다.
public/index.html
<!-- CODELAB: Add the install script here -->
<script src="/scripts/install.js"></script>
beforeinstallprompt 이벤트 수신
홈 화면에 추가 기준을 충족하면 Chrome에서 beforeinstallprompt 이벤트를 실행하여 앱을 설치할 수 있음을 나타내고 사용자에게 설치를 요청하는 메시지를 표시합니다. 아래 코드를 추가하여 beforeinstallprompt 이벤트를 수신합니다.
public/scripts/install.js
// CODELAB: Add event listener for beforeinstallprompt event
window.addEventListener('beforeinstallprompt', saveBeforeInstallPromptEvent);
이벤트 저장 및 설치 버튼 표시하기
saveBeforeInstallPromptEvent 함수에서 beforeinstallprompt 이벤트에 대한 참조를 저장하여 나중에 prompt()를 호출하고 설치 버튼을 표시하도록 UI를 업데이트할 수 있습니다.
public/scripts/install.js
// CODELAB: Add code to save event & show the install button.
deferredInstallPrompt = evt;
installButton.removeAttribute('hidden');
메시지 표시 및 버튼 숨기기
사용자가 설치 버튼을 클릭하면 저장된 beforeinstallprompt 이벤트에서 .prompt()를 호출해야 합니다. .prompt()는 저장된 이벤트마다 한 번만 호출할 수 있으므로 설치 버튼도 숨겨야 합니다.
public/scripts/install.js
// CODELAB: Add code show install prompt & hide the install button.
deferredInstallPrompt.prompt();
// Hide the install button, it can't be called twice.
evt.srcElement.setAttribute('hidden', true);
.prompt()을 호출하면 사용자에게 모달 대화상자가 표시되어 홈 화면에 앱을 추가해 달라고 요청합니다.
결과 로깅
저장된 beforeinstallprompt 이벤트의 userChoice 속성에서 반환한 프로미스를 수신 대기하여 사용자가 설치 대화상자에 어떻게 응답했는지 확인할 수 있습니다. 프로미스는 메시지를 표시하고 사용자가 응답한 후 outcome 속성이 있는 객체를 반환합니다.
public/scripts/install.js
// CODELAB: Log user response to prompt.
deferredInstallPrompt.userChoice
.then((choice) => {
if (choice.outcome === 'accepted') {
console.log('User accepted the A2HS prompt', choice);
} else {
console.log('User dismissed the A2HS prompt', choice);
}
deferredInstallPrompt = null;
});
userChoice에 관한 한 가지 주석은 예상한 함수가 아닌 사양으로 속성으로 정의하는 것입니다.
모든 설치 이벤트 로깅
앱을 설치하기 위해 추가하는 UI 외에도 사용자는 다른 방법(예: Chrome의 점 3개 메뉴)을 통해 PWA를 설치할 수 있습니다. 이러한 이벤트를 추적하려면 앱 설치 이벤트를 수신합니다.
public/scripts/install.js
// CODELAB: Add event listener for appinstalled event
window.addEventListener('appinstalled', logAppInstalled);
그런 다음 logAppInstalled 함수를 업데이트해야 합니다. 이 Codelab에서는 console.log만 사용하지만 프로덕션 앱에서는 애널리틱스 소프트웨어를 사용해 이벤트로 로깅하는 것이 좋습니다.
public/scripts/install.js
// CODELAB: Add code to log the event
console.log('Weather App was installed.', evt);
서비스 워커 업데이트
이미 캐시된 파일을 변경했으므로 service-worker.js 파일의 CACHE_NAME을 업데이트해야 합니다. DevTools에서 Application 패널의 Service Workers 창에 있는 Bypass for network 체크박스를 사용 설정하면 개발은 작동하지만 실제로 도움이 되지 않습니다.
사용해 보기
설치 단계가 어떻게 진행되었는지 확인해 보겠습니다. 안전을 위해 DevTools의 애플리케이션 패널에 있는 사이트 데이터 지우기 버튼을 사용하여 모든 정보를 지우고 새로 시작하세요. 이전에 앱을 설치한 경우 제거해야 합니다. 그렇지 않으면 설치 아이콘이 다시 표시되지 않습니다.
설치 버튼이 표시되는지 확인
먼저 설치 아이콘이 제대로 표시되는지 확인해 보겠습니다. 데스크톱과 모바일 모두에서 시도해 보세요.
- 새 Chrome 탭에서 URL을 엽니다.
- Chrome의 점 3개로 된 메뉴 (주소 표시줄 옆)를 엽니다.
▢ 메뉴에서 '날씨 설치..." - 사용자 참여 휴리스틱을 충족하도록 오른쪽 상단의 새로고침 버튼을 사용하여 날씨 데이터를 새로고침합니다.
▢ 앱 헤더에 설치 아이콘이 표시되는지 확인합니다.
설치 버튼 작동 확인하기
이제 모든 항목이 제대로 설치되고 이벤트가 올바르게 실행되는지 확인합니다. 이 작업은 데스크톱 또는 모바일에서 할 수 있습니다. 모바일에서 테스트하고 싶다면 콘솔에 어떤 내용이 로깅되었는지 확인할 수 있도록 원격 디버깅을 사용하고 있는지 확인하세요.
- Chrome을 열고 새 브라우저 탭에서 날씨 PWA로 이동합니다.
- DevTools를 열고 Console 패널로 전환합니다.
- 오른쪽 상단의 설치 버튼을 클릭합니다.
▢ 설치 버튼이 사라졌는지 확인
▢ 설치 모달 대화상자가 표시되는지 확인합니다. - 취소를 클릭합니다.
▢ 확인 확인사용자가 A2HS 메시지를 닫았음"이 콘솔 출력에 표시됩니다.
▢ 설치 버튼이 다시 표시되는지 확인합니다. - 설치 버튼을 다시 클릭한 다음 모달 대화상자에서 설치 버튼을 클릭합니다.
▢ Verify "User accepted a A2HS 프롬프트"가 Console 출력에 표시됩니다.
▢ Verify "날씨 앱이 설치됨"이 콘솔 출력에 표시됩니다.
▢ 날씨 앱이 일반적으로 앱을 찾는 위치에 추가되었는지 확인합니다. - Weather PWA를 실행합니다.
▢ 앱이 독립형 앱(데스크톱의 앱 창 또는 모바일의 전체 화면)에서 실행되는지 확인합니다.
.
iOS 설치가 제대로 작동하는지 확인하기
iOS의 동작도 확인하세요. iOS 기기는 이미 있지만 Mac을 사용하는 경우 Xcode에서 사용할 수 있는 iOS 시뮬레이터를 사용해 보세요.
- Safari를 열고 새 브라우저 탭에서 날씨 PWA로 이동합니다.
- 공유
버튼을 클릭합니다. - 오른쪽으로 스크롤하여 홈 화면에 추가 버튼을 클릭합니다.
▢ 제목, URL, 아이콘이 올바른지 확인 - 추가
▢ 앱 아이콘이 홈 화면에 추가되었는지 확인합니다. - 홈 화면에서 날씨 PWA를 실행합니다.
▢ 앱이 전체 화면을 실행하는지 확인합니다.
보너스: 앱이 홈 화면에서 실행되는지 감지
display-mode 미디어 쿼리를 사용하면 앱 시작 방법에 따라 스타일을 적용하거나 자바스크립트로 실행된 방식을 결정할 수 있습니다.
@media all and (display-mode: standalone) {
body {
background-color: yellow;
}
}
또한 자바스크립트에서 display-mode미디어 쿼리를 확인하여 독립형으로 실행 중인지 확인하세요.
보너스: PWA 제거하기
앱이 이미 설치되어 있으면 beforeinstallevent가 실행되지 않으므로 개발 중에는 앱을 여러 번 설치하고 제거하여 모든 것이 예상대로 작동하는지 확인해야 할 수 있습니다.
Android
Android에서 PWA는 설치된 다른 앱을 제거하는 것과 동일한 방식으로 제거됩니다.
- 앱 검색 창을 엽니다.
- 아래로 스크롤하여 날씨 아이콘을 찾습니다.
- 앱 아이콘을 화면 상단으로 드래그합니다.
- 제거를 선택합니다.
Chrome OS
Chrome OS의 경우 PWA를 런처 검색창에서 쉽게 제거할 수 있습니다.
- 런처를 엽니다.
- 검색창에 "날씨"를 입력하면 날씨 PWA가 검색결과에 표시됩니다.
- 날씨 PWA에서 마우스 오른쪽 버튼으로 클릭 (Alt 클릭)
- Chrome에서 삭제...를 클릭합니다.
macOS 및 Windows
Mac과 Windows에서는 Chrome을 통해 PWA를 제거할 수 있습니다.
- 새 브라우저 탭에서 chrome://apps를 엽니다.
- 날씨 PWA에서 마우스 오른쪽 버튼으로 클릭 (Alt 클릭)
- Chrome에서 삭제...를 클릭합니다.
설치된 PWA를 열고 오른쪽 상단의 점 3개로 된 컨텍스트 메뉴를 클릭한 후 "Uninstall Weather PWA..."
축하합니다. 첫 번째 프로그레시브 웹 앱을 빌드했습니다.
웹 앱 매니페스트를 추가하여 웹 서버를 설치할 수 있도록 추가하고 서비스 워커를 추가하여 PWA가 항상 빠르고 안정되도록 했습니다. DevTools를 사용하여 앱을 감사하는 방법과 이 앱이 사용자 경험을 개선하는 데 어떻게 도움이 되는지 배웠습니다.
웹 앱을 프로그레시브 웹 앱으로 전환하는 데 필요한 주요 단계를 알아봤습니다.
다음 단계
다음 Codelab을 확인하세요.