【個人開発】面倒な献立作りから解放し、レベルを上げて料理を楽しむレコメンド献立アプリを作った話

![fun_coo_app_top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/fe6096ad-f0fb-8f0c-e72b-664f385a191b.png)

<font color="#FFA45B">**こちらの記事を訪れていただき、ありがとうございます。**</font>
この度、タイトルにもある通りポートフォリオとして **「面倒な献立作りから解放し、レベルを上げて料理を楽しむレコメンド献立アプリ(FunCooApp)」** を開発しました。

こちらの記事では、**アプリの制作過程や実際の機能など** を紹介していきます。
**アプリの使用イメージは『[主な機能](#主な機能)』で確認することができます。**

- (旧)アプリURL：　~~https://fun-coo-app.com~~ ~~(費用を考慮してHerokuに移行予定です)~~
- (新)アプリURL： ~~https://fun-coo-app.herokuapp.com/~~ (Herokuの無料プランが廃止されたため現在公開を停止しています)
- GitHubリポジトリ: https://github.com/iloveomelette/funcoo_app
- Twitter(学習継続130日[執筆時点]): https://twitter.com/icanenrichlife7

## 簡単な自己紹介

- 神奈川県在住の24歳、男
- 2月前後にRubyの学習を始め、2月中旬からRailsの学習を開始(総学習継続130日[執筆現在])
- 同時に基本情報技術者試験の学習も並行し4月末に受験し『合格』しました。
- 好きな食べ物は **「オムライス」** です！

## 開発目的、背景

### どうしてポートフォリオ？

大きく **『3つ』** あると考えています。

1. 転職活動をする上で、現時点での自分のスキルを証明するため
2. 実際の現場に入る前に開発の全体的な流れを大まかに把握するため
3. 実践を通して技術力を向上させるため

### どうしてレコメンド献立アプリなの？

お恥ずかしい話、私は現在実家に住まわせていただいております。
そんな我が家では、以下のような会話が毎日繰り返されていました。

母 「今日の夕ご飯は何にしようか？」

私 「ん〜、何がいいかな〜。なんでもいいよ！」

> 心の声： どうしよう...何を食べたい気分なのか、がない！

母 「なんでもいいじゃ分からないでしょ！献立を毎日考えたり料理をするのってすごい面倒だよね。」

私 「うん...」

みなさんもこんな会話があるのではないでしょうか。
聞く側も「なんでもいい」と言われ **自分で考えて作らなければならない面倒な気持ち** を抱え、聞かれた側も答えられず、**申し訳なさなど** を抱える。

**そんなマイナスしか生まない会話を解消したい** という想いから当アプリが生まれました。

「FunCooApp」という名前も **「Fun Cooking Application(料理を楽しむアプリ)」** の各頭3文字を取ったものです！


ただ献立を提案するだけでは、献立や料理に関する**プラスの会話は生まれません。**

では、どうしたら料理が楽しくなるのか考えました。それは **「達成感」** だと思います。
「こんだけ頑張ったんだ」という気持ちが湧くと **「もっと頑張ろう！」** と思えますよね？
それを実現するのが当アプリです！

＊補足
レシピの質を担保するため、投稿者は食に精通した個人農家、個人飲食店経営の方々を想定しています。
ユーザの属性を「法人」と「一般利用」に分けて「法人」のみが投稿をできるように実装しています。

### 開発期間は？

最初のコミットが3月下旬だったので、**1ヶ月強** [執筆時点]です！
以下は、GitHubのcontributionsです。（ポートフォリオ以外も含まれています）

![github-contribution.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/dca16c6c-b6c6-cff6-5195-94a30de8bf3f.png)


## 使用技術

### フロントエンド

- HTML / CSS / Sass / Tailwind / Javascript

基本は**Sassを使ってスタイルは仕上げています。**
Tailwindは補助的な立ち位置で使っていましたが、 **分かりやすいクラス名を考えるまでもなく自由に実装できる点は助かりました。** もう少し厳格に使用基準を設定してあげるとコードの見栄えも良くなったかなというのが反省点です。
これは、徐々に改善していきます。

### バックエンド

- Ruby 3.1.0
- Ruby on Rails 6.1.5

Ruby/Railsを選択した主な理由は、
日本語でのドキュメントが充実している点
実際に触れてみて自分との相性が良くスピード感を持って開発できる点　です。

### DB

- MariaDB 10.6.7

MariaDBを選択した主な理由は
実務で普及しているMySQLと高い互換性がある点
学習中はMariaDBを使用しており、スムーズに開発に進められる点　です。

### インフラ

- AWS(VPC / EC2 / RDS / ACM / ALB / Route53 / S3 / CloudFront / IAM)

AWSを選択した主な理由は
Herokuでは起動するまでに時間がかかってしまう点
実務では圧倒的に支持されているであろうAWSの大枠に触れたい点
1年間の無料枠をこの機会に活用して勉強できる点　です。

### ツール

- Rubocop
- FactoryBot / RSpec
- Visual Studio Code
- Git / GitHub(Git-flow)
- FontAwesome
- Adobe Color
- Draw.io
- Figma
- Notion

## 主な機能

![functions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/0940bcca-693a-85a4-cf93-3bd40786da42.png)

以下は、パソコン画面での紹介になります。
**【レコメンド機能】**
「作ってみた！」ボタンを押すと、そのレシピ情報に合わせてログインユーザへレシピをレコメンドします。

![recommend-recipe.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/ffc68c9f-6ca4-a411-bac4-2ec6b63ab1fc.png)


**【レシピ投稿】**
タイトル、内容、調理コストなどの情報、ジャンル、料理イメージを入力して投稿します。

![recipes-form.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/e1d80c54-c0b2-039a-f1cb-3105e43b876a.png)


**【レシピ検索】**
レシピタイトル、レシピ内容、調理時間、ジャンルなどから検索をすることができます。

![search-recipes.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/299acbb1-051c-da25-24ab-858e19a9c42e.gif)


**【アカウント登録】**
アカウント登録では、属性を選択して一般利用か法人かを選択します。ユーザ名、メールアドレス、パスワードを入力してアカウント登録します。また、Googleアカウントでもログインができます。

![user-registration.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/8008b997-880f-194f-8d3c-7f815881867d.png)


**【ユーザページ】**
ユーザページでは、プロフィール画像、メールアドレス、現在のレベルなどの情報、「作ってみた！」ボタンを押した投稿、お気に入りの投稿を閲覧することができます。

![user-mypage.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/fe3ee004-efeb-1459-993f-bfed72a171b2.gif)


使用したGem[一部抜粋]は以下の通りです。

![gem_list_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/940ca01c-dbd5-210d-b594-d74427f2a720.png)


## 開発過程での意識した点

### 実務(チーム開発)を意識した開発

実際の現場を想定して`Git-flow`モデルに沿って開発を行っています。
`Commit`メッセージとプルリクエストのタイトルは **誰が見ても概要がわかる** ようにし、メッセージは **具体的にどんな実装を行ったのか** 、理由を記載すべき時は理由を含めて記述していました。

↓実際に使用したブランチ

| ブランチ名 | 目的 | 用途 |
|:-:|:-:|:-:|
| main/master  | 本番用  | 開発作業は行わず、タグでバージョン管理  |
| develop  | 開発用  | featureブランチを作成して機能開発  |
| feature  | 機能実装用  | 新規機能を開発してdevelopにマージ  |
| release  | リリース直前用  | リリース直前のバグ修正など小さな変更してmain/developにマージ  |

### 難しいタスクは細分化して開発

詰まってしまったり、処理が複雑な場合は以下のように**細分化**したり、対象が1人の場合どうなるか、など**簡単に実装できるところから徐々に膨らませていきました。**
この時にNotionを活用していました。

1. ゴールを明確に言語化する
1. それに到達するまでの中間ポイントを明確に言語化する
1. 一つ一つを実現するための手順や必要な変数などを言語化する
1. コードに落とし込んでいく

以下は、実際に細分化している一部です。

![notion_define_task.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/466585d6-cc8b-07c1-094b-ffe3f90f485f.png)


### ユーザ視点でのUI/UX設計

まず、**色がぶつかり合って見づらくならないように**
アプリ内で使用するカラーを **3色** ないしは **4色** に限定しました。

| 優先度 | 色コード | 選定基準 |
|:-:|:-:|:-:|
| 1  | <font color="#FFA45B">**オレンジ**</font>  |料理は火のイメージが強く、それに近く元気なイメージを与えられる色 |
| 2  | <font color="#5EAF23">**グリーン**</font>  | 近年では健康志向が強いため、健康かつ体に優しいイメージを与えられる色 |
| 3  | <font color="#FFDA77">**イエロー**</font>  | オレンジとの相性がよくサブとして活躍できる色 |
| 4  | <font color="#AEE6E6">**スカイブルー**</font>  | オレンジ、グリーン共に相性が良くアクセントに使用できる色 |

次に、**通常時以外** の時に行動を促すメッセージを表示するようにしました。
例えば、レコメンド機能ですが最初の段階では「作ってみた！」を押さない限り、レコメンドすることができません。
そういった場合は、「作ってみた！」を押すように促すメッセージを表示しています。

![recommend_exception_message.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/2d7462e3-3c39-2bc0-1061-1fd7f2078c14.png)

まだまだスタイルの改善点はあるので、随時更新していきます！


## 開発過程での苦労した点

苦労した点はかなりあるのですが...大きく**3つ** 挙げさせていただきます。

### RailsデザインであるFormオブジェクトでの実装

特に更新アクションにおいて、パラメータがうまく渡らず苦戦しました。

1つのフォームで複数のテーブルに保存をかける方法の一つとして、**Formオブジェクト**での実装を行いました。
理由としては、`accepts_nested_attributes_for`の評判があまり良くなく、Rails開発者の方も無くしたいと言っているメソッドであり、現役のエンジニアの方々が会社の方針として使用禁止にしていることを聞いたためです。

こちらの記事でアウトプットさせていただきました。宜しければご覧ください。

- [【Rails】1つのform_withで複数のテーブルに保存をかける](https://qiita.com/iloveomelette/items/88c81dafa97500130c70)

Formオブジェクトで実装したことで、**パラメータがどのように渡っていたり**、**どのような流れで渡されていくのか**を`binding.pry`を通して学ぶことができました。
また、**どのように問題を切り分けるか**地力の向上に繋がりました。
まだまだ奥が深いので、引き続き深ぼっていきたいです。

### レコメンド機能の実装

レコメンド機能のアルゴリズム設計に苦戦しました。

調査を行なっていくと、「協調フィルタリング」「Matrix Factorization」「SVD（Singular Value Decomposition）」など様々あることに驚きました。
**「強調フィルタリング」** がレコメンドを実装する上で基礎となりうると判断し、**コサイン類似度**を用いて実装しました。

レコメンドの土台となるデータ(`Genre`テーブルの`enum`型カラム)を定めて、どのように他ユーザとの関連性を求められるかを考え抜きました。
**こちらも後に記事にしていきたいと考えております。**

「強調フィルタリング」はユーザやレシピの数が増えると、それだけ次元が増え計算が困難になる **「次元の呪い」** があります。これを回避する次元削減という考え方があるみたいなので、こちらも触れていきたいと思っています。

### AWSへのデプロイ

エラーを解決して1歩進むたびにエラーと遭遇する、を繰り返してかなりの根気を要しました。
この時に意識したことが、基本ではありますが

- エラー文を**隅々までよく読むこと**
- それをもとに、**どこで**エラーが発生しているのか(AWS側なのかアプリケーション側なのかなど)、**何が**要因なのか仮説を立てる
- 調査、検証を繰り返す

詳細は省きますが、以下の記事でアウトプットさせていただきました。

- [AWS - Capistranoを使ってデプロイした際のエラー奮闘記](https://qiita.com/iloveomelette/items/3c0f2dd3faa2fcefc948)
- [AWS/Rails デプロイ後の『502』エラー奮闘記](https://qiita.com/iloveomelette/items/19bacb6658a2c62cc9c7)
- [【ACM/ALB】504 Gateway Time-out と向き合って見つけた解消法](https://qiita.com/iloveomelette/items/27f45f6c2c14627a19fd)

## ER図

![recommend_exception_message.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/ddc5655a-1b80-5f19-aabb-5ac4aff32641.png)

ER図は上記の通りです。
抜粋してテーブル/カラムの説明を記載します。(左から)
- **`Genres`テーブル**：　**レシピのジャンル。全て`enum`型で管理**
    - `staple_food`：　主食
    - `main_dish`：　主菜
    - `side_dish`：　副菜
    - `country_dish`：　各国料理
- **`Users`テーブル**：　**ユーザ**
    - `characteristic`：　属性。法人もしくは一般利用。`enum`型で管理
    - `level`：　レベル
    - `experience-point`：　経験値
    - `rest-point`：　次のレベルまでの残り経験値
    - `url`：　法人が運営するHPのURL
- **`LevelSettings`テーブル**：　**レベル参照テーブル**
    - `passing_level`：　合格レベル
    - `threshold`：　閾値(次のレベルを満たす経験値)


## インフラ構成図

インフラ構成図は以下の通りです。

![Infrastructure diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/25c4ee58-30d9-1ed6-6ea9-0d42f2e64a37.png)


## 今後の展望と課題

### 課題

- `Users`テーブルのカラムが少々多いため、正規化してクリーンにする
- 当たり前ですが、現状コードのリファクタリングを行うこと
- レコメンド機能の比較他ユーザ5人のみの比較となっており、正確な関連性ではないため、こちらの最適化
- レベルアップ機能は現状 Lv.50までしか用意しておらず、それ以上の計算となると膨大な計算量になるため、こちらの最適化
- 1つの食材ずつ登録ができるようにFormオブジェクトでの投稿フォームの改善
- アカウント編集の際、新しいプロフィール画像を選択すると、それに応じてビューの画像も切り替わるように改善
- セキュリティ面の向上

などなど課題は山積みですが、一つ一つ改善していきたいです。

### 展望/実装したいこと

- レシピに対するコメント機能
- ユーザ同士の褒め合いや相談投稿
- 相互フォロー機能
- チュートリアルページ(ヘルプページ)
- お問い合わせ
- Vue.jsを活用したフロント側の向上
- Docker/Docker-composeを活用したコンテナ化
- 自動テストの導入

## 開発をしてみての感想

実際に0から開発を行って**大まかな全体像を掴むことができた**ことで、私にとって**自信に繋がり開発を行う楽しさを味わえました。**

試行錯誤を繰り返しながら実装していったため、少々コミット数が多かったり、効率的に記述できる部分があったり、と**今後の伸び代がある点は反省点でもあり、嬉しい収穫でもあります。**

開発途中でエラーと遭遇した時 **「よしきた！」** と思えるようになったことは、エラーを恐れていた開発前と比べて**非常に成長できました。**
まだまだ至らない点が数えきれないほどにありますが、**1つ1つ身につけていけるよう今後も精進してまいります。**

<font color="#FFA45B">**最後までお読みいただきありがとうございます。**</font>
<font color="#FFA45B">**`LGTM`を押していただけますと、大変励みになります！**</font>