虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Amazon Pinpoint + FCM でお手軽に Web プッシュ通知試してみた 🔔

Web アプリでもプッシュ通知はできる!

こんにちは、虎の穴ラボの後藤です。

あるサイトのスケジュール情報をリマインドしてくれるものが欲しいものの、 プッシュ通知のためにモバイルアプリ開発はコストが高いなぁ...🫠 と思っていました。

しかし、実は Web アプリからプッシュ通知ができる!また、Amazon Pinpoint + FCM で複雑な実装をあまり意識しないで済む!ということで試してみました 🔔

参考にした AWS ブログ記事

こちらの記事をベースに実装しましたので、併せて参照ください!

aws.amazon.com

概ねブログ記事通りですが、2 点修正しました。

  • ライブラリのアップデート:私の Node v20.12.2 環境ではError: error:0308010C:digital envelope routines::unsupportedのエラーになり、クライアント側アプリが起動できませんでした。記事で紹介されているアップデート手順を実行したのち、仕様が変更されているライブラリのモジュールインポートを修正すると起動することができました。
// 一例として、aws-amplify のモジュールインポート修正
// export 'default' (imported as 'Auth') was not found in '@aws-amplify/auth'
// export 'default' (imported as 'Analytics') was not found in '@aws-amplify/analytics'

- import Auth from "@aws-amplify/auth";
- import Analytics from "@aws-amplify/analytics";
+ import { Amplify, Analytics } from "aws-amplify";

- Auth.configure(awsconfig);
+ Amplify.configure(awsconfig);
Analytics.configure(awsconfig);
  • FCM HTTP v1 API の利用:記事内では FCM のCloud Messaging API(レガシー)を使うと示されていますが、こちらの API は 2023 年 6 月に非推奨となっています。そのため、移行先である HTTP v1 API を利用し、Amazon Pinpoint には FCM JSON ファイルを設定しています。

firebase.google.com

作ったもの

イベントの 5 分前にプッシュ通知を送ってくれる Web アプリを作成しました!

システム構成

ざっくりとした処理の流れは下記の通りです。

  • Web サイトのスケジュール情報を Chrome 拡張機能を利用して JSON ファイルとして保存
  • JSON ファイルを Amazon S3 へアップロード
  • Amazon EventBridge で AWS Lambda を定期実行
  • Amazon S3 を参照し、Amazon Pinpoint プッシュ通知キャンペーンを作成
  • イベントの 5 分前に FCM 経由でユーザーへプッシュ通知

使ったサービス・ライブラリ

  • Amazon Pinpoint:SMS、E メール、プッシュ通知など様々な配信が可能。スケジューリングやダッシュボード分析も可能。「Push 通知キャンペーン管理ツール」の役割。
  • Firebase Cloud Messaging(FCM):今回は「中継サービス」として利用。各デバイスに対して一意のトークンを生成し、ユーザーとマッピングする。
  • CRXJS:Chrome 拡張機能の開発で HMR や静的アセットのインポートが利用でき、開発効率がグッと良くなる✨

実装ポイント① FCM を用いた Web プッシュ通知実装の抽象化

Web プッシュ通知の仕様は Web プッシュプロトコルで定められています。

この仕様に従って自前実装する場合、下記のような実装を考える必要があり複雑になります。

  • 公開鍵/秘密鍵の生成と管理
  • 署名
  • 署名付き情報のやり取り

今回は FCM を用いることで、このあたりの実装が簡易になる点が良いですね。

Amazon Pinpoint を使った Web Push 通知の送信方法 | Amazon Web Services ブログ

実装ポイント② Web アプリをインストール可能にする

今回対象としていた iOS は、インストールなしでは Web プッシュ通知が機能せず、やむなくインストール可能な PWA として実装しています。 (詳しく検証できておらず私の環境だけかもしれませんが...🫠

PWA はネイティブアプリのように振る舞う Web アプリのデザインパターンです。主な特徴として、インストール可能、オフライン動作、プッシュ通知などが挙げられます。詳しくは下記記事を参照ください!

web.dev

幸い、インストール可能とするだけであればウェブアプリマニフェストを設定するだけで対応できます!

実装は簡単で、はじめに.webmanifestファイルを作成します。(設定については、ウェブアプリマニフェスト メンバーを参照ください)

{
  "name": "Reminder App",
  "short_name": "reminderApp",
  "description": "sample reminder app",
  "icons": [
    {
      "src": "../images/icon-192x192.png",
      "sizes": "32x32",
      "type": "image/png"
    },
    {
      "src": "../images/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/index.html",
  "display": "fullscreen",
  "theme_color": "#B12A34",
  "background_color": "#B12A34"
}

最後に<head>ブロックに.webmanifestファイルを設定するだけです

<!DOCTYPE html>
<html lang="ja">
  <head>
    <!-- ... -->
    <link rel="manifest" href="calendar4h.webmanifest" />
    <!-- ... -->
  </head>

これだけでインストール可能となります🎉

実装ポイント③ AWS Lambda での Amazon Pinpoint プッシュ通知キャンペーン作成

Amazon Pinpoint ではスケジューリングされたプッシュ通知が可能なので、常時サーバー起動する必要はなく、今回は日次でラムダ関数を定期実行しています。

参考として、ラムダ関数内の Amazon S3 ファイル参照と Amazon Pinpoint プッシュ通知キャンペーン作成の処理を下記に示します。ランタイムには Node.js 18 、ライブラリには AWS SDK for JavaScript v3 を利用しています。

// 一部省略しています

const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3");
const {
  PinpointClient,
  CreateCampaignCommand,
} = require("@aws-sdk/client-pinpoint");

// s3 からファイルの読み取り
const s3Client = new S3Client({ region: process.env.REGION });
const params = {
  Bucket: process.env.BUCKET_NAME,
  Key: process.env.OBJECT_KEY,
};
const s3Command = new GetObjectCommand(params);
const s3Response = await s3Client.send(s3Command);
const body = await s3Response.Body;
const str = await body.transformToString("utf-8");
const data = JSON.parse(str);

// Amazon Pinpoint プッシュ通知キャンペーン作成
const pinpointClient = new PinpointClient({ region: process.env.REGION });
await Promise.all(
  data
    .filter(({ date }) => date === tomorrow) // 翌日のイベントを対象にする
    .map(({ date, eventName, casts, startTime, url }) => {
      const input = {
        ApplicationId: process.env.APPLICATION_ID, // Amazon Pinpoint のプロジェクト ID
        WriteCampaignRequest: {
          Description: description,
          IsPaused: false,
          MessageConfiguration: {
            // NOTE: FCM サービスを介して送信するメッセージでは GCM サービス名を使用すること
            GCMMessage: {
              Action: "URL",
              Body: `${startTime} ${casts.join(", ")}`,
              SilentPush: false,
              Title: eventName,
              Url: url,
            },
          },
          Name: `${date} ${startTime}`,
          // スケジュール設定
          Schedule: {
            Frequency: "ONCE",
            IsLocalTime: false,
            StartTime: startTime,
            Timezone: "UTC+09",
          },
          // 配信対象となるセグメントの ID
          SegmentId: process.env.SEGMENT_ID,
        },
      };

      const command = new CreateCampaignCommand(input);
      return pinpointClient.send(command);
    })
);

より詳細な情報は下記のドキュメントを参照ください!

docs.aws.amazon.com

振り返り

AWS ブログ記事に沿って Amazon Pinpoint + FCM を用いることで、Web プッシュ通知の複雑な仕様をあまり意識することなく実装することができました。 Amazon Pinpoint には単純な通知機能だけでなく、分析機能も豊富なのでぜひ活用してみてください!

また、Web プッシュ通知実装の別アプローチとして、web-push のようなライブリを用いても良さそうです。ぜひ参照ください

github.com

Fantia開発採用情報

虎の穴ラボでは現在、一緒にFantiaを開発していく仲間を積極募集中です!
多くのユーザーに使っていただけるtoCサービスの開発をやってみたい方は、ぜひ弊社の採用情報をご覧ください。
toranoana-lab.co.jp