Next.js : 本番運用における Tips of next.config.js
こんにちは。この記事は Next.js Advent Calendar 2019 1日目の記事です。Next.js のアドカレやりたかったので勢いで作って、直接お会いしたことがない方で、この人は Next.js 使ってそうだ!という方に Qiita アドカレから招待を送っていました。エントリーいただいた皆さまありがとうございます。
ご存知ない方のために Next.js とは?という話をしますが、フロントサーバでの SSR を完備した React Framework です。デフォルトで AMP 対応が出来たり、TypeScript での実践にフレンドリーだったり、スタティックなエクスポートだったりも可能です。
ZEIT CEO @rauchg が Chrome Dev Summit 2019 のキーノートに登壇したりしていました(JSConf.jp でもセッションがありましたね)。
Google Chrome team ほか @addyosmani, @developit がパフォーマンスやバンドル最適化について issue もしくは PR で応戦していたりするのでコミュニティが豊かになっていることもうかがえます。
本番運用における next.config.js
ここからが本題です。Next.js 自身は zero-config で動くのですが、本番運用もしくは開発を進めるにつれて、どうしても next.config.js を捏ねくり回す必要が出てきます。特に Next.js が持っている webpack ビルドのカスタムを考え出すと弄らざるを得ない気がします。
next.config.js の設定については GitHub README に書いている通りなので多く説明しませんが、おおかた次のようになります。
// もっとも一般的
module.exports = {
configProperty: "value",
};// プラグイン差し込みたくなった
module.exports = withFoo(withBar{
configProperty: "value",
}));// webpack デフォ設定を触りたくなった
module.exports = withFoo(withBar{
configProperty: "value",
webpack(config, { buildId, dev, isServer, webpack }){
// config.output など設定値の変更
},
}));
記述量が少ないうちはいいですが、いろいろ込み入った事情が入ってくると職人しか触らない・見ない領域になってしまいがちです。
今日は本番運用の中で出てきた、README だけでは解決できないnext.config.js Tips をいくつか紹介します。
x-powered-by ヘッダの削除
README に実際に載っていない、簡単なところから。ソース見たら Next.js を使っているなというのはわかるのですが、ヘッダに x-powered-by
入れたくないというのは需要があると思うんです。
README に書いていませんがこのようにすることでヘッダから削除できます。
クライアントコードビルドで無を返す
Next.js は基本的にシンプルな SSR を提供しているので Server, Client 間でのコード共通化に関しては意識的にならざるを得ません。利用用途や目的、手法にもよるかもしれませんが、ある程度戦略を固めておかないと、本番適用というシーンにおいて苦しい部分・複雑な部分が出てこざるを得ません。
getInitialProps
で isServer
が、ロジックやコンポーネントにまたがってprocess.browser
が、レイヤーを跨いで頻出してくるとかなり辛いものがあります。Server, Client 同一コードによる複雑性は JSConf.jp のセッションで @rauchgも話していた内容ではあります。
そういったケースの中で Sentry とのインテグレーションを例に取り上げて、“Server, Client 共通化するようでしない webpack のカスタム” をご紹介します。
ユニバーサルな Sentry とのインテグレーションを考えた場合、Next.js Example にあるような webpack config resolve.alias での解決が主になります。
config で全て解決できるので十分そうですが、私の本番運用ケースですと、ユニバーサルに共通化したくない、むしろクライアント側の Sentry は別ビルドでバンドルファイルを作成し、遅延読込・実行させて初回の UI スレッドへの影響を削りたいといった要件が出てきたので、それを満たせそうにありません。
そこでダミー用のクライアント Sentry モジュールを無に差し替えて無を返すようなことをします。
実はこれだけでは不十分で @sentry/node
,@sentry/browser
由来のレポーティングのメソッドを @sentry/minimal
由来のメソッドに変更する必要があります。このパッケージは Sentry のコアモジュールを含まず、breadcrumbs/messages/events のためだけのメソッドを提供しています。
クライアント用の webpack.config を作成する必要はあるものの、これで要件が満たせそうです。
本番環境のパブリックな場所に Sourcemap を置きたくない
上記の Sentry とも関連があるのですが、モニタリングのために Sentry への Sourcemap 連携は必須です。トラブルシューティングのデバッグスピードにも関わりますし、難読化する以上 Sourcemap がないとだいぶきついものがあります。
現状 Next.js は Sourcemap を Production ビルドで吐き出さないので(RFC として addyosmani が適用の提案をしています)、コミュニティが提供している Sourcemap プラグインなどを利用して Sentry に連携しなければなりません。
ですが、パブリックな場所に Sourcemap を置きたくないというのも要件として出てくるはずです。
では、デプロイ時に *.js.map ファイルを削除すればいいかというとそういうわけでもなく、バンドルされたファイルフッター // #sourceMappingURL=
に指定された Sourcemap ファイルが存在しない場合、ブラウザ側がリクエストを投げてくるのでサーバでは 404 となるわけです。モニタリングやアラート設定をする中ではノイズとなり得るので非常によろしくない。
解決させるために webpack 用のプラグインを作成します。
ここまで来ると next.config.js どうこうという話ではなく、ただの webpack plugin の話ですが、こういったアプローチで next.config.js に組み込んでいきます。
3つほど、next.config.js tips をご紹介しました。
紹介していないものも含めて、今プロダクトで扱ってる next.config.js はかなり混雑しています。属人化が排除できているかと言われると正直怪しいので、この辺にはだいぶ課題感がありますね…。
明日の Next.js アドカレ 2019 2日目の記事は @terrierscript さんの「Gatsbyからnext.jsにしようとしている話かそれを頓挫した話(予定)」です。
アドカレは 6 枠まだ埋まっていないのでぜひ参加してください 🙋🏻♂️