Next.jsで個人サービス作ったので技術スタックを公開する
一昨日「買って応援!」というサイトをリリースしました。
コロナの影響で打撃を受けている生産者や飲食店、販売店の方々が「廃棄になるよりは・・・」という思いで各所で販売されていますが、情報が散らばっていたのでまとめている【買って応援!】するためのサービスです。
割とモダンな構成で作っているかなと思うので、使用している技術・サービス周りを少しまとめてみました。
Frontend
今回は初めてProductionでNext.js(React)を使いました。
元々自分はNuxt.js(Vue)を書くことが多く、いつもだったら「Nuxt.js(Vue) + Firebase + Heroku + Algolia (+ 必要に応じてFastly)」を採用することが多いんですが、直近のNext.js 9.3から強力なSSGサポートが入ったこともあり、個人でやるんだしせっかくなら、ということでフレームワークも変えてみようことで変えました。
Next.jsのチュートリアルは以下を参考にしながら公式のチュートリアルを参考にしました。
自分の場合はNuxt.js(Vue)に慣れていたので、Next.js(React)で書くときどう書くか・・・みたいな変換が多かったので、この記事がだいぶ役に立ちました。
---
fallbackをtrueにすることにより事前にビルドしてないパスにアクセスされても404になりません。
以下がユーザーが事前にビルドしていないパスへアクセスしたときのフローです。
useRouterを使い一時的にpageのfallbackバージョンを表示させる
getStaticPropsがサーバー側で実行され静的なファイルを生成する
生成が完了したらfallbackバージョンから切り替える
以後のリクエストに対しては事前にビルドしたページと同じように静的なファイルを返す
SSGに関して上の記事にも書かれていますが、特にこのあたりの挙動が優れているな・・・と。
つまり、一覧ページはSSR / 詳細ページはSSGにしておくことで、一覧ページはある程度リアルタイムにデータ反映がされつつ、詳細ページは事前にSSGされたコンテンツであれば静的コンテンツを返し、デプロイ以降に追加されたデータであれば初回リクエストでビルドして2回目以降のリクエストではビルド済みの静的コンテンツを返す、というような挙動になります。
SSR or SSGというどちらか一方を選択するのではなく、SSR + SSGというハイブリッドが選択肢になるのは明確なメリットでした。
---
CSS周りは今まではBulmaを採用することが多かったんですが、今回はTailwindのみを使うことになりました。当初は、Tailwind+Bulmaのテンプレートを入れて書いていたんですが、これBulmaなくても問題ないな・・・ということで途中で外して書き直してこの構成になっています。
Backend
Firebaseを使っています。Cloud Functionsは使っていないので実際はFirestoreのみです。
ユーザ情報も持っておらずデータ構造がシンプルなので、Spreadsheetをデータベース代わりに使うという選択肢も十分にあったのですが、SpreadsheetをAPI経由して習得すると安定性が悪く(10回中2回はリクエスト弾かれる) かつ レスポンスが遅いのでSSRにしたときにページ表示が遅くなるのが嫌だったので辞めました。一覧ページも含めてSSGにする想定であればSpreadsheet運用もありだと思います。(この設計で運用した実績もある)
Hosting
Next.jsと親和性が高いということでVercel(旧Now)を使用しています。個人リポジトリだと無料というのが地味にデカイです。フェアユースポリシーによると無料プランで転送量100GB/月くらいまでは許容するよ、とのこと。100万〜300万PV/月くらいまでは余裕でこの中に収まりそうです。
HostingはいつもどおりHerokuも検討したんですが、Herokuの場合は
- 最低でも$25/month掛かる
- (Enterpriseでない限りは)リージョンがEurope/United Statesのどちらかしか選択できない
ということもあり、Vercel上でnodeも動くということでSSR要件も満たしているのでこちらを採用しました。
---
Vercelのビルド周りで若干ハマったのがTimezoneが絡む日付の処理で、ビルド実行するサーバの現在時刻になってしまうので日本時間で表示したい箇所の時間がズレてしまうというのがありました。
普通は環境変数に TZ でタイムゾーンを渡してやれば良いんですが、Vercelだと TZ が予約語扱いで設定できなかったので、ビルドコマンドを実行するときにTZ='Asia/Tokyo' という形で渡すようにして対応しました。
Data
データはいまのところ楽天/ヤフーショッピング/STORES.JPあたりから取ってきています。
公式APIがあるものはそれ使っているんですが、「コロナ 支援」などで検索するとタイトルコロナ支援入れてるけど年中セールみたいな商品や、割引代わりにクーポン系、ふるさと納税の商品なども入ってしまうので、終了期限フィルタや出品開始日フィルタなど、考えうる範囲でフィルタ掛けまくって必要な情報だけにしています。
ただ、まだ小規模の農家/生産者さんだったりとかが楽天/ヤフーショッピングあたりにストアを持っていないところも圧倒的大多数だとは思うので、データ観点での拡充が現状の最優先課題かなぁとは思ってはいます。
自分が観測している範囲だとBASE / STORES.JPでストアを開設して販売しているケースが多いので、そのあたりは力を入れていくつもりです。あとは、FacebookグループだとFacebook内のコメント or メッセンジャーで販売対応している方も多いですが、ここまではちょっと拾いきれない・・・ので、一旦はストアに絞ってデータを拡充していく見込みです。
---
カテゴリはこんな感じの管理画面を作って手動でぽちぽちと付けていたんですが、ある程度パターンが学習できたので80%くらいは自動で付けれるようになりました笑
まとめ
今回の構成の一番のいいところは「とにかくお金が掛からない」ことだと思っています。Firebaseはほぼ無料枠だし、Vercelも上述の通り無料。
いつもサービス作るときはHeroku+Algoliaで大体6,000円/月は最低でも掛かっていたから、このメリットは小規模でやるときにはかなりでかい・・・!
個人運用においては月額コストを垂れ流し続けると運用するモチベーションが下がってしまうというのが過去に多々あったので、コストを切り詰めてほぼ0にできるのはかなりデカイです。
思いついたのがGWの後半だったのでちょっと出遅れた感はあったんですが、着手からローンチまではだいぶ早くて、実作業20時間くらい?でリリースできました。ずっと苦手だったCSS周りがここ半年でだいぶ鍛えられたのが地味にでかいです笑
サービスに対する要望や掲載依頼などはサイト内のお問い合わせ出して頂けると嬉しいです!
この記事が気に入ったらサポートをしてみませんか?