LoginSignup
5
2

More than 3 years have passed since last update.

Next.jsでCore Web VitalsをAmazon CloudWatchに送信してみる

Last updated at Posted at 2020-12-18

この記事は、ニフティグループ Advent Calendar 2020の19日目の記事です。
昨日は@shin27さんで「Amazon SageMaker Feature StoreにKaggleのTitanicデータを登録してみた」でした。

はじめに

今年も早いものでもう2週間を切ってしまいましたね。今年11月に水樹奈々さんがおめでたを公表したり、近頃は寒い日が続いたりで皆さんいかがお過ごしでしょうか。

私は、今年11月にdアニメストアで配信が開始されたアニメ「頭文字D」に今更ながらハマったり、イベント「五等分の花嫁展 MAKEOVER」に参加したりして、コロナ禍でイベントが軒並み中止になる状況でもプライベートはそれなりに充実していたかなと思っています。

そして2021年1月から放映される予定のアニメ「五等分の花嫁∬」が待ち遠しいです。推しキャラは中野三玖です。

弊社内の話に移すと、ニフティではAWSやスクラム開発、フロントエンドフレームワークなどの勉強会が定期的に開催されていて、私も運営メンバー兼勉強メンバーとして精進しています。難しさもありますが、自分の中の知識をアップデートできる楽しさもあります!
ちなみに勉強会について少しでも気になった方や詳しい話を聞きたい方はぜひ説明会に来てみると良いかと!
ニフティ株式会社 採用情報

さて前置きが長くなりすぎたのでそろそろ本題に入りますが、今回はNext.jsの勉強も兼ねつつ、Next.jsでCore Web VitalsをAmazon CloudWatchに送信してみた話を書きます。

どうしてこのお題で執筆しようと思ったのかについて語っておくと、それはNext.jsでCore Web VitalsをGoogle Analyticsに送信する記事をたまたま見つけたので、仮に送信先をAmazon CloudWatchに変更してみたらどうなるのだろうかと興味を持ったためです。

用語の説明

ここではこの記事のタイトルに登場する用語について簡単に説明します。

Next.jsとは

オープンソースのReactフロントエンド開発Webフレームワークで、主に以下の機能を持ち合わせています。

  • サーバーサイドレンダリング
  • ReactベースのWebアプリケーション用の静的Webサイトの生成

公式サイトはこちらで、執筆当時の最新はバージョン10.0.3です。

Core Web Vitalsとは

WebサイトやWebアプリケーションのパフォーマンスを計測するための仕組みで、Next.jsではバージョン9.4から導入されています。
Blog - Next.js 9.4 | Next.js

なおCore Web Vitalsについて詳細を書くと記事が長くなってしまうのでここでは書きませんが、FE Study #2というイベントでも話に上がっていましたので、気になる方は以下の資料を見てください。
メタ・パフォーマンスチューニング

Amazon CloudWatchとは

DevOpsエンジニア、開発者、SRE、および ITマネージャーのために構築されたモニタリング/オブザーバビリティサービスで、公式サイトはこちらです。

それでは早速チャレンジしてみましょう

今回はGetting Startedを参考にしながらアプリケーションを作成するところから始めます。

まずは適当な内容のページpages/index.jsを用意します。自己紹介を行うページでも何でも良いです。
次にCore Web Vitalsを使うコードSDKを組み込むためのコード例、そしてSDKドキュメントとにらめっこしながら、以下の内容をpages/_app.jsに書きます。

pages/_app.js
// import App from 'next/app'
var AWS = require('aws-sdk');

AWS.config.update({
  credentials: new AWS.Credentials(
    "AKIAIOSFODNN7EXAMPLE",
    "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  ),
  region: 'us-west-2'
});

// Create CloudWatch service object
var cw = new AWS.CloudWatch({apiVersion: '2010-08-01'});

export function reportWebVitals(metric) {
  const name = "mypage"
  var params = {
    MetricData: [
      {
        MetricName: 'PageAnalytics',
        Dimensions: [
          {
            Name: 'NextJSAPP',
            Value: 'Initial'
          },
        ],
        Unit: 'None',
        Value: 0.0
      },
    ],
    Namespace: 'Page/Analytics'
  };
  console.log("=======CW Before=======");
  console.log(params);

  switch (metric.name) {
    case 'FCP':
      console.log("=======FCP=======");
      console.log(metric);
      params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name;
      params["MetricData"][0]["Dimensions"]["Value"] = name;
      params["MetricData"][0]["Value"] = metric["value"];
      break
    case 'LCP':
      console.log("=======LCP=======");
      console.log(metric);
      params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name;
      params["MetricData"][0]["Dimensions"]["Value"] = name;
      params["MetricData"][0]["Value"] = metric["value"];
      break
    case 'CLS':
      console.log("=======CLS=======");
      console.log(metric);
      params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name;
      params["MetricData"][0]["Dimensions"]["Value"] = name;
      params["MetricData"][0]["Value"] = metric["value"];
      break
    case 'FID':
      console.log("=======FID=======");
      console.log(metric);
      params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name;
      params["MetricData"][0]["Dimensions"]["Value"] = name;
      params["MetricData"][0]["Value"] = metric["value"];
      break
    case 'TTFB':
      console.log("=======TTFB=======");
      console.log(metric);
      params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name;
      params["MetricData"][0]["Dimensions"]["Value"] = name;
      params["MetricData"][0]["Value"] = metric["value"];
      break
    default:
      break
  }

  console.log("=======CW After=======");
  console.log(params);
  cw.putMetricData(params, function(err, data) {
    if (err) {
      console.log("Error", err);
    } else {
      console.log("Success", JSON.stringify(data));
    }
  });
}

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

最終的なディレクトリは以下のようになります。

.
├── pages
│   ├── _app.js
│   └── index.js
├── .gitignore
├── package.json
├── README.md
└── yarn.lock

準備が整ったので実際に動かしてみましょう。

$ yarn add react react-dom next aws-sdk
$ yarn build
$ yarn start

実際に動かしてみましたが、Webブラウザに搭載されている開発者ツールにあるコンソールにも
image.png

Amazon CloudWatchにも記録されていました。
image03.png

なお、今回は実装を簡単にするため、AWS認証情報をソースコード内に直接記述する方式にしていますが、GitHubのパブリックリポジトリに上げてしまうと攻撃されるのであまり望ましくはありません。あらかじめ.gitignoreファイルに記載した.envファイルを使用するなどしてよりセキュアにしましょう。
また、認証情報を設定する際には、AWS.config.loadFromPath('./credentials.json');を記述する方法もありますが、こちらのマニュアルにも記載されている通り、対応していないケースがあるのでご注意ください。
さらに、こちらの記事にも記載されている通り、ソースコード内に認証情報を直接記述する方式には非推奨のものが存在しています。ご注意ください。

おわりに

今回はNext.jsでCore Web VitalsをAmazon CloudWatchに送信してみた話を書きました。

パフォーマンスメトリクスを追っていくことはサービスやシステムの運用を行う上で欠かせないことの一つなので、この記事が参考になれば幸いです。

また送信したメトリクスをもとにして、Slack通知など何らかのアクションを行うLambda関数を発火させるのもきっと面白いかもしれません。

そしてこの記事を執筆していたときに似たようなことをやっている方を見つけました。ただこちらはサーバレスでしたね。
https://github.com/serverless-nextjs/serverless-next.js/issues/762

ここまで読んでいただきありがとうございました。明日は@D_Wさんの記事です。GitHub ActionsでStoryBookをデプロイする方法について執筆してくれるそうです。楽しみにお待ちください!

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2