🎸

メン募サイトをNext.jsとFirebaseで作りました

4 min read 2

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の制限に引っかかりました。しょうがないので別プロジェクトに分割して対応しました。

最後に

まだアクティブユーザーが少ないので、音楽をやっている方はぜひ登録していただけると嬉しいです。

https://muu.app

なぜか以下のエラーが頻繁にロギングされているので、「Googleではじめる」ボタンを押した後にプロフィール登録フォームに移動しなかった方は連絡をいただけると大変ありがたいです

Error: The popup has been closed by the user before finalizing the operation.

(ポップアップが開いてからGoogleアカウントを選択せず閉じただけでも発生するので、必ずしもバグではないようですが・・・)

Discussion

デザインがクッソ綺麗ですねえ

ありがとうございます!

ログインするとコメントできます