メン募サイトをNext.jsとFirebaseで作りました
muuというバンドメンバー募集サービスを作ったので、使用したライブラリや所感をまとめました。
機能概要
ユーザーページ
活動拠点や楽器、ジャンルなど、基本的な情報をまとめたユーザーページを作成できます。
バンドページ
各ユーザーはバンドを作成して、そのメンバーになることができます。
バンドはユーザーを招待して、メンバーとして紐付けることができます。
活動実績のタイムライン
これまで投稿した動画や楽曲などをタイムライン形式で表示できます。YouTube や Spotify、SoundCloud などは埋め込みプレーヤーに対応しており、再生数やいいねの数なども取得して表示します。
実績は個人またはバンドのいずれか紐付きます。バンドに追加された実績はメンバーの個人ページでも共有されます。
募集
メン募サイトなので、当然「バンドメンバー募集」と「バンドに加入希望」の 2 種類の募集を掲載できます。
工夫した点として、募集用の OG 画像を動的に生成するようにしました。メン募サイトに限らず、SNS やマッチングサービスなどは既存ユーザーがいなければ新規ユーザーも増えないというジレンマがあります。「Twitter などでも募集を宣伝できる」ことをウリにすれば、既存ユーザーの少なさを補えるのではないかと思い実装しました。
検索
検索はできます。必須なので・・・。
作った理由
「ミュージシャンのキャリアを可視化することで、マッチングを最適化する」ことが最大の目的です。
既存サービスの問題点
OURSOUNDSのような既存のメン募サイトは、正直言って初心者と高齢者が多いイメージがあり、あまり使う気がしません。というより、バンドや音楽系の Web サービス全般がなんとなく古臭く、かっこいい音楽を作っている人が使っているとは思えません。
また、こういったサイトのアカウントはたいてい使い捨てで、募集が終われば放置されます。これまでどんなバンドをどれくらいやってきたのか、といったことは記録されません。
Twitter をやっているバンドやミュージシャンは多いので、最近はむしろこちらのルートから探す人が多いと思います。ただ、検索性はあまり高くないですし、Twitter だけではどんなスキルがあるのかは分かりにくいです。
仕事を頻繁に請けているような人であればポートフォリオサイトを持っていたりしますが、こちらも検索性は低いです。
muu が解決したいこと
muu では、1 人のミュージシャンのスキル・実績を簡単に記録し可視化できるサービスを目指しています。
現状はわかりやすさのために「メン募サイト」を名乗っていますが、クラウドソーシングや演奏依頼サービスなどにも展開していけると思っています。ミュージシャン同士だけでなく、ライブハウスや音楽制作会社などの法人も、安心して仕事を頼める相手を探しているはずだからです。
使用したライブラリと所感
ここからは技術的な話題になります。
Next.js
特殊な選択でもないのであまり書くことはないかなと思います。
1 つ特筆するなら、各プロフィールページは ISR で配信されていますが、ログインユーザーが管理しているページだけはクライアントサイドで最新のデータをフェッチしています。これは確か Zenn でも同じことをしていたと思います。
Firebase
バックエンドには Firebase を採用しました。
採用理由
- プロトタイピングにはちょうどよさそう
- それほど複雑なサービスではない(と思ってた)
- 無料で使える
採用した感想
正直、ここまで Firebase で開発を進めた感想は「かなりキツい」です。
- 多対多の構造が出現した時点ですでにキツさを感じた。中間テーブルの役割をするコレクションを作らざるを得ないので、NoSQL の利便性(の一部)を放棄するしかない。
- セキュリティルールがあまりにめんどくさい。宣言的といえば聞こえがいいが、単純に制限が多すぎるだけかもしれない。バックエンドコードを書いたほうが楽。
- 特定のカラムにユニーク制約をつけられないという制限もキツい。
- クエリの制約上、現在実装されている検索機能は Firestore では実現できなかった。そのため現状はSupabaseとデータを同期している。
- バンドルサイズがデカい。
リストにも出てきたSupabaseは Firebase の代替サービスですが、内部は Postgres であり、単なるマネージド DB として使うこともできます。しかも Firebase 並の無料枠があるため、RDBMS をタダで使うなら最良の選択肢の気がします(現在移行を検討中)。
React Query
SWR みたいなやつですが、より多くの機能を備えています。
一例として、react-query の方がより柔軟にキャッシュをクリアすることができます。
const todoListQuery = useQuery("todos", fetchTodoList);
const todoSingleQuery = useQuery("todos/7", fetchTodoList);
// 全てのtodoをinvalidate
queryClient.invalidateQueries("todos");
// idが7のtodoだけinvalidate
queryClient.invalidateQueries("todos/7");
SWR の場合、useSWR('todos')
を invalidate しても useSWR('todos/7')
はされないという違いがあります(react-query は {exact: true}
オプションを渡すことで SWR と同じ挙動が可能)。
個人的にこの機能が非常に使いやすく感じたため、SWR でなくこちらを採用しました。
Tailwind
スタイリングには Tailwind を採用しました。
賛否両論ありますが、個人的には好きです。一人でプロトタイピングをするには最適な選択だと感じています。
一番の利点は速度(パフォーマンスではなく開発の)だと思います。css-in-js にしろ CSS Modules にしろ、スタイルを書くときは JSX から離れる必要がありますが、Tailwind ならそこのオーバーヘッドがありません。
柔軟性という意味では他の選択肢には劣るし、場合によっては余分な時間がかかることもあります。多人数での開発には向かないこともあるかもしれません。
node-canvas
OG 画像は、node 環境で Canvas API が使える node-canvas というライブラリで動的に生成しています。
ただ、正直 Canvas は使いにくいので、慣れていない一般的な Web エンジニアはヘッドレスブラウザなどを使った方がいいと思います(文字のオーバーフローや動的な位置調整などがかなりめんどくさかったです。)
また、ライブラリのサイズがかなり大きいので、muu では Vercel の制限に引っかかりました。しょうがないので別プロジェクトに分割して対応しました。
最後に
まだアクティブユーザーが少ないので、音楽をやっている方はぜひ登録していただけると嬉しいです。
Discussion
デザインがクッソ綺麗ですねえ
ありがとうございます!