そうだ、ライブラリをアップデートしよう。~ Gatsby v2からv5に上げてみた

[Ateam Group U\-30 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ateam-group-u30)の4日目は株式会社エイチームフィナジーの @Adacchi3 が担当します。

# はじめに

皆さんはライブラリのバージョンをアップデートしてますでしょうか？
私が所属しているチームではRenovateという依存ライブラリの自動更新ツールを運用しており、定期的にライブラリをアップデートしております。

2022年11月下旬にGatsby v5が公開され、私の所属チームでもGatsby v4からv5へのバージョンアップしたレポジトリがありました。私はここで2年前にプライベートで構築したWebサイトのGatsbyがまだv2であることを思い出しました。

今回は2年間ほどRenovateを運用・ライブラリアップデートを確認していた知見を、実際にGatsby v2からv5をしながら紹介できたらと思います。

# ライブラリのアップデートって何が嬉しいの？

ライブラリのアップデートで嬉しいことは以下の通りだと思っております。

* セキュリティアップデート・脆弱性対策
* 新しく実装された機能を利用できる
* バグフィックス

ライブラリにて脆弱性が報告され、セキュリティアップデートが行われた際にはぜひアップデートしたいところです。
新しい機能などを享受できるのも良い点ですね。Gatsby v5ですと、Slices APIやPartial Hydration(Beta)が導入されているようです。
バグフィックスは期待どおりの動作をしていなかった箇所が修正されている場合がありますので、リリースノートやIssueをチェックしておくと良いです。

# ライブラリアップデート確認手順

Renovateで作られるブランチや`yarn upgrade`等を実施し、ライブラリの差分を確認できる状態にしてから、実際に行っているライブラリアップデートの確認手順は以下の通りです。

1. メジャーバージョンアップなのか、マイナーなのか、パッチなのかを確認する
1. ChangelogまたはReleasesで更新された内容を確認する
1. 実際にローカルでライブラリアップデートして動作検証する

## メジャーバージョンアップなのか、マイナーなのか、パッチなのかを確認する

ライブラリはSemantic Versioningで管理されていることが多いです[^1]。更新するライブラリがいずれのバージョンアップなのかを確認することで、システムの影響度合いを大まかに把握します。

メジャーバージョンアップの場合は、破壊的変更などの多くの変更が含まれている可能性があるので、念入りに確認する必要があること。マイナーバージョンアップの場合は新しく追加された機能、それ付随して更新が入った内容を確認、パッチバージョンアップの場合は基本バグフィックスなのでどんなバグがあったのかなと確認する、といった確認計画を立てます。

## ChangelogまたはReleasesで更新された内容を確認する

NPMライブラリはGithubで管理されていることが多く、直接そのレポジトリにアクセスし、ChangelogやReleasesに更新内容が記載されています。

例えば、Gatsby v5.2.0であれば下記のページを確認します。

* https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/CHANGELOG.md#520-2022-11-25
* https://github.com/gatsbyjs/gatsby/releases/tag/gatsby%405.2.0

更新内容を確認し、システム上で使われているところがあったかなぁと考えます。また、新しい機能が追加されていた場合、その機能を自分たちのシステムで使うことで嬉しいことがあるかなと思いを馳せます。

## 実際にローカルでライブラリアップデートして動作検証する

更新内容を確認した後、実際にローカルにて本番にリリースしてよいものなのかを動作検証します。

業務上ここは止まってはいけないといった箇所はE2Eテストやコンポーネントテストなどを作成し、CIで確認すると効率よく動作検証することができます。テストの恩恵はとてもありがたく、ライブラリやプログラミング言語のメジャーバージョンアップを実施する際、どれだけテストが網羅されているかによって動作検証の効率化が図れます。

人手で確認する箇所としては、バージョンアップするライブラリが使われているところが主になります。期待どおりの振る舞いがされるかを確認します。まれに期待どおりに動作しないときがあったりするので、欠かさず実施していきましょう。

これらの手順を通して、問題なければ本番公開のリリースフローに載せていきます。

# Gatsby v2 → v5にアップデートしてみる

では、実際にGatsby v2からv5にアップデートを通して、確認していきましょう。

プライベートでGatsbyを用いて構築したWebサイトは約2年前でメンテナンスが止まっており、2つ対応したいことがあります。
1つ目は、url-loaderがpublic archivesになっている件です。今後メンテナンスされないため、他の代替を使っていく必要があります。こちらはGatsbyのバージョンアップで解消されます。
2つ目は、Google Analytics v4(GA4)への移行です。ユニーバサルアナリティクス(UA)が廃止になるため、`gatsby-plugin-google-analytics`で使っていたUAを`gatsby-plugin-gtag`を使ってGA4にしていきます。

## Gatsby v3, v4のRelease Notesを確認

Gatsbyはメジャーバージョンアップのマイグレーションの仕方を公開してますので、そちらでBreakingChangesや対応しなければいけない箇所を確認していきます[^v2v3][^v3v4]。幸い、該当しそうな箇所がなかったです。

## Gatsby v5のRelease Notesを確認

BreakingChangesや対応箇所を確認していきました[^v4v5]。

* Node v18以上
* React v18以上
* GraphQL Queryのソート書き方変更

上記3つを対応する必要がありそうです。

## Nodeのバージョンアップ

Gatsby v5でサポートしているNodeのバージョンが18系以上になりますので、Nodeのバージョンをv13.6からv18.12.1にアップデートしました。

過去の私はなぜ13系を選んでいたかはわかりませんが、Nodeは偶数番がstable(安定版)で奇数番がlatest(最新版)になっていますので、基本的には安定版を選んでおけばOKです。新しい機能を早く試してみたい！方は最新版を選ぶことがあるかもしれないです。

## ライブラリアップデート実施

今回はコマンドライン上からアップデートを実施していきました。

```sh
$ yarn upgrade-interactive --latest
```

上記のコマンドを実行し、gatsby, gatsbyのプラグイン、react周りを指定します。

また、Release Noteにあった通り、GraphQL Queryを変換してくれるコマンドを実行しました。

```sh
$ npx gatsby-codemods@latest sort-and-aggr-graphql .
```

ちゃんとGraphQLクエリの書き方が変わっていることを確認します。

## コンソール上のwarningを確認

続いて、実際に動作検証中にシステム上で指摘された箇所を修正していきます。

### gatsby-plugin-purgecss

```sh
warn Plugin gatsby-plugin-purgecss is not compatible with your gatsby version 5.2.0 - It requires gatsby@^2.0.0 || ^3.0.0 || ^4.0.0
```

こちらは該当レポジトリのIssueに対応予定であることが記載されていたため、そのままにしておきます[^2]。

### gatsby-plugin-react-helmet

```sh
warn gatsby-plugin-react-helmet: Gatsby now has built-in support for modifying the document head. Learn more at https://gatsby.dev/gatsby-head
```

Gatsby Head APIが4.19.0から導入されたとのことなので、`gatsby-plugin-react-helmet`を外します。

```sh
$ yarn remove react-helmet gatsby-plugin-react-helmet
```

react-helmetを使って作成していたメタタグ周りを修正し、gatsby-config.jsに記載されているヘルメットのプラグインの記述を削除しました。

```diff_tsx
import React from 'react'
- import { Helmet } from 'react-helmet'
import { FETCH_SITE } from '../../queries/site'

interface MetadataProps {
  title: string | undefined
  description: string | undefined
}

const Metadata: React.FC<MetadataProps> = ({
  title,
  description,
}) => {
  const site = FETCH_SITE()

  return (
-     <Helmet
-       title={title}
-       titleTemplate={`%s | ${site.siteMetadata.title}`}
-       meta={[
-         {
-           name: 'description',
-           content:
-             description || site.siteMetadata.description,
-         },
-        （中略）
-      ]}
-    />
+    <>
+      <title>
+        {title + ' | ' + site.siteMetadata.title}
+      </title>
+      <meta
+        name="description"
+        content={
+          description || site.siteMetadata.description
+        }
+      />
+      (中略)
+    </>
  )
}

export default Metadata
```

`src/pages`にファイルがあるものは以下のような修正をしました。

```ts
import { HeadFC } from 'gatsby'

export const Head: HeadFC = () => (
  <Metadata title={'Top'} description={undefined} />
)
```

createPagesで作成されるページはTemplateにて修正しました。

```ts
export const Head: HeadFC<PostTemplateProps> = ({
  data,
  pageContext,
}) => {
  const post = data.markdownRemark

  return (
    <Metadata
      title={post.frontmatter.title}
      description={post.excerpt}
    />
  )
}
```

### gatsby-transformer-sharp

```sh
warn [gatsby-transformer-sharp] The "fixed" and "fluid" resolvers are now deprecated. Switch to "gatsby-plugin-image" for better performance and a simpler API. See https://gatsby.dev/migrate-images to learn how.
```

Release Noteを読みながら対応していきます。まずはライブラリのインストールをしていきます。

```sh
$ npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp
```

続いて、gatsby-configにgatsby-plugin-imageを追加し、gatsby-imageを使っていた箇所をgatsby-plugin-imageの記述に書き換えてくれるコマンドを実行します。

```
$ npx gatsby-codemods gatsby-plugin-image
```

最後に下記のような箇所があったため、StaticImageを使うよう修正しました。

> For images using static query, you should move to use the StaticImage component instead. 


```diff_tsx
import React from 'react'
- import { useStaticQuery, graphql } from 'gatsby'
- import Img from 'gatsby-image'
+ import { StaticImage } from 'gatsby-plugin-image'

const ProfileIcon: React.FC = () => {
-  const data = useStaticQuery(graphql`
-    query FetchProfileIcon {
-      file(relativePath: { eq: "profile_icon.jpg" }) {
-        childImageSharp {
-          fixed(width: 92, height: 92, quality: 100) {
-            ...GatsbyImageSharpFixed
-          }
-        }
-      }
-    }
-  `)

  return (
    <div className="float-right mt-2 mr-8">
-      <Img
-        fixed={data.file.childImageSharp.fixed}
+      <StaticImage
+        src="./../../images/profile_icon.jpg"
        className="rounded-full border-2 border-gray-100 bg-blue-700 h-24 w-24 lg:h-40 xl:h-40 lg:w-40 xl:w-40"
+        alt="プロフィール画像"
      />
    </div>
  )
}
export default ProfileIcon
```

## 動作検証して気になるところを解消していく

Gatsby周りのプラグインの指摘を解消したところで、実際に動作確認をして気になった点の解消を行っていきます。

`gatsby-transformer-remark`にて`truncate: true`をつけることで、サイトの概要欄で使われている日本語が切れてしまうのを解消してくれる記述を見つけ、対応しました[^3]。

| before | after |
| --- | --- |
| ![スクリーンショット 2022-12-01 0.27.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206630/398e559c-b6bc-3e57-4623-cec3a3859fa4.png)　| ![スクリーンショット 2022-12-01 1.28.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206630/66670d08-8bf1-0026-a59c-63305ccb1518.png) |

## gatsby-plugin-google-analyticsからgatsby-plugin-gtagへの変更

最後に`gatsby-plugin-google-analytics`を外し、`gatsby-plugin-gtag`を導入しました。gatsby-config.jsには以下のような記述を追加しました。

```diff_tsx
+    {
+      resolve: 'gatsby-plugin-google-gtag',
+      options: {
+        trackingIds: ['G-xxxxxx'],
+        pluginConfig: {
+          head: true,
+        },
+      },
+    },
```

GA4で計測できているかどうかは本番リリースで確認します。

## 実際にリリースしてみた

動作検証を終え、本番リリースしてみました。
Webページの表示、GA4の計測もちゃんとできていることを確認しました。

Gatsby v2からv5にあげてみて、PageSpeed Insightsでのパフォーマンススコアを確認しました。
Total Blocking Timeの指標が改善しており、パフォーマンスが100点出ていますね :tada: 

| before | after
| --- | --- |
| ![スクリーンショット 2022-11-30 0.56.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206630/99ae7b6f-c69b-f893-0672-0a396e34eb7f.png) | ![スクリーンショット 2022-12-01 1.23.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206630/6ec74bd4-952f-a77a-005e-5e8d3a8292b2.png)　|

# まとめ

ライブラリアップデートの確認手順をまとめ、実際にGatsby v2からv5へのライブラリアップデートを実践してみました。
セキュリティ対応や新しい機能を試せることがあるので、皆さんもこの機会にライブラリアップデートチャレンジしてみてください。

明日は @zumi0 が担当します！　
ぜひ[Ateam Group U\-30 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ateam-group-u30)を定期購読して、明日の記事もご覧いただけたら幸いです。

[^1]: https://docs.npmjs.com/about-semantic-versioning
[^v2v3]: https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v2-to-v3/
[^v3v4]: https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v3-to-v4/
[^v4v5]: https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v4-to-v5/
[^2]: https://github.com/anantoghosh/gatsby-plugin-purgecss/issues/1018 2022年11月30日時点閲覧
[^3]: https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-remark#excerpts-for-non-latin-languages