commmune Developer Blog

commmune開発者ブログ

【全文和訳】Next.js 9.0.7

(以下は2019年9月30日に公開された Next.js 9.0.7 の日本語和訳です。 以下原文 )

https://nextjs.org/blog/next-9-0-7

約2か月前、Next.js 9.0がリリースされました。以来、私たちは9.0.1、9.0.2、9.0.3、9.0.4、9.0.5、9.0.6、および9.0.7といった、小さいものの、非常に重要な7つのリリースの開発に取り組みました。

重大な変更点はありませんが、これらのリリースによって皆さんのウェブサイトやアプリに係る点をご説明いたします。
(訳者注* アップデート機能名は原文のまま)

  • Improved Concurrency in Windows Environments:
    Windowsでの’next build’プロセスの信頼性が向上し、作業の並行化が向上しました。

  • Gzip Compression by Default:
    最適化のステップを省略するため、Gzip圧縮がデフォルトで追加されました。

  • TypeScript Report on Active Pages Only:
    ビルトインされているTypeScriptをアクティブページの変更のみを感知するように拡張したことにより、速度と信頼性の向上を図りました。

  • Telemetry:
    私たちが今後Next.jsのどの部分を最適化し、さらに最適化された箇所が意図した効果を発揮しているか検証することが可能になりました。

  • Improved next/head Element Tracking:
    ‘next-head’クラスが削除されたことにより、実装を検証するツールの実装が容易になりました。

  • Preventing Non-Pages in the Pages Directory:
    Non-pagesが意図せず公開されることを未然に防ぐことにより、アプリケーションのセキュリティと‘next-build’にかかる時間を最適化します。

  • Runtime Improvements:
    例えば、next / dynamicをはじめとしたNext.jsの特定の部分が使用されていない場合、Runtime時にこれらが不要になり、バンドルサイズが小さくなります。

  • AppTree Support:

  • Removal of the next-server Package:

Improved Concurrency in Windows Environments

Next.jsは、next buildプロセス中に多くの並行処理を行います。 主な用途は、Terserと並行して、ビルド出力を最小化することです。

以前、この作業はworker-farmというパッケージを使用して、複数のCPUで処理されていました。 しかし、多くのWindowsユーザーがカスタムwebpack構成で最小化機能を無効にしていることを私たちは気づきました。さらに調査したところ、worker-farmは、Windowsマシンでは一貫した動作が認められませんでした。

この問題を解決するために、私たちはworker-farmからjest-workerに移行しました。 これにより、MacOSLinuxWindowsの各マシンでビルドの信頼性と一貫性が確保されました。

jest-workerは、その名前が示すように、Jest test runnerの一部です。これは、Jestがテストケースの並列化に使用するパッケージです。 このパッケージには実績があり、信頼性が高く、メンテナンスも行き届いています。

jest-workerは、Node 12の新機能である‘worker_threads’もサポートしています。child_procesとは異なり、worker_threadsはメモリを共有できます。つまり、新しいバージョンのNodeでのビルド時間が短縮されます。

jest-workerに移行したことにより、Windowsユーザーのビルドの並行処理を再び有効にすることができました。


Gzip Compression by Default

企業がカスタムサーバーを使用する理由を調査した結果、最も頻繁な理由は圧縮処理のためだということがわかりました。 多くの企業は、HTTPレスポンスをGzipに圧縮処理するExpress Middlewareを追加して、対応していました。

このMiddlewareはレスポンスを圧縮することにより、ユーザーに送られるバイトの数を少なくすることができます。通常、このような処理は、NginxのようなReverse Proxyによって行われる必要があります。Reverse Proxyは、Single-Threaded Node ProcessにおけるCPU負荷の高い処理をなくすことができます。

しかしながら、WebにおけるNext.jsの使用状況を調べると、多くの企業が圧縮設定を行っていないことが分かりました。

ZEIT Nowなどのプラットフォームでは、gzipbrotliがプロキシレベルで自動的に処理されます。

セルフホスティングの場合、企業は自分でgzipを(compressionまたはリバースプロキシを使用して)追加する必要があります。

Next.js 9.0.4以降では、next startまたはカスタムserver.jsを使用する場合、gzip圧縮がデフォルトで含まれています。

Node.jsがネイティブでBrotliをサポートするようになったため、brotliのサポートもまもなく開始されます。

既にカスタムサーバー経由で圧縮処理が皆さんのアプリケーションに設定されている場合、Next.jsは独自の圧縮処理を追加しません。

Serverless Targetに対し、Next.jsはデフォルトでは圧縮処理を行いません。理由としては、Serverless Targetを使用する場合、AssetはNode.js経由ではなく、単体でアップロードされるからです。

ZEIT Nowをはじめとした、圧縮処理を行うプラットフォームを既に導入されている場合、特段変更は必要ありません。


TypeScript Report on Active Pages Only

Next.js 9には、TypeScriptのビルトインサポートが含まれています。ページの名前を.jsから.tsxに変更するだけでこの機能は使用可能です。 他にセットアップが必要な場合、Next.jsはこれらを自動的行うか、皆さんに必要な手順をガイドしてくれます。

開発プロセスと並行して、Next.jsは’tscwatch’を実行することで、型チェックを行います。Next.jsにはon-demand entriesと呼ばれる概念があります。 この機能は、作業中のページのみをコンパイルします。詳細についてはこちらをご覧ください。

f:id:commmune:20191016213241j:plain
The on-demand entries flow

9.0.4では、On-demand entriesによってコンパイルされているType errorのみ報告されます。これにより、特定のページのみに作業を集中している際の型チェックノイズを大幅に軽減することができます。

依然として、アプリ全体の型チェックは、’next build’又は皆さんの使用されるエディターによって行われます。

Telemetry

Next.jsは約3年前にリリースされ、そのフレームワークは新たな機能やより優れた初期設定を含め、ここ3年で目まぐるしい成長を遂げてきました。

この改善プロセスは、人の手を駆使した、実に手作業的でした。

ZEITには、いくつかの大きなNext.jsアプリケーションがあります。 たとえば、 zeit.co, zeit.co/docs, と nextjs.org。私たちは、ZEITでNext.jsをドッグフーディングして、その経験に基づいてNext.jsを改良しました。

さらに、フィードバックを収集するためにコミュニティと積極的に連携してきました。読者の皆さんの中にも、それぞれの会社でどのようにNext.jsを使用されているかについて、Timと話をした人がいるのではないかと思います。

たとえば、カスタムサーバーを使用されているとか、カスタムwebpackを設定されているとか。このフィードバックは、Next.jsの機能開発において非常に貴重です。

ただし、このアプローチには一部のユーザーからのみフィードバックが得られるという問題があります。この一部のユーザーがかかえるニーズとユースケースは、皆さんと異なるかもしれません。

1つの例として、あまり通常な使用例ではありませんが、それなりの数のユーザーがCSSファイルをインポートすることを、next-css(又は、Sass/Less)かカスタム設定を通して行っています。このような使い方をしているユーザーの割合が分かれば、私たちはこれらの機能改良を優先的に行うことができます。

このため、Next.jsをさらに改良するために、より多くのフィードバックを収集する方法として、匿名の、より自動化されたアプローチを導入しました。

これにより、フレームワークに導入された改良がすべてのアプリケーションの底上げにつながっているか確認することが可能になります。

Telemetryの詳細については、nextjs.org / telemetry をご覧ください。 また、フィードバックに参加したくない場合は、オプトアウトする方法も書いてあります。


Build Feedback with Dots Indicating Activity

Next.jsのユーザーに聞き取りを行った際、’next build’が実行されている時、特に視覚的に何も起きていないように見えるという声が上がりました。

これを解決するために、next buildが実行されている際、コンソールに読み込みインジケーターが表示されるようにしました。このインジケーターが表示されることにより、コマンドがまだ実行中であり、プロセスがフリーズしていないことを視覚的にとらえることができます。

将来的には、このインジケーターを改良して、ビルドがどれくらい進んでいるのか、段階的に表示させるようにすることを計画しています。


f:id:commmune:20191016215330p:plain
The new build indication dots


Improved next/head Element Tracking

Next.jsは、アプリケーションのHTMLをレンダリングする<head>要素を管理する仕組みが組み込まれています。このAPIは、next / headモジュールを通じて公開されています。

たとえば、ページにタイトルを追加するには:


> import Head from 'next/head'
> 
> export default function MyPage() {
>   return (
>     <>
>       <Head>
>         <title>My Title</title>
>       </Head>
>       <h1>Hello World</h1>
>     </>
>   )
> }  


HTMLにレンダリングする場合、Next.jsは<Head>内でレンダリングされたすべてのコンポーネントを収集し、ページの<head>にタグをレンダリングします。

ただし、Next.jsでは、<Link>コンポーネントを使用して、シングルページアプリケーション(SPA)タイプのルート遷移を許可します。

<Link>をクリックすると、Next.jsはクライアント側でページをレンダリングするために必要なJavaScriptを取得します。 次に、ファイルに関連付けられたReactコンポーネントレンダリングします。

この遷移はクライアント側で発生するため、前のページから挿入された’’要素をクリーンアップする必要があります。そうしないと、古い要素が別のページにも表れてしまう可能性があります。

これまで、Next.jsは、<Head>によって提供された要素にクラス名を追加することにより、これらの要素を追跡していました。

上記の例をとると、<head>は次のようになります。


<head>
  <title class="next-head">My Title</title>
</head>


この方法は、next/headによって注入されたすべての要素がうまくタグ付けされていた点と容易にクリーンアップできた点から良く機能していました。

しかし、それぞれの要素に加えられた’class’属性により、第三者のサービスによって書き足されたスクリプトの認証がうまくいかないという問題を、一部のユーザーから問い合わせがきました。

そこで、Google ChromeチームのGerald Monacoは、要素にクラス名を加えずにクリーンアップ動作を維持する方法を思い付きました。

新しいアプローチは、(next / head)を通してレンダリングされた要素の数を保持する追加の<meta>タグを挿入することです。これにより、Next.jsはカウントを使用して既存の要素をクリーンアップできます。

結果として、このアプローチは、複数の要素がページの<head>に挿入される場合の初期HTML負荷を軽減します。

Preventing Non-Pages in the Pages Directory

Next.jsを使い始めるとき、最初にすることはpagesページディレクトリを作成することです。

慣例では、’pages’ディレクトリ内のすべてのファイルがアプリケーション内のルートになります。 簡単な例として、pages/ about.js/ about’になるということです。 時間が経つにつれ、ユーザーのアプリケーション(大小を問わず)のビルドパフォーマンスが低下するという報告が時折届きました。

さらに調査すると、これらのユーザーは’pages’ディレクトリにコンポーネント構造全体を作成したことが明らかになりました。

pagesディレクトリ内のすべてのファイルはページとして扱われるため、すべてのコンポーネントはアプリケーション内のページとしてコンパイルされます。これにより、ビルド時に多くのオーバーヘッドが発生し、無効なページに対して2つ以上のJavaScriptファイルが生成されてしまいます。

さらに、これはNext.jsがcode-split chunkを生成する基準にも影響を与えます。 これは、Next.jsがページ間でのライブラリの使用に関するヒューリスティックを使用しているためです。

このため、ユーザーがアプリケーションを作成する際、こうした落とし穴にはまらないようにする必要がありました。

現在、Next.js 9は、pagesディレクトリ内のファイルがReact Componentをエクスポートすることを検証するようになりました。

実際使用してみると、Next.jsは’pages’ディレクトリ内に潜在的なnon-pageが発見されたことを表示してくれます。

これにより、ユーザーはnon-pageを他のディレクトリに移動するように勧められます。結果として、開発、プロダクション、およびコード分割がより高速で正確になります。


Runtime Improvements

Next.jsフレームワークは多くの部分で構成されています。これらの1つは、クライアント側ランタイムです。 このランタイムは、hydration、クライアント側のルーティングなどを処理します。

この改良が焦点を当てたhydrationは、サーバーレンダリングまたはプリレンダリングされたHTMLをインタラクティブにするために必要なものです。 hydrationはイベントハンドラーを追加し、useEffect()componentDidMountなどのライフサイクルメソッドを呼び出します。これにより、エンドユーザーがアプリケーションを使用できるようになります。

さらに、Next.jsは基本的なhydration以上のものを処理します。たとえば、クライアント側ルーターのセットアップ、next / headの設定、next / dynamicを介した追加のアプリケーションロジックの読み込みなどです。

これらには、固有のランタイム部分もあります。

next / dynamicの場合、Next.jsはサーバー側でゆっくりとロードされたコンポーネントがクライアント側で準備されているか確認する必要があります。next / dynamicを使用するたびに、追加のJavaScriptバンドルが生成されます。これらのファイルは、hydration mismatchを避けるために、hydration前にロードされる必要があります。

以前、このランタイムは常にNext.jsランタイムバンドルに含まれていましたが、今はアプリケーション内にnext / dynamicが使用されている場合にのみ含まれます。これは、next / dynamicを使用しないアプリケーションに対してダウンロード、解析、実行されるJavaScriptが少なくなることを意味します。


AppTree Support

Reactエコシステムの一部のライブラリは、非常に具体的な方法でサーバー側レンダリングを実装しています。最たる例として、’getDataFromTree’と呼ばれるApolloのサーバーサイドレンダリングソリューションは、React tree及び各’Query’をレンダリングし、結果を待機してから、Reactツリーを再レンダリングします。

Next.jsは初期設定で、React treeにコンテキスト値を加えます。例えば、読み取り可能なルーターuseRouterで追加することです。

with-apolloの例で React treeをレンダリングする方法は、<App>レンダリングし、欠落しているプロパティをマニュアル入力することです。React Contextの追加により、コンテキストプロバイダーは別の要素であるため、これはできなくなりました。

Next.js 9.0.4から、getInitialPropsのコンテキストオブジェクトに’AppTree’という新しいプロパティが追加されました。 これは特に、ApollogetDataFromTreeのように外部ライブラリがReact tree全体を横切る必要がある場合などのために追加されました。

with-apolloは、変更を反映するように更新されました。 アプリに既にApolloを実装している場合、UseRouterおよびその他のAPIがNext.jsアプリケーションで動作するように、AppTreeを使ったアプローチに更新することをお勧めします。

Next.jsチームは、一般的にReact treeを横切ることを推奨していないため、Apolloやこれに似たライブラリーを使用されていない場合’AppTree’を使わないことを推奨いたします。 それは、React treeが1回のみならず、複数回レンダリングされるため、パフォーマンスに多くの負荷がかかってしまうからです。


Removal of the ‘next-server’ Package

1年ほど前にサーバーレス展開用にNext.jsの最適化を開始したとき、next-serverというパッケージを作成しました。 このパッケージは実験的であり、nextパッケージと一緒に公開されます。 どこにも正式に明文化されていませんが、これは可能な限り最小のNext.jsサーバーランタイムを実現する試みとして開発されました。

最終的に、パッケージは成功し、実稼働サーバーのランタイムが小さくなりました。 しかし、私たちは、Next.jsコンパイラーとstatic analysisを使用してランタイムをさらに小さくする革新的な方法を編み出しました。

これにより、next-serverは時代遅れになったため、[serverless target] (https://nextjs.org/docs#serverless-deployment)に置き換えられました。 このターゲットの出力は、next-serverパッケージをnextの代わりとして使用するよりもはるかに最適化されています。

このパッケージは時代遅れで、直接使用することも不可能だったので、いったんそのままにしてありました。理由としては、その一部が他のパッケージと共有された機能を有しており、その部分だけ切り離すためにはそれなりの人工が必要だったからです。

今回、私たちはこの人工をかけて、対象コードをnext-serverからnextパッケージに戻しました。これは、つまり、Next.jsフレームワークのすべてのコードは現在’next’パッケージに保存されているという意味です。

これにより、初心者もベテランのユーザーもNext.jsに貢献しやすくなりました。 現在、Single Compilation Process とUnified Build Configurationがあります。 以前は、コードがどのパッケージに属するかで、’next-server’と’next’とで個別の設定が存在しました。

Upgrading Next.js

プロジェクトが古いバージョンのNext.jsで実行されている場合、Next.js 9にアップグレードすることをお勧めします。

ほとんどの場合、アップグレードをするために何かしらの変更が必要になることはありません。 アップグレードガイドに従って、スムーズなアップグレードエクスペリエンスを確保できます。

ガイドが公開されてからアップデートに貢献してきたコミュニティのメンバーに感謝します。


What's Coming in the Future?

このブログ投稿された新たな最適化は、私たちが取り組んでいるより広範な最適化と機能拡張の始まりにすぎません。

進行中のRFCについては、近日中にアップデートを公開します。 それまでは、GitHubRFCラベルで一足早く覗き見ることができます。

これには、我々が研究してきたbuilt-in CSS support, public directory support, 及び src directory support等の機能が紹介されています。


Community

Next.jsコミュニティの継続的な成長を楽しみにしています。

800人以上のユーザーが少なくとも1度は貢献( 1 Commit)してくれました。 GitHubでは、41,100以上の星が付きました。

spectrum.chat/next-jsのNext.jsコミュニティは、最後のメジャーリリース以降、メンバーが倍になり、10,900人を超えるメンバーが登録しています。 一緒に参加しましょう!

コミュニティへの継続的な貢献と、企業やユーザーからの外部フィードバックに心が躍ります。

【全文和訳】Next.js 9

(以下は2019年7月8日に公開された Next.js 9 の日本語和訳です。 以下原文 )

nextjs.org

70のカナリアリリースを経て、遂に Next.js 9 がお披露目されます
主な特徴は以下です。 (訳者注* アップデート機能名は原文のまま)

  • Built-in Zero-Config TypeScript Support:
    TypeScriptサポートと統合型チェックにより信頼性を高めたアプリケーション構築を可能に。

  • File system-Based Dynamic Routing:
    カスタムサーバーなしでファイルシステムを介した複雑なアプリケーションルーティング定義を実行可能に。

  • Automatic Static Optimization:
    デフォルトでサーバーサイドレンダリングおよび静的事前レンダリングを利用することで超高速ウェブサイトの構築が可能に。

  • API Routes:
    ホットリロードと統合パイプラインを利用することで即座にバックエンドアプリケーションエンドポイントの構築が可能に。

  • More Production Optimizations:
    ビューポート内のプリフェッチ、その他最適化によってアプリケーションはよりレスポンシブに。

  • Improved DX:
    最高の開発を支える、ささやかかつ使い易い改善の数々

これまで通り、上記全ての成果は後方互換であることを保証できるよう努めています。 Next.js アプリケーションへは、以下を実行すればよいだけです。

$ npm i next@latest react@latest react-dom@latest



稀にコードベースの変更を必要とする場合があります。詳しくは upgrade guideをご覧ください。

前回のリリース以後、IGNBang & OlufsenIntercomBuffer、そして Ferrari のような企業が Next.js でのローンチを行っていることを嬉しく思います。より多くの事例は showcase をご覧ください!!

Built-in Zero-Config TypeScript Support

1年前、Next.js 6は@zeit/next-typescriptというプラグインを通じたTypeScriptの基本的なサポート機能を導入しました。 ユーザーは、この機能を使うために自身の.babelrcをカスタマイズし、next.config.jsを有効にする必要がありました。

このプラグインを設定することにより、Next.jsは.tsファイルと.tsxファイルを構築できるようになります。 しかし、型チェックは実装されておらず、Next.jsコアによって提供される型定義もありませんでした。 つまり、DefinitelyTyped上で、最新のリリースに同期が追い付いていない可能性のあるコミュニティパッケージを別個管理する必要がありました。

多くの新旧ユーザーに伺った結果、ほとんどのユーザーがTypeScriptを使用することに非常に興味があることが明らかになりました。ユーザーは、TypeScriptを既存または新規のコードベースに簡単に統合できる、より信頼性の高い、スタンダードソリューションを望んでいました。

これを受け、私たちはTypeScriptサポート機能をNext.jsコアに実装し、開発者エクスペリエンスの向上と開発プロセスの高速化を図りました。

Automated Setup

Next.jsでTypeScriptを使い始めるのは簡単です。ファイル、ページ、またはコンポーネントの拡張子を.jsから.tsxに変更し、next devを実行してください!

これにより、Next.jsはあなたのプロジェクトでTypeScriptが使用されていることを検出します。 Next.js CLIはReactとNode.jsに必要な型定義をインストールする案内をします。

Next.jsは、tsconfig.jsonが作成されていない場合、適切なデフォルト値のデフォルトtsconfig.jsonを作成します。 このファイルは、Visual Studio Codeのようなエディタでの統合型チェックを可能にします。

Next.js 9 Automated TypeScript Setup 動画


Integrated Type-Checking

Next.jsは、開発とプロダクションビルド、両方であなたのために型チェックを行います。

開発の途中、ファイルを保存した際にNext.jsは型定義エラーを表示します。 型チェックはバックグラウンドで行われるため、ブラウザで更新されたアプリケーションを即座に操作できます。 型定義エラーは、利用可能になるとブラウザに伝播します。

Next.js 9 Development Type-Checking 動画

型定義エラーが検出された場合、Next.jsはプロダクションビルド(すなわち、next build )を自動的に失敗させます。 これは、破損したコードをプロダクション環境に送り込むのを防ぐのに役立ちます。

f:id:commmune:20190804175523p:plain
Next.js 9 Production Type-Checking


Next.js Core Written in TypeScript

ここ数カ月の間に、私たちはコードベースの大部分をTypeScriptに移行しました。これは私たちのコード品質を強化しただけでなく、すべてのコアモジュールに型定義を提供することも可能にしました。

たとえば、next/linkをインポートすると、TypeScriptに対応したエディタであれば利用可能なプロパティやそれらが受け付ける値を表示します。

f:id:commmune:20190804175459p:plain
Next.js Core Types


Dynamic Route Segments

ダイナミックルーティング(URLスラッグまたはプリティ/クリーンURLとも呼ばれます)は、Next.jsが2年半前にリリースされた際に、最初にGitHubにリクエストされた機能の1つでした!

Next.js 2.0では、Next.jsをプログラムで使用するためのカスタムサーバAPIを導入することで、このリクエストにお答えしました。これにより、Next.jsをレンダリングエンジンとして使用することが可能になり、特定のページをレンダリングするための着信URLの抽象化とマッピングが可能になりました。

私たちはユーザーの声を聞き、彼らのアプリケーションを調べたところ、ユーザーの多くはカスタムサーバーを使っていたことがわかりました。そして、カスタムサーバーを利用するのはダイナミックルーティングのためだという傾向が浮かび上がりました。

しかし、カスタムサーバーには独自の落とし穴があります。ルーティングはプロキシではなくサーバーレベルで処理され、モノリスとして展開・拡張され、パフォーマンス上の問題が発生しやすくなります。

カスタムサーバーでは、アプリケーション全体を1つのインスタンスで使用できるようにする必要があるため、これらの問題を解決するサーバーレス環境に展開することは通常困難です。サーバーレスリクエストはプロキシレイヤーでルーティングされ、パフォーマンスのボトルネックを回避するために独立してスケーリング/実行されます。

また、私たちはより良い開発者エクスペリエンスを提供できると考えています。 Next.jsの魔法は、pages / blog.jsという名前のファイルを作成した瞬間、/ blogにアクセス可能なページが作成されたときから始まります。

/blog/my-first-post(/blog/:id)のようなルートをサポートするために、なぜユーザーは独自のサーバーを作成し、Next.jsのプログラムAPIについて学ぶ必要があるのでしょうか。

このフィードバックとビジョンを念頭に、私たちは多くのユーザーが既に知っているpages/ディレクトリを基にルートマッピングソリューションの調査を開始しました。

Creating a Dynamically Routed Page

Next.jsは、基本的な名前付きパラメータを使ったルートの作成をサポートしています。このパターンはpath-to-regexpExpressの動力源となるライブラリ)によって一般化されたものです。

pagesディレクトリにpages/post/[pid].jsという名前のファイルを作成することで、/post/:pidルートに一致するページが作成できます。

Next.jsは/post/1, /post/hello-nextjsなどのようなリクエストに自動的にマッチし、pages/post/[pid].jsで定義されたページをレンダリングします。 一致したURLセグメントは、[square-brackets]の間に指定された名前でページにクエリパラメータとして渡されます。

たとえば、次のページと /post/hello-nextjsをリクエストすると、queryオブジェクトは{pid:'hello-nextjs'}になります。

static async getInitialProps({ query }) {
  // pid = 'hello-nextjs'
  const { pid } = query

  const postContent = await fetch(
    `https://api.example.com/post/${encodeURIComponent(pid)}`
  ).then(r => r.text())

  return { postContent }
}


複数のダイナミックURLセグメントも対応しています!

[param]構文は、ディレクトリ名とファイル名に対応しています。つまり、次の例が成り立ちます。

./pages/blog/[blogId]/comments/[commentId].js
./pages/posts/[pid]/index.js


この機能についての詳細はread more about this feature in the Next.js Documentation 又は Next.js Learn sectionで確認できます。

Automatic Static Optimization

Next.jsは、2年ほど前にリリースされたv3で静的サイト生成に対応しました。これは当時Next.jsへの追加を最も求められていた機能でした。

それも当然で、静的サイトは文句の付けようもなく速いからです!サーバー側の計算が不要な上、CDNロケーションを通して即座にエンドユーザーへストリーミングできます。

ただし、サーバーサイドレンダリングされたアプリケーションと静的生成されたアプリケーションは2者択一で、サーバーサイドレンダリングと静的生成のどちらか一方を選ばなくてはいけませんでした。間を取るということができなかったのです。

現実には、アプリケーションごとに異なる要件があり得ます。これらの要件によって異なるレンダリング方法とトレードオフが必要となります。 例えば、ホームページとマーケティングページは一般的に静的コンテンツを含むため、静的最適化の対象として有力な候補となります。

その一方で、プロダクトダッシュボードはデータ更新を頻繁に行うサーバーサイドレンダリングを適用することにメリットがあります。

両方の利点を活かしデフォルトで速いサイトをユーザーに提供するためにどうすれば良いのか、私たちは探し始めました。ユーザーに静的マーケティングページや動的サーバーレンダリングされたページを使ってもらうにはどうすれば良いでしょうか。

Next.js 9を使い始めることで、ユーザーはフルサーバーレンダリングと静的エクスポートのどちらをアプリケーションに適用するべきか選ぶ必要がなくなります。ページ単位で両方の利点を得られるのです。


Automatic Partial Static Export

ページが静的HTMLにプリレンダリングできるか自動で判定するために、ヒューリスティックを導入しました。

これは、getInitialPropsを使うことでページにデータブロッキング要件が設定されているかを判定しています。

このヒューリスティックによって、Next.jsはサーバーレンダリングされたページと静的生成されたページの両方を備えるハイブリッドアプリケーションを出力可能となりました。

組み込みNext.jsサーバー (next start) とプログラマティックAPI (app.getRequestHandler()) の両方がこのビルドアウトプットを透過的に対応しています。構成や特別な処理は何も必要ありません。

静的生成されたページは引き続きリアクティブな状態です。Next.jsはアプリケーションのクライアント側をハイドレートすることで、完全な双方向性を実現します。

さらにページがURLのクエリパラメータを信頼している場合、Next.jsはハイドレーション後にアプリケーションを更新します。

開発中にページが静的に生成される場合、Next.jsはその生成を視覚的に知らせます。この視覚的成果物はクリックして非表示にできます。

f:id:commmune:20190804192654p:plain
Next.js Static Optimization Indicator

また、静的生成されたページはNext.jsビルドアウトプットに表示されます。

f:id:commmune:20190804180651p:plain
Next.js Build Output Type Indicator


API Routes

Reactアプリケーションを構築する際、多くのケースで何らかのバックエンドが必要となります。例えば、データをデータベースから検索する場合やユーザーから提供されたデータを処理する場合などです。(例: 問い合わせフォーム)

私たちが調べたところ、バックエンドを必要とする多くのユーザーが、カスタムサーバーを使ってAPIを構築しているようです。その結果、ユーザーはいくつかの課題を抱えています。 例えば、Next.jsはカスタムサーバーコードをコンパイルしません。つまりimport / exportやTypeScriptを使うことができません。

このため、多くのユーザーはカスタムサーバー上にカスタムコンパイルパイプラインを実装せざるを得ない状態になっていました。このパイプラインによって目的は達成できるのですが、同時にいくつもの落とし穴を生み出す傾向がありました。例えば、間違った構成を行ってしまった場合、ツリーシェイキングがアプリケーション全体で無効化されてしまいます。

このことはある問いを生み出しました。「Next.jsを使ったAPIバックエンド構築を可能とする開発者エクスペリエンスを提供してみてはどうだろう。」

今、Next.jsを使ってバックエンドを構築できる、ベストインクラスの開発者エクスペリエンスであるAPIルートをご紹介できることをうれしく思います。

APIルートを使うためには、まず始めにpages/ディレクトリ内にapiというディレクトリを作成します。 このディレクトリ内のすべてのファイルは、他のページのファイルがルートにマッピングされるのと同じ方法で、/api/<your route>に自動マッピングされます。

例えば、pages/api/contact.js/api/contactマッピングされます。

注: APIルートはダイナミックルートにも対応しています!

pages/api/ディレクトリ内のすべてのファイルは、Reactコンポーネントの代わりにリクエストハンドラ関数をエクスポートします。


export default function handle(req, res) {
  res.end('Hello World')
}


* ‘req’はhttp.IncomingMessageを拡張するNextApiRequestを表します。 * ‘res’はhttp.ServerResponseを拡張するNextApiResponseを表します。

一般的には、APIエンドポイントはクエリ文字列や、リクエストボディ、クッキーなどいくつかの入力データを受け取り、他のデータを使って応答します。

APIルートのサポートをNext.jsへ追加するために調査をしていた際、私たちは、ユーザーは多くのケースでNode.jsリクエストを使うことなくオブジェクトへ直接応答していることを発見しました。ユーザーはNode.jsリクエストの代わりに、Expressのようにサーバーライブラリで提供されているアブストラクションを使っていました。

理由としては、入力データは多くの場合、何らかの形式のテキストで、まず初めに構文解析をしなければ利用できないためです。これら特定のサーバーライブラリは手動でデータを構文解析する手間を、ほとんどの場合ミドルウェアを通して、省く手助けとなります。最も広く使用されているミドルウェアではクエリ文字列や、ボディ、クッキーの構文解析を提供していますが、使用するためにある程度のセットアップ作業が求められます。

Next.js内のAPIルートは、デフォルトでこれらのミドルウェアを搭載しており、APIエンドポイントを作成する際の生産性を即座に上げることができます。


export default function handle(req, res) {
  console.log(req.body) // The request body
  console.log(req.query) // The url querystring
  console.log(req.cookies) // The passed cookies
  res.end('Hello World')
}


加えて、入力データを使うことで、APIエンドポイントは一般的にデータを返します。この応答は通常JSONになります。データ送信をより簡単にするため、Next.jsはデフォルトでres.json()を提供します。


export default function handle(req, res) {
  res.json({ title: 'Hello World' })
}


開発中にAPIエンドポイントを変更する場合、コードは自動でリロードされるため、サーバーをリスタートする必要がありません。


Production Optimizations

Prefetching in-Viewport<Link>s

Next.js 9はビューポート内に表示される<Link>コンポーネントを自動で読み込みます。

この機能によって新しいページへのナビゲーションをより早く作成でき、アプリケーションの応答性を向上できます。

Next.jsはバックグラウンドで必要なアセットを読み込むため、Intersection Observerを使用します。

これらのリクエストの優先順位は低く、fetch()やXHRリクエストが優先されます。ユーザーがデータセーバーを有効にしている場合、Next.jsは自動読み込みを行いません。

アクセスの少ないページに対して、この機能はprefetchプロパティをfalseに設定することでオプトアウトできます。


<Link href="/terms" prefetch={false}>
  <a>Terms of Service</a>
</Link>


Optimized AMP by Default

現行のNext.js 9はデフォルトで最適化されたAMPをAMPファーストおよびハイブリッドAMPページに対しレンダリングします。

AMPページがオプトインする一方で、Next.jsは自動でアウトプットを最適化します。これらの最適化によって、レンダリング速度を最大50%高速化できます!

この変更はAMPオプティマイザに対するSebastian Benzの驚異の取り組みによって実現しました。


Dead Code Elimination for typeof window Branches

Next.js 9はサーバーとクライアントビルド中に、typeof windowを適切な値(undefined 又は、object)と置換します。この変更によって、Next.jsは自動でデッドコードを本番環境用にビルドされたアプリケーションから削除できます。

アプリケーション内のgetInitialPropsやその他の部分にサーバー専用コードがある場合、ユーザーはクライアント側バンドルサイズの減少を想定することになります。


Developer Experience Improvements

Compiling Indicator

9より前のバージョンでは、ホットコード置換が発生すること (およびNext.jsコンパイラツールチェインが動作していること) を確認する唯一の方法は、開発者コンソールを確認することでした。

ですが多くの場合、実行されているレンダリングを確認して上記の代わりとしているため、Next.jsがコンパイル動作を行っているか確認することが困難になっていました。例えば、微妙な変更をページ上のスタイルに対して行い、スタイルが更新されたかすぐに判断できない場合などでこの問題が起きます。

そのため、動作実行中かどうかを示すという問題について、潜在的な解決策を話し合うためのRFC / "good first issue"を発行しました。

多くのデザイナーやエンジニアの方からRFCについてのフィードバックをいただきました。例えば、インジケータのデザインについてどのようなものが好まれるか、デザインの潜在的な方向性などについてです。

Rafael Almeidaはこの機会をとらえて私たちのチームと協力し、まったく新しいインジケータを実装しました。そのインジケータは現在Next.js 9のデフォルトで利用できます。

Next.jsがコンパイル動作を行っているときはいつでも、小さな三角形がページ右下の隅に現れます!

Next.js Compilation Indicator 動画


Console Output

これまで開発中に変更を加えた場合、Next.jsはロード状態バーのゲージを満たすことでコンパイルインジケータの状態を表し、続けて変更が完了したらスクリーンをクリアしていました。

この動作によっていくつかの課題が生じました。最も注目に値するのは、例えばconsole.logコンポーネントに追加する場合などで、両方のアプリケーションコードからコンソールアウトプットをクリアしてしまうことです。Now CLIdocker-composeのようにログアウトプットをつなぎ合わせる外部ツールを使った場合も同様です。

Next.js 9から開始した場合、ログアウトプットの飛び越しは減り、スクリーンはクリアされなくなります。これによって全体のエクスペリエンスは向上し、ターミナルウィンドウにはより関連の深い情報が表示され、フリッカーは少なくなります。同時に、すでに使っているツールがある場合、Next.jsはそのツールとより一体化した動作をするようになります。

Next.js Development Console Output 動画

アウトプットクリアリングに関してご協力くださったJustin Chaseさんに、心よりの感謝を申し上げます。


Build Output Statistics

next buildを使って本番環境用のアプリケーションを構築することで、構築された全てのページについて詳細に確認することができます。 どのページもいくつかの統計を自動で行っています。

最も注目すべきものはバンドルサイズです。アプリケーションが大きくなるに連れてJavaScriptバンドルもまた大きくなります。ビルドタイム表示は本番環境用バンドルの大きさを示しています。将来的には、本番環境用の構成に失敗するページに対してパフォーマンスバジェットを設定できるようになります。

f:id:commmune:20190804184454p:plain
Next.js Built Page Size

バンドルサイズに加えて、いくつのプロジェクトコンポーネントnode_modulesコンポーネントが各ページに使用されているか示します。これによってページの複雑さがわかります。

f:id:commmune:20190804184527p:plain
Next.js Page Package Count

各ページは異なる動作をすることが可能なため、静的に最適化されているかサーバーサイドレンダリングされているかを示す表示があります。

f:id:commmune:20190804184554p:plain
Next.js Built Page Type


Per-Page Configuration Object

各ページは構成オブジェクトをエクスポートできます。この構成は、初めはAMPにオプトインすることを可能としますが、将来的にはより多くのページ特殊オプションを構成できるようになります。

// pages/about.js
export const config = { amp: true }

export default function AboutPage(props) {
  return <h3>My AMP About Page!</h3>
}


ハイブリッドAMPレンダリングをオプトインするには、値hybridを使います。 高次コンポーネントはこの新しい構成に合わせて削除されます。


import { useAmp } from 'next/amp'

export const config = { amp: 'hybrid' }

export default function AboutPage(props) {
  const isAmp = useAmp()
  return <h3>My About Page!{isAmp ? <> Powered by AMP!</> : ''}</h3>
}


withAmpの使用法を新しい構成オブジェクトに自動変換するcodemodを提供します。より詳細な情報はアップグレードガイドをご参照ください。

少し前にツーリングにいくつかの変更を行いました。これによって、コードベースを提供し、コードベースの増大に合わせて安定性を確保しつつ、エクスペリエンスを向上させることができます。


Codebase Improvements

TypeScriptのセクションで示したように、現行のNext.jsコアはTypeScriptで記述され、型定義は使用するNext.jsアプリケーションにあわせて生成されます。

Next.jsを使って構築されたアプリケーションに便利なことに加えて、コアコードベースを使った動作においても便利さを発揮します。例えば、型定義エラーや自動でオートコンプリートを実行した場合などで便利です。

Next.jsにはすでに、50+ Next.jsアプリケーションとそれらに対して行われるテストで構成される、極めて大規模な統合テストスイートがあります。これらのテストは、新バージョンがリリースされたとき、以前から利用できていた機能を同テストスイートによってテストし、アップグレードが確実にスムーズに行われるようにします。

多くの場面で開発にNext.jsを使っている「現実の」開発者を再現しているため、テストの大半は統合テストです。例えば、あるテストはホットモジュール置換が動作するか確認するNext.jsアプリケーションに変更を加える動作を再現しています。

統合テストは主に、ヘッドレスChrome内でテストするためにchromedriver と組み合わせたSelenium webdriverをベースとしています。ただし時間の経過とともに、ある程度の課題が他のブラウザ、特にInternet Explorer 11のような古いブラウザで発生するかもしれません。

Seleniumを使用したため、テストを複数のブラウザで自動実行できました。

今のところ、テストスイートをChromeFirefoxSafari、そしてInternet Explorer 11.で実行しています。

Google Chrome Collaboration

Google ChromeチームはRFCとプルリクエストを提供することで、Next.jsを改良するための取り組みを続けています。

このコラボレーションの目標は、バンドルサイズ、ブートアップ、そしてハイドレーション時間に重点を置いた大規模なパフォーマンス向上です。

例えば、これらの変更によって小規模サイトだけでなく、HuluTwitchDeliverooなどの強力なアプリケーションのエクスペリエンスも向上できます。


Module / Nomodule

まず重視しているものとして、最新JavaScriptに対応しているブラウザに最新JavaScriptを搭載する領域があります。

例えば、現行のNext.jsはasync/awaitに対応していないブラウザ上でコードが実行され、ブラウザがクラッシュしてしまう場合に、シンタックス用にポリフィルを提供しなければいけません。

f:id:commmune:20190804185046p:plain
Next.js Module/Nomodule Collaboration RFC

Next.jsはmodule/nomoduleパターンを使用することで、最新のJavaScriptを対応ブラウザに送りながら古いブラウザをクラッシュさせないようにしています。Module/nomoduleパターンによって、古いブラウザをポリフィルしたES5へフォールバックさせつつ最新JavaScriptを最新のブラウザに送る際に、信頼性のあるメカニズムを提供します。

Next.js内のmodule/nomoduleについてのRFCこちらで確認できます。


Improved Bundle Splitting

現行のNext.js内バンドルスプリッティングストラテジは、単独の「commons」チャンク内モジュールを対象に含めた割合ベースのヒューリスティックに基づいています。バンドルが一つだけで細分性がほんのわずかしかないため、コードは不必要にダウンロードされる (commonsチャンクが特定のルートに実際には不要なコードを含むことができるため) か、コードが複数のページバンドルに跨って複製されます。

f:id:commmune:20190804185237p:plain
Next.js Chunking Collaboration RFC

改良バンドルスプリッティングについてのRFCこちらで確認できます。


Other Improvements

Chromeチームは他にもNext.jsを改良する多くの最適化や変更に取り組んでいます。それらについてのRFCはもうすぐご覧いただけるようになります。

これらのRFCとプルリクエストはNext.js話題トラッカー内で簡単に見つけられるよう、 "Collaboration"とラベルされています。


Community

私たちは、Next.jsコミュニティが成長し続けていることを大変うれしく思います。

今回のリリースでは、65名を超える プルリクエスト作成者の方々が、コアの改良や用例の作成にご協力くださいました。

用例について言うと、Next.jsを異なるライブラリやテクノロジに統合する方法について、現在200を超える用例をお使いいただけます!その中には大半のcss-in-jsライブラリやdata-fetchingライブラリが含まれています。

  • 少なくとも一つの取り組みに対し、720を超える方々からの投稿がありました。
  • このプロジェクトはGitHub上で38600スター以上を獲得しています。
  • 最初のリリースから3400を超えるプルリクエストが提案され、前回のメジャーリリースからでも800を超えるご提案をいただいています!


spectrum.chat/next-js上のNext.jsコミュニティは、メンバー数が8600人を超え、前回のメジャーリリース時から倍増しています。ぜひご参加ください!

今回のリリース実現にあたり、コミュニティや外部フィードバックなどを通じてご協力くださった全ての方々に感謝いたします。

Next.jsとTypescriptが奏でるUniversal JSの世界 ~commmune を支えるアーキテクチャ~

commmuneとは?

企業独自の「ユーザーコミュニティ」のカンタンな構築と、効果的な運用を実現する日本初の企業向けコミュニティタッチツールです。

f:id:commmune:20190703184902j:plain
ユーザー画面

f:id:commmune:20190703185723j:plainf:id:commmune:20190703185731j:plain
管理画面

詳細はサービスサイトに譲り、本稿では1年間以上温めてきたアーキテクチャを紹介します。

アーキテクチャの全体像

概観


GCPフルパワーです。「鶏を割くに焉んぞ牛刀を用いん」という考え方を私たちは大切にしており、極力シンプルなWeb3層アーキテクチャを採用、維持しています。トレンドを追うことは大切ですがそれ自体を目的にするのではなくcommmuneに真に適するものを導入していこう、というスタンスです。

* フロントエンド:Next.js
* APIサーバ群:Express
* アプリ:React Native

が各レイヤーのベースライブラリになります。

Universal / Isomorphic JavaScript

commmuneは全レイヤーがTypescript(JavaScript)実装になります。この方式で開発を進めると2つの利点が見えてきました。

1. 型定義ファイル、定数ファイルを楽に共有できる

バックエンド(APIサーバ群)とフロントエンドで型や定数を共有したい場面がしばしば訪れます。その際 .ts ファイルをそのまま使い回せるのはやはり便利です。通常YAMLなど汎用フォーマットを中間に挟むようですがその手間がなくなるイメージです。

2. 未知の領域への不安を和らげることができる

私たちのチームの開発体制(後述)と関係します。簡単に言うと例えば、「Expressは経験がないけど同じJavaScriptだしやってみよう!」と感じやすい(フロントエンドメンバー談)。あるいは逆に「Reactやるぞ!」と勇気を出しやすい(バックエンドメンバー談)。そんなイメージです。些細ですが皆のやる気や勇気はきっと全てに勝るのです。

組織アーキテクチャ

一般的には「サーバサイド」「フロントエンド」という区分で所属やタスク割り振りをするチームが多いと聞きます。一方で私たちは原則「サーバサイドとフロントエンドを別け隔てなく」書いています。

技術領域ベース タスクベース
f:id:commmune:20190704090418j:plain f:id:commmune:20190704090543j:plain

もちろんどちらかが絶対的に優れているわけではないと考えています。しかし、私たちはスタートアップです(リソースが非常に限られています!)。また、リモートワークが盛んです(対面よりも意思疎通の難易度が上がります!)。そういう事情を鑑ると私たちには後者のほうが適していると判断しました。良い意味で責任の所在も明らかになり、トラブルもないため当面はこの形を維持する予定です。

ただし馴染みが薄いアーキテクチャのため、実践する場合各メンバーの理解と協力が必須になりそうです。

おもな要素技術のご紹介

プロジェクト基盤

Typescript

f:id:commmune:20190704090639p:plain:w300

開発当初は100% JavaScriptでした。しかしながら予想以上のスピードで規模が拡大しており「ズルズルとJSで進むよりはTSに全置換したほうがグロスの生産性は上がりそうだ。」と判断し「人類TS化計画」を開始しました。これは計3ステップから成る大計画で現在2ステップ目(anyをなくそう!)です。CTOの私を含めTS未経験者が半数近くでしたが、「やると決めたらやり抜く」を胸に皆で一緒に前進しています。

尚1ステップ目(JSからTSに拡張子を変えよう!)は3ヶ月間で全ソースコード85.6%を移行できました。これはチームメンバーのとてつもない士気と「ボーイスカウト・ルール」の実践があったからこそ実現できたものです。本当に感謝しております...。

Circle CI


「デプロイは早期から自動化して損なし」と感じています。属人化を防げますし、正味作業ではないから(本質的な価値を生まない作業ということ)です。高速(計5分以内)、完全無停止(pm2docker-swarmによる完璧なグレースフルリロード)を目標にして日々研鑽しています。デプロイの質は(間接的に)開発工程の質に繋がります。

Cloud Functions

f:id:commmune:20190704091106p:plain

Slack上でBotくんに話しかけると、Cloud Functionsがイベントを検知し好きな環境にビルド、デプロイが可能です。私たちのユースケースではコストゼロで運用できるので大変懐にやさしいです。

Cloud DNS


commmuneはいわゆるマルチテナント型のアーキテクチャです。その中でも「最も効率的な真のマルチテナンシー」と呼ばれる形式を採用しています。実際に運用してみると、他の方式に比べ間違いなく実装の難易度は上がりますが、その分サーバアーキテクチャはとてもシンプルになります。そしてなにより技術的なチャレンジは常に推奨されるべきです。

バックエンド

nginx (Let's Encrypt)

SSL対応のために linuxserver/letsencrypt というdockerイメージを使用しています。PHPとの連携を推しているようですがNode.jsでも全く問題なく動作します。

Express + Passport + Sequelize

f:id:commmune:20190704091759p:plain:w200

(画像は closed-api-server/services)伝統的なM(V)CSアーキテクチャに沿って実装しています。クラス(モジュール)の分け方は難しい部分もありますが、画像の通り概ね機能ドメインに沿っています。

SendGrid

メール通知用です。Compute Engineの公式ドキュメントで紹介されているサービスなので素直に採用を決めることができました。

Elasticsearch

全文検索エンジンとして使用しています。開発初期はMroonga(MySQL)が担っていました。しかし将来のスケーラビリティ(DBをCloud SQLに移行するなど)を考慮して1ヶ月ほど前に移行を決断、実行しました。

尚オンプレミスではなくElastic CloudというSaaSを利用しています。GUIでポチポチするだけでスケールアウト、スケールアップ、クラスタリングなどが可能なのでおすすめです(現状最も近いサーバの所在がoregonであるという悲しみを乗り越え)

MySQL

O/Rマッパを使用していることもあり、極力JOINクエリは避ける戦略をとっています(吐き出されるクエリを理解した上でのJOINであればもちろん可)

尚、キャッシュとしてRedisなどのNoSQLを併用する例が多いですが、commmuneでは現在のところ導入していません。キャッシュレイヤーを導入する場合、導入によるアップサイド(大半のケースではパフォーマンスでしょうか)と、ダウンサイド(アーキテクチャ、実装が多少複雑になってしまう、など)を比較することになります。そして私たちのケースではまだまだ前者よりも後者が大きい、と考えているためです。

なお、弊社ではビジネスサイドも全員MySQLを駆使し、仮説構築から分析まで自律駆動できる体制になっています。

BigQuery

ユーザーの行動ログの格納、解析に用います。

フロントエンド

Next.js

f:id:commmune:20190704092151p:plain

フルスタック寄りのReact.jsフレームワークです。Server RenderingやZero Setupが特徴です。国内ではNuxt.js(というよりVue.jsの勢い)に押されていますが、世界的には一定の支持を得ているようです。

commmuneではcreate-react-appとNext.jsで迷いましたが、ここは攻めた判断(攻めると言っても当時すでに十分すぎるスター数、情報量がありました)を採り後者にしました。

* 規模の割に学習コストが小さい(フレームワーク特有の記法が少ない)
* 公式のexamplesが大量にある(本当に多いです…)
* Redux化やTypescript化が容易(拡張性の高さ)

など、思った以上に素直で扱いやすいフレームワークです。もっと日本でも布教していきたいので私たちが先頭を切り礎を築く所存であります。

Redux (redux-saga)

Smalltalk由来の)MVC実装を持ち込む際の定番ライブラリです。reducerをどの粒度で分割するかは永遠の課題(と考えています...)ですが、commmuneでは現在4つに分割しています。

// ログイン中ユーザのアカウント情報
user
  id
  nickname
  email
  .....

// (readonly)当該テナントの静的なサイトデザイン
site
  logo
  favicon
  colors
  .....

// ユーザ画面上での動的なグローバルステート
app
  news
  point
  badge
  .....

// 管理画面上での動的なグローバルステート
app_admin
  posts
  pointSetting
  adminLibrary
  .....

site は管理画面上でのみ更新が許されるreducer(即ち管理者ユーザしか更新しない)で、ユーザ画面上では参照専用のstateという位置づけです。一方 app は動的な状態変化を受け持つので、両者は対照的な存在になります。一般的な分割法と比べると粒度は大きめですが、はじめから小さく割るよりは必要に応じて都度リファクタリングしていこう、という姿勢です。これもやはり「鶏を割くに」の思想に基づいた判断です。

締めの一言

本記事に魂を揺さぶられた方は是非うちの応募フォームへ。
万が一魂が震えなかった方もうちの応募フォームへ。