Next.js with Netlify와 함께 Service Worker 사용

일반적으로 Next.js에서 Service Worker를 사용하는 경우 server.js 등을 직접 작성합니다.
그러나 Netlify는 정적 콘텐츠 배포를 수행하는 서버에 대해 위의 방법을 선택할 수 없습니다. 반대로 정적 콘텐츠로 디렉토리에 존재하면 실행할 수 있습니다. 이에 대한 기술은 여기에 설명되어 있습니다.


추가 2018.8.1


사용 플러그인이 변경되었습니다 sw-precache-webpack-plugin -> workbox-webpack-plugin


패키지 설치



workbox



webpack을 통해 동적으로 번들 된 파일을 캐시 해주는 편리한 플러그인이 존재합니다.
몇 가지 있습니다만, 이하의 기사를 참고로 이번은 workbox 를 채용했습니다.

Workbox에서 Service Woker 캐시를 도입해 보았고 Webpack과 함께 | Nagisa
htps : // bg. 나기사 인 c. jp/아r치ゔぇs/1132

이번에는 workbox를 webpack에서 사용하기 위해 workbox-webpack-plugin라는 플러그인을 설치합니다.
$ yarn add workbox-webpack-plugin -D

next.config.js



next.js의 webpack을 override하기 위해 루트에 next.config.js를 만듭니다.
$ touch next.config.js
next.config.js 는 다음과 같이 씁니다.runtimeCaching 에 적절히 추가하는 느낌입니다.

next.config.js
const WorkboxPlugin = require('workbox-webpack-plugin');

module.exports = {
    webpack: config => {
        config.plugins.push(
            new WorkboxPlugin.GenerateSW({
                cacheId: 'workbox',
                swDest: 'service-worker.js',
                skipWaiting: true,
                clientsClaim: false,
                runtimeCaching: [
                    {
                        urlPattern: '/',
                        handler: 'networkFirst',
                        options: {
                            cacheName: 'page',
                            expiration: {
                                maxAgeSeconds: 60 * 60 * 24
                            }
                        }
                    },
                    {
                        urlPattern: /\/api\/.+/,
                        handler: 'networkFirst',
                        options: {
                            cacheName: 'api',
                            expiration: {
                                maxAgeSeconds: 60 * 60 * 24
                            }
                        }
                    },
                    {
                        urlPattern: /\.(png|svg|woff|ttf|eot)/,
                        handler: 'cacheFirst',
                        options: {
                            cacheName: 'assets',
                            expiration: {
                                maxAgeSeconds: 60 * 60 * 24 * 14
                            }
                        }
                    }
                ]
            })
        );

        return config;
    }
};

이 상태에서 next build를 실행하면 생성되는 .next 디렉토리 바로 아래에 service-worker.jsprecache-manifest.[hash].js라는 파일이 생성되고 있음을 알 수 있습니다.

package.json



Netlify에서 사용하는 것은 next export 명령을 통해 생성된 out 디렉토리가 해당됩니다.
따라서 next build 에 의해 생성되는 .next 디렉토리내는 일치하지 않습니다.
그래서 service-worker.jsprecache-manifest.[hash].jsout 디렉토리에 복사하는 태스크를 package.json 에 기재합니다. 그리고 이 복사하는 태스크를 next export 때때로 실행하도록(듯이) 합니다.

구체적으로는 다음과 같습니다.

package.json
"scripts": {
    "storybook": "start-storybook -s ./ -p 6006",
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "export": "npm run build && next export && npm run copySW",
    "copySW": "cp .next/service-worker.js out/service-worker.js && cp .next/precache* out/"
},

이렇게 하면 Netlify에서 사용하는 out 디렉토리에 service-worker.js 들이 복사되어 사용할 수 있는 상태가 됩니다.

service-worker.js 실행



복사 된 service-worker.js가 구성 요소에서 실행되도록합시다.
컴포넌트가 마운트된 타이밍이 바람직하기 때문에 componentDidMount 로 실행하는 것이 적절합니다.

먼저 OfflineSupport.js 라는 구성 요소를 만듭니다.

OfflineSupport.js
import React, { PureComponent } from 'react';

class OfflineSupport extends PureComponent {
    componentDidMount() {
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker
                .register('./service-worker.js')
                .then(() => {
                    console.log('service worker registration successful');
                })
                .catch(err => {
                    console.warn(
                        'service worker registration failed',
                        err.message
                    );
                });
        }
    }

    render() {
        return null;
    }
}

export default OfflineSupport;

이것을 _app.js에서 사용합시다.

_app.js
import App, { Container } from 'next/app';
import React from 'react';
import OfflineSupport from '../src/components/OfflineSupport';

class RootApp extends App {
    render() {
        return (
            <Container>
                <YourComponents />
                <OfflineSupport />
            </Container>
        );
    }
}

export default RootApp;



여기까지의 설정을 하는 것으로 server.js등을 스스로 작성하지 않아도 Netlify를 Service Worker를 사용하는 것은 가능합니다.
Lighthouse의 스코어도 꽤 좋은 느낌이 되었습니다.



꼭 기회가 있으면 시험해보십시오!

좋은 웹페이지 즐겨찾기