カテゴリー: FrontEnd

Next.js 入門してみた

はじめに

Next.jsに入門して1週間で学んだことを記事にまとめます。

なぜNext.jsなのか?

今年は自分でも何か作ってみたいと思っているのですが、エンジニアとしてのテーマとして、経験したことのないフレームワークやプログラミング言語を取り入れたいと考えています。
とはいえ、領域から大きく外れすぎるとなかなか完成しないと思ったので、開発するものはWebアプリケーションとして、その中で新しい学びのある技術を選定する中で、フロントエンドにNext.jsを使いたいと思いました。
Reactでも良い気はしましたが、開発するとしたら、to C向けで、SNSの要素のあるサービスを開発したいと考えていて、その場合はSEOに強かったり、外部でシェアされる際にOGPが効くのもNext.js強みだと思ったため、とりあえずNext.jsを学んでみることにしました。

Next.jsとは何か

Next.jsは、ReactベースのWebフレームワークです。Reactを単体利用する場合との最大の違いは、ページをコンポーネントをSSRできるかどうかという点になります。
例えば、create-react-appで生成されたアプリケーションをビルドすると、生成されるHTMLはindex.htmlのみで、実際に表示されるDOMはすべてJavaScriptによって描画されます(SPA – Single Page APplication)。

かつてはRailsで構築されたWebアプリケーションのように、テンプレートエンジンがHTMLを生成するタイプのMPA(Multiple Page Application)が全盛の時代もありましたが、画面遷移するごとにページそのものが遷移するため、もっさり感がありました。
そこにSPAが登場することで、いちいちページ遷移することの煩わしさが解消されるようになりましたが、SPAはJavaScriptによってページが描画されるため、HTMLを静的解析するタイプのクローラに情報を提供することが難しいという問題がありました。

GoogleはSPAのページも読み取ってくれるようですが、TwitterやFacebookなどでOGPを設定するのは難しいようで、パブリックアクセスが必要となるニュースサイトやECサイトなどでは、導入が難しかったのではないかと思います。
そこで、Next.jsの強みは、ページが読み込まれたタイミングで、初期レンダリングされたHTMLをブラウザ側に返すことができる点です。これについて、Next.jsではいくつかのアプローチがあり、その概要を説明します。

Static Site Generation (SSG)

Next.jsのページは、デフォルトでビルド時にPre-renderされ、静的なHTMLファイルとして出力されます。レンダリングされる内容は、各ページのReactコンポーネントの初回レンダリングの内容になります。そのため、stateによって変化する内容については、含まれません。
これが一番シンプルなSSGなのですが、実際のコンテンツはAPIで取得するケースがほとんどではないかと思います。その場合は、ビルド時にAPIリクエストし、その内容をページのPropsの初期値として設定することができる getStaticPropsを使用することで、外部リソースを含めてPre-renderすることができます。

さらに、Vercelが提供している useSWRというhookを組み合わせて、アクセス時にクライアント側で最新データをフェッチすることで、ビルド後も常に最新の状態を表示することができます。

Incremental Static Regeneration (ISR)

SSGによってPre-renderされるのは、Componentに埋め込んだ内容か、ビルド時にフェッチしたデータです。ビルド時より後に追加されたデータはPre-renderには含まれないため、SEOやOGPで不利であることは、CSRとほとんど変わりません。
そこで、ブラウザによりページアクセスがあった際に一旦は古いHTMLを返しつつ、前回のHTMLの生成から一定時間が経過している場合は、最新のデータを取得しなおした上でHTMLファイルを再生成することができるのが、ISRです。
これにより、最新のデータを含めるために再ビルドし直すことなく、SSGによるパフォーマンスやSEO・OGPなどの恩恵を受けることができます。ただし、ISRを使用している場合は、トリガーとなったページへのレスポンスや、HTML生成中のアクセスでは古いHTMLがレスポンスされるため、タイミングによっては整合性が取れなくなります。

Server Side Rendering (SSR)

ISRではタイミングによっては整合性が取れない場合がありますが、リクエスト毎に最新データを取得しつつ、サーバサイドでレンダリングすることでSEO・OGPに対応するには、どのようにすれば良いでしょうか。

そのような場合は、 getServerSidePropsを使用します。この関数はページがリクエストされる毎にサーバサイドで実行され、この関数で返却されたPropsが渡された状態のコンポーネントがSSRされ、クライアントにレスポンスします。
あとは、SSGの時と同じように渡されたPropsをそのまま使ってコンポーネントを生成すれば、静的なHTMLとしてレスポンスされるため、SEO・OGPに対応することができます。

実際にNext.jsをセットアップ

Next.jsを特徴が分かってきたところで、実際にNext.jsを使ったアプリケーションを作ってみたいと思います。

create-next-app

create-next-appを使用すると、次のように対話形式でセットアップすることができます。

% yarn create next-app . 
✔ Would you like to use TypeScript with this project? … Yes
✔ Would you like to use ESLint with this project? … Yes
✔ Would you like to use `src/` directory with this project? … No
✔ Would you like to use experimental `app/` directory with this project? … No

後半2つの質問はNext.js特有のものになります。

Would you like to use `src/` directory with this project?

ここまで説明していなかったのですが、Next.jsではpagesディレクトリ配下の構成がそのままRoutingのパスとして解釈されます。デフォルトでは、pagesディレクトリはプロジェクトのルートに存在する必要があるのですが、これを src/pagesに変更できるオプションです。
デフォルトではNoになっているため、私も今回はNoを選択しましたが、もしかしたらソースコードはsrcディレクトリの中にまとめてしまった方が、ルートがスッキリしてわかりやすくなるかもしれません。

Would you like to use experimental `app/` directory with this project?

Next.js 13で追加されたapp directoryを使用するかどうかです。現時点ではベータ版の機能です。app directoryを使用しない、従来のNext.jsでは、コンポーネントの初回レンダリングの結果をHTMLとしてレスポンスし、その後はクライアント側でCSRが行われるという流れが1つのコンポーネントで完結できていました。
app directoryを使用した場合は、コンポーネントをServer SideとClient Sideに明確に分けるようになります。デフォルトではServer Sideで、Client Sideにする場合はファイルの先頭に use clientとする必要があります。

Server Sideコンポーネントの場合は、これまで通りサーバサイドでレンダリングされ、これまで通りデータフェッチは可能ですが、stateやイベントハンドリングといったインタラクティブなことはできません。そのような部分はClient Sideとして実装する必要があるのですが、Client Sideコンポーネントはサーバ側ではレンダリングできません。

このような明確な分け方をすることにより、SSRした時のパフォーマンスが向上するとのことですが、実装時にはServer SideとClient Sideを上手く使い分ける必要がありそうです。
Next.jsのドキュメントに使い分けの例があるので、こちらを参考にした方が良さそうです。
https://beta.nextjs.org/docs/rendering/server-and-client-components

今回は、app directoryを使用しないことにしました。その理由は、MUIが未対応であるからです。
https://github.com/mui/material-ui/issues/34896

MUIほどの人気パッケージでさえ未対応なので、app directoryは正式版になるまで待っても遅くはないかなと思い、今回は使用しないことにしました。

nextの起動

yarn devでnextアプリケーションを起動することができます。その他のコマンドは、以下がpacakge.jsonに定義されていました。

  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  }

ちなみに、 devで立ち上げた場合は、ページにアクセスされる毎に getStaticPropsが呼び出されますが、 yarn buildでプロダクション用のビルドをすると、ビルド時に取得したHTMLが使用されるようになります。

さいごに

次回も引き続き学んだ内容を記事にしていきたいと思います!

おすすめ書籍

カイザー

シェア
執筆者:
カイザー

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前