はじめに
同期とランチに行くとき、いつも何を食べるか迷ってしまいます。
「何でもいいよ」
「美味しいもの食べたい」
「食べられるものならOK」
毎日のこんな会話をやめるべく、優柔不断なぼくたちの意思決定 (ごはん選び) をサポートするアプリを作りました。これで意思決定にかかる時間を最小化して休憩時間を確保し、「時間価値を最大化する」ことができますね!
つくったもの
今回作ったのは、お昼ごはんを決めるためのWebアプリケーションです。
制作物はここからアクセスできます。GitHubはこちらからどうぞ。
レスポンシブ対応してないのでスマホからアクセスしてください。
あとデザインださいです。
簡単に説明すると、現在地付近の店舗一覧を取得して、ルーレットでどのお店に行くか決めるサービスです。ユーザーは行きたくないお店を候補から除外することもできます。
開発の流れ
少し話がそれますが、今は内定先の会社でアルバイトとして働いています。まだペーペーなので、要件定義や工数管理なんてまったく分かりません。
仕様書を書くモチベーションなんてない。
ということで、ベストプラクティスでないことは確実ですが、以下のようにプロジェクト(個人開発)の管理を行いたいと思います。
- 実装したい機能の仕様等をQiitaに定義する。
- 要件をGitHubのIssueに分割し、マイルストーンを設定して工数管理する。
- 詳細な要件はコメントやテストコードに記載する。
- Qiitaに作成物の概要を追記する。
このような管理方法にした理由は、3つあります。
- 要件定義や工数管理の知識がないため、まずは最低限の方法で試しながら学びたい。
- とりあえず開発を1サイクルして公開したい背景があるので、手を広げすぎない方法にしたい。
- 現段階では自分しかプロジェクトに関わらない。
初めて要件定義とかするので、温かい目で見てもらえると嬉しいです!
目的
現状の課題として、お昼ごはん選びに時間的・精神的なコストがかかっています(本当)。そこで、最低限の希望に沿ったお昼ごはんを決めてくれるWebアプリケーションを提供することで、この課題を解決します。
念のためにお伝えすると、ランチタイムは楽しいです。
コミュニケーションの活発化と食後の作業効率向上のために2時間ほしい。
ちなみに実は、以前にプロトタイプを作りデプロイまでしたのですが、公開はしていませんでした。
上司「エンジニアとして生きていくためには、開発の成功体験を積んだりアウトプットをした方が良いよ。ちなみにやってもそれを公開してないなら(周りから見ると)やってないのと一緒だよ。」(超意訳)
ところがつい先日このようなお言葉をいただいたので、改めてアウトプットしようと思いこの記事を書いています。そのため、サービスを公開して自分以外の人に使ってもらうことが副次的な目的でもあります。
要件定義
前提環境
- スマホのブラウザでアクセスする。
- GPSの使用を許可する。
機能要件
- 条件を満たす店舗を現在地から近い順に表示する。
- 店舗情報を表示する。
- 任意の店舗をルーレットの候補として選択できる。
- ルーレットを回すと、選択された店舗からランダムに選ばれた1件が表示される。
- ルーレットはユーザーによってスタート・ストップ・リセットされる。
画面要件
- すべての画面にヘッダーを表示する。
- すべての画面はスマホサイズで設計する。
ヘッダー
- サービス名を表示する。
- ハンバーガーメニューのアイコンボタンを表示する。
- Qiitaに遷移するアイコンボタンを表示する。
- GitHubに遷移するアイコンボタンを表示する。
スタート画面
- 店舗を選択可能なリストとして表示する。
- 店舗が取得できない場合、スケルトンを表示する。
- スタートボタンを表示する。
ストップ画面
- 選択された店舗をルーレット風に動くリストとして表示する。
- 店舗は選択できない。
- ストップボタンを表示する。
リセット画面
- 遷移直後にエフェクトを描画する。
- 選択された店舗からランダムに選ばれた1店舗を表示する。
- 店舗はクリック可能な要素として表示する。
- 店舗をクリックすると、Google Mapsの該当店舗のページに遷移する。
- リセットボタンを表示する。
技術選定
全体構成
アプリケーションはSPA(Single Page Application) + APIの構成で開発します。
この構成にした理由は3つです。
- モダンなアプリケーションで取り入れられている構成である。
- 慣れ親しんでいる構成なので、素早く開発したい目的を達成できる。
- MVPとしては既存のAPIのみを使用するため、そもそもサーバーサイドが(ほぼ)必要ない。
(モダンなアプリケーションで取り入れられている理由は調べておきます…)
SPA
フロントエンドでは、以下の技術を使用します。
- JavaScript
- React
- Next.js
- HTML
- CSS
Vue.jsを使わない理由は、Reactに慣れているからです。あと外部APIを叩くときはNext.jsのサーバーサイドを使いたいので、その点で便利だからというのも理由の1つです。あとVercelを使ったデプロイもかんたん。
API
バックエンドでは、以下の技術を使用します。
- Python
- Fast API
- TensorFlow
Pythonを使用する理由は、のちに機械学習をしたいから・慣れているからです。また、Fast APIを使用する理由は、フレームワークが大きすぎないから・実行速度が速いから・慣れているからです。
TensorFlowはまだ使いませんが、ユーザー数が多く知見を得やすいから・エコシステムが豊かだから・慣れているからという3つの理由で選定しました。
外部サービス
外部APIとして、店舗情報を取得するためにGoogle MapsのPlaces APIを使用します。
選定理由としては、会社の規模を考えるとサービスが廃止される可能性が低い・慣れているからです。
結局、全部慣れてるからやないかい。
基本設計
SPA
フロントエンドでは、ページとしては1枚(URLが1つ)だけ実装します。
名実ともにSingle Page Applicationですね(?)。
pages/index
- リクエスト
- 現在地を取得する。
- 店舗一覧を取得する。
- 状態管理
- 店舗一覧の選択状態を管理する。
- ルーレットボタンの状態を管理する。
- レンダリング
- ヘッダーを描画する。
- 取得した店舗一覧を描画する。
- 店舗一覧を取得できないときにスケルトンを描画する。
- ルーレットボタンを描画する。
- イベント処理
- イベントに応じて、各店舗の選択状態を変更する。
- イベントに応じて、一定時間エフェクトを描画する。
- イベントに応じて、次のイベントまでの間、選択された店舗一覧の要素順を並び替える。
components/Header
- 状態管理
- ハンバーガーメニューの状態を管理する。
- レンダリング
- ロゴを描画する。
- ハンバーガーメニューのアイコンボタンを描画する。
- Qiitaに遷移するアイコンボタンを描画する。
- GitHubに遷移するアイコンボタンを描画する。
components/Place
- 店舗名を描画する。
- 店舗情報を描画する。
components/RouletteButton
- ルーレットボタンを描画する。
- components/Skelton
- スケルトンを描画する。
utils/shuffleArray
- 配列の要素をランダムに並び替える。
utils/getCurrentLocation
- クライアントから現在地の緯度・経度を取得する。
pages/api/getPlaces
- 条件を満たす付近の店舗一覧を取得する。
API
今回の要件ではバックエンドが必要ないため、実装しません。
おわりに
以上がサービスの仕様です。
あれ、意外と仕様書書くの楽しくない…?
最初のハードルは高かったけど、いざ書き始めると思考が整理されて実装しやすくなる気がします(あたりまえ)。
次回は、ディープラーニングを使った検索機能を実装したいなと考えています!
アドバイスやコメントなどいただけると嬉しいです〜
文章固くなるのなんとかしたい。