UUUO Tech Blog

水産流通のDXや業務効率化を実現するアプリケーション「UUUO」「atohama」を開発・展開している株式会社ウーオのテックブログです。プロダクトにまつわる様々な情報発信を行います。

Next.js13 x AppRouter でFlutterアプリのWeb版を1週間で立ち上げした話

はじめに

こんにちはウーオでエンジニア兼PdMをしているt3qyoです。
7月に、モバイルアプリ(Flutter)でしか公開されていなかったUUUOのWeb版を約1週間の開発合宿を経て立ち上げしました!

合宿中に食べたお好み焼

以下のページのリンク先になります。 app.uuuo.jp

今まではモバイルアプリだけの公開でしたが、より広いお客様に産直鮮魚アプリのUUUOの商品を知ってもらいたく、Web版を公開することになりました。

なぜNext.jsを採用したのか

モバイルアプリでも採用しているFlutterで利用可能なFlutter Webと比較して考えました。
すでに、出品者側のアプリはFluttter Webで実装しており、Flutter Webもかなり有力な候補でした。
Flutter Webの立ち上げについては以前の記事で紹介しております。

tech-uuuo.hatenablog.jp

この記事にもある通り現状のFlutter Webでは、コードが流用できるという工数面でのメリットはかなり大きいのですが、 デフォルトでブラウザ検索がサポートされていない、レンダリングが重い等の問題を感じていました。

また、Flutter Webは基本SPA(Single Page Application)であるため、SSR(Server Side Rendering)でHTMLを構成するよりはそこまでSEOが期待できないと考えました。
今回は、より多くの購入者の方に使ってもらうために、SEOと性能面を強化したいと思い、
Web特化のフレームワークで、SSRにも力をいれているNext.jsを採用しました。

比較対象として、Vue.js&Nuxt.jsについても検討してみましたが、
社内ではReactの経験者が多いということと、ChatGPTにも聞きながら、(あとは直感と趣味嗜好で)
Next.jsで行こうとなりました。

ちなみに社内にWebフロントエンドエンジニアはいなかったので、Flutter Webを採用するよりはかなりリスクが高く、
なかなか挑戦的な決断だったなあと思います(遠い目)
この決断にも社内のエンジニアメンバーは嫌な顔ひとつせず賛同してくれました。(涙)

インフラ

インフラはGCPのCloudRunを採用しました。(Terraformで構築して、GitHub Actionsでデプロイ)
これについてもまた記事を書ければと思います。

Next.js13 x AppRouter の壁

Next.jsでの開発を決め、インフラも整い、さぁ開発だ!とチュートリアルなどを読み漁り、簡単なデモページなどを作っていったのですが、
Next.js13から採用されたAppRouterを採用するのかどうか、というところで大きな壁が待ち受けていました。

5月ぐらいに参考実装した段階ではまだAppRouterもNext.js13も使っておらず、従来のPageRouterでの参考実装となっていたのですが、

nextjs.org

nextjs.org

7月に開発を始めようとなった段階ではAppRouterは安定版となっており、
参考情報もちらほら出始めていました。
create-next-appでも recommended と表示されるのもあり、 これは、後で変更するのもなかなか大変そうなので今の段階でAppRouterを採用するのが良いのでは?
ということで、Next.js13 x AppRouterの構成で行くことに決めました。

npx create-next-appでApp Routerがデフォルトに

nextjs.org

AppRouterを採用して詰まったところと回避策

AppRouterを採用し、PageRouterからの参考実装の移し替えを始めましたが、多々問題があることに気づきました。

(当然ですが) AppRouterに対応していないライブラリが多かった

2023/10時点では、MUIがAppRouterに対応し、対処方法が公開されていましたのでこちらを参照ください。

mui.com

大体の問題はこれにつきるかなと思います。 UIフレームワークとしてMUIを採用しましたが、

github.com

MUIはCSR(Client Side Rendering)のページでしか使うことができず、 SSRしたいページでは以下のエラーが出てそのままでは使うことができませんでした。

TypeError: createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component

そのため、SSRが必要なページでは、以下のように、use client を記載したコンポーネントを独自で用意して、そこを経由して読み込む というような回避策をとりました。

components/common/Material.tsx

"use client";

export {
  Box,
  Button,
  ButtonBase,
  Divider,
  Grid,
  ...
} from "@mui/material";

export {Mail, TextsmsOutlined, ArrowForwardIos} from "@mui/icons-material";

getServerSidePropsが使えない

AppRouterでは getServerSideProps は deprecatedとなっており、 特にそれを使わなくても SSRの場合は、async/awaitでのfetch処理をコンポーネント内にそのまま記述すれば良いとなっています。

PageRouterの場合は、特に意識せず、getServerSideProps を使えば SSRだろうがCSRだろうがNextのサーバーサイドと通信できて欲しい値がとれていた?と思うのですが、 AppRouterの場合は、「基本的にはSSRで、末端にCSRを使う」という思想のため、 認証方式や、APIをfetchする方式がSSRしたい画面と、CSRを使わざるを得ない画面で異なり、また、こちらもライブラリによってはCSRにしか対応していないものがあり、 getServerSideProps が使えたらなぁという箇所がいくつかありました。

これについては、特に初期表示にCSRさせる必要のない画面では、Server Componentsを利用してAPI fetchし、Client Components内では Tanstack Queryの useQuery を使う方式でいきました。

nextjs.org

SSRのローディングはどうする?

SSRではまず初めにAPI fetchを行うため、「CSRのように、部分的なレンダリングができず、fetchの間、画面が真っ白になってしまう」という問題が起きました。 ただこれについては単純に、「ServerComponentsの描画時のローディングには <Suspense> を使うと良い」というのが公式にも書いてあるので、 それを参考にすれば特に問題なく、実装できました。

nextjs.org

AppRouterを採用して良かったこと

苦戦したAppRouter採用でしたが、
基本的に画面の情報取得のAPIのfetchをする際にはSSRで実装していくのが、不要なjsのロードを少なくし画面をより軽量にできるのでパフォーマンス的にも良いのかなと思いました。 PageRouterでも意識すればできるのかもしれませんが、AppRouterでは、getServerSideProps が使えないなど、基本的にはServer Componentを使って、 Server SideでのAPI fetchを行うということが推奨されているので、 そのあたりでパフォーマンス + SEOの恩恵を受けられるのは良いと思っています。


Client Componentsには use client を使う方式になっていますが、その点もこのコード上でSSRCSRがわかりやすいのも 従来よりはいいのかなと思っています。

短期開発合宿というスタイルをしてみて

開発合宿の様子

弊社は東京 or 広島 or リモートの各拠点があり、普段開発メンバーはリモートで開発していますが、 今回は、広島に集まって同期コミュニケーションをとりながら開発しました。

他部署からの刺激 & 同期コミュニケーション

普段の落ち着いた環境の中でのリモート開発も良いですが
広島ではセールスメンバーやコーポレートメンバーの平常業務に囲まれており、
間近でフィードバックをもらえたり、魚の売買の臨場感も感じられるので、開発にも精が出る部分があります。

「合宿の振り返りでは普段よりコミュニケーションが取りやすく、スピード感があった」
という声も多く上がりました。
また、普段であれば合間にMTGなどが挟まってきますが、合宿期間中は、定例MTGなどは一切いれないよう調整したのも集中が途切れず、よかったです。

Figjamを使って振り返り

(👆にもありますが、フロントエンド経験のない私たちに最近話題のChatGptが大活躍でした)

差し入れが嬉しい!

リモートからでもスタバが贈れる時代、素敵です🎉

合宿は計画的に

1週間という期間制約を持たせるのは良いことですが、
1週間では、立ち上げまではできても、本番リリースまでは今考えるとどう考えても不可能でした。
どこまでの成果物を作り上げるのか、最初に決めるのは難しくても、もしくは期間中にでも見直してゴールを明確にしていくのが大切だと感じました。

終わりに、今後

今回は、全国の産直鮮魚仕入れプラットフォームであるUUUOアプリのWeb版を作りましたが、 より広い水産事業者に使っていただけるよう、 水産業内の受発注をより簡単に行える atohamaアプリのWeb版もUUUOのマルチテナントとして作っていく予定です!

atohama.uuuo.co.jp

ウーオでは水産流通を革新するため、プロダクトを通じてあらゆるアプローチをしています。ウーオの事業やプロダクト開発にご興味がある方は、以下をぜひご覧ください 👇

uuuo.co.jp

UUUOではモバイルアプリだけでなく、今後はWeb frontendも強化していきたいので、 今回のように開発合宿でしのぎを削りたい方はぜひともご応募お待ちしております!

👇👇👇👇👇 フロントエンドエンジニアの応募はこちら! 👇👇👇👇👇
<業務委託スタート可>ソフトウェアエンジニア(Frontend/Flutter on the Web) / 株式会社ウーオ

では。