Hack Uで優勝した話

こんにちは！nakaCです！
LINEヤフー主催のハッカソンである**Hack U**に参加してきました！

結果として優勝することができたので、備忘録も兼ねてハッカソンの振り返りをしたいと思います！

# 概要

Hack Uとは
> LINEヤフー社員のサポートのもと、限られた期間の中で学⽣がプロダクトを⾃ら企画・開発・発表するイベントです。

開催期間は6/10 ~ 6/29の3週間でした。

チームメンバーは全員はじめましての面々であり、4人でそれぞれ

- フロントエンド1名
- バックエンド2名(私はバックエンドメンバー)
- デザイナー1名

という構成でした。

チームメンバーの得意領域が被らず分担できたので、各々の得意分野がうまく噛み合いスムーズにチーム開発をすることができました！

# プロダクト

家具専用フリマアプリ「Pasha-niture(パシャーニチャー)」を作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/bdd0a6c9-d7b3-4c7a-8e24-acf4d8b31800.png)

> 写真をパシャっと撮るだけで面倒な家具情報を自動入力。そのまま簡単出品!
写真をパシャっと撮るだけであなたの部屋の雰囲気にぴったりの家具を提案!
スマートで楽しい家具選びを体験しよう!

既存のフリマアプリで家具を出品する際、サイズを測ったり商品説明を考えたりと面倒な手間がありました。
また家具を探す際、部屋の空いているスペースや部屋の雰囲気にあった家具をオススメしてくれるサービスはありませんでした。
このような面倒なこと、難しいことを一挙に解決してくれる使いやすいフリマアプリを作成しました！

### 作成した機能一覧

- ユーザー周りの機能
- 家具の出品機能(+ AIによる自動入力)
- 家具の検索機能(+ AIによるオススメ)
- いいね機能
- 取引周りの機能
- チャット機能

出品機能の詳細

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/8ddff2fd-c84f-8b64-9a44-688ae84c497e.png)

検索機能の詳細

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/4b623cd7-ec55-b8a3-2237-2ec09445802c.png)

# 開発の流れ

### アイディア出し

miroを使ってブレインストーミングを行いました。
質より量を重視しつつ、連想ゲームのようにして様々なアイディアが出ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/81207989-31af-706c-5e2f-6913faf48aad.png)

アイディアにはざっくりと、ゲームなどの**娯楽系**か、生活で使用する**ペインを解消する系**の2つがありますが、私たちは話し合いの結果、**ペインを解消する**アイディアに絞り込みました。

また、フロントエンドのメンバーがモバイルアプリつよつよエンジニアであり、その長所を活かしてもらう方向でwebアプリではなくモバイルアプリを作成することにしました。
そこで、カメラ機能や現在地情報などのモバイル端末の強みを活かしたアプリを作れないか頭の片隅に入れながら範囲を狭めていきました。

できるだけ他チームと被りたくなかったため、エンジニアのペインや日常生活でのペインを解消するものではなく、ニッチなアイディアに絞り込むことにしました。

今回のPasha-nitureのアイディアが生まれたきっかけとしては、ハッカソンの成果発表の翌日に私の引っ越しがあり、またメンバーもちょうど最近引っ越ししたばかりという方がいたため、その引っ越しの話から発展しました。

引っ越し関連というところまで絞り込んだ後、ペルソナを決めてカスタマージャーニーマップを作成し、引っ越しでどのようなペインがあるかを洗い出しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/47e6148a-8f53-75c4-222b-d054bd3b3845.png)

解決したいペインが決まったら、市場調査と技術調査をそれぞれ分担して行いました。

最終的なアイディア決定ではメンターさんにアドバイスをもらいにいきましたが、後にも先にもメンターさんに質問するのはこのアイディア決定のときだけでした。

メンバーが優秀すぎて課題があっても自分たちで自己解決できてしまいましたが、せっかくLINEヤフーの現役エンジニアの方からアドバイスが貰えるので、もう少しくらい質問してもよかったかも！と終わった後になって思いました。

### 要件定義

アイディアが確定したら、どのような機能がほしいか、満たすべき要件は何かを話し合いました。
その際、優先順位として松竹梅というレベルに分けました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/2b4de52d-c0f1-5814-2f5a-7cc87367d82f.png)

その後、デザイナーの方にストーリーボードを作成していただきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/df13b76a-effc-9141-07d1-1b5eed0744ea.png)

ストーリーボードによってお互いが考えているアプリの使用方法や流れについて、実装前にすり合わせることができました。
デザイナーさんがいてくれたおかげで、サービス理解がとても深まりました！

### 技術選定

Flutter + Python(FastAPI)で開発しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/5a03bf7c-ce22-e9c9-aead-019d49f904a5.png)

フロントエンドの言語としては、SwiftとFlutterが候補にあがっていたようですが、メンバーの過去の実績や、私がiosではなくAndoroidだったことを鑑みて、Flutterで開発することになりました。

バックエンドの言語は、お互いに開発経験のあるPythonにしました。
フレームワークとしてはDjango, Flask, FastAPIの3つが候補に上がりました。

今回のハッカソンでは主に以下の3点を中心にフレームワーク選定を行いました。

- APIサーバなのでフルスタックフレームワークではなくシンプルなものにしたい
- フロントエンドと円滑な連携を行うためにスキーマ駆動で開発がしたい
- チーム開発なので型があると嬉しい

上記のことを考慮しつつも、本音を言ってしまうと、Django,Flaskは私が既に経験済みだったので、新しい技術に挑戦してみたいという個人的な理由も3割くらいあり、FastAPIにしました(よくない)。

今回のプロダクトはモバイルアプリであり、審査に時間がかかることを考えリリースに見切りをつけ、クラウドサービスは使わず全てローカルで完結するシステムにしました。
そのためS3互換であるローカルオブジェクトストレージのMinIOを採用しています。
最初は、ローカルで画像データを保存する場合、Pythonの`Open`関数を使ってそのまま保存すればいいと安直に考えていましたが、IOバウンドが思ったより大きかったため後からMinIOを導入しました。

### 設計

##### ワイヤーフレーム設計

デザイナーさんにワイヤーフレームを作成していただきました。これを行うことでAPIでの必要なリクエスト・レスポンスがとてもわかりやすくなりました。
ワイヤーフレームが無かったら、今回のハッカソンでスムーズな開発やUI,UXをしっかりと練ることはできませんでした。
お互いのなんとなくもっていた共通認識が視覚化されるため、ワイヤーフレーム作成はとても大事だとわかりました。
(本当に作成してくれてありがとう！！)

##### DB設計

DB設計は割とすんなり終わりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3143089/b8aee1e6-7f91-e2b0-8413-21c72fb6ad3e.png)

##### API設計

機能を満たすためにどのようなAPIを作成すればいいのかを話し合いながらOpenAPIで定義していきました。
API設計はうまくいったとは言いづらく、MTGを行うたびに加除修正が発生しました。
原因としてはDBに引っ張られすぎたことと、フロントエンドのことを考えすぎて通信を1回で済むようにレスポンスが盛々になってしまったという点です。エンドポイントの切り方も汚い部分があり反省点は多かったです。
失敗から学べることがたくさんあったので、今後に活かそうと思います！

### 環境整備

コーディングを始める前に以下のことを行いました。

- ディレクトリ構成を考える(モノレポで)
- Dockerを使った開発環境の構築
- mainブランチの保護をして、必ずレビューを通してからマージするように設定
- チーム開発におけるGitの流れを周知
- PRテンプレートを作成
- 各々の進捗がわかるようにGitHubのProjectsを作成(他ツールと迷ったけどGitHubが一番無難)

チーム開発として重要なGit周りを特に整備しました。
「Gitの流れを周知」に関しては私が書いたGitの記事を貼り付けただけでしたが、メンバーからたくさん見返していたという話を最終日に聞けました。
少しでも役に立てていたことがわかり、とても嬉しかったです！
(優勝したことの次に嬉しかったかも)

### 実装

フロントエンドはワイヤーフレームを参考にしつつSwagger UIを使ったスキーマ駆動で開発を行っているようでした。
デザインとバックエンドの板挟みになりながら、一人で何十枚もあるページを実装しており本当にすごい方でした！
ちょっとした画面遷移やローディング、ボタンを押したときの挙動など、細かなUI,UX部分もユーザー目線で考え抜かれており勉強になりました。

バックエンドはOpenAPIからFastAPIのコードを自動生成し、ロジックだけに集中することができました。
バックエンドエンジニアは私含め2人でしたが、今回のアプリの根幹であるAI部分の実装を完全にもう一人の方にお任せしました。
CS専攻であり研究でAIを使われているこれまたつよつよエンジニアの方でしたが、特にアプリの強みとなる「部屋の雰囲気にあった家具をオススメする機能」が、完璧に実装されていて本当に頼もしかったです。
ネットワークの知識やAIの活用方法など、とても勉強させていただきました。

実装は基本的に楽しく行っていましたが、Pythonの型の挙動に気が付かず詰まりました。

```python
                data = await websocket.receive_json()
                sender_id: int = data["sender_id"]
                receiver_id: int = data["receiver_id"]
                message: str = data["message"]
                send_date_time: str = data["send_date_time"]
```

型ヒントを信じすぎて`sender_id`や`receiver_id`がint型だと考えていましたが、実際はstr型でした。
エラーが出ず、WebSocketは確認が難しいため、原因を特定するのにチーム総出でデバッグに3時間ぐらいかかりました...

私はこの経験を通して型アノテーション過激派に転じました。

# ふりかえり

ハッカソンとしてはアイディア出しや設計・スキーマ駆動など、それなりにしっかりとしたプロダクト開発ができていたんじゃないかと思います。

日数が限られている中だと、早く手を動かしたい！コードを書きたい！となってしまいます。ですが、チームがなんとなく持っているイメージをしっかりとすり合わせてから設計したうえで実装しないと、結局手戻りが発生したり、**中途半端な何が作りたかったのかわからない強み0のプロダクト**になってしまうことがあります。
今回のハッカソンではすぐに手を動かさず、サービスについての深い理解とユーザー体験に真摯に向き合う経験ができ、個人的にも成長を感じることができました。

またFastAPIやWebSocket, MinIOなど、他にも様々な未経験の技術に挑戦することができ、技術的にも成長することができました。

# おわりに

3週間とにかく楽しかったです。これも全てメンバーに恵まれたおかげです。みんなありがとう！！

また非常に貴重な経験を積むことができ、ハッカソンを開催していただいたスタッフの皆様にも心より感謝しています！

ありがとうございました！！