React Router v7 × Elixir × Cloudflareで音楽系の支援サイトを作った話
初めまして。新作ゲームを横目に未だにエルデンリングの周回で遊んでいるSRKと申します。好きな武器種は落葉格闘と軽大剣です。
今回は深掘りした技術的な話というより、主にこんな構成で個人開発したよ!というご報告です。
完成物
既にクリエイターエコノミー系のサービスはめちゃくちゃあると思うんですが、UI含めて音楽に特化したものは少ないなと感じたので着手したのが経緯です。
あと聴き放題系のサブスクはアーティストさんに還元される金額も微々たるものですし、大手プラットフォームへの配信にディストリビューターを使うと販売実績に関係なく固定の手数料が発生するので、いくつかある販路の一つとして併用してもらい、アーティストさんの活動のお手伝いができたらなと思い立った次第です。
開発期間
2024年7月中旬頃に着手して、一通りの機能を作り終えたのは2025年1月頃だったので約半年ほどでした。
そこからリリースした3月10まではバグ取りをしたり、決済会社の審査待ちといった期間です。
NuSoundの技術構成
今回作ったサービスであるNuSoundの技術構成を紹介します。
フロントエンド
特にVercel使いたい!という感情がなかったので、フロントエンドにはReact Router v7を採用しています。
開発着手当時はRemix v2でしたが、正式リリースと同時にReact Router v7にアップグレードしました。
またCSSフレームワークにTailwindcssを使用していますが、他のUIライブラリは特に使用していません。強いていうとアイコンライブラリぐらい...?
UIデザインを考えるのが好きなので、なるべく自作したいな〜という欲求が大きな理由です。フロントエンドの使い方については特筆する点はあまりないかも...。
バックエンド
バックエンドにはElixir、WebフレームワークとしてPhoenixを採用しています。
Elixirといえばよく並行処理が取り上げられると思いますが、この辺りは書いてて楽しいものでした。
並列してデータを取得したい時も、以下の例みたいに簡単に実装できるので、Web業界のキャリアスタートがWebデザイナーだった自分にはとっつきやすかったです。
defmodule MyApp.DataFetcher do
import Ecto.Query
alias MyApp.Repo
alias MyApp.{User, Post}
def fetch_user_and_posts(user_id) do
user_task = Task.async(fn -> get_user(user_id) end)
posts_task = Task.async(fn -> get_posts_by_user(user_id) end)
user = Task.await(user_task)
posts = Task.await(posts_task)
%{user: user, posts: posts}
end
defp get_user(user_id) do
Repo.get(User, user_id)
end
defp get_posts_by_user(user_id) do
Repo.all(from p in Post, where: p.user_id == ^user_id)
end
end
音源ファイル関連
基本的にアーティストはマスター音源をWavファイルに書き出すことが一般的です。
ただこれをそのままストリーミングで配信するのはつらいので、ストレージにアップロードしてもらったマスター音源を非可逆圧縮ファイルと、高音質で楽しみたい人向けに可逆圧縮ファイルへ変換するサーバーを用意しています。
こちらは比較的重い処理を実行するのがメインなのでGoを採用しています。
画像ファイル関連
ジャケ写やプロフィールなど画像関連はCloudflare Workersを介してストレージとやり取りしてます。
また配信する際も、Workersでリクエストを受け取って返してます。
Cloudflare Imagesの機能を使って最適化した後、Cache APIを用いてエッジサーバーにキャッシュを保存しつつ、画像を返すようにしています。
その他、決済周りの成り行きで法人作った話とか
いわゆる「支援」系のサイトになるので決済機能の導入が必要不可欠です。
比較的容易に決済を導入できるサービスといえばStripeが代表かと思いますが、ここは規約上無理でした。
というのも、Stripeを介して発生した売上をクリエイター等に還元する場合、StripeのConnect機能を使うことが必須となり、Connectを使わず、サービス運営者が振込などで売上を還元する場合は規約違反になるとのことです(Stripeの営業さんに問い合わせて確認済み)。
マネロン防止とのことなので納得なんですが、じゃあConnect使うか?と悩むんですが
- Connectを使うにはStripeアカウントをアーティストに取得させることになる。
- アーティストの売上管理をConnect上でしてもらうことになる(Stripeの画面に遷移させることになる)。
加えて、音楽系サービスということで国際化を気にした時に
- クリエイターがConnectで収益を受け取るには、銀行口座等が日本円を受け取れる必要がある。
- あるいは、プラットフォーム側がクリエイターの現地通貨で受け取ることになる。
となり、プラットフォームの運用以外にも資産管理面までStripeの都合に依存するのはちょっと...となったので国内の決済会社を利用するに至りました。
ただ国内の、特に大手決済会社だと法人のみというケースがほとんどです。個人は相手にしてもらえないので「じゃ、会社作る!」ということにして、NuSoundを運営するニューサウンド株式会社を作りました。
今はこういうサービスを使えば1週間足らずで印鑑から定款まで作ってくれるので、会社も簡単に作れて便利ですね...。
最後に
今回バックエンドにElixirを採用しましたが、コードを書いててとても楽しい言語でした。
技術的に深掘りした話はまた別の機会があればやりたいと思います。
以上、運営としては法人なのですが、開発は全て私個人1人のみで行なったお話でした。
せっかく会社作ったのでサービス運営はもちろん、音楽を中心にクリエイターのお役に立てることを色々やっていきます。
Discussion