【個人開発】Railsでサ活(サウナ活動)を管理できるアプリを作成しました。

# 追記 (2022.08.28)
都内の自社開発企業様からの内定を承諾し、2022年の9月15日からWebエンジニアとしてスタートすることになりました！
このポートフォリオの面接官の方からのウケは良かったです！
特に自分の**好きなことを元にサービスを作って、細かいところ(N+1問題の解消など)に関しても頑張って作っている**ところなどは褒めていただけました。
たまにサウナ好きな面接官の方がいて、普通にサウナトークで盛り上がることもあって、そのときは普通に楽しかったです笑

https://twitter.com/matsuken_web314/status/1558292031809949696?s=20&t=btrNKp_VfyBXkICZqv8O7w


# 1.はじめに
本記事を閲覧いただきありがとうございます！
転職活動用のポートフォリオとしてRailsでサ活(サウナ活動)の記録を管理するためのアプリを作りました。この記事はアプリの紹介や作成した経緯をまとめたものです。


## 自己紹介
- メーカー勤務の24歳。4月で新卒3年目になりました
- 働きながら毎日プログラミング学習中
- サウナとサッカーが大好き

# 2.作成したアプリについて
サウナ好きがサ活（サウナ・水風呂・外気浴の時間）を記録して管理するためのアプリです。
- アプリ名: **『ととのい』**

- アプリURL: ~~https://totonoi.org/~~ (現在運用停止中🙇‍♂️)
- GithubURL: https://github.com/matsuken314/onsen_app

ホーム画面からゲストログインできます。（その他、新規登録画面からも可能です。）
![guestlogin.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/bd92403f-750e-2f50-3801-5337a633fd77.jpeg)


## 読む前に知ってほしい簡単なサウナ用語
**「ととのう」**・・・ サウナ・水風呂・外気浴を繰り返すことで身体はリラックスし、頭は興奮するトランス状態になること

**「サウナー」**・・・サウナ大好きな人

参考サイト　: [現役医師が教える、サウナで「ととのう」条件と正しい入浴方法](https://www.pen-online.jp/article/008062.html)

## メインのターゲットユーザー
- 「サウナー」

# 3.作成背景
私はサウナ大好きのいわゆるサウナーです。週２でサウナでととのってます。
日頃から**最高にととのった日の場所や、サウナ・水風呂・外気浴の時間を記録して、まとめたい**思いがありました。
そうすることでより良い「ととのい」を追求できるのではないかと思っていました。

また、**初めてのサウナ施設に行くとき、その施設を利用する他のサウナーのサ活（サウナ・水風呂・外気浴の時間）を目安にして、ととのいたい**思いもありました。

**これらの課題**を解決するため、このWebアプリを作りました。

# 4.作成目的

- 日々の自分のサ活を管理したいから
- WEBアプリを0から開発することが**技術力を高めるための最高の学習**だと思ったから
- 転職活動で技術力を分かりやすくアピールするため

# 5.使用画面のイメージ

使用画面のイメージについては以下の通りです。

[①トップページ](#トップページ)
[②新規ユーザー登録ページ](#新規ユーザー登録ページ)
[③サウナ施設一覧ページ](#サウナ施設一覧ページ)
[④サウナ施設詳細ページ](#サウナ施設詳細ページ)
[⑤サ活投稿ページ](#サ活投稿ページ)
[⑥マイ保存サウナ施設ページ](#マイ保存サウナ施設ページ)
[⑦マイページ](#マイページ)
[⑧管理者ページ（サウナ施設投稿ページ)](#管理者ページサウナ施設投稿ページ)

## ①トップページ

- ファーストビューで何をするアプリなのか分かりやすくしました。
- ログインしてないユーザーにはアプリの３つの特徴が表示されます。
- 新着のサ活（ユーザーの投稿）が3件、サウナ施設（管理者の投稿）が6件表示されます。

![top.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/46dda367-b6fa-a5f1-2c8a-d9dc03488d24.gif)




## ②新規ユーザー登録ページ

- メールアドレスでの登録以外にお試ししたい方向けにゲストログインリンクを貼りました。

![signup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/9c80ac98-880c-0ed8-20e1-fc53383280be.png)


## ③サウナ施設一覧ページ

- PCでは3カラム,タブレットでは2カラム,スマホでは1カラムで一覧表示します。
- 一覧ページからでもブックマークアイコンを1クリックするだけでお気に入りのサウナ施設を保存できます。

![sauna_list.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/31abc75e-d611-62ea-fc60-bcb22926ee30.gif)


## ④サウナ施設詳細ページ

- サウナ施設での全ユーザーのサ活（サウナ・水風呂・外気浴のそれぞれの時間）の平均が円グラフに表示されます。
- サウナ施設でのサ活を一覧表示します。サ活が無い場合、ユーザーのサ活を促すデザインにしました。(下画像参照)


| サ活投稿あり | サ活投稿なし(サ活を促すデザイン) |
----|---- 
| ![data_onsenshow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/52226376-4ab2-943c-d63d-a11a49bb5a42.png) | ![nodata_onsenshow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/711754cc-b15b-679b-8d2a-d3b2c152b70e.png) |


## ⑤サ活投稿ページ

- サウナ施設名とサウナ・水風呂・外気浴の時間を入力して、サ活を投稿できます。感想は空でも投稿可能です。
- サウナ施設名は１文字入力すると、文字に関連する候補のサウナ施設名が検索窓のような形で表示されます。


![autocomplete.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/c7cd0b26-8704-a7cb-e3c0-1f3a29fe3e13.gif)



## ⑥マイ保存サウナ施設ページ

- 保存したサウナ施設を表示します。他のユーザーは見れません。

![favorite_page.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/104360f2-4c68-c0ea-e466-ec2153b0aef4.png)

## ⑦マイページ

- ユーザーのサ活（サウナ・水風呂・外気浴のそれぞれの時間）の平均が円グラフに表示されます。
- これまでのサ活、サ活した施設もタブ切り替えで表示できます。
- ユーザーのサ活が無い場合、それを促すデザインにしました。(下画像参照)

| サ活投稿あり | サ活投稿なし(サ活を促すデザイン) |
----|---- 
| ![data_mypage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/054fb145-6824-634e-81dd-96e692ff74ba.png)| ![nodata_mypage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/bfe0b785-e783-d62d-fcb6-dfbe8eec4c55.png) |


## ⑧管理者ページ（サウナ施設投稿ページ)
- サウナ施設の【名前】・【ふりがな】・【場所】・【施設画像】・【施設メモ】・【サウナの温度】・【水風呂の温度】を投稿・編集・削除できます。


![admin_view.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/24f69007-576d-0cfd-2134-4c93e5a4a4c7.gif)




# 6.機能一覧
## 基本機能
- サ活関連
  - サ活投稿
  - サ活編集
  - サ活削除
  - フラッシュメッセージ機能（サ活の投稿・編集・削除）
- ユーザー関連
  - マイページのサ活円グラフ表示
  - マイページでサ活した施設の表示
  - 保存したサウナ施設の閲覧
  
- サウナ施設関連
  - サウナ施設一覧を12件ごとにページネーション表示(kaminari)
  - サウナ施設保存(Ajax)
  - サウナ施設詳細ページでみんなのサ活円グラフ表示機能(Chart.js)
  - 管理者機能(Active admin)
    - サウナ施設投稿
    - サウナ施設編集
    - サウナ施設削除

- 各ページにパンくずリスト表示（gratel）

## 認証機能
- 新規ユーザー登録・ログイン・ログアウト
- ゲストログイン
- パスワード再設定
- 新規登録時、パスワード再設定時のメール認証
- プロフィール編集
- 退会

# 7.使用技術
## フロントエンド
- **HTML**
- **CSS,Sass**
- **JQuery 3.6.0**


「おやっ？」て思った方いるかもしれません。そうです。CSSフレームワーク（Booststrapなど）、使ってないんです笑笑。Sassを使って、デザインをカスタマイズしてました。
下記、3点の理由からです。
- フレームワークを使用することで、CSSの構造を把握するのが難しくなるのが嫌だった
- デザインにはこだわりたかった
- Web制作の副業でCSSを扱うのには慣れていた


## バックエンド
- **Ruby 2.6.8**
- **Ruby on Rails 6.1.4.1**

**【技術選定理由】**
- 初学者向けのわかりやすい教材・記事が多数ある
- 関東での転職を目指しているため、そこでの求人数が多い言語とフレームワークの組み合わせであるRuby/Ruby on Railsを選択

## インフラ
- **Heroku(ステージング環境→本番環境)＋AWS(S3)**
- **Cloudflare(Herokuに適用した独自ドメインをSSL化するために使用)**

**【技術選定理由】**
アプリの開発作業に集中したかったため、インフラにはデプロイ作業が簡易的にできるHerokuを採用しました。
その他にも下記、３点の理由があります。
- ステージング環境が無料で作れる
- 管理画面のUIが整っていて使いやすい
- 無料プランで活用できるアドオンが多い(ログ監視機能など)

また、無料アドオンの[Heroku Schedular](https://devcenter.heroku.com/articles/scheduler)を利用することで、
<font color ="blue">**「30分以上アクセスが無いとスリープモードになる(立ち上がりが遅くなる)」**　というHerokuの弱点</font>を解消しています。

とは言っても、、、現在のクラウドサービスで一般的に使われている
<font color="red">**AWSにはデプロイしたかったです。。。**</font>
なので、今後AWSについてはこのアプリのデプロイ作業を通して、学習していく予定です。
## データベース
- **PostgreSQL14.0**

## その他
- **Visual Studio Code（エディター）**
- **Github(ソースコード管理)**
- **Rubocop (静的コード解析ツール)**
- **Draw.io (ER図の作成)**
- **unDraw(イラスト素材)**
- **COOL COLORS（全体の配色選定）**
- **Notion（コードのドキュメント）**

エラーにハマった時のメモツールとして活用してたのがNotionです。
下の動画を参考にして、**もう一度同じエラーに出会ったらすぐ対処できるようにするため**、
以下の4つをメモとして未来の自分に向けて、書いてました。

- 何をやろうとして、エラーになったのか
- 調査して、分かった原因
- 解決策
- 参考サイト

<iframe width="560" height="315" src="https://www.youtube.com/embed/nbNj4d8_cMU?start=418" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 8.ER図
![ER.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/6521d04c-0fe4-a401-f083-063c07d89385.png)

# 9.工夫した点
## N+1問題の解消

- gemの[bullet](https://github.com/flyerhzm/bullet) を使用してN+1問題を発見した場合はリファクタリングをしていました。N+1を発見した場合は下記のように警告画像が出現します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/e46d8368-0fc5-a4fa-bad3-05c4047b500e.jpeg
" width="50%">

- 部分テンプレートのrenderメソッドを使う際はeach文の中で使わず、collectionを使用。そうすることで部分テンプレートの呼び出しはレコードの件数が多くなっても１回に抑えることができるので、パフォーマンスが悪化することを防ぎました。（下記のBefore▶afterのコード参照）

**【Before】N+1発生コード**
```Ruby:
 インスタンス変数に入ってるonsenの個数分だけ部分テンプレートを表示するロジックなので、
onsenの個数が多いとパフォーマンスが悪くなります。
<% @onsens.each do |onsen| %>
  <%= render partial: 'onsens/onsen', locals: { onsen: onsen }%>
<% end %>
```

**【After】N+1解消コード**
```Ruby:app/views/homes/index.html.erb
 collectionを使用して、リファクタリング。
 コントローラから渡された@onsensに入っている各onsenの値を表示するようにしました。
<%= render partial: 'onsens/onsen', collection: @onsens, as: 'onsen'%>
```

## オートコンプリート機能の実装

サ活投稿機能を作成する際、**「サウナ施設名」をユーザーに正しく入力してもらうのが大変という課題**がありました。

その**課題を解決するために**サウナ施設名のワードを１文字でも入力すると、それに応じた温泉名が自動で下に出力されるオートコンプリート機能(検索エンジンなどでよく見かける、文字入力をすると候補が並ぶ機能です)を[easy-autocomplete](http://easyautocomplete.com/)を利用して、導入しました。

![autocomplete.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/c7cd0b26-8704-a7cb-e3c0-1f3a29fe3e13.gif)　


## ユーザーのサ活(投稿)に連動した円グラフ
マイページで各ユーザーのサウナ・水風呂・外気浴の平均データを円グラフで表示しています。こうすることで「ととのう」時の時間配分の目安になるようにしています。
個人的に1番お気に入りの機能です笑。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/5f2b90d0-1e73-8f5a-276e-b226c53b1e03.png
" width="50%">

## 実務を想定したチーム開発

実務を想定して、下記の手順で開発していました。
1. issueにタスクを登録し、
2. それに応じてブランチを切って、
3. プッシュする際はプルリクを発行して、
4. Github上でマージ。
5. issueに登録したタスクが完了後closed。

## UI・UX（デザイン）
-  アイコンやロゴを使用して、パッと見で分かるシンプルなデザインにしました。
-  スマホサイズではユーザビリティを高めるため、ログインしている場合、最下部に固定メニューバーを自作しました。（↓下画像参照）

![sp_fix_under.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/6affa468-85b0-e30f-d039-4edc654f3ba1.png)
  

- サ活を記録するとマイページの円グラフが変化していき、記録するほど、自分のサ活の傾向が見えてくる設計。
- ユーザーの目に優しく、奇抜な色使いはせず、以下の配色を参考に全体的な落ち着いたデザインにしました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/a8e151ca-96fe-0194-3ce7-f7dd321dc2d5.png
" width="65%" background-color="#333">


# 10.作成中に意識したこと
作成中に意識したことは以下の3点です。
[①エラーは40分取り組み、分からなかったら質問](#エラーは40分取り組み分からなかったら質問)
[②難しいタスクは分解し、細かくする](#難しいタスクは分解し細かくする)
[③粘り強く取り組む（投げ出さないこと）](#粘り強く取り組む投げ出さないこと)

## ①エラーは40分取り組み、分からなかったら質問

起こったエラーに対しては、公式ドキュメント・関連記事などを調べながら、
なぜ起こったのか言語化することを意識していたので、
Notion(メモツール)に下記3点を書き、言語化して、解決しようとしていました。

- 目的　(やりたいこと)
- 現状　(どこで詰まったのか・どんなエラーコード・ログが出たのか)
- 現状に対し、試したこと

それでも解決しない場合、オンラインスクールのSlackにて質問を行っていました。
なるべくメンターさん(解答者)の時間を奪わないように、上記3点のメモを元に参考記事なども添えて、質問を行いました。

1人で悩み続けても自分の時間を奪うだけなので（実務においては会社に迷惑をかけることになるので）、40分という時間制限を設けて、エラー解決に取り組んでいました。


## ②難しいタスクは分解し、細かくする

**やるべきことが明確になる**ので、タスクをできるだけ細かくすることを意識していました。

例えば、このアプリのサウナ施設保存機能(非同期処理)は以下のようにタスクを細かくして、実装しました。

**【タスク分解例(非同期処理)】** 
1. bookmarkモデル作成
2. UserモデルとOnsenモデルのアソシエーション設定
3. createアクションとdestroyアクションのルーティング設定
4. bookmarkコントローラ作成
5. サウナ施設保存アイコンのビュー(部分テンプレート)作成
6. 非同期処理をするための.js.erbファイルの作成


こうすることで、**やるべきことが明確になり**、作業の取り掛かりの時にまず何をすべきかを考える時間が無くなり、より効率的に開発を進めることができました。


## ③粘り強く取り組む（投げ出さないこと）
根性論はあまり好きではありませんが、結局のところ、これかなと思います。天才型のようなタイプではないので、**どんなエラー・問題が起きても粘り強く取り組む**ことは意識していました。特に[devise](https://github.com/heartcombo/devise) の認証関係のエラーには悩まされ、メンターさんに何度もアドバイスを頂き、ようやく機能実装できました。(メンターさんには本当に感謝です。)

>負けないこと・投げ出さないこと・逃げ出さないこと・信じぬくこと 駄目になりそうな時、それが1番大事

ある有名な曲の歌詞にもありますね。


# 11.今後の課題・やりたかったことなど
- **サウナ施設の投稿をAPI連携などで自動化**
- **「サウナ施設名」入力時の<font color="blue">オートコンプリート機能 </font>→<font color="red">プルダウン式</font>に変えて、入力を簡易化**
- サウナ施設のGooglemapと連携
- サウナ施設の絞り込み検索機能
- サ活投稿のTwitterシェア
- AWSにデプロイ
- テストコード
- Vue.jsの使用

**※特にやりたかったのは太字です**

## サウナ施設の投稿をAPI連携などで自動化
**現状**はサウナ施設の投稿が管理者画面からの手作業になっているので、手作業を自動化するための**解決策**として、サウナまとめサイトなどのAPIを利用して、自動で全国のサウナをアプリに反映したいです。
# 12.アプリ作成後の感想


結論、楽しかったです。
自分で思い描いて、設計した大好きなサウナの記録アプリが１つ１つ形になっていくのはワクワクしました。一方で実装中に気の遠くなるようなエラーに遭遇することもありましたが、粘り強く取り組み、アプリを実際にデプロイまでして、ネット上に公開することができて、本当に良かったです。

また、パフォーマンスに配慮して(N+1が発生しないように)コードを書けたときが気持ちよかったです。ととのいました笑。

最後までご覧いただき、ありがとうございました！！！