Yahoo!クイックマート管理画面でNext.js + Static Exportsを選んだ理由

投稿日時:
嘉手苅 志朗のアイコン

株式会社出前館 / フロントエンドエンジニア

嘉手苅 志朗

株式会社出前館の嘉手苅(かでかる)と申します。
この記事では、Yahoo!クイックマートの管理画面に「Next.js + Static Export」を採用した背景をご紹介します。

Yahoo! クイックマートとは

2024年に出前館とLINEヤフーが立ち上げた「Yahoo!クイックマート」は、Yahoo!ショッピング、または専用のアプリからコンビニやスーパーなどで販売されている生鮮食品や日用品をご注文いただき、出前館の配達員がピックアップしてお届けする新しいデリバリーサービスです。

そして、このYahoo!クイックマートの管理画面は、Yahoo!クイックマートに出店している加盟店の管理者やスタッフ、そして出前館側のオペレーターが利用する管理用ツールです。主に店舗・商品・注文情報の管理のような機能を提供しています。

Yahoo!クイックマート管理画面

これらの機能を円滑に扱うために、今回フロントエンドの実装としてNext.jsを利用し、Static Exportsを選択して構築しました。どういった経緯でこの技術選定になったのかをご紹介します。

開発期間と前提条件

まず、この管理画面の開発期間についてご説明します。 2023年の11月にフロントエンドメンバーが集まりチームを立ち上げました。そこから技術選定や開発基盤の構築、翌年の2024年1月頃に仕様が固まり本格的に開発をスタート。そして同じ年の8月にリリースとなりました。

開発時の状況、考慮した条件としては以下の3つです。

①リリース日が必達でタイトなスケジュール
社内でも優先度が高いプロジェクトだったため、機能をある程度削ってでもリリース日だけは絶対に守らなければいけないという状況でした。開発期間は約半年ほどあったものの、要件はそれなりにボリュームがあり、かなりタイトでした。

②サーバーの保守を極力避けたい
出前館の組織はフロントエンドとバックエンドで分かれており、それぞれの職能でチームを組んで開発するスタイルです。今回集まったメンバーはフロントエンド開発に特化していたので、基本的にはサーバー保守の経験が少なく、可能ならサーバーを持たない構成が望まれました。

③SEO・UX最優先ではない
今回開発するのはログインユーザー向けのツールなので、SEOを重視する必要がありません。また、注文数が売上に直結するサービスでもないため、UXを最優先にする必要も限定的でした。

ライブラリやフレームワークに求める要件

上記の3つの条件から、フレームワークやライブラリに求める要件が自然と導き出されました。

  • 学習コストを抑え、立ち上がりを早くしたい
  • サーバー保守を避けられる構成を希望
  • サーバーサイドレンダリングは不要

こうした条件を踏まえて、私たちは候補として 「Next.js(Pages Router) + Static Exports」と「Vite + react-router-dom」という2案を検討することになりました。

検討した2つの構成

Next.js (Pages Router) + Static Exports
通常、Next.jsを使うときは専用サーバーを立ち上げる形が多いですが、今回はサーバーの保守を避けたいという理由から、Static Exportsを選択する形を考えました。

Vite + react-router-dom
ビルドにはViteを用い、ルーティングにはreact-router-domを使う、いわゆるオーソドックスなSPAの構成を検討しました。

技術選定をしていた2023年11月頃はNext.jsは14系のリリース直後でApp Routerが推奨されていましたが、社内にApp Routerの採用事例がなく、メンバーも未経験だったため、学習コストを抑えたいという方針から今回は見送りました。

Next.jsでの懸念点

Next.jsの採用に関して、Dynamic RoutesをStatic Exportsで利用するときに、Nginxなどでrewrite設定が必要になるという懸念点がありました。

例えばpost/[id].tsxというファイルをビルドすると、出力されたファイルはpost/[id].htmlとして生成されます。しかし実際に/post/xxxのようなリクエストが来た場合、ファイルを返すにはrewrite設定でpost/[id].htmlにルーティングさせる必要があります。

サーバーの保守や設定を極力避けたい要件と衝突するため一時は見送りも検討しましたが、ファイルベースルーティングや自動コードスプリッティングの利便性が勝り、最終的に Next.js + Static Exports を採用する結論に至りました。

Dynamic Routesを使わない理由

Next.js + Static ExportsでDynamic Routesを使う場合、Nginxのrewrite設定が必要になる、という懸念に対してはDynamic Routesをそもそも使わない方法を選択しました。

ページにパラメータを渡す際はクエリパラメータを利用することで、useRouter()フックからパラメータ値を参照できます。こうすることでrewrite設定が不要になり、問題なく仕様を満たせるようになりました。

trailingSlashオプションの活用

もうひとつの懸念点は、デフォルトのビルド設定だと各ページに対応する.htmlファイルが生成されるので、パスの指定が合わずに404が発生する可能性があることでした。 例えば、about.tsxをビルドするとabout.htmlが生成されますが、ブラウザで/aboutにアクセスすると404になるケースがあります。これを解決するためにnext.config.jstrailingSlash:trueを設定して、各ルートに対してindex.htmlが生成されるようにしました。

これにより、たとえば/aboutにアクセスするとabout/index.htmlが返される形になるため、追加のルーティング設定が不要でアプリケーションを正しく動かすことができます。

採用されなかった構成について

Vite + react-router-dom

検討の結果、採用されなかったVite + react-router-domによる構成についても説明します。実装調査したところ、以下の点を確認しました。

ルーティング設定

createBrowserRouter()という関数を使い、パスとコンポーネントを1ページごとに紐づけて定義する必要があります。Next.jsのようにファイルベースで自動生成される仕組みはないので、地道にルーティングを設定していく形です。

コードスプリッティング

createBrowserRouter()でコンポーネントを読み込む際、lazy()を使って動的インポートする形をとれば、ページごとにバンドルを分割できます。これによりメインバンドルから分離された各ページのJSファイルが生成されます。

react-router-domを試した感想

チーム内でreact-router-domも試してみましたが、学習コストが高いという印象を持ちました。 また、react-router-domではページ単位のコードスプリッティングを行う場合、ルートごとにlazy()を利用して動的インポートを明示的に指定する必要がありますが、Next.jsであれば自動でコードスプリッティングをしてくれるため、実装の手間も少なく済みました。

こうした理由から、チームとしてはやはりNext.jsを選択する結論に至りました。


以上が、チームのスキルやプロジェクトの状況を踏まえた条件を満たすために2案を検討し、Next.jsのStatic Exportsを選択した経緯です。これらの工夫によって、サーバー保守を最小限にしつつ、チームが慣れ親しんだNext.jsを使って素早く開発できる環境を整えることができました。

リリースと現在の状況

その後のプロジェクトの進捗は、Next.js + Static Exportsの構成を選んだことによる遅延もなく、無事に予定通りリリースすることができました。 最終的にはビルドとルーティングのためだけにNext.jsを使う形になりましたが、チームやプロジェクトの条件を鑑みると適切な選択だったと感じています。

Yahoo!クイックマート管理画面は、リリース日を厳守するためにやむを得ず落とした要件が多く、現状ではまだまだ実装されていない機能が存在します。例えば、商品の登録はCSV入稿メインなどは今後も継続して改善開発を進めている段階です。

We Are Hiring!

また、出前館では、他にもフロントエンドを持つプロダクトが複数あります。基本的にはReactをメインで採用しつつ、出前館のWebサービスはNext.js + BFF(GraphQL)の構成で開発しています。興味を持っていただけた方、詳細についてはぜひカジュアル面談でお話ししましょう![1]

Loading...Loading...Loading...
脚注
  1. 本記事は、2025年3月17日に開催されたイベントの内容を元に編集した記事です

プロフィール