医者インタビューサイトを Next.js と microCMS でリプレイスした話

弊社が運営している「[Doctorview](https://doctorview.byoinnavi.jp)」というサイトがあります。こちらのサイトは元々、非エンジニアの方がHTMLを作成し、それをS3に置いて公開していました。今年は、このDoctorviewのコンテンツをより充実させていくという事になり、現状の運用ではカバーしきれないので、CMS化するという話になりました。

本記事では、このCMS化にあたって、技術選定からリリース後の振り返りまでを記載します。

# 1. 技術選定

CMS化にあたって以下の要望がありました。
* CMS化するが、サイトのレイアウトは基本的に変更なし
* ただ、今後、追加改修は入るので、可能な限りレイアウトは自由に設定したい
* SEOは重要である
* 記事公開前にプレビュー機能が欲しい(公開前にお医者様に確認してもらうため)
* 今後、記事作成は社外の人に頼む可能性もあるが、公開は弊社でのみ実施する

端的にいうと「CMS化したいが、サイトのレイアウトは自由に設定したい」というものでしたので、Headless CMS と Next.js による Jamstack なサイトを作成する事にしました。

以下、選んだ技術です。

### microCMS
`Headless CMS` として `microCMS` を選びました。一番の決め手は日本の会社が運営しており、サービスが日本語表示になっていることです。CMSを使うのは非エンジニアということもあり、日本語は大きな利点でした。また、権限周りもある程度、自由に設定でき、プレビュー機能も実装できるため、求められていた要件を満たしていました。

### Next.js と Vercel
`Next.js` はSEO(CWV)に強いと聞いていたので、採用致しました。その時に調べた内容は zenn の方に書いております。
https://zenn.dev/fukurose/articles/e15df7129cc421

プラットフォームとしては `AWS Amplify` と `Vercel` で悩みましたが、`Vercel` が推奨するワークフロー `DPS(develop preview ship)` がスピード感を持って開発できると思い `Vercel` に決めました。

### TypeScript
これは導入するか最後まで悩みました。このCMS化対応のエンジニアは私一人だけで、私自身も `TypeScript` の実務経験が無かったため、導入して、開発スピードが落ち、納期に間に合わないのは避けたいです。最終的には「導入するのであれば最初から」と自分に言い聞かせて導入に踏み切りました。

# 2. 自動テストと自動デプロイ
選んだ技術で問題ないかの確認のために、簡単なモックを作成し、`microCMS` の使用感も含めて社内確認していた時に、自動テストと自動デプロイの準備をしていました。とはいっても、自動デプロイは `Vercel` が自動で実施してくれるので自動テストを動かせればOKです。
自動テストは e2e として `cypress`、unit テストとして `Jest` を `GitHub Actions` で動作するように設定致しました。 e2e の方は、`Vercel` のプレビュー環境デプロイ完了通知をトリガーとして、そのプレビュー環境で実行するようにしています。

また、開発ワークフローは `GitHub Flow` を採用しました。 `Vercel` で運用する場合、このフローが一番マッチすると考えたからです。ただ、`GitHub Flow` では main と feature ブランチだけですが、弊社では demo 環境も必要でした。`Vercel` は環境とブランチが1対1の関係となっているため、demo環境用のブランチも追加しています。基本的な流れは `GitHub Flow` と同じで、main ブランチから feature ブランチを切って、実装を開始する。実装を push した時には、`Vercel` がプレビュー環境を自動構築する。プレビュー環境で動作OKならPRをマージすることで、本番環境へ自動反映となります。また、feature が main にマージされ次第、`Github Actions` で同じ内容を demo にもマージするように致しました。これにより、常に本番環境と同じ実装となる demo 環境を用意しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265134/9f0c4403-ac8c-a3d0-5955-25f8a5f37f49.png)


# 3. 実際に実装してみて
実装してみて最初に思ったのは `TypeScript` は導入して良かったでした。私は性格的に「しっかりと計画を立てて実装よりも、まずは実装してみて、気になれば都度変更する」タイプなのですが、この変更が発生する時に、 `TypeScript` が必要な変更箇所を教えてくれるので、変更への心理的不安がかなり減ったと思います。優秀なエンジニアとペアプログラミングをしているような感じでした。「そこを変更したらな、ここも変更しないとね」みたいな。

 また、 `Vercel` のプレビュー環境もとても役に立ちました。`Vercel` は push 毎にプレビュー環境を作成してくれるので、A案とB案どちらが良いか社内確認を取る時にも、簡単に両案のプレビュー環境を用意できます。今回で言えば、CMS化前の `Doctorview` では webフォント を 3つ読み込んでいました。ここはパフォーマンス改善のためにも、どれか1つに出来ないかと検討する際、フォントなしと各webフォントを1つだけ読み込んだプレビュー環境を作成し、見た目とパフォーマンスを調べて調査致しました。
結果としては、フォントなしになりましたが、、それも各プレビュー環境での比較があってこそだと思います。

`Next.js` も事前に調べていた通り、フロントエンド側のベストプラクティスが詰まったフレームワークで選択して良かったと思います。画像の最適化、(今回は使わなくなりましたが)webフォントの最適化、外部JSの読み込みの最適化などをフレームワークで用意してくれています。ES2020で導入された `dynamic import` が使えるのも良かったです。`Doctorview` では「他のドクターをお探しですか？」というレコメンド機能があるのですが、こちらは初期表示時には不要なので、 `dynamic import` を使って表示することにより、初期ロードの負荷を軽減しています。その他にも無駄なCSSを削除したりなど、パフォーマンス改善には力を入れて対応致しました。その結果もあり、light house のスコアを大幅に改善出来ました!!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265134/f07d46b5-30c7-5bf0-5c29-76aeb996901e.png)

逆に上手く出来なかったのは、CSSの箇所です。`Next.js` では全ページ共通のCSSはグローバルCSSとして読み込み、その他はコンポーネントレベルでCSSを適用するように推奨しています(Next.jsに限った話ではないですが)。ただ、弊社ではCSSを触るのがデザイナーで、実装に詳しくないデザイナーの方がコンポーネント単位でCSSを設定するのは、難しいと判断しました。特に、変更したいCSSが、どのコンポーネントなのかを判断するのが難しいと考えています。

そのため、コンポーネント単位のCSSはやめて、ページ単位のCSSにしました。使っているのは全ページのCSSとページ別のCSSだけになるので、修正箇所は少なくなります。
* 全ページのCSS: `src/styles` ディレクトリにあるcssファイル
* ページ別のCSS: `src/pages/` 配下の index ファイルにある `<style jsx global>` 内
```
<style jsx global>{`
  /* ここにCSSを追記、修正してもらう */
`}</style>
```
これで、変更するCSSの場所は分かりやすくなりました。ただ、やはり、サイト全体に統一感を持たせるためにも、コンポーネント単位でのCSSを設定したいので、ここは今後、改善出来ればと考えています。個人的には、class名とかを考えるのも手間なので、 `CSS Modules` を使いたいというのもあります。

# 4. 全体を通して振り返り

**最初に自動テスト/自動デプロイを作成していたことが、良かったかなと思います。** ここが上手く動作していると、開発サイクルが速く回るので、開発が楽しくなります。実装フェーズでは `TypeScript` にチェックしてもらい、実装出来れば push して、自動テストを走らせる。オールグリーンなら、main にマージして本番反映という流れをスムーズに行えるので、**より実装に集中することが出来ました。**

**`Vercel` はプレビュー環境作成、自動デプロイという点で素晴らしかったです。** ただ、`AWS Amplify` でも同じような事はできるはずなので、(`Vercel` に依存しすぎているのも良くないので)今後のため調べておこうと思います。また、`GCP` でも同様な事は出来るはずなので、こちらも合わせて調べたいと思います。

全体を通して、今回選んだ技術は上手くマッチしていたと思います。しかし、これが常に正解という訳ではないので、**サービスを開始するときは、マッチする技術をしっかりと見極めていきたいです。** また、Doctorview が今後、大きくなっていくと、合わなくなってくる技術も出てくるかと思います。その時は、**今の技術に縛られずに、自由に技術選定していきたいです。** その時は、また記事に出来ればと思います。