友達と複数人で車に乗る運転手向けに、運転手への傾斜割り勘・ETC/現金の高速料金・集金をまとめて扱う Web アプリです。
- 本番 URL: https://warikan-drive-7cij.vercel.app/
- ランディングページ: https://warikan-drive-7cij.vercel.app/lp (LP → ログイン → アプリの流れ)
- 運転手への傾斜割り勘の計算が難しい
- ETC の高速料金がその場でわからない
- 集金が遅れる
| 機能 | 説明 |
|---|---|
| 時間帯入力 | 出発時刻を入力(ETC 割引率に影響) |
| 経路候補 | Google Maps Routes API で複数経路を表示・選択 |
| 高速料金 | ETC/現金の料金を自動取得 |
| 追加費用 | レンタカー・駐車場代の入力 |
| 傾斜割り勘 | 運転手優遇率を設定して自動計算 |
| Google Map 連携 | 選択ルートを Google Map ナビに連携 |
| PayPay 即時請求 | 計算結果から請求リンクを発行して集金 |
| ルート修正 | ルートごとに後から経路の選択・修正 |
| レイヤー | 技術 |
|---|---|
| フロントエンド | Next.js 16 (App Router) + React 18 + TypeScript |
| スタイリング | Tailwind CSS + アプリ内テーマ(ダーク/ライト) |
| バックエンド | FastAPI (Python 3.11) + Pydantic v2 |
| ORM | SQLAlchemy |
| データベース | PostgreSQL 16 |
| 外部API | Google Maps Routes API |
| 環境 | Docker / docker-compose(開発時は override でホットリロード) |
| フロントデプロイ | Vercel |
| バックデプロイ | Vercel(Serverless Functions + Mangum) |
warikan-drive/
├── README.md
├── docker-compose.yml
├── docker-compose.override.yml # 開発用(コードマウント・ホットリロード)
├── .env.example
├── .gitignore
│
├── db/
│ └── init.sql # DB初期化(テーブル・ENUM・デフォルトユーザー)
│
├── frontend/ # Next.js
│ ├── Dockerfile # 本番ビルド用
│ ├── Dockerfile.dev # 開発用(npm run dev)
│ ├── package.json
│ ├── package-lock.json # npm install で生成(再現ビルド用)
│ ├── tailwind.config.ts
│ ├── tsconfig.json
│ └── src/
│ ├── app/
│ │ ├── layout.tsx
│ │ ├── page.tsx # トップ(未ログイン時はログイン/新規登録、ログイン時はプラン一覧)
│ │ ├── callback/
│ │ │ └── page.tsx # Google ログイン コールバック
│ │ ├── lp/
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx # ランディングページ(/lp)
│ │ └── trips/
│ │ ├── new/
│ │ │ └── page.tsx # 新規ドライブプラン作成
│ │ └── [tripId]/
│ │ ├── page.tsx # プラン詳細・ルート管理・割り勘タブ
│ │ ├── SplitView.tsx
│ │ ├── PaymentStatusView.tsx
│ │ └── split/
│ │ └── page.tsx # 割り勘専用ページ(/trips/:id/split)
│ ├── components/
│ │ ├── ui/
│ │ ├── ThemeToggle.tsx # ダーク/ライト切替
│ │ ├── Settings.tsx # 設定モーダル
│ │ ├── RouteCard.tsx
│ │ ├── RouteMap.tsx
│ │ ├── PlaceInput.tsx
│ │ └── SegmentSelector.tsx
│ ├── contexts/
│ │ ├── AuthContext.tsx
│ │ └── ThemeContext.tsx
│ ├── lib/
│ │ ├── api.ts # バックエンド API クライアント
│ │ └── utils.ts
│ └── types/
│ └── index.ts
│
└── backend/ # FastAPI
├── Dockerfile
├── requirements.txt
├── main.py
├── database.py
├── models/
├── schemas/
├── routers/
├── services/
│ ├── google_maps.py # Routes API呼び出し
│ └── split_calculator.py # 割り勘計算ロジック
└── alembic/ # マイグレーション
git clone https://github.com/Yukinkmr/warikan-drive
cd warikan-drivecp .env.example .env.env を編集します。
- DATABASE_URL(必須)
Supabase でプロジェクトを作成し、ダッシュボードの Connect から接続文字列(URI)をコピーしてDATABASE_URLに貼り付けます。初回のみ Supabase の SQL Editor でdb/init.sqlを実行してテーブルを作成してください。 - Google Maps 経路・料金・地図表示を使う場合
- バックエンド(経路検索):
.envのGOOGLE_MAPS_API_KEYを設定。未設定時はモックで動作します。 - フロントエンド(地図・住所オートコンプリート):
.envにNEXT_PUBLIC_GOOGLE_MAPS_KEYを同じ API キーで設定してください。未設定の場合は地図は「API キー未設定」と表示され、住所は手入力のみになります。Maps JavaScript API を有効にしたキーが必要です。
- バックエンド(経路検索):
docker compose up --buildデフォルトでは DB コンテナは起動しません。 バックエンドとフロントエンドだけが立ち、.env の DATABASE_URL(Supabase)に接続します。
開発時は docker-compose.override.yml が自動で使われ、コードをマウントした状態で起動します。
- バックエンド: ソースをマウントし
uvicorn --reloadで起動。Python を編集して保存すると自動で再起動します。 - フロントエンド: ソースをマウントし
npm run devで起動。コードを保存するとブラウザが自動更新(ホットリロード)されます。
コード変更のたびにイメージを再ビルドする必要はありません。初回のみ --build でビルドし、2回目以降は docker compose up だけでOKです。package.json や requirements.txt を変更したときだけ再ビルド(docker compose up --build)してください。
初回はイメージのビルドに少し時間がかかります。開発時はフロントエンドの初回起動でコンテナ内の npm install が走るため、1〜2 分待つことがあります。ログに Ready in や Local: http://localhost:3000 が出れば準備完了です。
起動が完了したら、次のURLでアクセスします。
| サービス | URL |
|---|---|
| フロントエンド(アプリ) | http://localhost:3000 |
| バックエンド API(Swagger) | http://localhost:8001/docs |
| バックエンド API(ReDoc) | http://localhost:8001/redoc |
※ バックエンドはコンテナ内 8000 をホストの 8001 にマッピングしています。
- http://localhost:3000 … 未ログイン時はログイン/新規登録、ログイン時はプラン一覧
- http://localhost:3000/lp … ランディングページ(「今すぐ試す」でログインへ)
- トップで 「+ 新しいドライブプラン」 をクリック(または LP から「今すぐ試す」→ ログイン)
- プラン詳細(ルート管理)で 「日付を追加」 で日付を追加
- 各日の 「+ ルートを追加」 でルートを追加し、出発地・目的地・出発時刻 を入力
- 「🔍 経路を検索」 で経路候補を取得し、「▼ 経路を選ぶ」 で使う経路を選択
- 割り勘に含めたいルートで 「割り勘に含む」 をクリック
- 「💰 割り勘」 タブ(または 「○ルートで割り勘計算 →」)で割り勘ページへ移動
- 追加費用があれば入力し、「割り勘を計算する →」 で結果を表示。PayPay 請求リンクで集金
docker compose downローカル DB を使っている場合にボリュームごと削除する場合:
docker compose --profile local-db down -vSupabase ではなく、PC 上の PostgreSQL コンテナで動かす場合は、.env の DATABASE_URL を削除するか postgresql://postgres:postgres@db:5432/warikan_drive にし、local-db プロファイルを付けて起動します。
docker compose --profile local-db up --builddb / backend / frontend の 3 サービスが起動し、データはローカルボリュームに保存されます。
フロント・バックともに Vercel、DB は Supabase の構成でデプロイできます。フロントとバックは別々の Vercel プロジェクトとして登録します。
- GitHub にリポジトリを push 済み
- Supabase でプロジェクト作成済み・
db/init.sql実行済み・接続文字列(Pooler の URI)を取得済み
- Vercel にログイン → Add New → Project
- 対象リポジトリを選択
- Root Directory を
backendに設定(Edit) - Environment Variables に追加:
DATABASE_URL… Supabase の接続文字列(Session または Transaction の URI)GOOGLE_MAPS_API_KEY… 任意(経路検索用)GOOGLE_CLIENT_ID… Google ログイン用 OAuth 2.0 のクライアント ID(後述の Google Cloud で取得)GOOGLE_CLIENT_SECRET… 上記のクライアントシークレット(任意。未設定なら「Googleでログイン」は表示されずメール/パスワードのみ)
- Deploy する
- デプロイ後の URL を控える(例:
https://warikan-backend-xxx.vercel.app)
- Add New → Project で同じリポジトリを再度選択(2つ目のプロジェクト)
- Root Directory を
frontendに設定 - Environment Variables に追加:
NEXT_PUBLIC_API_BASE_URL… バックエンドの URL +/api/v1(例:https://warikan-backend-xxx.vercel.app/api/v1)NEXT_PUBLIC_GOOGLE_CLIENT_ID… バックエンドと同じ GOOGLE_CLIENT_ID の値(Google ログイン用。未設定なら「Googleでログイン」は出ない)NEXT_PUBLIC_GOOGLE_MAPS_KEY… 地図表示・住所サジェスト用の Google Maps API キー(任意。未設定なら地図は「API キー未設定」、住所は手入力のみ。Maps JavaScript API を有効にしたキーを設定)
- Deploy する(環境変数を追加・変更した場合は Redeploy で再ビルドが必要です)
フロントの Vercel URL を開き、プラン作成や割り勘計算ができることを確認します。Supabase の Table Editor でデータが入っているかも確認できます。
| 変数 | 設定する場所 | 説明 |
|---|---|---|
DATABASE_URL |
Vercel バックエンド | Supabase の接続文字列(Pooler 推奨) |
GOOGLE_MAPS_API_KEY |
Vercel バックエンド | 経路検索用(任意) |
GOOGLE_CLIENT_ID |
Vercel バックエンド | Google ログイン用 OAuth 2.0 クライアント ID(任意) |
GOOGLE_CLIENT_SECRET |
Vercel バックエンド | 上記のクライアントシークレット(任意) |
NEXT_PUBLIC_API_BASE_URL |
Vercel フロント | バックエンドの URL + /api/v1 |
NEXT_PUBLIC_GOOGLE_CLIENT_ID |
Vercel フロント | 上記と同じクライアント ID(Google ログイン用・任意) |
NEXT_PUBLIC_GOOGLE_MAPS_KEY |
Vercel フロント | 地図・住所サジェスト用(任意。Maps JavaScript API を有効にしたキー) |
- Google Cloud Console で 認証情報 を開く
- + 認証情報を作成 → OAuth 2.0 クライアント ID を選択
- アプリケーションの種類で ウェブアプリケーション を選択
- 承認済みのリダイレクト URI に本番のコールバック URL を追加:
https://あなたのフロントのVercelドメイン/callback
(例:https://warikan-drive-xxx.vercel.app/callback) - 作成後に表示される クライアント ID と クライアントシークレット をコピー
- Vercel バックエンド の Environment Variables に
GOOGLE_CLIENT_ID= クライアント IDGOOGLE_CLIENT_SECRET= クライアントシークレット
- Vercel フロント の Environment Variables に
NEXT_PUBLIC_GOOGLE_CLIENT_ID= 同じクライアント ID
- 両方のプロジェクトで Redeploy して反映
- Vercel Hobby: 非商用利用。Serverless のコールドスタートで初回 1〜3 秒遅くなることがあります。
- Supabase 無料枠: 7 日間アクセスがないと DB がスリープします。デモ前に Supabase ダッシュボードや API にアクセスして起こしておくと安全です。
- push を main にマージすると、Vercel が自動で再デプロイします。CI(Lint 等)は
.github/workflows/ci.ymlで実行されます。 - DB の自動マイグレーション: main への push 時に、GitHub Actions の DB Migrate ジョブで
alembic upgrade headが実行されます。GitHub リポジトリの Settings → Secrets and variables → Actions にDATABASE_URL(Supabase の接続文字列)を登録しておくと、本番 DB へマイグレーションが自動適用されます。未登録の場合はマイグレーションジョブが失敗するため、手動でcd backend && DATABASE_URL=... alembic upgrade headを実行してください。
- API キー
Google Maps Routes API のキーは Google Cloud Console でプロジェクトを作成し、Routes API を有効化して取得します。 - 開発用と本番用の Docker
- 開発時:
docker-compose.override.ymlが自動でマージされ、フロントはDockerfile.devで起動します。イメージビルドではnpm installは行わず、コンテナ起動時にnpm install && npm run devを実行するため、イメージビルドは短時間で完了します。node_modulesは名前付きボリュームで永続化されます。 - 本番ビルド:
docker-compose.ymlのみで起動する場合はfrontend/Dockerfileが使われ、マルチステージビルドでnpm run buildした結果を配信します。package.jsonやrequirements.txtを変更したあとはdocker compose up --buildで再ビルドしてください。
- 開発時:
- Node.js バージョン
フロントエンド(Next.js 16)は Node.js 20 以上を想定しています。Docker では node:20-alpine を使用しています。 - バックエンド単体
ローカル DB を Docker で動かし、バックエンドだけ手元で実行する場合は、.envのDATABASE_URLをpostgresql://postgres:postgres@localhost:5432/warikan_driveにし、docker compose --profile local-db up db -dのあとcd backend && uvicorn main:app --reloadで起動できます。API は http://localhost:8000 でアクセスできます(compose では 8001 にマッピング)。 - フロントエンド単体
cd frontend && npm install && npm run devで http://localhost:3000 で起動できます。バックエンドは別途起動し、NEXT_PUBLIC_API_BASE_URLをバックエンドの URL(例:http://localhost:8001/api/v1)に合わせてください。