【個人開発】VTuberの歌枠から歌唱部分のみを抽出して一覧化したWebサービスを開発しました！【Next.js×Supabase×Cloudflare】

![推しの歌声が、すぐ見つかる.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/0205ae7d-0d68-c898-dc41-57bb4ff6d5b8.png)

# <font color="#FF2E2B">🚀はじめに</font>
こんにちは！<a href="https://x.com/Kei_dev_1213">Kei_dev_1213</a>と申します！
このたび、<font color="red">`約半年間のReact学習の集大成`</font>として個人開発しましたWebサービスをリリースいたしましたので、本記事でご紹介いたします。
これまでの個人開発で得られた学びや知見についても共有させていただきますので、ぜひ最後までお読みいただけると嬉しいです！

# <font color="#2B6300">💻️サービス概要</font>
さっそくサービスの概要をご説明いたします。
大きくはタイトルの通りですが一言で言うと、`VTuberの歌枠配信から歌唱部分を抽出した埋め込み動画を一覧化したサービス`です。  

##### 主な特徴  
- **クロップ再生機能**: 動画URLに再生開始位置の秒数を設定し、トーク部分を除き歌唱シーンのみを再生（※ `react-youtube`ライブラリを活用）  
- **検索機能**: 複数の条件で楽曲の検索が可能  
- **いいね、お気に入り登録機能**: ユーザー毎に楽曲のいいね、お気に入り登録が可能

##### 用語解説  
- **VTuber**: 2D/3Dアバターを使用した動画配信者  
- **歌枠**: YouTubeライブで複数曲を披露する音楽配信コンテンツ  
- **歌唱部分の抽出**: 動画URLに`start`パラメータを付与する技術的実装  

**サービスURL**: 

https://vtune.jp/

・Webサービス利用イメージ

TOPページから検索画面に遷移し、楽曲を検索して選択した楽曲の再生画面を開く。
| PC |
| :---: |
|![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/567e9190-9e2f-9021-ef45-2b3f958fbf42.gif)|
| スマホ |
|![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/082db991-7445-1dbf-cd86-19fffb8b46c4.gif)|

# <font color="#35B7F8">🕶️サービス作成の目的</font>
VTuberの歌枠は、通常1時間以上の長尺動画で、その中に複数の楽曲が含まれています。
特定の楽曲だけを聴きたい場合、毎回動画の中から該当部分を探す必要があり、これが非常に手間になります。
また、`長時間動画は視聴者が離脱しやすいということから、歌唱部分だけを抽出して、簡単に楽曲を聴くことができる仕組みが出来れば、新たな視聴者獲得の手助けができるのでは？ということでこちらのサービスを思いつきました`（<a href="https://wistia.com/learn/marketing/optimal-video-length">こちら</a>によると、30-60分動画のエンゲージメント率は約26%と示されています。歌枠は基本的にそれ以上の長尺動画なので、アーカイブ動画のエンゲージメント率は更に低いことが推察できます）。また、それに伴って、様々な条件を指定して楽曲を検索できるような機能もつければ、今まで知らなかったVTuberを新たに知る機会にもなりうるということで、V界隈の盛り上がりに寄与できるのではないかと考えました。

 まとめると以下の通りです。
 
- **時間の効率化**: 歌唱部分のみを即座に再生できるようにすることで、視聴者の時間を節約。
- **楽曲の発見**: VTuberが歌った楽曲を簡単に検索・発見できる仕組みを提供。
- **新人支援**: 新人VTuberの歌唱動画も同様に見つけやすくすることで、知名度向上の一助となることを目指す。

これらを目的として、本Webサービスの開発に着手することを決めました。

# <font color="#FF66C0">🙎制作者のスペック</font>
では具体的なサービスのご紹介に入る前に、私自身のスペックについてご紹介いたします（いらねーよという方は飛ばしてください）。

具体的には以前書いた↓の記事のとおりですが、端的に言うと`約半年前はReactに触れたことすらない状態`でした。
約10年ほどIT業界で働いており、フロントエンド・バックエンドともに一通りの開発経験はあるので何もわからないという状態ではなかったのですが、Reactを始めとしたモダンな技術に関する知識はほぼありませんでした（だからこそ、このままじゃヤバいかなと思って勉強を開始した次第です）。
また、`個人開発というものを今までしたことがなく、勉強といえばUdemyか技術書を脳死で見る`という感じで今までやってまいりました。そんな私がこの半年間はReactについてアウトプットを中心として勉強してきたのですが（その経緯も↓の記事に書いています）、`今回ご紹介するWebサービスはその集大成`という形です。また、サービスのご紹介だけでなく、この半年間で得られたものや知見についても本記事の最後で色々語っていますので、そちらもぜひお読みください。

https://qiita.com/Kei-dev-1213/items/89cc83f674693bd97e06

# <font color="#5E10E5">📱サービスのご紹介</font>
それでは、開発したサービスの機能を具体的にご紹介いたします。

サービス名：Vtune
サービスURL：

https://vtune.jp/

画面遷移図：
![画面遷移図 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/3c0e7e19-bbd1-13ca-8eb6-fed9dc19e583.png)

以下、本Webサービスの主要な機能を6つご紹介いたします。
| 機能 |
|----------------|
| [機能1. TOPページで形式別に楽曲を表示](#機能1-TOPページで形式別に楽曲を表示) |
| [機能2. ログイン機能](#機能2-ログイン機能) |
| [機能3. 楽曲検索画面で楽曲一覧を表示](#機能3-楽曲検索画面で楽曲一覧を表示) |
| [機能4. 楽曲再生ページで楽曲を再生](#機能4-楽曲再生ページで楽曲を再生) |
| [機能5. 楽曲登録ページで情報を登録](#機能5-楽曲登録ページで情報を登録) |
| [機能6. 管理者用ページで承認・一括登録](#機能6-管理者用ページで承認一括登録) |

### <font color="#FF2E2B"><INS>機能1. TOPページで形式別に楽曲を表示</INS></font>
まずはTOPページの機能のご紹介です。
TOPページでは、Pick Up、ランキング別、VTuber別、その他ソート順を指定して楽曲一覧を表示する機能を備えています。ここではセクション別にご紹介いたします。
<br>

- #### _Pick Upセクション_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/f5beb6ec-2cef-7393-02c1-3abd2f61c0df.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/ea170ca6-5786-4f0a-4568-6456a41370f3.gif)|

  ランダムで取得した楽曲7件をスライダー形式で表示する機能です。
  この機能は正直無くても良かったのですが、TOPページなので映えのためにヒーローエリアとして設置してみました。
  スライダー用のライブラリでyoutubeの動画を埋め込んでいるだけなのですが、それっぽくなっていますね。
  VTuberの宣伝も兼ねてチャンネルボタンを設置しており、こちらをクリックすると該当のVTuberのyoutubeのチャンネルページが開くようになっています。また、「楽曲再生ページへ」ボタンをクリックすると楽曲再生画面に遷移します。
  
  また、`楽曲ごとにいいね、またはお気に入り登録`をすることが出来ます。ご利用にはログインが必須ですが、後ほどご紹介する検索画面にていいねした楽曲、お気に入り登録した楽曲を条件指定して検索することが出来ます。
<br>

- #### _ランキングセクション_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/b0d983a6-a92d-d847-5ecc-abcbea50a059.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/ac23c884-e247-6f6f-088d-3f030ff016ae.gif)|

  いいね、お気に入り登録された楽曲のトップ３をランキング形式で表示する機能です。
  ランキングでは、新人VTuber（※）/事務所所属VTuber/無所属VTuber別に、いいね数順、お気に入り登録順それぞれのランキングを表示します。「4位以降を見る」ボタンをクリックすると検索画面に遷移し、`ランキングと同じ条件で検索された検索結果の楽曲一覧を表示`します。

  （※）⋯新人VTuberですが、本サービスでは`デビュー日が2年以内、またはチャンネル登録者数が2万人以内のVTuber`と定義しています。これが実態に沿っているかというと正直微妙で、実際は登録者数が1万人もいれば十分大手と言っていいレベルだと思うのですが、登録者数がそれ以上（10万人や20万人以上）の超大手のVTuberをたくさん登録している関係上、相対的に新人ということでこのような定義にしています。
  <br>

- #### _VTuber一覧セクション_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/b92b7ef3-c185-0c2e-5b03-69b34bcc236c.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/f100fd69-49fa-bf6e-4af6-08432b12a007.gif)|

  サービスに登録されたVTuber一覧を表示する機能です。所属事務所別にタブを分けて一覧を表示します。
  VTuber名のボタンをクリックすると検索画面を表示し、`クリックされたVTuberの楽曲一覧を表示`します。また、新人VTuberにはそれと分かるようにバッジを表示しています。
<br>

- #### _楽曲一覧セクション_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/06dec73c-6eb5-53cb-477b-4eecfe081730.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/4351f5d2-e1f2-d7e3-fbcc-d753b99b2962.gif)|

  サービスに登録された楽曲一覧を表示する機能です。
  右側のセレクトボックスから任意の並び順を指定することが出来ます。
  楽曲の行をクリックすると楽曲再生画面を表示し、クリックされた楽曲を聴くことが出来ます。
  また、一覧の下の「↓もっと見る」ボタンをクリックすると、追加で20件を表示します（最大300件表示）。
  <br>

- #### _リリースノートセクション_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/0ec6a5a9-8f13-da7d-8993-d30167f77d37.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/3277c7bb-6340-a62b-7321-abf611f6ea6b.gif)|

  リリースノートを表示する機能です。
  新機能（New Features）、バグの修正（Bug Fixes）を表示します。

### <font color="#2B6300"><INS>機能2. ログイン機能</INS></font>
本Webサービスでは、supabase Authを使ったGoogleアカウントとXのアカウントでのログインに対応しています。
ログインはいいねとお気に入り登録、あとは後述する楽曲情報を登録するための機能です。未ログインの状態だといいね、お気に入りボタンクリック時にエラーになるようにしていますが、ログイン済の状態だとそれぞれ正常にできるようにしています。
<br>

  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/5604b978-bb22-57e0-be8d-7d67e0d55b41.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/727dbe93-7832-6bd3-bb94-b9ccc1b2287c.gif)|

  いいね、お気に入り登録した楽曲に関しては、検索画面で条件指定することが出来ます。
  また、余談ですが、supabaseのドキュメントが充実しすぎていて、2日くらいで認証機能を実現することが出来ました（ドキュメントの通りに上から実装しただけで出来ました。スゴイ）。

### <font color="#35B7F8"><INS>機能3. 楽曲検索画面で楽曲一覧を表示</INS></font>
次に、楽曲の検索機能のご紹介です。
楽曲検索ページでは、様々な条件を指定して楽曲一覧を表示する機能を備えています。
<br>

  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/6f6d8846-320d-103d-ef06-df54fad29e61.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/a490613d-1558-a452-c94b-40210100c5ef.gif)|

  現状は以下の条件で楽曲を検索することが出来ます。
  | 検索項目 | 詳細 |
  |----------|------|
  | テキスト検索 | 楽曲名/VTuber名/事務所名<br>（半角スペース区切りで複数条件指定可能） |
  | 所属事務所 | 事務所での絞り込み |
  | VTuber | VTuber名での絞り込み |
  | 新人VTuber | 新人VTuberの楽曲に限定 |
  | いいね済 | いいねを付けた楽曲に限定(ログイン必須) |
  | お気に入り登録済 | お気に入り登録した楽曲に限定(ログイン必須) |
  
  ここで一つ<font color="red">`反省ポイント`</font>があります。上のイメージや表を見ていただくか、実際に操作していただくと分かるのですが、<font color="red">`いいねボタンとお気に入り登録ボタンの機能の差異がない`</font>んですよね。。
  というのも、`いいねとお気に入りは、検索画面で条件として指定するための機能になるのですが、このままだとボタンの名前が違うだけで、どちらも条件に追加するだけなので別に分ける必要がない`ということになってしまいます。。
  （この辺は正直あまりよく考えず、「とりあえずいいねとお気に入りで分けてみるか」という思いだけで作ってしまいました。今後の修正で、`お気に入り登録は複数のお気に入りリストを作れる`、みたいな感じで機能を分けようかな、とか考えています。最初にきちんと設計を考えてから作ることが大切ですね...）

  また、検索フォームの下には指定された検索条件に従って楽曲一覧を表示する楽曲一覧があり、右側のセレクトボックスから任意の並び順を指定することが出来ます。楽曲の行をクリックすると楽曲再生画面を表示し、クリックされた楽曲を聴くことが出来ます。一覧の下の「↓もっと見る」ボタンをクリックすると、追加で20件を表示します（最大300件表示）。この一覧はTOPページの楽曲一覧と同じ仕様ですね。
<br>

### <font color="#FACC14"><INS>機能4. 楽曲再生ページで楽曲を再生</INS></font>
本サービスのメイン機能となる、楽曲の再生機能のご紹介です。
楽曲再生ページでは、react-youtubeというライブラリを使って、DBに保存した歌枠動画のURLと、楽曲の再生時間から、歌唱部分を再生開始位置としてページに埋め込んでいます。
<br>

- #### _楽曲再生動画エリア_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/11611721-ab06-40ff-f3ce-6e713c2e71d9.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/ead38a50-a742-6442-afac-1c2b3b879906.gif)|

  指定した楽曲を再生する機能です。
  楽曲を再生するだけでなく、動画の下の操作エリアで楽曲のオプションを指定することが出来ます。

  **プレーヤーコントロール**

  | 機能 | 説明 |
  |------|------|
  | プレーヤー | 通常の再生コントロール |
  | 音量 | 音量調整 |
  | 再生時間 | 楽曲の再生時間を指定<br>・時間経過後に自動停止 / 次の楽曲ページへ自動遷移 |
  | 自動再生スイッチ | OFF: ページオープン時に楽曲を再生しない<br>ON: ページオープン時に自動で楽曲を再生し、再生時間に指定した時間が経過後、自動で次の楽曲を再生 |
  | ランダム再生スイッチ | OFF: リスト順に次の楽曲を再生<br>ON: リストからランダムに選択して再生 |
  | 画面更新ボタン | 画面を更新 |

また、プレーヤーコントロールの下のバッジをクリックすると、バッジに表示された条件で検索した検索一覧を表示します（「元動画」バッジをクリックすると表示中の楽曲のyoutubeの元ページを表示します）。これにより、同じ楽曲を歌っている別のVTuberの楽曲や、別の曲を歌っている同じVTuber、同じ事務所に所属するVTuberの楽曲に簡単にアクセス出来ます。
<br>

- #### _楽曲再生リスト_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/4f151738-9284-54b4-9f5e-1abdbb10ed1e.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/ddc26df9-e4b3-a373-d8dc-6645e431708e.gif)|

<font color="red">`検索画面に表示された楽曲と同じ一覧を表示する機能`</font>を提供します（検索画面に表示された楽曲一覧がそのままプレイリストになるイメージ）。
現在再生中の楽曲には再生中バッジが表示され、楽曲の行をクリックすると対応する再生画面へ遷移します。
`TOP画面や検索画面の楽曲一覧では「もっと見るボタン」で表示件数を増やしていく仕様(スクロール方式)なのに対し、再生画面の楽曲一覧ではセレクトボックスを使用して表示位置を制御`するようにしています（1度の表示件数は10件固定）。
この表示方式は、`特にスマートフォンでの使い勝手を考慮して採用`しました。`他画面のようなスクロールの方式では、画面の下にあるおすすめ動画へのアクセスが面倒になるほか、画面上部に戻りづらい`という課題がありました(というか自分で使ってみてそう思いました)。また、`例えば100件先の楽曲を表示したい場合に「もっと見る」ボタンを何度もクリックする必要がある`という欠点もあったので、こちらの方式にすることでより使いやすくしています(TOPページと検索画面は一覧の下には大した情報が無いので、スクロール形式で下に画面が伸びても使いにくくはならないという判断です)。
<br>

- #### _おすすめ動画リスト_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/a567830d-133a-14cd-12e1-b67039147607.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/079b454c-75ec-79db-5ef8-814cdeb72315.gif)|

  おすすめ動画一覧を表示します。
  楽曲カードをクリックすると、選択した楽曲の再生画面が表示されます。
  なお、以下の条件で楽曲を取得し、本画面に表示します。
  | カテゴリ | 表示件数 | 選択条件 |
  |----------|----------|----------|
  | 同事務所の楽曲 | 4件 | 表示中のVTuber以外の楽曲から |
  | 現VTuberの楽曲 | 2件 | 表示中のVTuberの楽曲から |
  | 他事務所の楽曲 | 7件 | 異なる事務所の楽曲から |
<br>

### <font color="#73A23B"><INS>機能5. 楽曲登録ページで情報を登録</INS></font>
事務所・VTuber・楽曲を登録するための機能です。
登録後に管理者の承認を経ることでサービスに本登録される仕組みとなっています（なお登録機能の利用はログインすることが前提）。
<br>

- #### _事務所登録タブ_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/13adabe5-f86e-2ee3-7eec-8de0a95d49a2.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/2e8bb761-0609-9912-6ec9-b8dc88bb2f01.gif)|

  入力した事務所を登録する機能です。
  本登録するには管理者（私）の承認が必要で、後ほど説明します管理者用画面で承認後にサービス上に表示される仕組みとなっています。機能としてはシンプルな登録画面ですが、登録後には紙吹雪を散らせるなどしてリッチに見えるように一工夫しています。
<br>

- #### _VTuber登録タブ_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/aaad2e1a-bc5e-d9e1-2f17-6e09aae78d3d.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/f4819855-dbce-aa49-bf3e-84e5400a6c3a.gif)|

  入力したVTuberを登録する機能です。
  所属事務所、名称、デビュー日、チャンネルURLを入力します。
  また、事務所登録同様、本登録には管理者の承認が必要となります。
  <br>
- #### _楽曲登録タブ_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/d42d4316-b1f0-76c6-a6d9-eeab901900b8.gif)|
  | スマホ |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/6e91f14b-c5fa-eb63-1a4f-f6b48a0e5e68.gif)|

  入力した楽曲を登録する機能です。
  VTuber、楽曲名、動画投稿日、動画URL、楽曲の再生開始位置を入力します。
  URL、再生開始位置を入力すると、入力楽曲確認項目にて、入力した内容の通りに楽曲が再生できるかの確認をすることが出来ます。
  また、事務所・VTuber登録同様、本登録には管理者の承認が必要となります。

### <font color="#F82D2A"><INS>機能6. 管理者用ページで承認・一括登録</INS></font>
管理者用画面です。
本画面は、ログインユーザーの中でも管理者設定されたユーザーのみがアクセス可能となるようにアプリで制御しています。
こちらでは、機能5で登録された事務所・VTuber・楽曲の承認を行い、サービスに本登録します。また、`管理者用機能として、楽曲の一括登録機能`を備えています。
<br>

- #### _事務所・VTuber・楽曲承認_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/fa12956e-0ab7-b3ef-acb7-0a518f90a0c4.gif)|

  楽曲情報の承認用機能です。機能5で仮登録された楽曲の情報一覧が表示されます
  承認ボタンをクリックすると、サービスに本登録され、削除ボタンをクリックすると、該当のデータを物理削除します。
<br>

- #### _一括登録_
  | PC |
  | :---: |
  |![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/2425675a-50b7-8a57-155a-b7d8d57b6495.gif)|

    楽曲の一括登録機能です。一つの動画内の複数の歌唱部分の再生開始位置を一括でサービスに登録する機能です。
  こちらの仕組みは、楽曲のセットリスト（開始位置と楽曲タイトル）を動画のコメント欄から取得し、生成AIで秒数とタイトルを抽出してサービスに一括登録しています（以下参照）。

  :::note info
    <details><summary>セットリストから再生開始位置・楽曲のタイトルを取得する方法</summary>
    
    ①Youtubeのコメント欄から楽曲のセットリストのコメントをコピー
    ![スクリーンショット 2025-02-02 11.14.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/44df87ea-fb7f-6a9b-f076-03eb66a78d5b.png)

    ②生成AIへのプロンプトで①のコメントを投げて開始位置と楽曲のタイトルを半角スペース区切りで取得

    - プロンプト
    ```
    以下のリストの時間を秒数に変換して、半角スペース区切りで秒数と曲名を出力してください。
    また、曲名ごとに改行してください。
    なお、曲名に含まれる以外の記号、括弧、歌手の情報や注釈は出力不要です。
    また、曲名が誤っている場合は正しいものに修正してください。

    00:10:02 Song 01. グロウアップ - Hysteric Blue (2000)
    00:16:26 Song 02. The Biggest Dreamer - 和田光司 (2001)
    00:21:50 Song 03. やっぱり世界はあたし☆れじぇんど!! - fripSide NAO project! (2008)
    00:26:50 Song 04. オトメロディー - 高橋美佳子 (2005)
    00:31:58 Song 05. おもかげ - 林原めぐみ (2002)
    00:36:46 Song 06. 青薄 - 堀江由衣 (2003)
    00:42:05 Song 07. 空耳ケーキ - Oranges & Lemons (2002)
    00:47:14 Song 08. 希望峰 - Strawberry JAM (2003)
    00:52:57 Song 09. カクテル - Hysteric Blue (2003)
    00:57:11 Song 10. Drastic my soul - 酒井ミキオ (2001)
    
    01:02:58 Song 11. 明日へのbrilliant road - angela (2003)
    01:10:36 Song 12. Season - 瀧川ありさ (2015)
    01:16:50 Song 13. memories - 大槻マキ (1999)
    01:22:53 Song 14. 童話迷宮 - 田村ゆかり (2006)
    01:27:16 Song 15. 帰り道 - 八九寺真宵 (加藤英美里) (2011)
    01:36:01 Song 16. スクランブル - 堀江由衣 (2004)
    01:40:09 Song 17. WILL - 米倉千尋 (1999)
    01:47:20 Song 18. エアーマンが倒せない - Team ねこかん【猫】 (2008)
    01:52:27 Song 19. 僕はここにいる - rino (2003)
    02:01:53 Song 20. 変わらないもの - 奥華子 (2006)
    ```

    - 回答
    ```
    602 グロウアップ
    986 The Biggest Dreamer
    1310 やっぱり世界はあたし☆れじぇんど!!
    1610 オトメロディー
    1918 おもかげ
    2206 青薄
    2525 空耳ケーキ
    2834 希望峰
    3177 カクテル
    3431 Drastic my soul
    3778 明日へのbrilliant road
    4236 Season
    4610 memories
    4973 童話迷宮
    5236 帰り道
    5761 スクランブル
    6009 WILL
    6440 エアーマンが倒せない
    6747 僕はここにいる
    7313 変わらないもの
    ```

    ③テキストエリアにAIの回答を貼り付けて登録（バルクインサートでデータ登録。）
    </details>
  :::
　
  1動画ずつ現状は手動で登録していますが、`工夫すればYouTube APIや生成AIをうまく使って全自動（ないしは半自動）で楽曲を登録する機構を作れそう`な気がするので、今後の改善ポイントとして考えていきたいと思います。

# <font color="#F82D2A">⚒️技術スタック</font>

| カテゴリ | 技術 | バージョン |
|----------|------|------------|
| **フロントエンド フレームワーク** | Next.js | 14.2.14 |
| | React | 18 |
| | TypeScript | 5.5.4 |
| **UI/スタイリング** | Tailwind CSS | 3.4.1 |
| | Shadcn/ui | - |
| | lucide-react | 0.447.0 |
| | framer-motion | 11.11.9 |
| **API/サービス統合** | react-youtube | 10.1.0 |
| **アナリティクス/インフラ** | Google Analytics | - |
| | Cloudflare | - |
| **テスト** | testing-library/dom | 10.4.0 |
| | testing-library/jest-dom | 6.5.0 |
| | testing-library/react | 16.0.1 |

はじめは勉強のためにサーバーサイドの言語を別途分けてみようかなと思ったのですが、Nextの機能が充実していたので分けませんでした。
この程度の規模のアプリであればNextの機能で必要十分ですね。
また、ホスティングサービスは元々Vercelを使っていたのですが、cloudeflareの方が無料枠のスペックが高そうだったため、Cloudeflareに変更しました。

:::note info

<details><summary>Cloudeflareと他ホスティングサービスの比較（AIの回答）</summary>

Next.jsアプリケーションのホスティングサービスとしてCloudflareが他のプラットフォームと比較して持つ主な優位点は以下の通りです：

###### 1. エッジネットワークを活用した超低遅延
- 平均15msのレイテンシでリクエスト処理
- HTTP/3 & QUICプロトコルによる通信最適化
- 250都市以上のグローバルPOP展開
- Argo Smart Routingによる経路最適化

###### 2. コスト効率とスケーラビリティ
| 特徴               | Cloudflare Pages | Vercel         | Netlify        |
|--------------------|------------------|----------------|----------------|
| 帯域幅制限         | 無制限           | 有料プラン必要 | 100GB/月       |
| 関数呼び出し       | 100K/日          | 125K/月        | 125K/月        |
| 月間ビルド数       | 500              | 300分          | 300分          |
| 商用利用可否       | 可能             | 制限あり       | 制限あり       |

###### 3. 開発者エクスペリエンス
- OpenNextによるNext.jsネイティブサポート
- `wrangler` CLIツールでのローカル開発
- Workers KV/D1/R2との統合
- GitHub連携による自動デプロイ（1分以内）

###### 4. セキュリティ機能
- 無料Web Application Firewall
- DDoS保護とボット管理
- 自動SSL証明書発行
- ゼロトラストネットワークアクセス

###### 5. 先進的なエッジコンピューティング
- Smart Placementによるレイテンシ最適化
- Durable Objectsでステートフル処理
- Edge Cachingによる50倍速度向上
- AI推論エンジン対応

</details>

:::

また、楽曲再生画面の機能に関してはreact-youtubeというライブラリを使って実現しています。
こちらは、再生する動画に対してオプションを渡すことにより、再生位置や音量などを柔軟に設定して動画を再生することが出来ます。

:::note info

<details><summary>react-youtubeの使い方（AIの回答）</summary>

ReactアプリケーションでのYouTube動画埋め込み実装方法とカスタマイズ機能を解説します（例）。

```jsx
import React, { useState } from 'react';
import YouTube from 'react-youtube';

const App = () => {
  const [player, setPlayer] = useState(null);
  const videoId = 'XKSYF2aZnkQ';

  const onReady = (event) => {
    setPlayer(event.target);
  };

  return (
    <div>
      <YouTube videoId={videoId} onReady={onReady} />
      
      <div className="controls">
        <button onClick={() => player?.playVideo()}>▶️ 再生</button>
        <button onClick={() => player?.pauseVideo()}>⏸️ 一時停止</button>
        <button onClick={() => player?.seekTo(60)}>⏭️ 1:00へ移動</button>
        <button onClick={() => player?.mute()}>🔇 ミュート</button>
        <button onClick={() => player?.unMute()}>🔊 ミュート解除</button>
      </div>
    </div>
  );
};
```

###### 主要制御メソッド
**基本操作**:
- `playVideo()`: 動画再生を開始
- `pauseVideo()`: 再生を一時停止
- `stopVideo()`: 再生を完全停止
- `seekTo(seconds)`: 指定秒数へ移動

**音量制御**:
```jsx
<button onClick={() => player?.setVolume(50)}>音量50%設定</button>
<button onClick={() => player?.setVolume(100)}>最大音量</button>
```

**状態監視**:
```jsx
const handleStateChange = (event) => {
  switch(event.data) {
    case 1:
      console.log('再生中');
      break;
    case 2:
      console.log('一時停止');
      break;
    case 0:
      console.log('再生完了');
      break;
  }
};

<YouTube
  videoId={videoId}
  onStateChange={handleStateChange}
/>
```

###### 応用機能
**現在の再生位置取得**:
```jsx
const getCurrentTime = async () => {
  const time = await player?.getCurrentTime();
  console.log(`現在時刻: ${time}秒`);
};
```

**動画時間取得**:
```jsx
const showDuration = async () => {
  const duration = await player?.getDuration();
  console.log(`総再生時間: ${duration}秒`);
};
```

**再生品質設定**:
```jsx
<button onClick={() => player?.setPlaybackQuality('hd1080')}>
  高画質設定
</button>
```

###### 実践的使用例
```jsx
const AdvancedPlayer = () => {
  const [player, setPlayer] = useState(null);

  const handlePlayFromStart = () => {
    player?.seekTo(0);
    player?.playVideo();
  };

  return (
    <div>
      <YouTube
        videoId="XKSYF2aZnkQ"
        onReady={(e) => setPlayer(e.target)}
        opts={{ playerVars: { controls: 0 } }}
      />

      <div className="control-panel">
        <button onClick={handlePlayFromStart}>最初から再生</button>
        <button onClick={() => player?.pauseVideo()}>一時停止</button>
        <button onClick={() => player?.seekTo(120)}>2分スキップ</button>
        <button onClick={() => player?.setVolume(0)}>ミュート切替</button>
      </div>
    </div>
  );
};
```

</details>

:::


<br>

  - **テーブル構成**
    ![vtune.draw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/8368a833-e558-d44e-a023-03257eaaedcc.png)
    こちら、正直そんなに複雑な構成ではないですね。
    音量や再生時間などはユーザー毎の制御となるため、ユーザーマスタに持たせています。また、開発途中で気付いたのですが、1曲に対して1人しかVTuberを登録できない仕様になっています。歌枠は1人とは限らず、複数人でコラボする場合もあるのでそのパターンに対応出来ていません。。(今後どうにか対応予定です。)
<br>
    
  - **システム構成**
    ![システム構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/a9f47cce-d555-7c38-0bf6-0107b31bfdce.png)
    こちらも見た通り、単純な構成ですね。
    認証機能はsupabase Authを使っており、Google、Xでのログインに対応しています。
    ローカル開発ではローカル用のsupabaseを使って開発を行います。
    また、Github Actionsを使ったCICDでリポジトリにpushするとテスト実行後、自動的にcloudflareにデプロイします。

# <font color="#F966C0">🦾力を入れたポイント</font>
今回の個人開発で最も力を入れたポイントは、何と言っても`使いやすさを意識したデザインと機能性`です。
サービスの性質上、エンタメに振ったデザインにしないとクオリティが微妙になってしまうという思いがあったので、一番力を入れました。
ただ、デザインに関しては`ド素人`の私がどのように今回のWebサービスを作り上げたかというと、これはひとえに`生成AIの力`ですね。メインで使っていたのは<a href="https://v0.dev/">`v0`</a>というReactとNext.jsに特化したUIを生成するツールです（他にもいくつかあるようですが今回は使っていません）。こちら、使っていただくと分かるのですが、プロンプトが適当でも結構しっかりしたUIを生成してくれます。
私の使い方としては、`デザインに迷ったらv0でコンポーネントのたたき台を生成→手直しして実装`という形ですね。
残念ながら、生成したものがそのまま使えるかといったら微妙で、結局生成したものを自分好みの動き・仕様に変える必要があるので、HTML・CSSの知識は必要になります。とはいえ、80%〜90%ぐらいはそのまま使えるので今回の開発では非常に重宝しました（というかv0が無かったら今回のWebサービスは絶対に作れませんでした）。

 あとは`機能面`の部分ですね。 本Webサービスは`直感的に使えるように`ということを意識して全体のUIを考えました。
例えば、機能のご紹介で述べましたが、一覧の表示方法はページングではなく半自動スクロールにしています。これは、スマホでの利用時に、ページング方式だと画面の上に戻る必要があるのに対し、半自動で下にスクロールしていく方式の場合は画面の上に戻る必要がなく、ストレスなく使えるためです（自動スクロールではなくボタンをクリックする方式にしているのは、その下のフッターに移動できるようにするためです）。
また、楽曲再生画面では、動画の音量を調整できるようにしています。
埋め込み動画のYouTubeのプレイヤーで調整することもできるのですが、あえてこれをサービスに取り付けた理由としては、動画ごとにベースとなる音量が異なるので、音量は頻繁に変えそうだなということで簡単にできるように取り付けました。
これらのように、`どうすれば一番使いやすいか`を意識して一つ一つ実装しているので、「なんでここはこうなってるの？」と聞かれたら全てに回答する理由があります。この辺は<font color="red">`教材からは得られない貴重な学び`</font>だと思っていて、というのも、最初はもっとシンプルで、画面に動画を埋め込んだだけだったのですが、`「サービスの使われ方」をイメージすることで、必要になりそうな機能`を考えて実装することが出来ました。様々な試行錯誤を経て、最終的には「誰でも簡単に使えるサービス」に仕上げることができたと思っています。
ただ、まだまだ改善の余地はあると思うので、もし使ってみて気になる点があればぜひフィードバックをいただけると嬉しいです！

# <font color="#F84383">📕半年間のアウトプット勉強で得られた学び</font>
今回の個人開発は、これまでの6ヶ月間のアウトプット勉強法の集大成として完成させたサービスとなります。
ここでは、これまで継続してきたアウトプット勉強法で得られた学びについて大きく以下の3点をご紹介したいと思います。

- ### 個人開発で<font color="#F82D2A"><INS>"詰む"</INS></font>ことはもうない
  こちらのWebサービス、企画からリリースまで全て1人で担当したものになりますが、個人開発で`"詰み"`は最早発生しないのではないかと思いました。というのも、今の時代`生成AI`があまりにも発達しているため、何かわからない事があれば即座に（しかも無料で）解決してしまうためです。実際、今回の開発で、おそらく1000回はエラーや不明な点に直面しましたが、発生した疑問・事象は全て生成AIで解決しています（普通にググったり公式ドキュメントを見たら解決したものもありますが）。
  一昔前はAIに聞くなんて出来なかったので、なにか開発したり調べたりするときは自分で調べて解決させなければなりませんでしたが、もうその必要はありません。非常にいい時代になりましたね。中には生成AIは使わないというエンジニアの方もいるようですが、これはあまりにももったいなさ過ぎますし、`生成AIが活用できれば、個人開発のハードルも相当下がる`ので、ぜひご活用いただくべきだと思います（個人的おすすめはPerplexityです）。
  
  ![DALL·E-2025-02-02-17.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/b8d15867-33b7-1f8e-b04f-92526508fb0f.png)
  `エンジニアに生成AIは必須の時代。うまく活用出来れば開発が相当楽になります。`
<br>

- ### 勉強にはモチベーションより<font color="#F966C0"><INS>"継続できる環境"</INS></font>が大切
  今回のWebサービスは、2024年の10月から着手し、モノが完成したのは2025年1月ですので、完成までは`約4ヶ月間`かかりました。平日はエンジニアとして働いているので、開発作業は平日の夜2〜3時間、または土日祝に7〜8時間（もしくはそれ以上）をかけたかと思います。
  「そんなに頑張ったということは相当やる気に満ち溢れてたの？」と思われるかもしれませんが、`正直そんなに高いモチベーションがあったわけではありません`（今回の個人開発をポートフォリオに転職を考えているとかでも無いです。「今後React案件に入れるように」くらいの思いでこのサービスを作りました）。
  じゃあなんでここまでやれたのか、ということですが、これに関しては`継続できる環境に身を置いたから`というのが一番だと思います。私が所属するReact専門のアウトプットコミュニティJISOU（詳細は<a href="https://qiita.com/organizations/jisou" target="_blank">こちら</a>）では、Slackでの日報の記載が習慣化されています。日報ではその日やったことをそれぞれ書き込むだけですが、「せっかくJISOUに入ったからにはここに進捗を書き込むことを習慣化しよう」と自分の中で決めていたので、毎日少しずつ進めていたらいつの間にかサービスが完成してた、というのが正直なところです（たまにサボりましたが）。
また、JISOUでは、技術的に詰まったところや分からないところをなんでもコーチに聞くことができる制度があります。が、私はこのサービスを考えてから完成させるまで、1度も質問をしていません（というか今までの課題で質問をしたことがないです。どうするべきかの方針の相談はしましたが）。と、考えると`別にJISOUに入らなくても１人でこのサービス作れたんじゃないの？`と思われるかもしれませんが、正直その通りで、`能力的には１人でも多分完成させることは出来た`と思います。ただ、それでもやっぱり`JISOUに入らなかったら完成させられなかった`と思います。どっちだよという感じですがこれはどういうことかというと、`能力的には出来たとしても、今までの約10年のエンジニア人生で個人開発をしてこなかった`という純然たる実績があるということを考えると、JISOUに入って継続する習慣が付けられなかったら、やっぱり何もしないまま11年目に突入していたよね、という意味ですね。
　では、ここで改めて10年目と11年目の違いって何なの？ということですが、それこそが`継続できる環境に身を置くか置かないか`ということです（だからJISOUに入るべきという話ではないです。継続できる環境というのは人それぞれ違うので）。ただ、今エンジニアを目指していて、何かアウトプットしたいという方の場合は、個人的には何らかのエンジニアのコミュニティに所属した方がいいかなと思います（無料のエンジニアコミュニティも探せば沢山あります）。JISOUの場合だと、みんなが同じ課題に取り組むので、みんなで同じ方向を向いて頑張ってる感が出るので私の場合はそこも良かったと思います。
エンジニアとしての成長には技術力の向上だけでなく、自分に合った継続的な学習環境を見つけることが重要で、それが見つかった時に初めて、本当の意味で成長できるんだなと思いました。


  ![DALL·E-2025-02-02-21.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/743c0c45-311a-cbb8-d7c4-e880a0f39e65.png)
  `自分なりに集中できる環境に身を置くさえできれば、あとはやるだけです。`
<br>

- ### アウトプット勉強法は<font color="#37B7F8"><INS>"逆あみだくじ"</INS></font>のようなもの
  私は約半年間、アウトプットを中心とした勉強に取り組んできましたが、かねてから、`インプット勉強は普通のあみだくじ、アウトプット勉強は”逆”あみだくじみたいだな`と思っていました。
　何を言っているかというと、まずインプットを中心とした勉強についてですが、`教材やUdemyに取り組む前ってあんまり取り組んだ後どうするかについては考えない`んですよね。具体的には、スタート地点は1か所（教材を読む前）で、ゴールした後はどこに行くか分からない（何を得られてどこに向かっているかはあんまり考えない）、ということです。少し無理があるかもしれませんが、まるで普通のあみだくじみたいじゃないですか？
　それに対し、`アウトプットを中心とした勉強の場合はこれの逆`だと思っています。どういうことかというと、まずゴールの完成品（1か所）があり、それに対して個々人の持っているスキルを基に、完成を目指す（人によってスキルは違うので複数個所のスタート地点がある）ということです（あみだくじを逆に辿っているイメージ）。
   ただの言葉遊びじゃんと言われてしまいそうですが、ここで大切なのは、<font color="red">`アウトプット勉強の場合は（あみだくじを下から上に辿る場合は）最低限の障害しか対処しなくてよい`</font>ということだと思います。例えば、インプット勉強（通常のあみだくじ）の場合は、ゴールがどこか分からないので、途中で理解できないところや難解なところがあったときに、<font color="red">`その障害を何とかしたとしても、自分にとっては対処する必要がある障害かどうかは分からない`</font>んですよね。別に自分のやりたいことに対して理解しなくてよいところもあるはずです。が、インプットを中心とした勉強の場合は、「ここ難しいけど多分理解しなきゃいけないだろうから頑張ろう」と思ってしまいがちになります（私がそうでした）。ただ、それって必ずしも理解しなきゃいけないかどうかは人それぞれで、別に放置しても一生困らない可能性も全然あると思います。一方、アウトプットを中心とした勉強の場合は、`まず完成品が目標にあるので、そこに対してできないところや分からないところを対処していく`ということで、<font color="red">`やらなくていいところはマジでやらなくていい`</font>ことになります。これは個人的には結構大発見で、技術的な勉強に対するハードルがかなり低くなりました（最低限しかやらなくていいから）。その上、実際に業務でシステムに触れているとわかるのですが、`システム開発で詰まるところは大体どんなシステムでも同じ（プログラミングも然り）`です。要は詰まったときの引き出しの数を増やすことが大切で、それにはやっぱり自分で経験するのが一番ということですね。
というようなことを考えると、世の中には色々な教材が溢れていますが、`プログラミングやシステム開発の勉強はとにかくアウトプットを中心に個人開発する`が正解なんだろうな、と思います。


  ![DALL·E-2025-02-02-21.24.53-A-person-lost-inside-a-giant-Amida-kuji-_ghost-leg_-maze.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/2d43e7b5-0072-9348-6873-fad126bce8f2.png)
  `大切なのは自分が今どの位置にいて、どこに向かっているのかを意識することです。`


# 🌺おわりに&謝辞
いかがだったでしょうか。
めちゃくちゃ長い記事になってしまいましたね。

ここまで読んでいただくと、まるで私が今回のサービスをすんなり作り上げたかのような印象をもたれる方もいらっしゃるかもしれませんが、実際はかなり苦労しながらなんとか完成までこぎつけました（実際何回か投げ出しかけた）。
それでもここまで出来たのは、JISOU運営のJINさん、kitaharaさん、またはコミュニティメンバーの皆さんのおかげです。この場を借りて御礼申し上げます。ありがとうございました！
今後もこちらのサービスについては機能拡張していく予定なので、ぜひお使い頂ければ非常にありがたく思います。（使ってみた感想など、コメントいただけたら大変うれしいです！）
また、<a href="https://x.com/Kei_dev_1213">私のX</a>もぜひフォロー頂ければ嬉しいです！
最後になりますが、ここまでお読みいただきありがとうございました！

## JISOUのメンバー募集中🔥
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？
気になる方はぜひHPからライン登録お願いします！👇

https://projisou.jp/