🥳

僕の「結婚式Web招待状」を支える技術

2023/05/12に公開

はじめに

先日、5/6に結婚式を挙げました🎉

多くの方に来ていただきとても楽しい会となりました。
ただ、経験したことのある方達はわかるかと思いますが、色々な準備があったりします。

そのうちの一つが招待状です。

紙で送って、送り返してもらう。というのが一般的かと思いますが、
友人など、ネットリテラシーが高めの人は返送の手間を省くという点で、Web招待状を用意してあげた方がありがたがってくれることも多いみたいです。

今回は、このWeb招待状を自分で作ったよ✌️という話になります。

経緯

最初は、自分で作るなんて時間のかかることせずに、この辺りをマネージしてくれるサービス使おうと思っていました。
そして、送付する1週間前に「結婚式Web招待状」で検索して、上位のサイトいくつかで登録して作ってみたのですが、以下のような課題がありました。

  • デザインがダサい(僕もデザイン能力はほぼ皆無なので、人のこと言えませんが)
  • 二次会についての情報掲載や参加可否のデータが取得できない
  • オンライン出席などの補足が書けない
  • バグって端末によってはアニメーションが動かないところがある

このことから、二週間という短い期間ではありますが、作ろう!と心に決め、行動に移しました。

全体的な構成

さて、前置きが長くなりましたが、ここからは技術のお話をさせていただきます。
全体的な構成は以下のような形です。

フロントエンド

Next.js

一番使い慣れているというのと、サーバーサイドの処理はそこまで多くはないので、Remixを使うほどではないだろう。ということでNext.jsを採用しました。

Nx

モノレポ管理するほどのサービスではないのですが、Linterなどの周辺技術を準備している時間が惜しかったので使いました。
一応、リモートキャッシュは使えているはずなので、本番のビルド時間短縮はできているんじゃないかなと推測してますが、そこまで重きをおいて選んだわけではないです。

Tailwind

CSS苦手マンなので、コンポーネントを含んだライブラリにしようかと思ったのですが、せっかく作るのに、結婚式のテーマから外れたデザインになるのは嫌だったのでカスタマイズしやすいTailwindを採用しました。

個人的には、Tailwindで書くのも難しかったですが、結果的に選んでよかったと思ってます。

その他ライブラリ

  • zod
    フォームのバリデーションと後述するtrpcのバリデーションに使いました。
  • react-hook-form
    仕事でも使っていてzodとの相性も良いので使いました。
  • react-use
    カスタムフックを自分で作るのは大変なのでいくつか使わせていただきました。
  • lottie-react
    アニメーションに使いました。
  • react-google-maps-api
    googleマップ表示
  • react-intersection-observer
    スクロールに応じたアニメーションに使いました
  • swiper
    画像のスライダー
  • etc...

バックエンド

Next.js API Routes × trpc

Next.jsのAPI Routesをtrpcで拡張しました。

詳しくは以下のリンクを見て貰えば良いかと思いますが、APIコールする時に型安全であって欲しかったので、trpcでAPI Routesを拡張しました。

https://trpc.io/docs/nextjs

Prisma

データベースとの通信とDBスキーマ定義にはPrismaを使いました。
そこまで話すことはないですが、簡単に扱えるので、小規模のアプリケーションであれば使い勝手がいいなと思いました。

schema.prisma
model Guest {
  id   Int     @id @default(autoincrement())
  user_id    String @unique @default(dbgenerated("uuid_generate_v4()"))
  last_name String
  first_name String
  last_name_kana String
  first_name_kana String
  submitted Boolean
  is_attend Boolean?
  is_attend_after_party Boolean?
  allergy String?
  note String?
}

Next.js Middleware

今回、各ゲストの管理のため、あらかじめseedでゲストを作成しておき、uidの発行を行いました。
Next.jsのmiddlewareでは、uidをクエリパラメータとして受け取り、httponlyのCookieに格納。
uidを消したURLへリダイレクトする。という形にしています。

以降のリクエストでは、Cookieに入っているuidを元に、アクセスコントロールが行われます。

Next.js ServerSideProps

ServerSidePropsに来る段階では、上記、middlewareにて、uidがCookieに格納されていることは保証されているため、Cookieからuidを取り出し、Prismaを用い、supabaseと通信。
submittedの状況に応じてアクセスコントロールをしています。

インフラ

Cloudflare

基本的にはドメイン取得にのみ使っています。
気力があれば、Workersでアクセスコントロールをしたりしようかなーと思って使いましたが、結果、時間がなかったので、その辺りはVercelにお願いしました。

Vercel

アプリケーション開発にNext.jsを使おうと思っていたので、簡単にマネージできるVercelを選択しました。
ほとんどが静的なファイルになることは明白だったので、Cloudflare WorkersとCloudflare KVなどでホスティングしてもよかったですが、なにせ時間がないことは明白だったので、
middlewareなどもよしなにエッジサーバー上で処理してくれるVercelが魅力的でした。

Supabase

データの永続化にはSupabaseを使いました。
理由はFreeプランでDBが立てれるからです。

そんなに多くアクセスがくるわけでもないですし、立てるのも楽なので個人利用するのであればすごく良いなと思います。

今回できなかったこと

1. パフォーマンス改善

開発当初は意識しており、90点以上は確保する方針でやっていました。

  • Google Map等、重たい部分については、dynamic importをする
  • 画像のサイズ調整(大体のことはNext/Imageがしてくれるのでそこまで意識する必要はないですが)
  • Bundle Analyzerを見て、サイズが重いライブラリの遅延読み込み

等までは頑張って対応しましたが、途中から本当に時間が足りなくなったので諦めました。。😢

2. Next.js Middlewareでのアクセスコントロール

トップページ(招待状ページ)とマイページ(送信後ページ)のアクセスコントロールをmiddlewareで完結させることで、エッジサーバー上でのアクセスコントロールが実現できるため、そうしたかったのですが、Prismaのedge clientがうまく動かせなくて、ちょっと調査に時間がかかりそうな予感がしたので時間の都合上諦めました。

3. Nxのキャッシュフル活用

Nxをキャッシュをフル活用するために、Next.jsのアプリケーションを機能ごとをマイクロサービス的にライブラリに切り出したいなと思っておりましたが、こちらも時間の都合上できなかったです。

4. Cloudflare Workersによるアクセスコントロール

上でも少し触れましたが、今回、middlewareとgetServerSidePropsで行ったアクセスコントロールはCloudflare Workers側で実行したいなと思っていました。

こうすることで、トップページ(招待状ページ)とマイページ(送信後ページ)を別のNext.jsアプリケーションとしてデプロイすることができ、招待状ページに影響を出さず、マイページの追加改修ができるなぁと思っていました。

ただ、こちらも優先度が低いので時間の都合上やめました。

5. Cloudflareのフル活用

VercelをやめてCloudflare Workers、KV、R2あたりを使って配信できたらいいなと思ったりもしたのですが、こちらもNext.js×Vercelの恩恵を捨ててそちらに舵を切るほど僕に知見がなかったので今回は諦めました。

6. 認可のパフォーマンス向上

認証jwtを発行し、jwtの検証のみエッジサーバー上で行うという形にしようと思ったのですが、一人一人のアカウントを作成して、マジックリンクを生成する。というのがちょっと面倒だったのでやめました。

結果、uidをurlパラメータに含め、いちいちDBに問い合わせを行うという仕様に、、パフォーマンスもかなり悪くなるし良くないなと思いました。

まとめ

上司や友人からも良い反応をいただけたり、奥さん側の友人からもポジティブな反応がもらえたので、大変だった1週間が報われたな。。

と思ってます。

技術的なところで言うと、普段はNext.js × GraphQL(Hasura) × Go(レイヤードアーキテクチャ)という感じで堅牢めな構成で開発しているので、ミニマムな開発を体験できて楽しかったです。

結婚式の時期にNext.js v13.4もリリースされたので、今度はappディレクトリで何か作りたいな

Discussion