はじめに
お疲れ様です。
今回の課題では、「Study Spot Vancouver」 という自習スポット検索アプリを開発しました。
React・TypeScript・Supabase・Google Maps API・Chakra UIを使用し、
地図上にピンを表示 → 条件で絞り込み → 詳細ページに遷移できる仕組みを実装しました。
この課題では、API連携・状態管理・UI設計の流れを一通り経験でき、
「地図アプリが動くまでの仕組み」を深く理解する良い機会になりました。
アプリ作成理由
- 個人的な課題の解決
バンクーバーで自習できる場所を探すとき、
Google Mapsでは「カフェ」「図書館」など大まかなカテゴリしかわかりません。
しかし、実際に自習する上で重要なのは、静かさ・電源の有無・Wi-Fiの有無など
といった現地でしかわからない情報です。
特に、英語の音読やプレゼン練習など「声を出して勉強できる場所」は情報がほとんどなく、その都度現地で確認する必要がありました。
この不便を解消するために、「自習条件で検索できる地図アプリ」 を作ろうと考えました。
アプリ概要
GitHubリポジトリ:
https://github.com/kazukashima/kadai5.git
このアプリでは、Vancouver市内の自習スポットを地図上に表示し、
WiFi・電源・声出しOKの有無で簡単に絞り込みできるようになっています。
また、以下のMVPを自ら設定し、それに従い実装しました。
- MVP1 React + TypeScript + Vite で開発環境構築。Firebase Hosting へデプロイ。make deploy による自動化。
- MVP2 GitHub Actions で CI/CD を構築。push 時に自動テストと自動デプロイを実行。
- MVP3 Google Maps APIを導入し、Supabaseから取得したデータをピンとして地図上に表示。
- MVP4 Wi-Fi / 電源 / 声出しOK で絞り込みできるフィルター機能を実装。
- MVP5 ピンをクリックすると詳細ページに遷移し、写真・住所・営業時間を表示。Google Mapsで開くリンクを設置。
ホーム画面
地図表示
左上の WiFi・Power・Talking の各ボタンを切り替えることで、
それぞれのスポットが WiFiを利用できるか、電源があるか、私語(声出し)が可能か を条件に絞り込みできます。

マップ動作デモ
地図上のピンをクリックすると、
施設名と「WiFi・電源・声出しOK」などの基本情報がポップアップ表示されます。
「詳細を見る」ボタンを押すと、
そのスポットの詳細ページへ遷移し、住所や写真などの情報を確認できます。
詳細ページの 「開く」リンク から、
実際のGoogleマップを開いてルート検索や周辺確認も可能です。
データの収集方法
自習スポットの情報は、実際に訪れて確認しました。
たとえば、「WiFiの有無」「電源の有無」「声を出して勉強できるか」などは
ネット上では分からないため、現地に行ってスタッフに直接聞いたり、
自分で勉強してみて雰囲気を確かめたりしました。
行けなかった施設については、メールで問い合わせを行い、
正確な情報を得るように心がけました。
使用した技術
| 分類 | 使用技術 / 内容 |
|---|---|
| フロントエンド | React + TypeScript + Vite |
| UIライブラリ | Chakra UI(トグルボタン・レスポンシブデザイン) |
| データベース | Supabase(自習スポット情報をクラウド管理) |
| マップ描画 | Google Maps API(@vis.gl/react-google-maps)を使用してピンを表示 |
| デプロイ | Firebase Hosting(本番環境に自動反映) |
| CI/CD | GitHub Actions(push 時にテスト & 自動デプロイ) |
| 自動化 | Makefile(make deploy / make test で簡易実行) |
アーキテクチャ図
開発で苦労したポイントと解決策
1. Google Maps API と React の再レンダー問題
Reactの再レンダーが発生しても、Google Maps側の表示が更新されず、
フィルターを切り替えてもピンが変わらない問題がありました。
原因は、ReactのVirtual DOMとGoogle Mapsの描画が別システムで動いていることにありました。
解決策として、<Map key={JSON.stringify(filters)}> のように
keyを変更して再描画する方法を採用しました。
<Map key={JSON.stringify(filters)} ... >
2.フィルターの状態管理(boolean型の扱い)
3つの条件(WiFi・電源・声出しOK)を切り替える際、
useStateでまとめて管理し、動的に更新できるようにしました。
const [filters, setFilters] = useState({
wifi: false,
power: false,
talking: false,
});
ボタンを押すたびにtoggleFiltersで特定のキーだけ反転させることで、
複数条件を簡潔に切り替えられるようにしました。
3.詳細ページ遷移とGoogleマップリンク生成
地図上のピンをクリックしたときに詳細ページへ遷移し、そのページで「Googleマップで開く」リンクを動的に生成しました。
<a
href={`https://www.google.com/maps?q=${spot.latitude},${spot.longitude}`}
target="_blank"
rel="noopener noreferrer"
>
Google Mapsで開く
</a>
テンプレートリテラル(...)を使うことで
緯度・経度から動的にURLを組み立てられることを学びました。
成長したポイント・学び
1. 外部APIとの連携力がついた
Google Maps APIやSupabaseなど、外部サービスを自分のアプリに統合する流れを実体験できました。
APIキーの管理・非同期処理・環境変数の扱いなどを理解できたのが大きな収穫です。
2. データ構造を意識して設計できるようになった
Supabaseのテーブル設計(id, name, latitude, longitude, wifi, power, talking)をUIの動きと合わせて考えたことで、データとUIの関係性を意識する力が身につきました。
3.状態と描画の関係を理解した
フィルター切り替え時にピンが更新されない問題を通して、
Reactの再レンダーと外部ライブラリ(Google Maps)の描画が別で動いていることを学びました。
感想・おわりに
まだUI/UXの改善や、Supabaseへのデータ追加など課題は残っています。
しかし、自分でMVPを設計し、段階的にアプリを完成させた経験を通じて、
“目的を持って開発を進める力” が身についたと感じています。
今後は、さらにデータを増やし、UI/UXを改善して、
「実際に価値を提供できるアプリ」へと成長させていきたいです。
JISOUでは仲間を募集中!
日本一のアウトプットコミュニティで、あなたも一緒に成長しませんか?
▶ https://projisou.jp/


