[ETC] Browser Service Worker

2022. 11. 22. 10:15ETC

서비스워커란?

서비스워커는 브라우저가 백그라운드에서 실행하는 스크립트로, 웹페이지와는 별개로 작동하며 웹페이지 또는 사용자의 인터랙션이 필요하지 않은 기능만 제공하고 있습니다.

서비스워커의 수명 주기는 웹페이지와는 완전히 별개입니다. 웹 서비스와 브라우저 및 네트워크 사이에서 프록시 서버의 역할을 하며, 오프라인에서도 서비스를 사용할 수 있도록 합니다.

주의

ServiceWorker 적용 전 알아두어야할 점은 서비스워커는 보안상의 이유로 HTTPS에서만 실행이 됩니다.

서비스워커 사용

서비스워커를 사용하기 위해서는 먼저 등록하는 단계가 필요합니다.

async function registerServiceWorker() {
  const swRegistration = await navigator.serviceWorker.register(
    "https://test.io/service_worker.js"
  );
  return swRegistration;
}

이후 앞단에서 서비스워커 허가를 요청, 지원 여부를 판단하는 함수

function checkSupport() {
  if (!("serviceWorker" in navigator)) {
    alert("Service worker needed for sending push is not supported by your browser");
    throw new Error("No Service Worker support!");
  } else {
    console.log("Service worker supported✅");
  }
  if (!("PushManager" in window)) {
    alert("Push API needed for sending push is not supported by your browser");
    throw new Error("No Push API Support!");
  } else {
    console.log("Push API supported✅");
  }
}

async function requestNotificationPermission() {
  const permission = await window.Notification.requestPermission();
  //'granted', 'default', or 'denied'
  if (permission !== "granted") {
    console.error("Permission not granted for Notification");
  }
}

등록, 설치 과정까지 마쳤다면, 활성화 단계로 넘어갑니다.

활성화는 activate 이벤트를 핸들링하여 조작할 수 있습니다.

self.addEventListener("activate", async () => {
  // This will be called only once the service worker is activated
  console.log("activating service worker");
  try {
    const applicationServerKey = urlB64ToUint8Array(
      // key generated for the app (VAPID)
      "KEY..."
    );
    const options = { applicationServerKey, userVisibleOnly: true };
    const subscription = await self.registration.pushManager.subscribe(options);
    const response = await saveSubscription(subscription.toJSON());
  } catch (e) {
    console.log("Error", e);
  }
  console.log("service worker activated");
});

활성화 후 이제 본 서비스워커는 기능적인 이벤트를 처리할 수 있습니다. ( push, fetch 등... )

서비스 워커 활성화 후 해당 구독 정보를 서버단으로 보내 값을 저장하고 처리할 수 있습니다.

const saveSubscription = async (subscription) => {
  //backend endpoint
  const saveSubEndpoint = "https://test.io/save-subscription";
  const response = await fetch(saveSubEndpoint, {
    method: "post",
    headers: {
      "Content-Type": "application/json",
      "Access-Control-Allow-origin": "*",
      "Access-Control-Allow-Credentials": "true"
    },
    body: JSON.stringify(subscription),
  });
  console.log(response);
  return response;
};

서버단에서는 넘겨받은 구독정보를 통해 푸시 이벤트를 발생 시켜 보겠습니다.

const express = require('express');
const webpush = require('web-push');
const bodyParser = require('body-parser');
const path = require('path');

let Mysql = require("./mysqldb/database");
let timez = require("moment-timezone");

require("dotenv").config();

const app = express();

const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;

const port = 5000;

app.use(express.static(path.join(__dirname, "client")));

app.use(bodyParser.json());

webpush.setVapidDetails('mailto:random@email.com', PUBLIC_KEY, PRIVATE_KEY);

app.post('/save-subscription', async (req, res) => {

    const subscription = req.body;
    const nData = await Mysql.Noti_Get_Data();                    
    const cData = await Mysql.Crawling_Get_Data(); 

    Mysql.Sub_Insert_Data(req.body);

    if (cData[0].length !== 0) {
        const payload = JSON.stringify({
            title: cData[0][0].market,
            url: nData[0][0].not_url,
            message: nData[0][0].not_message,
        });

        // pass Object into sendNotification
        webpush.sendNotification(subscription, payload).catch((err) => console.log(err));
    }
    else return console.log('push no exists...');
});

app.listen(port, () => console.log(`server started on port ${port}`));

해당 코드는, 5000 포트로 열려있으며 /save-subscription 라우트로 요청이 올 경우 알림 전송을 하고 있습니다.

이렇게 푸시 된 이벤트를 앞단에서 형식에 맞게 푸시 알림을 구현하면 끝입니다.

self.addEventListener("push", function (event) {
  if (event.data) {
    console.log("Push event!! ", event.data.text());
    showLocalNotification("Message from backend", event.data.text(), self.registration);
  } else {
    console.log("Push event but no data");
  }
});

const showLocalNotification = (title, body, swRegistration) => {
  const options = {
    body,
  };
  swRegistration.showNotification(title, options);
};

'ETC' 카테고리의 다른 글

[ETC] Debounce 와 Throttle 차이점  (0) 2022.12.06
[Homestead] 디렉토리 추가  (0) 2022.12.02
[ETC] Chrome CORS 이슈 회피  (0) 2022.11.22
[ETC] 개발 Tools  (0) 2022.09.06
[ETC] 개발 용어 정리  (0) 2022.08.31