はじめに
こんにちは、平川です。普段はサーバーサイドエンジニアとして業務に携わっております。
フロント側は基本的に手をつけない領域ですが、業務でNext.jsを触る可能性が出てきたので、実際に手を動かして知見を得ていこうと思います。
Next.jsとは
Next.jsはReactベースのフロントエンドフレームワークです。
できることは色々あるのですが、大きな特徴としては、
- SSR/SSG
- Zero Config
- File-system Routing
- Fast Refresh
- Image Optimization
などが挙げられます(他にも色々あります)
今回触ってみるもの
個人的に気になった機能がいくつかあるので今回はそれらを実際にコードベースで触ってみて挙動などを理解していきます。
- Zero Config
- configファイルがなくても自動コンパイルとバンドルが可能
- SSR/SSG
- あらかじめレンダリングして表示
- API Routes
- APIバックエンド機能を提供
実際に触ってみる
create next app
まずは開発する環境を作成します。
下記のコマンドを任意のコマンドラインで実行します。今回はTypeScriptに対応したいのでオプションに--ts
をつけます
npx create-next-app@latest --ts
コマンドを実行するとアプリ名を尋ねられるので任意の名前をつけます。その後ディレクトリが作成されます。
どのWebフロントエンドフレームワークもそうですが、この時点でWebアプリとして実行が可能になります。
// アプリケーションをビルド npm run build // localhost:3000で起動 npm run start
起動するとlocalhost:3000でWebアプリが立ち上がっていることが確認できます。
Zero Config
文字通りwebpack等の設定ファイルを記述しなくても動かすことができるというものです。
webpack(複数のjsファイルやcssファイル等を1つのファイルにまとめるツール)を扱う際にはwebpack.config.jsに設定内容を記述する必要があるのですが、初学者には少し学習コストが高かったり、設定方法が難しかったりします。
そういった点において複雑な設定をはじめから請け負ってくれることは嬉しいですね。
ただ、業務で扱う上で何かしらの設定を行いたいケースが出てくる場合もあると思います。
その際はnext.config.js
に内容を記述することで自動で読み込み、設定してくれます。
今回はwebpackではありませんが、起動環境毎に環境変数を設定してページ上に表示してみます。
next.config.jsでの環境変数の設定については下記リンクの記事を参考にしました。
page/index.html
とnext.config.js
を下記のように追記していきます。
<!-- index.html --> ... <main className={styles.main}> <h1 className={styles.title}> Welcome to <a href="https://nextjs.org">Next.js!</a> </h1> <!-- 追記 --> <h2>{process.env.TEST_ENV}</h2> <!-- 追記終了 -->
/* next.config.js */ const { PHASE_PRODUCTION_BUILD } = require( "next/constants" ) ; const { PHASE_PRODUCTION_SERVER } = require( "next/constants" ) ; const { PHASE_DEVELOPMENT_SERVER} = require( "next/constants" ) ; const { PHASE_EXPORT } = require( "next/constants" ) ; /** @type {import('next').NextConfig} */ module.exports = ( phase, { defaultConfig }) => { if( phase === PHASE_DEVELOPMENT_SERVER ){ return { env: { TEST_ENV : "develop_env" } } } else if( phase === PHASE_PRODUCTION_SERVER ) { return { env: { TEST_ENV : "production_env" } } }else if( phase === PHASE_EXPORT ) { return { env: { TEST_ENV : "export" } } }else if( phase === PHASE_PRODUCTION_BUILD ) { return { env: { TEST_ENV : "production_build" } } } }
設定できたかどうかを実行してlocalhost:3000
で確認してみます。
npm run dev
を実行してみます。
PHASE_DEVELOPMENT_SERVER
の時のENVが参照されていることが分かります。
次にnpm run start
を実行してみます。
PHASE_PRODUCTION_BUILD
の時のENVが参照されていることが分かります。
このようにnext.config.js
を利用することで詳細な設定を行うこともできます。
SSR/SSG
そもそもSSRとは何なのかという話になりますが、SSRとはServer-side Renderingの略称です。
従来だとクライアントサイドで画面を描画していたものをサーバー側で行う技術のことです。
メリットとして、初回アクセス時の描画が早い点やSEOに強い点などが挙げられます。
また、SSG(Static Site Generation)という技術もあり、Next.jsの公式サイトではこちらが推奨されています。
理由としてはSSRはリクエスト毎にサーバー側でHTMLが生成されるのに対し、SSGはビルド時にHTMLを生成し、リクエストの際にそれを再利用するため、パフォーマンスがいいという点が挙げられます。
実装してみる
さて、実際にSSRとSSGの実装やパフォーマンスの違いを確認していきたいと思います。
郵便番号検索APIを使って外部データを取得し、郵便番号、都道府県、市区町村を画面に表示するページをSSR、SSGの両方で実装していきます。
実装の簡略化のため、使用するAPIのパラメータ(郵便番号)は固定で取得するようにします。
/page
ディレクトリ以下に新たにSSR.tsx
を作成し、下記のコードを追加します。
function SSR(api: any) { return ( <div> <div>郵便番号: {api.data.zipcode}</div> <div>都道府県: {api.data.address1}</div> <div>市区町村: {api.data.address2}</div> </div> ) } export async function getServerSideProps() { // 郵便番号検索APIを叩く const res = await fetch(`https://zipcloud.ibsnet.co.jp/api/search?zipcode=1000000`) const apiData = await res.json() // データパラメーター(results[])の受け取り const data = apiData.results[0] // 要素1つの配列のためインデックス0で受け取り return { props: { data } } } export default SSR
ここでキモになるのが、getServerSideProps()
です。
この関数を置いてあげることで、Next.js側はサーバーサイドレンダリングを行ってくれます。
実際に動かしてみると単に取得したデータを表示するページができました。
同様にSSGで実装してみます。/page
ディレクトリ以下にSSG.tsx
を作成します。
// function部分はメソッド名のみSSGに変更、内容は同じ ... // 以下を変更 export async function getStaticProps() { const res = await fetch(`https://zipcloud.ibsnet.co.jp/api/search?zipcode=1000000`); const apiData = await res.json(); const data = apiData.results[0] return { props: {data} }; } export default SSG
実行結果はSSRと変わらないので省略しますが、getStaticProps()
関数を置くことで、Next.js側はSSGと認識して動作してくれます。
パフォーマンスの違い
先の文でも記述していますが、SSRとSSGの違いはリクエスト毎にHTMLを生成しているのかどうかです。
Chromeのシークレットモードで検証した結果が以下のとおりです。
SSRでの結果
SSGでの結果
どちらも同じ表示なので見えづらいですが、SSRの表示読み込みが1.91s
に対し、SSGが186ms
と圧倒的な速さを見せております。
再リロードして検証してみてもSSRは856ms
、SSGは50ms
となっておりました。
API Routes
Next.jsはフロントエンド向けのフレームワークですが、APIの構築も可能となっております。
とはいっても標準で同一オリジンからしかAPIを受け付けていません。CORSのミドルウェアをラップしてあげれば動くみたいです。
/page/api
ディレクトリ以下にファイルを配置してあげるとpageの代わりにAPIのエンドポイントとして認識してくれます。
create next app
した際に既に/page/api/Hello.tsx
が存在しており、/api/hello
でJSONが返ってくるようになっています。
サンプルを参考にして、メソッド毎にレスポンスを変える実装をしてみます。
/page/api
ディレクトリ以下にcomment.tsx
を作成し、下記の内容を追加します。
// comment.tsx import type { NextApiRequest, NextApiResponse } from 'next' type Data = { comment: string } export default function handler( req: NextApiRequest, res: NextApiResponse<Data> ) { if (req.method === 'POST') { res.status(200).json(req.body) } else { res.status(200).json({comment: "comment"}) } }
コードを見て分かるようにPOSTメソッドの際はリクエストの内容を単純にオウム返しするだけとなっており、それ以外は固定値を返すようにしています。
Postmanを使ってAPIを叩いてみます。
POSTメソッドの結果
ちゃんとbodyの中身がJSONの形式で返ってきていることが分かります。
POST以外の結果
hello.tsx
の内容を参考にしているので当たり前ですが、固定のレスポンスが返ってきているのが分かります。
利用シーンとしては、フロントエンド開発時のモックAPIとしての活用ができそうな気がしました。
まとめ
- Next.jsはReactベースのフロントエンドフレームワークだよ。
- Zero Configで設定を意識せずに動かすことができるよ。
- SSR/SSGでHTMLをサーバー側で描画できるようになるよ。Next.jsはパフォーマンスの向上を理由としてSSGを推奨しているよ。
- Next.jsをAPIサーバーとしても利用することができるよ。ただし、標準では同一オリジンしか叩けないよ。
久々にフロントエンド系のフレームワークに触れてみましたが、機能や考え方などが変わってきていることを身を持って実感しました。
Next.jsとしての機能はほんの一部をかじった程度に過ぎないので、これからも継続して勉強を行い、ブログのネタにできたらと思います。
最後に
弊社では絶賛エンジニア募集中です。BtoCのサービス開発をしてみたい方や、植物に興味のある方は是非応募してください。 カジュアルに話だけでも聞きたいという方もお待ちしてます。 www.wantedly.com