[23-01-19 TIL] CRA에 Service worker 적용하기

O2o2✨·2023년 2월 3일
1

TIL

목록 보기
19/25
post-thumbnail

1. 배경

코드를 수정해서 새로 배포하면 사용 중인 웹에 바로 적용되면 좋겠다.
회사 플랫폼은 현재 강제 새로고침(ctrl + F5) 또는 인터넷 사용 기록을 삭제해야만 적용이 된다.
업데이트 여부도 모르는데 ctrl + F5를 직접 누르는 사용자는 없을 것이다.

처음에는 강제로 새로고침 하는 코드를 찾아봤지만 window.location.reload(true)는 deprecated됐다.
(window.location.reload()는 존재하지만 이건 강제 새로고침이 아니다.)

두번째는 웹팩 설정을 통해 파일명에 매번 새로운 해시를 넣는 것이다.
하지만 해시는 이미 사용 중이었고 기존 열린 창에서는 업데이트를 알 수 없다.

그러다 발견한 것이 서비스 워커다.
서비스 워커를 사용하면 새로운 버전이 존재할 때마다 아래처럼 사용자에게 알림을 줄 수 있다.


2. 설정

CRA 4.0+부터 아래처럼 설정할 수 있다.

방법1 새로운 프로젝트에 적용

서비스워커를 사용하는 프로젝트를 생성한다.
npx create-react-app my-app --template cra-template-pwa-typescript

방법2 기존 프로젝트에 적용

이미 존재하는 프로젝트에 사용하려면 2.1의 명령어를 통해 만들어진
service-worker.ts, serviceWorkerRegistration.ts 파일을 기존 프로젝트의 'src/'에 추가한다.

또한 package.json에 아래 내용을 추가한다.

"workbox-background-sync": "^6.5.4",
"workbox-broadcast-update": "^6.5.4",
"workbox-cacheable-response": "^6.5.4",
"workbox-core": "^6.5.4",
"workbox-expiration": "^6.5.4",
"workbox-google-analytics": "^6.5.4",
"workbox-navigation-preload": "^6.5.4",
"workbox-precaching": "^6.5.4",
"workbox-range-requests": "^6.5.4",
"workbox-routing": "^6.5.4",
"workbox-strategies": "^6.5.4",
"workbox-streams": "^6.5.4"

3. 서비스워커 사용 등록

import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
...
serviceWorkerRegistration.register({
   onUpdate: (register: ServiceWorkerRegistration) => {
     // update
   },
   onSuccess: (register: ServiceWorkerRegistration) => {
     // success
   },
 });
...

서비스 워커를 사용하려면 루트 파일(index.tsx 또는 App.tsx)에 serviceWorkerRegistration.register를 호출해야된다.
(serviceWorkerRegistration.unregister는 사용하지 않을 때 등록 취소를 의미한다.)

  • onUpdate: 새로운 업데이트가 존재할 때 호출된다.
  • onSuccess: 가장 처음에 등록 성공 시

그리고 서비스워커는 기본적으로 prod에서만 적용된다.
배포 후 개발자 도구(F12) > 애플리케이션 > Service Workers를 확인하면 등록됐는지 확인 할 수 있다.
예시로 gmail을 가져왔다.


4. 업데이트 확인

3번까지만 진행하면 사용자가 새 탭을 열 때까지 새로운 서비스워커가 존재하는지 확인할 수 없다.

const unlisten = history.listen(() => {
  if (!navigator.serviceWorker) {
    return;
  }
  navigator.serviceWorker.getRegistrations().then((regs) =>
    regs.forEach((reg) => {
      reg.update().catch((e: any) => {
        // Fetching SW failed.
      });
    })
  );
});

리액트 컴포넌트에서 페이지 path가 변경될 때마다 브라우저에 등록된 서비스 워커에 update를 수동으로 호출했다.
새로운 서비스워커가 존재하면 3번 코드의 onUpdate가 호출된다.

참고로 history.listen의 리턴 값인 unlisten을 호출하면 listen을 멈춘다.
(컴포넌트가 mount할 때 마다 같은 이벤트가 계속 등록되어 여러번 호출되는 것을 막을 수 있다.)


5. 새로운 서비스 워커로 변경

navigator.serviceWorker.getRegistrations().then((regs) =>
  regs.forEach((reg) => {
    reg.waiting?.postMessage({ type: "SKIP_WAITING" });
  })
);

새로운 서비스워커는 waiting이라는 status를 갖게 되는데 이때 SKIP_WAITING 메시지를 보내면
서비스워커가 새거로 교체된다. (active인 현재 것은 버리고 waiting중인 새것이 installed됨)

새로운 버전이 존재하는 상태에서 아직 업데이트 안 했을때 개발자 도구를 열면 저렇게 보인다.


6. 서비스 워커 교체 후 리프레시

navigator.serviceWorker.addEventListener("controllerchange", () => {
  window.location.reload();
});

서비스워커가 교체되면 controllerchange 이벤트가 발생하는데
이 때 페이지를 reload하면 새로 배포한 버전의 웹이 보인다.


7. 끝!이 아니다

배포 후 리프레시를 하면 아래의 문제가 발생했다.

  • 문제
    배포할 때마다 blank(빈 화면)이 나타난다.
  • 원인
    번들 파일인 2.[hash].chunk.js가 서비스워커에 캐시되지 않는다. 서비스 워커가 캐시할수있는 파일 사이즈는 디폴트로 최대 5MB다.
    우리 파일의 경우 사이즈가 5MB를 훌쩍 넘었다
The maximumFileSizeToCacheInBytes configuration option might be at play here.
In c-r-a, it's set to 5mb, so if your chunk is larger than 5mb, it will be 
excluded from the list of assets to precache.
  • 해결

    • maximumFileSizeToCacheInBytes 를 변경한다.

    • 이를 위해 worbox-webpack-plugins 설치한다.

      npm i workbox-webpack-plugins

    • react-rewired를 사용중이어서 웹팩을 설정하는 코드인 config-overrides.js를 수정했다.
      캐시 가능한 파일 사이즈를 더 큰 값으로 변경했다.

            const { override, useBabelRc } = require('customize-cra');
            const { GenerateSW } = require('workbox-webpack-plugin');
    
            module.exports = {
              webpack: override(
                (config, env) => {
                  config.plugins.push(
                    new GenerateSW({
                      maximumFileSizeToCacheInBytes: INPUT_YOUR_CUSTOM_SIZE, 
                      // 1MB = 1 * 1024 * 1024
                    })
                  )
                  return config;
                }
              ),
            }
    
  • 적용 확인
    - 변경 전

    - 변경 후

--

8. 소스코드

개인 깃허브에 별도의 프로젝트를 만들어 코드를 작성했다.
Github


➕그 외

중간에 offline-plugin이란 것을 찾았었다.
시간을 설정해서 특정 시간마다 업데이트 여부를 확인한다.
적용했을때 잘 됐지만 last published가 4년 전이라서 사용하지 않았다.


도움 사이트

Create react App - Making a Progressive Web App
스택오버플로우 답변 > 캐시크기 5MB 제한
elice - Progressive Web App (2) React PWA 적용 방법

profile
리액트 프론트엔드 개발자입니다.

0개의 댓글