오프라인 빠른 시작

이 Codelab은 Google Developers 교육팀에서 개발한 프로그레시브 웹 앱 개발 교육 과정의 일부입니다. Codelab을 순서대로 진행하는 경우 이 과정의 가치를 최대한 높일 수 있습니다.

교육 과정에 대한 자세한 내용은 프로그레시브 웹 앱 개발 개요를 참조하세요.

소개

이 실습에서는 Lighthouse를 사용하여 웹사이트에 프로그레시브 웹 앱 (PWA) 표준에 대한 감사를 수행합니다. 서비스 워커 API로 오프라인 기능도 추가할 것입니다.

학습할 내용

  • Lighthouse로 사이트를 감사하는 방법
  • 애플리케이션에 오프라인 기능을 추가하는 방법

유의해야 할 사항

  • 기본 HTML, CSS, 자바스크립트
  • ES2015 약속에 관한 지식

필요한 항목

  • 터미널/셸 액세스 권한이 있는 컴퓨터
  • 인터넷 연결
  • Chrome 브라우저 (Lighthouse 사용)
  • 텍스트 편집기
  • 선택사항: Android 기기의 Chrome

pwa-training-labs 저장소를 GitHub에서 다운로드하거나 클론하고 필요한 경우 LTS 버전의 Node.js를 설치합니다.

offline-quickstart-lab/app/ 디렉터리로 이동하여 로컬 개발 서버를 시작합니다.

cd offline-quickstart-lab/app
npm install
node server.js

언제든지 Ctrl-c을 사용하여 서버를 종료할 수 있습니다.

브라우저를 열고 localhost:8081/로 이동합니다. 사이트가 단순하고 정적인 웹페이지임을 확인할 수 있습니다.

참고: 서비스 워커를 등록 취소하고 localhost에 대한 모든 서비스 워커 캐시를 삭제하여 실습을 방해하지 않도록 하세요. Chrome DevTools에서는 애플리케이션 탭의 저장용량 비우기 섹션에서 사이트 데이터 지우기를 클릭하여 삭제할 수 있습니다.

원하는 텍스트 편집기에서 offline-quickstart-lab/app/ 폴더를 엽니다. app/ 폴더는 실습을 빌드하는 위치입니다.

이 폴더에는 다음이 포함되어 있습니다.

  • images/ 폴더에 샘플 이미지가 있음
  • styles/main.css는 기본 스타일시트입니다.
  • index.html는 샘플 사이트의 기본 HTML 페이지입니다.
  • package-lock.jsonpackage.json는 앱 종속 항목을 추적합니다 (이 경우 종속 항목은 유일하게 로컬 개발 서버에만 사용됨).
  • server.js는 테스트용 로컬 개발 서버입니다.
  • service-worker.js은 서비스 워커 파일 (현재 비어 있음)입니다.

사이트 변경을 시작하기 전에 Lighthouse에서 감사를 통해 무엇을 개선할 수 있는지 알아보겠습니다.

Chrome 앱으로 다시 돌아가 개발자 도구감사 탭을 엽니다. Lighthouse 아이콘과 구성 옵션이 표시됩니다. '모바일'에서 '기기'를 선택하고 감사를 모두 선택한 다음 제한 옵션 중 하나를 선택하고 저장용량 비우기를 선택합니다.

감사 실행을 클릭합니다. 감사를 완료하는 데 몇 분 정도 걸립니다.

설명

감사가 완료되면 개발자 도구에 점수가 포함된 보고서가 표시됩니다. 다음과 같은 점수가 표시됩니다 (점수는 정확히 일치하지 않을 수 있음).

참고: Lighthouse 점수는 근사치이며 환경에 영향을 받을 수 있습니다 (예: 브라우저 창이 많은 경우). 점수가 여기에 표시된 점수와 정확히 일치하지 않을 수 있습니다.

Progressive Web App 섹션은 다음과 유사합니다.

보고서에는 5개의 카테고리로 구성된 점수와 측정항목이 있습니다.

  • 프로그레시브 웹 앱
  • 성과
  • 접근성
  • 권장사항
  • 검색 엔진 최적화(SEO)

프로그레시브 웹 앱 (PWA) 카테고리에서 앱의 점수가 낮습니다. 점수를 높여 보세요!

잠시 시간을 내어 보고서의 PWA 섹션을 살펴보고 누락된 부분이 있는지 확인하세요.

서비스 워커 등록

보고서에 나열된 실패 중 하나는 등록된 서비스 워커가 없다는 것입니다. 현재 app/service-worker.js에 빈 서비스 워커 파일이 있습니다.

index.html 하단의 index.html를 닫는 </body> 태그 바로 앞에 다음 스크립트를 추가합니다.

<script>
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('service-worker.js')
      .then(reg => {
        console.log('Service worker registered! 😎', reg);
      })
      .catch(err => {
        console.log('😥 Service worker registration failed: ', err);
      });
  });
}
</script>

설명

이 코드는 페이지가 로드되면 빈 sw.js 서비스 워커 파일을 등록합니다. 하지만 현재 서비스 워커 파일은 비어 있으며 아무 작업도 하지 않습니다. 다음 단계에서 서비스 코드를 추가합니다.

리소스 사전 캐시

보고서에 나열된 또 다른 실패는 앱이 오프라인일 때 200 상태 코드로 응답하지 않는다는 점입니다. 이 문제를 해결하려면 서비스 워커를 업데이트해야 합니다.

서비스 워커 파일 (sw.js)에 다음 코드를 추가합니다.

const cacheName = 'cache-v1';
const precacheResources = [
  '/',
  'index.html',
  'styles/main.css',
  'images/space1.jpg',
  'images/space2.jpg',
  'images/space3.jpg'
];

self.addEventListener('install', event => {
  console.log('Service worker install event!');
  event.waitUntil(
    caches.open(cacheName)
      .then(cache => {
        return cache.addAll(precacheResources);
      })
  );
});

self.addEventListener('activate', event => {
  console.log('Service worker activate event!');
});

self.addEventListener('fetch', event => {
  console.log('Fetch intercepted for:', event.request.url);
  event.respondWith(caches.match(event.request)
    .then(cachedResponse => {
        if (cachedResponse) {
          return cachedResponse;
        }
        return fetch(event.request);
      })
    );
});

이제 브라우저로 돌아가 사이트를 새로고침합니다. 서비스 워커가 있는지 Console에서 확인합니다.

  • 등록됨
  • 설치됨
  • 활성화됨

참고: 이미 서비스 워커를 등록했거나 모든 이벤트를 실행하는 데 문제가 있는 경우 서비스 워커를 등록 취소하고 페이지를 새로고침하세요. 실패하면 앱의 모든 인스턴스를 닫았다가 다시 엽니다.

그런 다음 Ctrl + c를 실행하여 명령줄에서 로컬 개발 서버를 종료합니다. 사이트를 다시 새로고침하고 서버가 오프라인 상태여도 로드되는지 확인합니다.

참고: 서비스 워커를 가져올 수 없음을 나타내는 콘솔 오류(An unknown error occurred when fetching the script. service-worker.js Failed to load resource: net::ERR_CONNECTION_REFUSED)가 표시될 수 있습니다. 이 오류는 브라우저가 서비스 워커 스크립트를 가져올 수 없기 때문에 (사이트가 오프라인 상태) 표시되지만, 서비스 워커를 사용하여 캐싱할 수 없기 때문에 표시되는 오류입니다. 그렇지 않으면 사용자의 브라우저가 동일한 서비스 워커로 인해 멈춥니다.

설명

index.html의 등록 스크립트에 서비스 워커가 등록되면 서비스 워커 install 이벤트가 발생합니다. 이 이벤트 중에 install 이벤트 리스너가 이름이 지정된 캐시를 열고 cache.addAll 메서드로 지정된 파일을 캐시합니다. 이를 '사전 캐시'라고 합니다. 일반적으로 사용자가 사이트를 처음 방문하는 install 이벤트 중에 발생합니다.

서비스 워커가 설치된 후 다른 서비스 워커가 현재 페이지를 관리하지 않는 경우 새 서비스 워커는 "활성화" (서비스 워커에서 activate 이벤트 리스너가 트리거됨)를 제어하고 페이지 관리를 시작합니다.

활성화된 서비스 워커가 제어하는 페이지에서 리소스를 요청하면 요청은 네트워크 프록시와 같이 서비스 워커를 통해 전달됩니다. fetch 이벤트는 요청별로 트리거됩니다. 서비스 워커에서 fetch 이벤트 리스너는 캐시를 검색하고 사용 가능한 경우 캐시된 리소스로 응답합니다. 리소스가 캐시되지 않으면 리소스는 정상적으로 요청됩니다.

리소스 캐싱을 사용하면 네트워크 요청을 회피함으로써 앱이 오프라인에서 작동할 수 있습니다. 이제 앱이 오프라인 상태일 때 200 상태 코드로 응답할 수 있습니다.

참고: 활성화 이벤트는 이 로그에서 로깅하는 것 외에 다른 용도로는 사용되지 않습니다. 이 이벤트는 서비스 워커 수명 주기 문제를 디버그하는 데 도움이 되도록 포함되었습니다.

선택사항: 캐시 저장소 섹션을 펼치면 개발자 도구의 애플리케이션 탭에서 캐시된 리소스를 확인할 수 있습니다.

node server.js로 개발 서버를 다시 시작하고 사이트를 새로고침합니다. 그런 다음 개발자 도구에서 감사 탭을 다시 열고 새 감사(왼쪽 상단의 더하기 기호)를 선택하여 Lighthouse 감사를 다시 실행합니다. 감사가 완료되면 PWA 점수가 크게 향상되지만 여전히 개선이 필요한 섹션도 있을 것입니다.

참고: 웹 앱 설치 배너 테스트는 실습 범위에 포함되지 않으므로 이 섹션은 선택사항입니다. 원격 디버깅을 통해 직접 사용해 볼 수 있습니다.

PWA 점수는 여전히 훌륭하지 않습니다. 보고서에 남아 있는 문제 중 일부는 사용자에게 웹 앱을 설치하라는 메시지가 표시되지 않고 주소 표시줄에 스플래시 화면 또는 브랜드 색상을 구성하지 않은 경우입니다. 이 문제를 해결하고 추가 기준을 충족하여 점진적으로 홈 화면에 추가를 구현할 수 있습니다. 무엇보다도 매니페스트 파일을 만들어야 합니다.

매니페스트 파일 만들기

app/manifest.json라는 파일을 만들고 다음 코드를 추가합니다.

{
  "name": "Space Missions",
  "short_name": "Space Missions",
  "lang": "en-US",
  "start_url": "/index.html",
  "display": "standalone",
  "theme_color": "#FF9800",
  "background_color": "#FF9800",
  "icons": [
    {
      "src": "images/touch/icon-128x128.png",
      "sizes": "128x128"
    },
    {
      "src": "images/touch/icon-192x192.png",
      "sizes": "192x192"
    },
    {
      "src": "images/touch/icon-256x256.png",
      "sizes": "256x256"
    },
    {
      "src": "images/touch/icon-384x384.png",
      "sizes": "384x384"
    },
    {
      "src": "images/touch/icon-512x512.png",
      "sizes": "512x512"
    }
  ]
}

매니페스트에서 참조되는 이미지는 이미 앱에 제공되어 있습니다.

그런 다음 index.html<head> 태그 하단에 다음 HTML을 추가합니다.

<link rel="manifest" href="manifest.json">

<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="Space Missions">
<meta name="apple-mobile-web-app-title" content="Space Missions">
<meta name="theme-color" content="#FF9800">
<meta name="msapplication-navbutton-color" content="#FF9800">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="msapplication-starturl" content="/index.html">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<link rel="icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="apple-touch-icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="icon" sizes="192x192" href="icon-192x192.png">
<link rel="apple-touch-icon" sizes="192x192" href="/images/touch/icon-192x192.png">
<link rel="icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="apple-touch-icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="apple-touch-icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="icon" sizes="512x512" href="/images/touch/icon-512x512.png">
<link rel="apple-touch-icon" sizes="512x512" href="/images/touch/icon-512x512.png">

사이트로 돌아갑니다. 개발자 도구의 애플리케이션 탭에서 스토리지 비우기 섹션을 선택하고 사이트 데이터 삭제를 클릭합니다. 그런 다음 페이지를 새로고침하세요. 이제 매니페스트 섹션을 선택합니다. manifest.json 파일에 구성된 아이콘 및 구성 옵션이 표시됩니다. 변경사항이 표시되지 않으면 시크릿 창에서 사이트를 열고 다시 확인해 보세요.

설명

manifest.json 파일은 브라우저에 Chrome, 홈 화면 아이콘, 스플래시 화면 등 앱의 프로그레시브 기능을 지정하고 형식을 지정하는 방법을 브라우저에 알립니다. 네이티브 앱에서와 마찬가지로 웹 앱이 standalone 모드에서 열리도록 구성할 수도 있습니다 (즉, 브라우저 외부에 위치).

이 글을 작성하는 시점을 기준으로 일부 브라우저에서는 개발이 아직 진행 중이며, <meta> 태그는 아직 완전히 지원되지 않는 특정 브라우저에서 이러한 기능의 하위 집합을 구성합니다.

index.html의 이전 캐시 버전을 삭제하려면 사이트 데이터를 삭제해야 했습니다(버전 링크가 매니페스트 링크가 없기 때문). 다른 Lighthouse 감사를 실행하여 PWA 점수가 얼마나 개선되었는지 확인해 보세요.

설치 메시지 활성화 중

앱을 설치하는 다음 단계는 사용자에게 설치 메시지를 표시하는 것입니다. Chrome 67에서는 사용자에게 자동으로 메시지를 표시하지만 Chrome 68부터 사용자 동작에 따라 설치 프롬프트가 프로그래매틱 방식으로 활성화되어야 합니다.

다음 코드를 사용하여 index.html 상단의 <main> 태그 바로 뒤에 '앱 설치' 버튼과 배너를 추가합니다.

<section id="installBanner" class="banner">
    <button id="installBtn">Install app</button>
</section>

그런 다음 styles/main.css에 다음 스타일을 추가하여 배너의 스타일을 지정합니다.

.banner {
  align-content: center;
  display: none;
  justify-content: center;
  width: 100%;
}

파일을 저장합니다. 마지막으로 다음 스크립트 태그를 index.html에 추가합니다.

  <script>
    let deferredPrompt;
    window.addEventListener('beforeinstallprompt', event => {

      // Prevent Chrome 67 and earlier from automatically showing the prompt
      event.preventDefault();

      // Stash the event so it can be triggered later.
      deferredPrompt = event;

      // Attach the install prompt to a user gesture
      document.querySelector('#installBtn').addEventListener('click', event => {

        // Show the prompt
        deferredPrompt.prompt();

        // Wait for the user to respond to the prompt
        deferredPrompt.userChoice
          .then((choiceResult) => {
            if (choiceResult.outcome === 'accepted') {
              console.log('User accepted the A2HS prompt');
            } else {
              console.log('User dismissed the A2HS prompt');
            }
            deferredPrompt = null;
          });
      });

      // Update UI notify the user they can add to home screen
      document.querySelector('#installBanner').style.display = 'flex';
    });
  </script>

파일을 저장합니다. 원격 디버깅을 사용하여 Android 기기의 Chrome에서 앱을 엽니다. 페이지가 로드되면 '앱 설치' 버튼이 표시되어야 합니다. 데스크톱에는 표시되지 않습니다. 따라서 휴대기기에서 테스트해야 합니다. 버튼을 클릭하면 '홈 화면에 추가' 메시지가 표시됩니다. 단계에 따라 기기에 앱을 설치합니다. 설치 후 새로 만든 홈 화면 아이콘을 탭하여 웹 앱을 브라우저 외부에서 독립형 모드로 열 수 있습니다.

설명

HTML 및 AMP 코드는 숨겨진 배너와 버튼을 추가하여 사용자가 설치 메시지를 활성화하도록 허용할 수 있습니다.

beforeinstallprompt 이벤트가 실행되면 Chrome 67 이하에서 사용자에게 설치를 요청하는 기본 환경을 차단하고 전역 deferredPrompt 변수에 beforeinstallevent를 캡처합니다. 그러면 '앱 설치' 버튼이 beforeinstalleventprompt() 메서드로 메시지를 표시하도록 구성됩니다. 사용자가 선택 (설치 여부 선택)하면 userChoice 프로미스가 사용자의 선택 (outcome)에 따라 해결됩니다. 마지막으로 모든 준비가 완료되면 설치 버튼이 표시됩니다.

Lighthouse로 사이트를 감사하는 방법과 오프라인 기능의 기본사항을 구현하는 방법을 알아보았습니다. 선택적 섹션을 완료했다면 웹 앱을 홈 화면에 설치하는 방법도 알아보았습니다.

추가 리소스

Lighthouse는 오픈소스입니다. 파일을 포크하고 자체 테스트를 추가하며 버그를 신고할 수 있습니다. Lighthouse는 빌드 프로세스와의 통합을 위한 명령줄 도구로도 사용할 수 있습니다.

PWA 교육 과정의 모든 Codelab을 보려면 과정의 Codelab 시작하기를 참고하세요.