PWA & 서비스 워커

Tei·2020년 11월 17일
11
post-thumbnail

PWA

최근 모바일 뷰에 대해 고민하면서 PWA 를 적용해보면 어떨까 하는 생각이 들어 PWA 를 공부해 보았습니다. 생각보다 자료가 알기 어려워서 정리할 겸 작성해봅니다.

pwa 란

  • Progressive Web App

    • APP LIKE 하게 동작하는 웹 사이트
  • 정의는 애매모호하지만 Google 은 신뢰성있고, 빠르고, 매력적이어야 한다고(?) 표현함.

    • offline에서 동작하고
    • 반응성이 좋고 (유저 동작에 빠르게 응답)
    • 풀스크린으로 동작하고
    • 홈스크린에 추가될 수 있고
    • 푸시를 받아야한다

잘 구현한다면, 웹사이트만으로도 정말 APP같은 사용자 경험을 만들 수 있습니다.홈 화면에 아이콘을 추가할 수 있어 사용자를 묶어둘 수 있고(유저의 지속성을 유지할 수 있습니다)
설치 라는 허들을 한단계 낮춰서 도달율을 높일 수 있겠습니다.

서비스워커

PWA 의 실질적인 동작은 대부분 서비스 워커를 통해 구현되는것 같습니다.
그럼 서비스 워커의 특징에 대해 잠깐 알아보도록 하겠습니다.

  • 브라우저가 백그라운드에서 실행하는 스크립트이며, 웹페이지와는 다른 라이프 사이클을 가집니다
  • 오프라인일 경우에도 웹 어플리케이션을 기동할 수 있도록 합니다(오프라인 캐시) 간단히 말해, 사용자의 fetch 요청을 가로채, 대신 응답해 줄 수 있습니다.
  • 보안을 위해 https 와 localhost 에서만 동작하는 특징이 있습니다.

서비스워커는, 다른 문서나 다른 소스로부터 발생되는 이벤트에 대응하기 위한 이벤트 드리븐 웹 워커라고 볼 수 있습니다(Push Notify 등).
다른 표준규격들이 웹에서 확장 가능하도록, 보편적 진입점(페이지 외에서 백그라운드 요소가 필요한것)을 제공합니다.

서비스 워커의 설치

먼저 아무런 일도 하지 않는 서비스 워커를 만들어보도록 하겠습니다.

//serviceWorker.js

self.addEventListener('install', function(event) {
    console.log("인스톨 되었다~")
}); // install 이 끝나면 인스톨되었다고 출력.

이런 서비스워커를 정의하고, index.js 에서 서비스 워커를 불러보도록 합니다.

//index.js
if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/serviceWorker.js')
            .then(registration => {
                console.log('DONE_', registration)  
            })
            .catch(e => {
                console.log('SW registration failed: ', e)
            })
    })
}// load 가 끝나면 워커를 등록해줌. 

저는 webpack 을 사용하고 있기 때문에, PWA 적용을 위해 workbox 웹팩 플러그인을 통해 해당 서비스 워커를 동작시켜보겠습니다.

workbox 설치
  • yarn add -D workbox-webpack-plugin

workbox 는 PWA의 캐싱(caching) 기능을 편하게 구현할 수 있도록 지원되는 표준 PWA 라이브러리입니다.

해당 플러그인을 통해 웹팩 빌드해 줍니다.

///webpack.config
.
.
const workboxInject = new InjectManifest({
    swSrc: './serviceWorker.js',
    swDest:'serviceWorker.js'
})
.
.
 plugins:[다른플러그인들~,workboxInject]

InjectManifest 함수는, 직접 설정한 서비스워커를 넣어줄 때 사용합니다.

generateSW 라는 함수도 존재하는데, 이는 간편하게 파일 캐싱용 서비스 워커를 생성할때 사용할 수 있습니다.

여기서는 직접 만든 서비스 워커를 사용했고, 추후에 서버 푸시를 받을 수 있도록 구현할것이기 때문에, InjectManifest 함수를 사용해 주었습니다.

웹팩 빌드를 해보면 서비스워커도 잘 등록되어있고, 콘솔에 인스톨 되었다~ 도 잘 나오는 것을 확인할 수 있습니다.

manifest.json

pwa 어플리케이션을 만들기 위해 mainfest 설정도 해줘야합니다.

  • yarn add -D webpack-pwa-manifest

역시나 웹팩을 통해 빌드할것이므로 웹팩용 라이브러리를 받아줍니다.

  {
      "name": "PWA_DEMO",
      "description": "FE STUDY PWA DEMO",
      "background_color": "#ffffff",
      "crossorigin": "use-credentials",
      "short_name": "PWA",
  }

웹팩 config 도 수정해줍니다.

const manifest = require('./manifest.json');
const manifestPlugin = new WebpackPwaManifest(manifest);
.
.
.
plugins:[manifestPlugin,workboxInject]

디버거의 Application 을 확인하면 Manifest 가 잘 들어가있는것을 확인할 수 있습니다.

서버 푸시 받아보기

아무 기능을 하지 않던 서비스워커를 확장해서, 서버 푸시를 받을 수 있도록 해봅시다.

//serviceWorker.js

import {skipWaiting,clientsClaim} from "workbox-core"
import {precacheAndRoute} from "workbox-precaching"

skipWaiting()
clientsClaim()

//푸시 이벤트 감지해서 보여주기
self.addEventListener('push', event => {
    const title = '푸시테스트'
    const options = {
        body: event.data.text(),
    }
    event.waitUntil(
        registration.showNotification(title, options)
    )
})

// precacheAndRoute(self.__WB_MANIFEST) <- 안써주면 웹팩 빌드가 안된다....(why...)

skipWaitingclientsClaim 이라는 새로운 함수가 보입니다. 서비스 워커는 최초 등록된 후 다음 로드시까지 이를 사용하지 않습니다.
하지만 skipWaitingclientClaim 함수를 사용한다면, 해당 페이지를 즉시 제어하게 됩니다.

그리고 push 이벤트를 감지하여, 푸시가 왔을 때 푸시를 보여주도록 합니다.

`waitUntil`` 함수는, activate 한 이벤트를 기다려줍니다(push 나 fetch 같은)

serviceWorker 파일을 수정해주었으니. index.js 파일도 수정해야 합니다.

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/serviceWorker.js')
            .then(reg => {
                reg.pushManager.subscribe({
                     userVisibleOnly: true,
                     //applicationServerKey: 원래는 줘야함...
                    })
                Notification.requestPermission()//푸시 허용할지 창 띄움
            })
            .catch(e => {
                console.log('SW registration failed: ', e)
            })
    })
}

pushManager.subscribe의 인자로 서버키를 줘야합니다. 그러기위해서 서버와 메시징 서비스가 필요한데요, 일반적으로는 구글의 파이어베이스 메시징 서비스를 사용합니다.

파이어베이스를 사용하여 웹 푸시를 보내기 위해서는, FCM 키와 공개키, 시크릿키가 필요합니다.

이것은 또 심오한 내용이기 때문에 다음 글에서 따로 다루도록 하겠습니다.

여기까지 설정해준다면, 서비스워커의 테스트 기능을 통해 푸시를 받아볼 수 있습니다.

캐싱

그렇다면, workbox 를 사용하여 , 최초 데이터를 받아온 이후에 캐시하고,

오프라인상태에서 실행시켰을 때 사용할 수 있도록 구현해보겠습니다.

캐싱전략

그 전에 캐싱 전략을 알아야 합니다. workbox 의 캐싱 전략은 아래와 같습니다.

  • cacheFirst : 캐싱된 데이터를 우선하여 보여줍니다
  • cacheOnly: 캐시에서만 보여줍니다.
  • networkFirst: 네트워크에 먼저 접근하고, 오프라인일 경우 캐시를 가져옵니다.
  • networkOnly: 네트워크에서만 보여줍니다
  • staleWhileRevalidate: 최초 로딩시에는 캐시에서 가져오고, 다음 요청부터 네트워크 요청된 결과를 캐시하여 보여줍니다.
캐싱구현

캐시를 사용하기 위해서, serviceWorker 에 registerRoute 함수를 작성해주어야 합니다.

위에서 알아본 전략 중, 네트워크 퍼스트 전략을 사용해 보도록 하겠습니다.

먼저 네트워크에 요청을 보내본 뒤, 네트워크 응답이 없다면 캐시된 데이터를 대신 건내주는 전략이었죠.

//serviceWorker.js

.
.
registerRoute((req) => req.event.request
    .headers
    .get('accept')
    .includes('text/html') //html 파일들을 캐싱
  ,new NetworkFirst());//네트워크 퍼스트 전략을 쓴다.
.
.

그리고 오프라인 상태를 만든 후, 새로고침을 해보면?
(서비스워커의 Offline 버튼을 눌러 오프라인 상태를 모킹할 수 있습니다)

짜잔 웹페이지가 정상적으로 불려오는것을 알 수 있습니다.
(콘솔창에 보시면, 네트워크 연결이 되지 않아서 에러가 뜨고있는것을 확인할 수 있습니다. 하지만 웹페이지는 정상적으로 불려옵니다.)

캐싱을 활용하는 방법도 여러가지인데요, 이것도 다다음글 정도에 작성해보도록 하겠습니다.

PWA 에 대해 간략하게 알아보았습니다.

공부하면서 생각보다 자료가 여기저기에 퍼져있어서 쉽지 않았던것 같네요,

도움이 되었으면 합니다.

더 좋은 글로 다시 돌아오겠습니다.

감사합니다.

참고자료

profile
Being a service developer

1개의 댓글

comment-user-thumbnail
2020년 11월 17일

선댓후감~ 좋은 글 감사합니다. ☺️

답글 달기