【個人開発】映画スワイプアプリ「サガシネマ」を作って学んだこと
はじめに
「何観よう」と Netflix や Prime Video を開いて何も決まらない、そんな経験はありませんか?
動画配信サービスが増えすぎて、サービス横断で作品を眺めているだけで時間が溶ける。詳細ページを開きまくっているうちに「まぁ今日はいいか…」となる。レコメンドはあるけれど、決め手に欠けて決めきれない。
このアプリは、その「決められない」を減らすために作りました。「作品をじっくり比較して選ぶ」のではなく、「予告編の雰囲気から一瞬でYES/NOを決める」。その積み重ねで、自分の好みのリストが勝手に溜まっていく——そんな体験を目指しています。
アプリの概要
https://movie-match-app.vercel.app/
デスクトップ

スマホ

サガシネマは、映画・ドラマを Tinder ライクなスワイプ操作で直感的に選べる Web アプリです。
- 右スワイプ(スマホ版のみ) or ❤️ボタン → LIKE(マイリストに追加)
- 左スワイプ(スマホ版のみ) or ❌ボタン → BAD(スキップ)
- 中央の i ボタン or カードタップ → 詳細モーダル(あらすじ・配信サービス・予告編)
主な機能は次のとおりです。
- 配信サービスフィルタ(Netflix / Prime Video / Disney+ / U-NEXT)
- ジャンルフィルタ(アクション、SF、アニメなど)
- ゲストログイン(メールアドレス・パスワード不要で試用可能)
- マイリスト機能(LIKE した作品を一覧表示、各配信サービスの検索ページへジャンプ)
Vercel にデプロイしており、スマホ・PC どちらからでも利用できます。
UI / UX 設計で意識したこと
スワイプ UI(スマホ版のみ) を採用した理由
Tinderからイメージを得たスワイプ UI にした理由は、指の動きと判断の流れを一致させるためです。
- 右にスワイプ → 「いいね」という肯定的な動き
- 左にスワイプ → 「スキップ」という否定的な動き
ボタンだけでなくスワイプにも対応することで、「見て→判断して→スワイプ」が一連の動作として自然になり、迷う時間を減らせるようにしました。
情報量を減らした理由
映画ポスターを主役にし、画面のほぼ全域を 1 枚のカードが占有する構成にしています。情報量を絞り、「この絵とタイトルで刺さるかどうか」だけを問いかける構造です。
視線の流れは次のように設計しています。
- 上部: 作品選びを後押しする短いキャッチコピー
- 中央: ポスターとスワイプ操作に集中するエリア
- 下部: タイトル・1 行あらすじ・評価(TMDB)を、視線を少し下げるだけで確認できる
「なんとなくスクロールして別のコンテンツへ」ではなく、視線がカードに留まり続けるようにレイアウトしています。あらすじはモバイルでは 2 行、デスクトップでは 1 行に制限し、詳細が気になるときだけ Info ボタンでモーダルを開く設計にしました。
技術スタックと構成
フロントエンド
- Next.js 15(App Router)
- React 18
- TypeScript
- Tailwind CSS
- framer-motion(カードスワイプのアニメーション)
Next.js の App Router を採用した理由は、app/ 配下のファイルベースルーティングが直感的で、API Routes とフロントを同じプロジェクトで管理しやすいためです。Tailwind は、コンポーネント単位でスタイルを完結させやすく、レスポンシブの md: プレフィックスが扱いやすい点を重視しました。
状態管理
- React Hooks(
useState,useEffect,useMemo,useRef) - Supabase(認証状態・LIKE/BAD の永続化)
- クライアントサイドの状態はページ単位で完結させ、Redux 等は使わずシンプルに保っています
API / バックエンド
Next.js の API Routes(App Router では app/api/ 配下)で、TMDB API や Supabase をラップしています。
| ルート | 役割 |
|---|---|
/api/discover |
TMDB から作品検索・フィルタリング |
/api/movies/[id] |
作品詳細取得 |
/api/movies/[id]/videos |
YouTube 予告編キー取得 |
/api/movies/[id]/watch-providers |
配信サービス情報取得 |
/api/synopsis |
OpenAI による 1 行あらすじ生成 |
/api/watch-providers |
利用可能な配信サービス一覧取得 |
TMDB の API キーや Supabase の URL はクライアントから直接参照しないため、API Routes 経由で取得することで、機密情報をブラウザに露出させずに済んでいます。
デプロイ
- Vercel に GitHub 連携でデプロイ
- 環境変数は Vercel のダッシュボードで設定(
NEXT_PUBLIC_SUPABASE_URL、NEXT_PUBLIC_TMDB_API_KEYなど)
実装で工夫したポイント
コンポーネント設計
SwipeCard コンポーネントは、カード表示・スワイプ判定・アニメーション・ボタン操作をすべて内包するようにしています。親(discover/page.tsx)からは movie、onSwipe、onInfoClick などを渡すだけでよく、責務を分離しています。
// SwipeCard の props のイメージ
type Props = {
movie: TmdbMovie;
onSwipe: (direction: "left" | "right") => void;
synopsis?: string;
onInfoClick?: () => void;
triggerSwipe?: "left" | "right" | null; // ボタン押下時のアニメーション用
};
framer-motion の motion.div に drag="x" を指定し、onDragEnd でスワイプ方向(オフセット量・速度)を判定しています。
閾値(120px / 500px/s)を超えたら LIKE または BAD として処理し、超えなければスプリングアニメーションで元の位置に戻します。
スマホの UI
モバイルでもカード内下部に BAD / Info / LIKE の 3 ボタンを配置し、スワイプ以外でも操作できるようにしました。
配信サービス・ジャンルのフィルターは sticky top-0 で画面上部に固定し、スクロールしても常に表示されるようにしています。
フィルターのドロップダウンはホバーではなくクリックで開閉するようにし、タッチデバイスでも迷わず操作できるようにしました。
TMDB API の制約への対応
TMDB の watch providers は日本(JP)のデータが少ないサービスがあります。
特に Netflix は日本向けの登録情報が少ないため、Netflix のみ選択時は watch_region=US で取得するようにし、作品数が増えるようにしています。
また、結果が 0 件の場合はフィルターなしで人気作品を返すフォールバックを入れています。
セキュリティ対応の話
Next.js の CVE 対応で何をしたか
2025 年初頭に、Next.js の脆弱性(CVE-2025-66478)が公表されました。
当時使用していた Next.js 15.0.0 は対象バージョンだったため、Vercel のビルドで「Vulnerable version of Next.js detected」エラーが発生していました。
対応内容は次の 2 点です。
1. Next.js を 15.0.7 にアップデート
package.json の "next": "15.0.0" を "15.0.7" に変更し、npm install で依存関係を更新しました。
2. 非推奨オプションの削除
next.config.mjs にあった experimental: { appDir: true } を削除しました。
Next.js 13 以降、App Router はデフォルトで有効なため、このオプションは不要かつ非推奨でした。
学んだこと
- フレームワークのバージョンは定期的に確認し、セキュリティアップデートには素早く追従する
- experimental オプションは将来的に変更・削除される可能性が高いため、必要なものだけ残す
- Vercel は脆弱なバージョンのビルドをブロックするため、本番デプロイ前にバージョン確認が重要
今後やりたいこと
-
AIを利用したあらすじの省略表示
あらすじを一行に省略し、一言でその作品の特徴や概要をイメージできるようにする -
セッションベースの提案
その日の気分(軽い/重い、短め/長めなど)を一言で選び、そのセッション中だけレコメンドを切り替える -
フレンド機能
友だちの LIKE をオーバーレイ表示し、「この作品、友だちも好き」が一目で分かるようにする
まとめ
サガシネマは、「映画選びの考えるコストを下げる」ことを目的にした個人開発アプリです。
スワイプ UI と情報量を絞ったカード設計で、一瞬で判断できる体験を目指しました。
個人的には予告編ではなくポスターのみであらすじの情報も表示せずにタイトルとポスターで選ぶ形にしてもいいかもしれないと思いました。なぜなら結局は見てみないと自分に合うかはわかりませんし一枚のカードの滞在時間を減らし、同じ時間でも多くの作品カードを見られる方が、利用者が好みの可能性がある作品に触れられる回数が増えるからです。
技術的には、Next.js 15 + React 18 + Supabase + TMDB API という構成で、シンプルに保ちながら必要な機能を実装しています。
セキュリティアップデートへの対応を通じて、依存関係の管理の重要性を改めて実感しました。
映画選びで迷う時間を減らしたい方、スワイプ UI や Next.js App Router の実装例を参考にしたい方は、ぜひリポジトリやデモサイトを覗いてみてください。