Firebase Hosting + GitHub で音楽配信システムを作ってみた
はじめに
Firebase Hosting + GitHub で音楽配信システムを構築してみたのですが、技術的には既に出尽くしている内容なので、どのような要件定義→技術選定をして構築したのかという切り口で文章を書いてみようと思います。
最初3000字ぐらいで書こうとしたのですが、気づいたら12,000字ぐらいの結構読み応えがある内容になってしまいました。お時間がある時に読み物としてどうぞ。
要件定義フェーズ
要件定義とは
要件定義とは、サービスオーナーが抱える課題を解決するためのシステムの 要件と制約 を 可能な限りシンプルな形 で洗い出す作業です。
それらが不十分だと、システム構築プロジェクトが失敗する可能性が高くなります。
- 要件の洗い出しが不十分 → 望んでいないモノ が生まれる
- 制約の洗い出しが不十分 →
- 納期が遅延して 機会損失 する
- 予算度外視などの 望んでいないモノ が生まれる
- 要件が実現不可能なため プロジェクトが頓挫 する
- HR(人的リソース)不足で プロジェクトが始められない or 進まない
- シンプルでない → 開発チームに方向性が正確に伝わらないことで 望んでいないモノ が生まれる
私見ですが、個人的に最も不幸だと思われるケースを赤字にしてみました。他のケースとの明確な違いは「結果的に何も生産できていない」ことです。要件定義ミスはサービスオーナー(経営者)なりコンサルタントの責任ですが、現場の立場としてもかなりツライものがあります。
要件と制約を如何にシンプル&漏れのない形にブレイクダウンできるかが肝要ですが、利害関係者(ステークホルダー)の数に従ってその難度が指数的に上がってしまうので、要件定義の時点ではステークホルダーを必要最小限に絞ることも重要です。
ただし、逆もまた然りですが。
例えば、開発チームに一切相談せずにトップダウンで始まるようなプロジェクトがよくありますが、そのケースでは要件と制約がアンバランスな状態になっていることが多く、そのまま調整をせずにプロジェクト開始してしまうと失敗に終わる可能性が高いといえます。
今回、サービスオーナー、システムコンサルタント(SIer)、開発チームの全てが私という、失敗することの方が難しい極めて特殊&簡単な環境における、要件定義からプロジェクト完了までの具体的な例を示します。要するに、要件定義といっても単なる自問自答なのですが、以下、その自問自答した内容を第三者にも分かるように(可能な限りローコンテキストな形で)文書化してみます。
サービスオーナーの要件事項
私は個人(趣味の同人活動)で「東方BGM on VGS」(東方VGS)という少し変わった音楽配信サービスを運営しています。
このサービスは、東方Projectの楽曲をVGS (SUZUKI PLAN - Video Game System; 私が作った想像上のゲーム機) に搭載されている波形メモリ音源用にアレンジしたもの(東方Project二次創作)を配信する音楽配信サービスです。
2013年から運営しているので、今年で9周年(10年目)になります。
6年ほど休止期間があったので、純粋に9周年とは言い難いですが、それでも入れ替わりが激しいスマホアプリ界隈ではそこそこ息の長いサービスかもしれません。
ほぼ同時期に始まり現存しているサービスとしては、ラブライブスクールアイドルフェスティバル、モンスターストライク、LINEマンガなどがあります。
前年(2012年)にパズドラが大ヒットしたことで、二匹目のドジョウを掬うべく本気度の高いサービス(ゲーム中心)がリリースされ始めた年だったので、この辺りから「個人がスマホアプリで一儲け」みたいなことが厳しくなっていったように思われます。
スマホアプリのレッドオーシャン元年とでも言うべきでしょうか。
そんなスマホアプリ業界に赤い霧が漂い始めた2013年春、境界の外側からひっそりとサービスインして、3年後の2016年頃に活動休止した東方VGSですが、6年の充電期間を経て今年(2022年)1月から久々にサービス再開してみました。
休止期間中も復活を切望されるユーザーさんの声があったことが、復活できた最大の理由です。
それ自体嘘偽りのない事実ですが、敢えて少し下世話な話しをすると、当時、東方VGS自体では一切マネタイズをしておらず、私が出している他のスマホアプリの売上とトータルで赤字にならないように運営していました。ですが2014年頃からスマホアプリの売上がサッパリになり、原資が底をついたので一時中断してました。(サービス終了したつもりは無くて、余裕ができたらライフワークとして再開しようと思っていたのでアカウントは残していました)
しかし、昨年(2021年)末にふと 東方Projectの二次創作ガイドライン を改めて確認したところ「スマホアプリで広告掲載OK」となっていたので、DL数がそんなに多くないもののリテンション率が高い東方VGSなら、わんちゃん広告で Apple Developer Program への加入に必要な年間12,980円のコスト(固定費)を安定的に捻出できるのではないだろうか...と思ったことが再開のキッカケです。赤字垂れ流しのままサービス継続できるほど裕福ではないので、とても助かります。寛大な許諾をして頂いている上海アリス幻樂団様には感謝しかありません。
そこで、まずは(コストが掛からない)Android版だけ復活させて収益性について検証してみたところ、早々に赤字にはならない見通しがたったので、iOS版も復活させることにしました。
iOS版復活の目安として、Android版単体で3月までに四半期あたりの損益分岐点(3,245円)をクリアできれば恐らく年間売上目標(12,980円)をクリアできるだろうというKPI設定をしてました。
広告で稼ぐにはトラフィックが必要で、出だし(1月)はそんなにトラフィックを稼げる筈が無いから、お金が掛からない範囲で地道にプロモーション(Twitterで呟いたり記事を書くなどして以前使ってくれていたユーザーさんに思い出して頂くetc)をして、3月で損益分岐点をクリアしよう...と考えていたのですが、想像よりも早くKPIをクリアできたので、急いで Apple Developer Program に再加入してiOS版も復活させた形です。
一番の課題だった Apple Developer Program の予算捻出はクリアでき、幸いなことに余剰な利益も頂けそうな見込みなので、余剰利益については全額「開発投資」に回したいと考えています。
ソフトウェア業界では、開発投資 = 人件費(工数) と読み替えてほぼ間違い無いと思います。
しかし、東方VGSは私のワンマン運営の同人活動(趣味)なので、人件費はプライスレスです。どちらかというと、主軸であるコンテンツ制作に没頭するため「その他の労力を削る(時間を作り出す)ことに掛かるコストを許容すること」が、ここでいう「開発投資」になります。
ご飯を食べに行く時間が惜しいからUberEatsを使うのも、私の考えでは一種の開発投資です。私は使ったことがないけど...合法的にできる「焼きそばパン買ってこい」みたいに見えるので、ジャイアニズム不足な私には少々敷居が高いです。
東方VGSは長らく、楽曲の配信に自前のコンテンツ配信サーバ(CDS)を持たず、AppBundleやAssetsに直接曲データを組み込み、アプリストアのアップデートで追加楽曲の配信を行うシステム形態で(つまり、AppStore や GooglePlay を CDS として)運用していました。
通常の音楽データ(mp3やm4aなど)と比べて、コンテンツデータ(独自形式のMMLファイル)のサイズが1曲あたり 10KB 前後と小さい(仮に1,000曲に組み込んでも圧縮すれば1分前後のm4aと同程度しかない)から、そういう芸当ができていたのですが、その方式だと曲を追加する都度審査に提出する必要があり、とても面倒です。
以前と比べてApple審査は早くなりましたが、逆にGooglePlay審査には結構時間が(最大72時間程度)掛かることがあったりなかったり、何よりユーザーさんにもアプデをする手間を掛けさせてしまいます。
それでもCDSを自前で持たなかった理由は、CDSを持つとサーバーコスト(通信コスト)の負担が発生するためです。
通信コストは従量制(変動費)で、データ伝送量に応じて(だいたい50TB前後までは)リニアに上昇します。
しかし、広告でマネタイズしている場合、基本的にトラフィックに比例して売上も上がるので、ARPUがアクティブユーザーあたりの通信コストよりも高ければ自前のCDSを持っても赤字になる可能性は低いと考えられます。
そこで、東方VGS向けの最初の開発投資として、自前のCDSを構築するのが良いと考えました。
要件と制約のブレイクダウン
前節の内容から、要件と制約を読み取ることはできたでしょうか?
実際はもっと余分なノイズが多かったり、ノイズだと思ったことが重要だったりするので、他人の心を読み取る程度の能力が欲しいものです。(今回は自問自答なので私にとっては楽勝現場ですが)
私が上記から抽出した要件定義は次の通りです:
1. 経営方針: 余剰利益の範囲で開発投資をしたい
※開発投資: コンテンツ制作に没頭するための時間を作り出すことに掛かるコストの許容
2. 課題: 新曲追加の都度アプリをアプデするのが面倒くさい
3. 要件: 新曲追加は自前のコンテンツ配信サーバ(CDS)で行うようにしたい
4. 制約: BAC + ランニングコスト < 余剰利益
5. BAC: ¥0 (推定)
6. 目標リクープ期間: 0日 (推定)
通常、5 と 6 が 0 になることは無いです。(人件費無料で開発ができ、有料の設備投資や消耗品が一切不要という特殊なケース限定で 0 になります)
要件と制約の調整
通常であれば、主に過去の開発実績などから BAC; Budget At Completion(完成時総予算=初期投資額)の推定値の試算とBAC回収までのリクープ期間の目標設定(※請負開発の場合はリクープ期間ではなく発注額の設定)をして、 要件と制約を調整 し、調整された要件定義がステークホルダー間で合議されることでプロジェクト開始となります。
要件と制約を調整する例
- 要件:
- BACが高すぎて予算が降りない → 要件のシュリンクを提案
- 要件がシンプルにならない → 要件のフェーズ分割を提案
- 制約:
- 要件の中に実現不可能なもの(科学的・技術的に不可能だったり法令上NGなどの制約)がある → 代替案や回避案を再検討
- プロジェクトを進めるのに必要なHRが不足している → 必要な人材や外注先を予め確保
なお、この時点のBACは飽くまでも推定(精度の低い見積り)で、BACは設計フェーズが完了した段階で確定します。
要件定義の共有
ローコンテキストな要件事項のテキストと、シンプル(ハイコンテキスト)な要件定義書は必ずセットで プロジェクトに携わるメンバー全員が参照や意見(コメント)が可能な形で共有 しなければなりません。(私はこれは SHOULD ではなく MUST だと考えています)
私見ですが markdown形式 で GitHub (Enterprise) や GitLab などで共有するのがベストです。プログラマ経験者以外にGitを使わせるのは難しいかもしれませんが、ドキュメント管理ツールとしてGit以上に良いものは無いと思うので、ちゃんと教育して経営陣や間接部門の人でも使えるようにした方が良いと思います...が、現実問題として多分難しいので、その場合はQiita Teamとかかなぁ。(リッチテキストベースのものだと差分が見難いので raw text ベースで修正履歴を管理できるものが望ましくて、Slackとかだと流れてしまうのでNGです)
私がこれを MUST だと考える理由は、解釈の齟齬を回避できる可能性が高くなる ことでプロジェクトが失敗するリスク(※)を大幅に減らすことができるためです。
※「顧客が本当に欲しかったもの」を参照
また、
- エラー(サービスオーナーの説明の誤りやSIerの解釈ミスなど)に早い段階で気づける確率が上がる
- 現場視点でより良い提案が得られる可能性がある
- 新規メンバーがプロジェクトの目的をすぐに理解できる
等のメリットがありますが、逆に何もデメリットがありません。
「こんな無能なヤツの下で働けるか!」と人材に逃げられてしまう可能性ならあるかもしれません。その点はデメリットかもしれませんが、一方で的確な提案をして出世する人も出てくる(人材発掘機会になる)可能性もあるので、トータルでメリットの方が大きいかなと思います。
サービスオーナーやコンサルタントは役職的に偉い人だったりする場合が多いですが、彼らの発言やドキュメントを神言や聖典と勘違いする必要はありません。彼らも単なる人間なので普通にミスはしますし、専門性が高い内容なら理解が不十分だったり間違っていたりすることもよくあります。
設計フェーズ
抽出された要件と制約を元にシステム設計をして、それを元にコスト(※BACとは別で運用後に発生するコスト)の試算+技術選定をした後、開発フェーズに掛かるコスト(開発工数)の試算を行い BAC を確定させます。
開発モデルの決定
私なら設計からコーディングまで目算でだいたい2〜3日で全部完了できる程度の簡単な作業内容なので、開発モデルとしては1サイクル・ウォーターフォール(1cw)を採用します。
もっと大規模な開発の場合、フェーズ分けして長くても1〜2週間のサイクルでウォーターフォールを繰り返す形(ショートスプリント・アジャイル)にするのがベターだと思っていますが、その方式だと最終的なBACが中々確定しないリスクがあります。
かといって、BACが確定しやすい(昔ながらの)ロングスプリント・ウォーターフォールだと、変化の早い市場環境に対応できなくなります。要件定義は聖典ではないので、市場環境の変化に従って柔軟にアップデートできなければなりません。
仮に2週間スプリントで平均5人×平均120万円/人月のフルスタック・エンジニアを動かす場合、1スプリントあたりのBACは300万円ということになりますが、その金額感だと昔ながらのアウトソーシング企業に発注するには小規模過ぎる(むしろ契約締結等のオーバーヘッドが大きすぎる)ので、設計・開発はその辺りの融通が効く自社HRで行う Dev&Ops が基本だと思います。
ただし、今回の案件では BAC ¥0 にしかならないので、その辺を気にする必要がありません。
システム設計
東方VGSの楽曲データ(MMLファイル)は、tohovgs-cli という GitHub リポジトリ (Public) で原本を管理しています。
そのため、tohovgs-cli の master ブランチが更新されたら GitHub Actions で曲データ(MML)と曲リスト(songlist.json)を CDS → CDN にデプロイして、アプリから songlist.json をダウンロードすることで追加楽曲を検出して MML をダウンロードするような感じにするが最も効率的(無駄な時間を削れそう)だと考えられます。
ただし、songlist.json は若干サイズがデカイ(現時点で約21KBもある)ので、全クライアントがそれを毎回取得するのはよろしくありません。
5G時代にたかだか21KB...かもしれませんが、たかだか21KBでも仮にDAU10万程度のアクセスがあれば、それだけで2GBになります。1GBあたりの通信コストなんてせいぜい10〜20円ぐらいで、DAU10万もあれば1日で少なく見積もっても数十万円程度の売上が出る筈です。ついでにCDNからクライアントに転送されるタイミングで圧縮も掛かるので、利益率への影響はかなり微々たるものだと考えられます。
しかし、利益率というのはそういったマイクロな無駄のチリツモで低下していくものです。工夫して何とかできるものは工夫して、それにより得られる利益をもっと有意義な投資に回すべきだと思います。もちろん、開発工数が発生する(通常の)プロジェクトの場合、工夫をするのにもコストが掛かるので、その辺のバランスを考える必要がありますが。
何より、通信コストの負担は運営サイドだけではなくユーザーサイドにもして頂くことになるので、トータルの通信量が1パケットでも削れるのであれば、徹底的に削っていく姿勢が大切だと思います。
そこで、CDS に yyyy.mm.dd
形式(10バイト) のバージョン情報ファイル(songlist.ver)も配信するようにして、アプリからは songlist.ver を見てクライアントが保持しているリストよりも新しいバージョンなら songlist.json + MML をダウンロードする仕組み(変更検出機能)も作ることにしました。
システム概要(リクエストとデータの流れに着眼したシーケンス図)は、下図のようになります。
コストに影響する部分は、主に図のCDSとClient間の通信部分です。
正確にはその間にCDNがありますが、分かりやすくするために省略してます。
コスト試算
自前のコンテンツ配信サーバ(CDS)を持つにあたって、
- 1MAUあたりの売上(ARPU)
- 1MAUあたりの通信データ総量
を把握する必要があります。
ARPU については、AdMob + Firebase を使っていれば管理コンソールで把握でき、通信総量は設計したシステムの仕様から概算できます。
仮に、次のような条件下で試算してみます:
- songlist.jsonのサイズ: 30KB
- 1曲あたりのデータサイズ: 10KB
- 1ヶ月に1回だけ新曲追加を行う
- 10曲分の追加ダウンロード曲がある
- MAU構成比率: 既存=5割(1曲/月ダウンロード), 新規=5割(11曲/月ダウンロード)
既存ユーザー = アップデートチェック(1KB) + songlist.json (30KB) + 追加楽曲 (10KB) = 41KB
新規ユーザー = アップデートチェック(1KB) + songlist.json (30KB) + 追加楽曲 (105KB) = 136KB
平均ユーザー = (既存ユーザー + 新規ユーザー) ÷ 2 = 88.5KB
平均1ユーザーあたり月間 88.5KB の通信を行う という試算結果になりました。
正確には、CDN から Client の間の通信ではデータが(BRやGZ等で)圧縮されるのが普通なので、実際の通信データサイズは試算結果の 1/10 程度になると考えられますが、圧縮分は安全マージンとして(無圧縮の試算結果で)コスト計算することにします。
その通信コストが、1MAUあたりの売上(ARPU)よりも高い場合は赤字になり、安い場合は黒字になります。
通信コストが幾らになるのかは、土管(クラウドサービス)の種類によって異なります。
クラウドサービス選定
通信コストは従量課金制で、目安としてだいたい 50TB/月 ぐらいまではリニアにコストが上昇します。
50TB を超えてくるとハネ上がりますが、今回の試算結果だと MAU 6億(606,633,799)以上にならなければ超えないので、実質そのラインは気にする必要は無いと考えられます。
コンテンツが動画などのデカイものだと Akamai あたりの太い CDN が必要になりますが、小規模通信だとそれは逆にコスパが悪いです。(使ったことがないですが、ターゲット市場が中国なら小規模〜大規模でもTencentあたりが良さそうに見えたので少し興味があります)
その点を踏まえ、以下の CDN サービスをターゲットとして選定することにしました。
Service | Cost | 損益分岐点 (1MAU毎のコスト) |
---|---|---|
Firebase Hosting | 10GB まで無料 & $0.15/1GB
|
$0.00001266002655 |
AWS S3+CDN(CloudFront) | 約$0.137/1GB | $0.000011562824249 |
GCP GCS+CDN | 約$0.113/1GB | $0.000009537220001 |
さくらインターネット ウェブアクセラレータ |
¥5/1GB | ¥0.00042200088501 |
単純にコスパだけ見ればさくらインターネットが一番安そうです。
ただ、(サイズが小さすぎてCDNの必要性が疑わしいレベルの)コンテンツ・データサイズの関係で、MAU 11,848 以下なら Firebase Hosting の無料枠(10GB)で捌けてしまえます。
ニッチサービスなので MAU は多分そんなにいかない筈ですが、万が一、何らかの形でバズって2016年頃のニコニコ動画と同じぐらいのMAU(参考: だいたい900万ぐらいらしい)になってしまった時に掛かる費用を試算してみたところ、一番高い Firebase Hosting でも月間$114(1万円ちょい)程度 にしかならない計算になります。
2016年頃のニコニコ動画と同じぐらいのMAUがあった場合、少なく見積もって(仮にARPU¥1程度として)も900万円以上の売上になるので、利益率は 99.89% ぐらいということになります。
それぐらいなら、どのクラウドサービスを選んでもコスト感に大差は無いので、通信コストは気にせず「一番使いやすい(機能性が良い)サービスを選択すればOK」と判断しました。
単純に機能の多さだけ見ればAWSに分があります。(ついでに本業でも AWS を使うことが多いので慣れています)
ただし、AWSは機能が多いけど構築するのがやや面倒です。例えば、CDN(CloudFront)のキャッシュ削除(invalidation)の手続きを実装するのとか。(面倒な反面、大規模なシステムならCDNのinvalidationをファイル単位でできるコストメリットもありますが、今回のようにミニマムな案件ではそのメリットもありません)
東方VGSは、コンテンツデータ(MML)を GitHub リポジトリで管理しているので、GitHub Actions との連携が最もシンプルに出来るサービスが「東方VGSにとって一番使いやすいクラウドサービス」だと考えられます。
という訳で、10GBの無料枠(※)があることと、GitHub Actions との連携が一番簡単にできた Firebase Hosting を土管として採用しました。
※「通信コストは気にせず」とか先述しましたが、やはり0円と1円の差は大きい ^^;
東方VGSにおけるCDNの必要性に関する議論(※)はさておき、Googleの強力なグローバルCDNが使えるのも心強いかもしれません。
東方VGSだとデータサイズが小さいことに加え、そもそもシステム仕様上CDSがダウンしても「サービス停止」にはならないフェールセーフ設計になっているので、強いCDNを持つシステム的なメリットはあまり無いと考えられます。(CDSがダウンしていても新曲がダウンロードできないだけで、プリセット楽曲やダウンロード済み楽曲は問題なく再生できる)
Firebase Hosting 以外の不採用理由 & フォロー:
- AWS: 機能は多いが小規模システムだとコストメリットが無く、ただ構築が面倒だったので不採用。ただし、今の所AWS案件は多い(≒覚えておけば食いっぱぐれない)と思うので、学習用なら趣味で使うのもアリだと思います。
- GCP: 実質Firebaseのスーパーセットだから、もっとシステムが成長してから検討でOK
- さくら: CDNのみだから origin を自前 or レンタルサーバーで準備しなければならないのが面倒なのと、海外ユーザーも結構居るので国内拠点のCDNという点がネックとなり不採用。ただし、転送コストが安いので「データ転送量がそこそこ多く日本国内ドメスティックなサービス」(ソシャゲとか?)であれば採用メリットがありそう。
なお、別の土管への移行は簡単にできる形にしておきたかったので、ドメインは Firebase Hosting のデフォルトではなく自前(カスタムドメイン)にしておきました。(ドメイン費用は10年間で1万円ほどの固定費ですが別件の趣味で既に持っているものを使っています)
BAC確定値の試算
確定した設計内容から、開発に掛かる予算の再見積り(精度の高い見積り)をして、BACの確定値を試算し、必要に応じて追加予算の請求などをしますが、今回はBAC ¥0 なのでその工程は省略します。
開発フェーズ
ここまで来れば、後は粛々と構築していくだけです。
サーバ構築
Firebase Hosting + GitHub Actions の構築については、既に参考になる記事が多いので、私が構築する際に見た記事の紹介だけしておきます。
クライアント実装
東方VGSは、アプリ自体 OSS (GPLv3) として公開しているので、実装した Pull Request をそのまま紹介します。本職でも触ったことがあるAndroidはともかく、趣味 only の iOS は Swift じゃなくて Objective-c で作っていたりするので少し恥ずかしいですが。 (Swift化はその内...)
なお、通信処理は、iOS では標準API(NSURLSession)、Android では OkHttp3 を使いました。
Android は Retrofit2 を使おうとしたのですが、使い所(モデル通信)が1箇所しかなかったので OkHttp3 をそのまま使用。
(iOS)
(Android)
リリース後日談
稼働後(初日)の通信状況は下図のような感じでした。
※正確には稼働初日に開発時のトラフィックを超えた時点のスナップショットです
- ストレージ: GitHubからFirebaseのorigin(CDS)+CDNへアップロードする時の通信量(10GB/月まで無料)
- ダウンロード: Firebaseからスマホへダウンロードする時の通信量(10GB/月まで無料)
19日〜23日のトラフィックは開発中のテストで発生したもので、24日以降が本番で発生したトラフィックです。
開発時に負荷的なテストをして沢山通信したので、「もしかするとこれがピークかも」と一瞬思ったりしましたが、流石に本番の方が開発時より多くトラフィックが発生したので安心しました。 (サーバ開発あるあるですが、開発時トラフィックがピークだと「このサービス大丈夫なのだろうか?」と不安になります)
コストを許容する投資をしたつもりですが、結果的に 10GB/月 への道のりはかなり遠いので、無料枠だけで十分収まりそうな雰囲気です。これにはサービスオーナーも大満足なのですが、余剰利益は全額開発投資にぶっ込むつもりなので、さてどうしたものか...
という訳で、MIDIキーボードを新調してみました。ひゃっほう!(単に欲しいモノを買っているだけの様に見えますが、東方VGSの運営で一番時間が掛かるのが耳コピ作業なので、コレも開発投資と言えなくないですが、音楽機材を買う時のワクワク感は否定できないです)
あとはARPUを(赤字にならない範囲で)下げるという通常のサービスではあまり考えることがない方向性についても検討中です。
ARPUについては高すぎても良くなくて、ARPUを上げることで短期的な売上は上がりますが、長期的にはリテンション低下に繋がります。東方VGSは私のライフワーク(趣味)なので細く長く続けていくことが重要だから、ARPUを上げすぎるのも良くないという理屈(これは持論ではなくパズドラ成功の文脈からの学び)です。もちろん、採算度外視で赤字になるような大盤振る舞いはできませんが。これは厳密には開発投資ではないかもしれませんが、投資しきれない余剰利益は貰っても意味が無いので、それならユーザーに還元すべきだと思っています(要するに投資戦略の一貫という認識です)。
Author And Source
この問題について(Firebase Hosting + GitHub で音楽配信システムを作ってみた), 我々は、より多くの情報をここで見つけました https://qiita.com/suzukiplan/items/8ba8f135da8a7749fb0a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .