웹 앱에 서비스 워커와 오프라인 추가

개요

9246b0abd8d860da.png

이 코드랩에서는 서비스 워커를 기존 애플리케이션에 통합하여 애플리케이션이 오프라인에서 작동하도록 하는 방법을 알아봅니다. 이 애플리케이션은 AirHorner라고 불리며, 경적을 클릭하면 소리가 납니다.

배울 내용

  • 기본 서비스 워커를 기존 프로젝트에 추가하는 방법
  • Chrome DevTools로 오프라인 모드를 시뮬레이션하고 서비스 워커를 검사 및 디버그하는 방법
  • 간단한 오프라인 캐싱 전략

필요한 사항

  • Chrome 52 이상
  • 프라미스, Git 및 Chrome DevTools에 대한 기본적인 이해
  • 샘플 코드
  • 텍스트 편집기
  • 로컬 웹 서버. 이 코드랩에서 설명하는 웹 서버를 사용하려면 명령줄에 Python이 설치되어 있어야 합니다.

샘플 코드 가져오기

SSH를 통해 명령줄에서 GitHub 저장소를 복제합니다.

$ git clone git@github.com:GoogleChrome/airhorn.git

또는 HTTPS에서 다음을 실행합니다.

$ git clone https://github.com/GoogleChrome/airhorn.git

샘플 앱 실행

먼저 완성된 샘플 앱이 어떤 모습일지 살펴봅시다. 실제로 보면 놀랍습니다.

master 분기를 확인하여 여러분이 올바른(최종) 분기에 있는지를 확인하세요.

$ git checkout master

로컬 웹 서버에서 사이트를 실행하세요. 임의의 웹 서버를 사용할 수 있지만, 이 코드랩의 나머지 부분에서는 포트 3000에서 Python의 SimpleHTTPServer를 사용한다고 가정하겠습니다. 따라서 localhost:3000에서 앱을 사용할 수 있습니다.

$ cd app
$ python -m SimpleHTTPServer 3000

Chrome에서 사이트를 엽니다. 그러면 다음 화면이 나타날 것입니다. 9246b0abd8d860da.png

앱 테스트

경적을 클릭하세요. 그러면 소리가 날 것입니다.

이제, Chrome DevTools를 사용하여 오프라인으로 전환하는 과정을 시뮬레이션하겠습니다.

DevTools를 열고 Application 패널로 이동한 후 Offline 확인란을 선택하세요. 아래 스크린샷에서는 마우스를 확인란 위로 가져간 상태입니다.

479219dc5f6ea4eb.png

확인란을 클릭하면 Network 패널 탭 옆에 경고 아이콘(느낌표가 있는 노란색 삼각형)이 표시됩니다. 이는 오프라인 상태임을 나타내는 기호입니다.

오프라인 상태임을 검증하기 위해 https://google.com으로 이동해 보세요. 그러면 Chrome에서 'there is no Internet connection' 오류 메시지가 표시될 것입니다.

자, 다시 앱으로 돌아갑시다. 오프라인 상태이긴 하지만 페이지 새로고침은 계속 완벽히 이루어져야 합니다. 경적도 계속 사용할 수 있어야 합니다.

이 경적이 오프라인에서 작동하는 이유가 바로 이 코드랩의 기본이며, 서비스 워커로 오프라인을 지원하는 것입니다.

Starter 앱 빌드

이제 애플리케이션에서 모든 오프라인 지원을 제거하고 서비스 워커를 사용하여 오프라인 지원을 다시 애플리케이션에 추가하는 방법을 알아보겠습니다.

서비스 워커가 구현되어 있지 않은 앱의 '망가진' 버전을 확인해 보세요.

$ git checkout code-lab

DevTools의 Application 패널로 뒤로 이동하여 Offline 확인란을 선택 취소하여 다시 온라인 상태로 전환하세요.

페이지를 실행하세요. 앱이 예상한 대로 작동해야 합니다.

이제, DevTools에서 Application 패널의 Offline 확인란을 선택해 다시 오프라인 모드를 시뮬레이션합니다. Heads up! 서비스 워커에 대해 잘 모를 경우에는 예상하지 못했던 동작이 나타날 것입니다.

어떤 결과를 예상하세요? 오프라인 상태이고 이 앱 버전에는 서비스 워커가 없기 때문에, Chrome에서 흔히 볼 수 있는 'there is no Internet connection' 오류 메시지가 나타날 것으로 예상하실 겁니다.

하지만 실제로는 완벽한 기능을 발휘하는 오프라인 앱을 볼 수 있습니다!

9246b0abd8d860da.png

무슨 일이 일어난 걸까요? 이 코드랩을 시작할 때를 떠올려 보세요. 완성된 버전의 앱을 시험해 보신 거잖아요? 그 버전을 실행할 때 앱에 실제로는 서비스 워커가 설치되어 있었던 거죠. 바로 그 서비스 워커가 앱을 실행할 때마다 자동으로 실행되는 겁니다. localhost:3000과 같은 범위에 서비스 워커를 일단 설치한 후에는(다음 섹션에서 범위에 대해 자세히 다룸), 프로그래밍 방식으로나 수동으로 서비스 워커를 삭제하지 않는 한 해당 범위에 액세스할 때마다 그 서비스 워커가 자동으로 시작됩니다.

이 문제를 수정하려면 DevTools의 Application 패널로 이동하고 Service Workers 탭을 클릭한 후 Unregister 버튼을 클릭하세요. 아래 스크린샷에서는 마우스를 버튼 위로 가져간 상태입니다.

837b46360756810a.png

사이트를 새로 고치기 전에 계속 DevTools를 사용하여 오프라인 모드를 시뮬레이션하는 중인지 확인하세요. 페이지를 새로 고치면 예상한 대로 'there is no Internet connection' 오류 메시지가 나타날 것입니다.

da11a350ed38ad2e.png

사이트에 서비스 워커 등록

이제는 반대로 오프라인 지원을 앱에 추가할 차례입니다. 이 작업은 다음 두 단계로 구성됩니다.

  1. 서비스 워커가 되는 자바스크립트 파일을 만듭니다.
  2. 이 자바스크립트 파일을 '서비스 워커'로 등록한다고 브라우저에게 알립니다.

먼저, sw.js라는 빈 파일을 만들어 /app 폴더에 넣습니다.

index.html을 열고 다음 코드를 <body> 맨 아래에 추가합니다.

<script>
if('serviceWorker' in navigator) {
  navigator.serviceWorker
           .register('/sw.js')
           .then(function() { console.log("Service Worker Registered"); });
}
</script>

브라우저가 서비스 워커를 지원하는지 확인하는 스크립트입니다. 브라우저가 서비스 워커를 지원하면 현재는 비어 있는 sw.js 파일을 서비스 워커로 등록한 다음 콘솔에 기록합니다.

사이트를 다시 실행하기 전에 DevTools로 돌아가 Application 패널의 Service Workers 탭을 살펴보세요. 현재는 비어 있어야 하는데, 이는 곧 사이트에 아무런 서비스 워커도 설치되어 있지 않다는 뜻입니다.

37d374c4b51d273.png

DevTools에서 Offline 확인란이 선택되어 있지 않은 상태임을 확인하세요. 페이지를 다시 새로 고칩니다. 페이지가 로드되면 서비스 워커가 등록되어 있는 것을 확인할 수 있습니다.

b9af9805d4535bd3.png

Source 레이블 옆에 등록된 서비스 워커의 소스 코드로 연결되는 링크가 보일 것입니다.

3519a5068bc773ea.png

어떤 페이지에 현재 설치되어 있는 서비스 워커를 검사해보고 싶다면 이 링크를 클릭하세요. 그러면 DevTools의 Sources 패널에 서비스 워커의 소스 코드가 표시됩니다. 예를 들어, 지금 이 링크를 클릭하면 빈 파일이 보일 것입니다.

dbc14cbb8ca35312.png

사이트 자산 설치

서비스 워커가 등록된 상태에서 사용자가 그 페이지를 처음 방문할 때 install 이벤트가 트리거됩니다. 이 이벤트는 페이지 자산을 캐시하려는 곳입니다.

다음 코드를 sw.js에 추가합니다.

importScripts('/cache-polyfill.js');

self.addEventListener('install', function(e) {
 e.waitUntil(
   caches.open('airhorner').then(function(cache) {
     return cache.addAll([
       '/',
       '/index.html',
       '/index.html?homescreen=1',
       '/?homescreen=1',
       '/styles/main.css',
       '/scripts/main.min.js',
       '/sounds/airhorn.mp3'
     ]);
   })
 );
});

첫째 줄은 Cache polyfill을 추가합니다. 이 polyfill은 저장소에 이미 포함되어 있습니다. Cache API가 아직은 모든 브라우저에서 완벽히 지원되는 것이 아니므로 polyfill을 사용할 필요가 있습니다. 다음에는 install 이벤트 리스너가 옵니다. install 이벤트 리스너는 caches 객체를 연 다음 우리가 캐시하려는 리소스의 목록으로 객체를 채웁니다. addAll 작업에 대한 한 가지 중요한 점은 전부 아니면 전무라는 점입니다. 즉, 파일 중 하나가 없거나 하나라도 가져오기에 실패할 경우 전체 addAll 작업이 실패합니다. 이러한 상황을 적절히 처리하도록 되어 있는 애플리케이션이 훌륭한 애플리케이션입니다.

그 다음은 서비스 워커가 가로챈 요청을 이런 리소스 중 아무 리소스로나 반환하고 caches 객체를 사용하여 각 리소스의 로컬 저장 버전을 반환하도록 서비스 워커를 프로그래밍하는 단계입니다.

웹페이지 요청 가로채기

서비스 워커의 한 가지 강력한 기능은 서비스 워커가 페이지를 제어하고 나면 페이지에서 이루어지는 모든 요청을 각각 가로채어 그 요청으로 무엇을 할지 결정할 수 있다는 점입니다. 이 섹션에서는 요청을 검색하려고 네트워크로 이동하지 않고 요청을 가로채어 자산의 캐시된 버전을 반환하도록 서비스 워커를 프로그래밍할 것입니다.

첫 번째 단계는 이벤트 핸들러를 fetch 이벤트에 연결하는 것입니다. 이 이벤트는 생성된 모든 요청에 대해 트리거됩니다.

sw.js의 맨 아래에 다음 코드를 추가하여 상위 페이지에서 생성된 요청을 로그에 기록합니다.

이걸 한번 테스트해봅시다. Heads up! 곧 더욱 예상치 못했던 서비스 워커의 동작을 볼 수 있을 것입니다.

DevTools를 열고 Application 패널로 이동합니다. Offline 확인란은 선택 취소되어 있어야 합니다. Esc 키를 눌러 DevTools 창 맨 아래에 있는 Console 창을 엽니다. 그러면 DevTools 창이 다음 스크린샷과 비슷한 모습이어야 합니다.

c96de824be6852d7.png

이제 페이지를 새로 고치고 DevTools 창을 다시 살펴보세요. 첫째, 콘솔에 다수의 요청이 기록되어 있을 것이라 예상했지만 그렇지 않습니다. 둘째, Service Worker 창에서 Status 가 바뀐 것을 알 수 있습니다.

c7cfb6099e79d5aa.png

Status 에는 활성화 대기 중인 새로운 서비스 워커가 있습니다. 방금 변경한 내용을 포함하는 새로운 서비스 워커임이 틀림없습니다. 그래서 어떤 이유인지, 우리가 설치했던 이전의 서비스 워커(그냥 비어 있는 파일이었음)가 여전히 페이지를 제어하고 있습니다. Source 옆에 있는 sw.js 링크를 클릭하면 이전의 서비스 워커가 여전히 실행 중인 것을 확인할 수 있습니다.

이런 불편함을 해소하려면 Update on reload 확인란을 선택하세요.

26f2ae9a805bc69b.png

이 확인란을 선택하면 DevTools가 페이지를 새로 고칠 때마다 서비스 워커를 업데이트합니다. 이 점은 서비스 워커를 실제로 개발할 때 매우 유용합니다.

페이지를 새로 고치면 새로운 서비스 워커가 설치되고 요청 URL이 예상한 것처럼 콘솔에 기록되는 것을 확인할 수 있습니다.

53c23650b131143a.png

이제는 그 모든 요청으로 무엇을 할지 결정할 필요가 있습니다. 기본적으로, 아무것도 하지 않는다면 요청이 네트워크로 전달되고 그에 대한 응답이 웹페이지로 반환됩니다.

애플리케이션이 오프라인으로 작동하도록 만들기 위해 캐시에서 요청을 가져와야 합니다(가져올 수 있는 경우).

아래의 코드에 일치하도록 fetch 이벤트 리스너를 업데이트하세요.

event.respondWith() 메서드는 브라우저에 앞으로 일어날 이벤트의 결과를 평가하라고 알려줍니다. caches.match(event.request)는 fetch 이벤트를 트리거한 현재 웹 요청을 받아 캐시에 일치하는 리소스가 있는지 찾아봅니다. 일치 여부 확인은 URL 문자를 살펴보는 방식으로 이루어집니다. match 메서드는 캐시에서 파일을 찾을 수 없는 경우에도 해결해주는 프라미스를 반환합니다. 즉, 무엇을 할지 자신에게 선택권이 주어진다는 뜻입니다. 위의 간단한 예에서는 파일을 찾을 수 없을 때는 그냥 네트워크에서 파일을 fetch하여 브라우저로 반환할 것입니다.

이는 가장 간단한 사례이며, 다양한 캐싱 시나리오가 있습니다. 예를 들어,이전에 캐시되지 않은 요청에 대한 모든 응답을 증분 방식으로 캐시할 수 있으므로,향후에 이 모든 응답이 캐시로부터 반환됩니다.

축하합니다!

이젠 오프라인 지원을 받게 되셨습니다. 계속 온라인 상태에 있는 동안 페이지를 새로 고쳐 서비스 워커를 업데이트한 다음, DevTools를 사용해 오프라인 모드로 전환하세요. 페이지를 다시 새로 고치면 완벽한 기능을 발휘하는 오프라인 에어 혼이 완성됩니다!

우리가 다루었던 내용

  • 기본 서비스 워커를 기존 프로젝트에 추가하는 방법
  • Chrome DevTools를 사용하여 오프라인 모드를 시뮬레이션하고 서비스 워커를 검사 및 디버그하는 방법
  • 간단한 오프라인 캐싱 전략

다음 단계

자세히 알아보기

문제가 있거나 의견이 있으세요?

언제든 망설이지 말고 문제를 제출해 주시면 코드랩에서 더욱 나은 서비스를 제공하는 데 큰 도움이 될 것입니다. 감사합니다!