webアプリケーションに通知を盛り込んでみた備忘録

# はじめに

先日参加したハッカソンにてweb通知を作ることになりました。その備忘録を残せたらなと思います。

# 書くこと

学んだ範囲で以下のことを書きます。

- web通知の動き
- web通知の実装コード紹介

# 通知の動き

FCMを用いた通知の動きは一般的にはこのようなイメージになっております。

<img width="500" src="https://qiita-image-store.s3.amazonaws.com/0/3630545/e2d1c936-b7ec-fae5-9dac-ac64efd1a422.png">

(引用元：[Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030#section-2))

かなり簡略化された図ですが単語を解説していきます。(僕が分かるレベルまで噛み砕いていきます)

<table>
  <caption>登場人物</caption>
  <thead>
    <tr>
      <th>名前</th>
      <th>説明</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ユーザーエージェント( UA )</td>
      <td>
        <ul>
          <li>ユーザーのWebブラウザ</li>
          <li>メッセージが受信されたら、UA 内に存在する Service Worker の push イベントが発火され、パラメータ(データ)を通じて取得される</li>
        </ul>
      </td>
    </tr>
    <tr>
      <td>プッシュサービス( PS )</td>
      <td>
        <ul>
          <li> UA と AS の中継となるサーバ</li>
          <li>通常はウェブブラウザのベンダがこのサービス（サーバ）を提供しており、24時間稼働している</li>
          <li>今回自分は <a href="https://firebase.google.com/docs/cloud-messaging?hl=ja" target="_blank" rel="noopener noreferrer">FCM</a> を採用</li>
        </ul>
      </td>
    </tr>
    <tr>
      <td>アプリケーションサーバ( AS )</td>
      <td>
        <ul>
          <li>ウェブサイトを運営しているウェブサーバ</li>
          <li>通知を送る元であり、通知は PS に向けて送信する(だから UA が起動していなくても問題ない)</li>
          <li> UA に通知を送る場合は、PushSubscription(情報) 内に格納されている Push Service の endpoint に対してメッセージを送信する</li>
          <li>バックエンド</li>
        </ul>
      </td>
    </tr>
  </tbody>
</table>

#### プッシュ通知(Push Message)とは
- ここでは AS から PS , UA　へと**通知を送ること**を指す

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630545/74372640-e254-09ea-72b9-25a432ae7522.png">

#### 購読とは

- サイトを訪れたユーザーに対してプッシュ通知を配信するための購読要求ダイアログを表示すること
```notification.js
function notifyMe() {
  if (!("Notification" in window)) {
    // ブラウザーが通知に対応しているか調べる
    alert("このブラウザーはデスクトップ通知には対応していません。");
  } else if (Notification.permission === "granted") {
    // 通知権限が既に付与されているかどうかを調べる。
    // その場合は、通知を作成する。
    const notification = new Notification("こんにちは！");
    // …
  } else if (Notification.permission !== "denied") {
    // ユーザーにその権限を要求する必要がある
    Notification.requestPermission().then((permission) => {
      // ユーザーが受け入れたら、通知を作成する。
      if (permission === "granted") {
        const notification = new Notification("こんにちは！");
        // …
      }
    });
  }

  // 最後に、ユーザーが通知を拒否している場合は、これ以上
  // ユーザーに迷惑をかける必要はありません。
}

```

(引用元：[Notification.requestPermission()](https://developer.mozilla.org/ja/docs/Web/API/Notification/requestPermission_static))

- ユーザー側ではこのようなダイアログが表示されます

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630545/728e6fdb-20ba-2798-040e-6077ee9e7e2e.png">

#### PushSubscriptionとは

- サブスクリプション情報を含むオブジェクト
- [MDNはこちら](https://developer.mozilla.org/ja/docs/Web/API/PushSubscription)

```json
{
  "endpoint": "https://example.com/test-text...",
  "keys": {
    "p256dh": "BNcRdreALo3w...",
    "auth": "tBHItJI5svb..."
  }
}
```

# FCM

今回はプッシュサービスとして **Firebase Cloud Messaging** を使用しました。
詳しくは紹介しませんが、色々な機能を提供しているようです。
- デバイス間やプラットフォーム間を統一された方法で通知を送信
- トークンの管理
- デバイスへの通知配信

# 最後に

実際に実装した際にはサービスワーカーの挙動だったり、choromeの設定だったりで動かない期間が多くハッカソンに使うべきではなかったなと感じました。苦労した実装を今後また記事にします。

また、通知に関して間違っている点があればコメントいただけますと幸いです。

# 参考文献
[サーバーからブラウザを通じてデスクトップ通知する方法（Push API を利用）](https://laboradian.com/web-push/#4)
[Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030#section-2)
[Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging?hl=ja)
[MDN](https://developer.mozilla.org/ja/docs/Web/API)