はじめに
はじめまして!shurisukeです。
プログラミングスクール「RUNTEQ」で2025年8月末から学習を始め、未経験からWebエンジニアへの転職を目指しています。
ポートフォリオとして開発した「DrivePeek」について、開発のきっかけや機能紹介、工夫したポイントについてご紹介します。
サービス概要
「地図検索」「AI提案」「みんなのプラン」の3つの方法でスポットを見つけ、ドライブプランを作成・共有できるアプリです。完成したプランはGoogle Mapsナビでそのまま出発。友達との旅行計画も、URLを送るだけで共有できます。
アプリURL
GitHubリポジトリ
開発のきっかけ
行きたい場所の“その先”が見つからない
外出を計画するたびに、一番行きたい場所はすぐ決まるのに、
その近くの素敵なスポットや飲食店を探すのに迷うことが何度かありました。
そこで、
• みんながお気に入りにしているスポットをワンタップで地図表示
• エリアを囲むだけでAIがプランを提案
• 他の人のプランをルート付きでそのまま確認
といった仕組みで、探す時間そのものを短縮することを目的に開発を始めました。
スムーズにドライブ計画を共有したい
友達と計画を立てる時、
「ここ行って、次ここで、そのあとここに寄って…」と文章で説明するのは意外と大変です。
地図アプリで場所を共有することはできても、
「複数スポット+ルート+スケジュール感」をまとめて送れるツールはまだ多くありません。
URLひとつでプラン全体を共有できて、
友達も同じツールで一緒に探せたら便利なのではないか。
そう感じたことも、開発の動機の一つです。
主な機能
🗺️ プランを作成する
| スポットをプランに追加 | 話題のスポットを表示 |
|---|---|
![]() |
![]() |
| 地図検索・コミュニティ・AI提案 など好きな方法でスポットを見つけてプランに追加 | 🔥ボタンをタップすると、みんなのお気に入りスポットが地図に出現。ジャンルを指定する事も可能です。 |
🤖 「AI提案」からプランを提案してもらう
| エリア&ジャンルを選ぶだけ | 提案プランは一括採用可能 |
|---|---|
![]() |
![]() |
| 地図上で指をなぞってエリアを囲み、ジャンルを選択 | 提案プランは、下のボタンからワンタップで一括採用 |
🌐 「みんなの旅」から他ユーザーのプランを参考にする
| プラン・スポット検索 | 気になるプランを地図で追体験 |
|---|---|
![]() |
![]() |
| 場所・ジャンル・フリーワードで他ユーザーのプランを検索 | 近くを旅した人のプランを、地図上に、プレビュー表示。 |
その他の機能
| 機能 | 説明 |
|---|---|
| プランコピー | 他ユーザーのプランを自分用に複製して編集可能 |
| スケジュール自動計算 | 滞在時間・移動時間を加味して予定時間を自動計算 |
| Google Mapsナビ連携 | 完成プランからワンタップでナビ起動、経由地もそのまま |
| SNS ログイン | LINE / X (Twitter) でワンクリック認証 |
| SNS シェア | プランを LINE / X / URL コピーで共有 |
| お気に入り | プラン・スポットをワンタップで保存、一覧管理 |
| メモ | スポットごとに注意点・買いたいものなどを記録 |
| コメント | スポットに感想・口コミを投稿・閲覧 |
使用技術
| カテゴリ | 技術・ツール |
|---|---|
| バックエンド | Ruby 3.2.2 / Ruby on Rails 8.1 / PostgreSQL |
| フロントエンド | Hotwire (Turbo + Stimulus) / Importmap / Bootstrap 5 / SCSS |
| 外部API | Google Maps JavaScript API / Places API / Directions API / OpenAI API |
| 認証 | Devise / OmniAuth (LINE / X) |
| インフラ | Render / GitHub Actions (RSpec / RuboCop / Brakeman) |
| テスト | RSpec / Factory Bot / Capybara / SimpleCov |
| その他 | Solid Queue / Solid Cache / Kaminari |
テスト: 652 examples, 0 failures
カバレッジ: 98.26% (1,696 / 1,726 lines)
技術選定の理由
- Ruby on Rails: 状態管理をサーバー主導で設計したかったため、Railsの規約とHotwireの組み合わせを採用
- Hotwire (Turbo + Stimulus): 地図操作はStimulusで対応、それ以外はTurboでJS最小化
- Google Maps API: 日本のスポットデータが充実、Mapboxより情報量が多い
- Devise + OmniAuth: LINE/X認証で手軽にログイン
- Solid Queue / Solid Cache: Rails 8標準のジョブ・キャッシュ基盤でシンプルに構成
- GitHub Actions: RSpec / RuboCop / BrakemanをPRごとに自動実行
ER図
工夫したポイント(3点)
- AI提案の3層分離
- 経路計算と時刻計算の分離
- Google Maps × Turbo Frame
1. AI提案の3層分離設計
AIにスポット選定を丸投げしていた設計を、3層に分離しました。
| Before | After | |
|---|---|---|
| 絞り込み | 地図の表示位置から | エリアを描画して選択、ジャンルは任意で指定 |
| 候補選定 | DBでランダムに選出 | DBがノイズとなるスポットをジャンルごとに除外。 描画エリア内のスポットをお気に入り数順で選出。 |
| 最終判断 | AIが主観的に選定 | AIは季節・ドライブ適性のみを考慮し選定 |
Before: ホテルやコンビニなど的外れな提案、ハルシネーションが発生
After: ユーザーが意図を伝え、DBがノイズを除き、AIが文脈を読む。
各層に適した役割を与えることで精度が向上。
詳しい実装については【Rails × OpenAI】AIにドライブスポットを提案させる機能を実装した話で解説する予定です。
2. 経路計算と時刻計算の分離
不要なAPI呼び出しを避けるため、経路計算と時刻計算を分離しました。
| クラス | 責務 |
|---|---|
| Plan::Recalculator | DrivingとTimetableを使い分ける指揮役 |
| Plan::Driving | スポット間の距離・移動時間を取得(API呼び出し) |
| Plan::Timetable | DBの移動時間を読み、到着・出発時刻を計算 |
# Plan::Recalculator(指揮役)
class Plan::Recalculator
def recalculate!(driving: false, timetable: true)
Plan::Driving.new(plan).recalculate! if driving # API呼び出し
Plan::Timetable.new(plan).recalculate! if timetable # DB内で完結
end
end
# 使い分け
# スポット追加・削除・並び替え時 → 経路 + 時刻
recalculator.recalculate!(driving: true, timetable: true)
# 出発時間・滞在時間の変更時 → 時刻のみ(APIなし)
recalculator.recalculate!(timetable: true)
この分離により、Directions APIの呼び出し回数とコストを削減。
ユーザーの操作へ即座に反応できる設計を意識しました。
3. Google Maps × Turbo Frame
Google Mapsの標準InfoWindowはスタイリングの自由度が低く、UIの拡張にも制約があります。そのため、カスタムUIでInfoWindowを実装しました。
InfoWindowとは、Google Mapsでマーカーをクリックした時に表示される吹き出しUIのことです。
さらに、クライアント側でHTMLを組み立てるのではなく、Turbo Frameを組み込むことで、サーバー主導でUIを管理する構成を採用しました。
| 項目 | 従来のJS実装 | Turbo Frame方式 |
|---|---|---|
| HTML生成 | JSで動的に組み立て | ERBテンプレートで記述 |
| ロジック | JS側で条件分岐 | Railsコントローラーに集約 |
| 部分更新 | 手動でDOM操作 | Turbo Streamで自動更新 |
| テスト | JSテスト環境が必要 | RSpecで完結 |
InfoWindow内にTurbo Frameを埋め込み、サーバーからHTMLを取得することで、お気に入りボタンなどの一部UIだけをTurbo Streamで更新できる構成にしています。
これにより、
- UIロジックをRails側に集約できる
- JavaScriptの肥大化を防げる
- テストがRSpecで完結する
というメリットがあります。
"地図はクライアントで動かすが、状態の正しさはサーバーで担保する"
この考え方を軸に、リッチなUIとRailsらしい設計を両立しました。
開発で得た学び(2点)
- 責務分離と「Railsらしさ」の重要性
- 制約の中で体験を設計する力
1. 責務分離と「Railsらしさ」の重要性
初期段階では、以下のクラスを切り出していました。
- AI提案用のサービス
- スポット検索用のサービス
- 住所変換用のサービス
しかし、明確な設計方針がないまま抽象化を進めた結果、
「どこに何の処理があるのか」が見えにくくなる状態に陥りました。
抽象化しているはずなのに、全体像が見えない。
修正のたびに関連箇所を探す時間が増え、設計の意図も曖昧になっていきました。
そこで一度立ち止まり、Railsの基本思想に立ち返ることにしました。
1. コントローラを薄く保つ
2. ドメインロジックはモデルに寄せる
3. 過度な抽象化は行わない
→ その結果、services/には複数モデルで共有する外部API連携くらいしか残らない
この方針でロジックを整理し直したことで、コードの見通しは大きく改善しました。
Railsらしい素直な構成を選ぶことは、
コードの整理と、開発者自身の迷いを減らすことの両方につながると実感しました。
過度な抽象化よりも、規約を信じてシンプルに保つこと。
その方が結果的に理解しやすく、安心して触れる設計につながることを学びました。
2. 制約の中で体験を設計する力
今回の開発を通して身についた最大の学びは、
制約の中でユーザー体験を軸に設計判断を行う力です。
地図中心のアプリでは、
- 地図を広く見せたい
- しかし操作UIも必要
という要求が常に衝突します。
そこで「主役は地図」という前提を置き、画面構成を設計しました。
また、デスクトップとモバイルを単なるレスポンシブ対応として扱うのではなく、
操作体験そのものが異なるものとして再設計しました。
- 視覚的中心へパンする実装
- スケルトンUIによる体感速度の改善
- フリーハンド描画による直感的なエリア指定
これらに共通しているのは、
「動けばいい」ではなく、「使いやすいか」を基準に考えたことです。
実機で何度も触り、タップ領域や表示タイミングを細かく調整しました。
仕様を満たすこと以上に、違和感がないか、迷いがないかを問い続けることが、アプリの完成度を高めると学びました。
今後の展望
- 作成プランの並び順最適化機能
- スポット写真投稿機能
- InfoWindowにコメント・ジャンル報告機能
おわりに
最後までご覧いただき、ありがとうございました。
2025年8月末にプログラミングスクール「RUNTEQ」に入学し、立案から約2ヶ月半かけて本アプリを完成させることができました。
地図中心のUIは、状態管理や責務分離を意識しないと破綻しやすく、設計面で多くの学びがありました。
まだまだ学ぶことは多いですが、「なぜその設計にするのか」を言語化できるエンジニアになれるよう、これからも継続的に学び続けていきます。
開発中にアドバイスをくださったRUNTEQ運営の皆様、フィードバックをくれた受講生の皆様、本当にありがとうございました。
もしよろしければ、「思い立ったらすぐ出発できる」体験を、ぜひ試してみてください。







