就活用ポートフォリオとしてWebサービス「Asobi」を作りました。

# はじめに
こんにちは、ササクラ([@n_sasakura870](https://twitter.com/n_sasakura870))と申します。
受託開発の会社で働いておりましたが色々あって会社都合での退職となりました。今はWeb業界に転職するべく就活中です。

今回は僕のポートフォリオを紹介するとともに、

+ どういった技術を使用して開発したか
+ どういった反省点、課題点が生まれたか

を解説できればと思います。

# 作ったもの
## Asobi
<img width="1430" alt="top.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/bbf30e36-e591-27ba-4bc3-a0e782b7dd3e.png">
様々なローカルルールや自分で考えた遊びを記録・共有するサービスです。
URL : http://www.asobi-app.com/
GitHub : https://github.com/sasakura870/asobi

# 作った背景
友達とたまーにローカルでアナログな遊び(トランプ使ったゲームとかレクリエーションとか)をやることがあるんですが、結構面白くて盛り上がるんですよね。ただ、ふとやりたいなーと思ってググるとローカルルールって探してもあんまり出てこないことが多いんです。
なのでそういったローカルな遊びを記録・共有できるサービスがあれば、投稿して後で思い出せると考え、作ってみました。

# 制作日数
GitHubへの最初のコミットからデプロイまでちょうど100日でした。

# 選定した技術
ここでは使用した技術の紹介と、めぼしい技術の選定理由を説明します。

## バックエンド
+ Ruby 2.6.3
+ Ruby on Rails 6.0.0
  - Webpacker 4.39.3
  - ActiveStorage
  - ActionText
  - slim 4.0.1
  - kaminari 1.1.1
  - ActiveRecord-Import 1.0.3
  - counter_culture 2.2.4
+ RSpec 3.9

### 選定理由
#### ActionText
サービスの要件上、遊びのルール説明にリッチテキストエディタを使用したかったため。
これがまあ簡単に実装できて素晴らしいものだったので、またQiitaに記事を書こうと思います。

#### ActiveRecord-Import
Rails6からバルクインサート機能が実装されたのですが、直接SQLを発行するものでIDのオートインクリメントやvalidation, callbackが効かなかったため。
[公式リファレンスはこちら](https://github.com/zdennis/activerecord-import)

Rails6の新しいバルクインサートメソッドに関してはこちらをご覧ください。
[Rails6 のちょい足しな新機能を試す85（insert_all upsert_all編）](https://qiita.com/suketa/items/f848ac739b6ed58a8ea4)

## フロントエンド
+ Vue.js 2.6.10
  - Vue Croppa 1.3.8
+ FontAwesome 5.10.2
+ sweetalert2 8.18.3
+ Tippy.js 5.1.1
+ selectize.js 0.12.6

最初CSSフレームワークに[Bootstrap](https://getbootstrap.jp/)を採用していましたが気に入ったデザインにならず、最終的に[FLOCSS](https://github.com/hiloki/flocss)に基づいて自作しました。
自分でCSSを書いてみると、思った100倍楽しかったです。

### 選定理由
#### Vue.js
jQueryを使用したことはあったのですが、DOMの操作がより簡単そうだったため。

#### Vue Croppa
ユーザーアイコンのトリミング機能に使用。[Cropper.js](https://fengyuanchen.github.io/cropperjs/)と悩みましたが、UIがこちらの方が好みだったのでこちらを採用しました。
[公式リファレンスはこちら](https://zhanziyang.github.io/vue-croppa/#/)

#### sweetalert2
アラート機能、トースト機能に使用。ポップで可愛いデザインがAsobiにぴったりだと思い採用しました。
カスタマイズ性が高く、Ajaxを絡めた実装も簡単でした。
[公式リファレンスはこちら](https://sweetalert2.github.io/)

#### selectize.js
投稿画面のタグ入力フォームに使用。こちらもカスタマイズ性が高く使いやすかったです。
[公式リファレンスはこちら](http://selectize.github.io/selectize.js/)

## インフラ
+ AWS
  - VPC
  - EC2
  - Route 53
  - RDS
    * PostgreSQL 11.5
  - S3
+ Nginx 1.16.1
+ Unicorn 5.5.1

### 選定理由
一度[Heroku](https://jp.heroku.com/)でデプロイしたことがあったのでAWSに挑戦しました。
~~SSL化したかったのですがまだできていません…。ここはもっと学習しないといけないです。~~
**2020/1/19 追記**
[Let's Encrypt](https://letsencrypt.org/ja/)を利用してSSL対応しました。

# データベース設計
<img width="729" alt="スクリーンショット 2019-12-12 12.32.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/2db1c13e-f1cd-fb18-4612-8d4d4df2f434.png">


+ ユーザーに関する`users`テーブル
+ ユーザーが投稿する遊びに関する`articles`テーブル
+ タグに関する`tags`テーブル
+ いいねに関する`favorites`テーブル
+ コメントに関する`comments`テーブル
+ ユーザー同士のフォローを実装する`relationships`テーブル
+ usersテーブル、articlesテーブルとtagsテーブルの中間テーブルである`tag_map`テーブル

Qiitaのようなユーザーがタグをフォローできる機能を実装するために、`tag_map`テーブルにポリモーフィック関連を実装しました。

# 主な機能
機能やUIはQiitaを参考に設計しました。
機能の概要は[GitHub](https://github.com/sasakura870/asobi)のREADMEにも記載しているので、こちらではより技術的な部分に踏み込んだ説明をしていきます。

## ユーザー
### 登録
![signup.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/bc98d6a7-e693-60f2-3abc-dc87acfd5b79.gif)
[Railsチュートリアル第11章](https://railstutorial.jp/chapters/account_activation?version=5.1#cha-account_activation)を参考に、登録後送られてくるメールのリンクから本登録が完了するシステムを採用しました。
仮登録状態では、一部のページへアクセスした場合に仮登録完了ページへリダイレクトすることで機能を制限しています。
また、仮登録完了ページにメール再送リンクを作成し、Ajaxで登録されたアドレスにメールを再送する処理を実装しています。

### ゲストログイン
登録せずとも一通りの機能を試してもらえるようにゲストログイン機能を実装しました。
ゲストログイン後は本登録ユーザーと同じように操作することができます。(退会とメールアドレス変更のみ禁止しています。)

### ユーザーアイコン
![usericon.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/8e742a6c-e486-ebb7-e78a-e8243f4185ad.gif)
`Vue.js`と`Vue Croppa`を使って画像をトリミングしてユーザーアイコンに設定する機能を実装しました。
画像作成後、適応するボタンを押すことでVue Croppaで作成された画像の`Base64`形式のデータを送信し、Rails側がそのデータをエンコードして`ActiveStorage`に保存する処理が走ります。

### 永続ログイン
![remember.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/3b7ef42c-6d38-4517-462b-bd60d5633bc2.gif)
ログイン時、またはユーザー設定のアカウント画面から「ブラウザを閉じた後もログイン状態を保持するか」を設定できます。
こちらは[Railsチュートリアル第9章](https://railstutorial.jp/chapters/advanced_login?version=5.1#cha-advanced_login)を参考に実装しました。

## 投稿
![post.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/e8969b12-7241-3e37-3eed-ac689315a672.gif)
タグ入力に`selectize.js`を使用しています。編集時には遊びに関連付いているタグを取得し、フォームに初期値として入れるように実装しています。
本文入力フォームには`ActionText`を使用しています。自動生成される`actiontext.scss`のCSSが入力フォームと本文の両方に適応されるのがいいですね。

## いいねとフォロー
![ajax.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/ea54bf00-1477-5edd-e629-692cdfd0e44f.gif)
いいねボタンとフォローボタンは`Vue.js`でコンポーネント化し、Ajaxで処理をするように実装しました。

## Asobiガチャ
![capsule_toy.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/8dd15879-0ec5-7dfd-725d-f40033815b8a.gif)
他のWebサービスにはない機能として、「Asobiガチャ」機能を実装しました。
ボタンを押すとAjaxでRails側でランダムに遊びを1つ取得し、そのデータをjson形式でフロントに渡してJavascriptで表示しています。
使いやすさを向上させるため、引いた後に「もう一度引く」ボタンで再度ガチャが引けるようになっています。

# 反省点
### 頑張らないと投稿出来ないような雰囲気のサービスにしてしまった
遊びのルールを記録・共有するサービスのため、しっかりとした記事が書けるようにリッチテキストエディタを採用しました。
そのためぱっと見で投稿するハードルの高いWebサービスになってしまいました。
もう少し投稿する心理的なハードルが下がるように工夫をしたいと考えています。

### ドメイン駆動設計に憧れてService層とか作っちゃった
作成途中に`ドメイン駆動開発(DDD)`を知り、「なにこれすげー！」と手を出したのが失敗でした。
Service層を作りながら「どこまでがこのServiceクラスの責務なんだ…?」と悩むタネを増やしてしまい、完成に余計な時間がかかりました。
最終的に`Handlerクラス`というものまで作成し、こういった処理の流れになりました。
<img width="810" alt="スクリーンショット 2019-12-11 17.07.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/f6e2b87f-eefd-ad56-4121-f550f09b1c0b.png">
`Controller`は自分のクラス、アクションに合った`Handlerクラス`を呼び出し、`Handlerクラス`は`Serviceクラス`を呼び出しています。
`Serviceクラス`は「ログインする」「タグ入力フォームに記載されたタグを取得または作成する」「いいねする」といった最小単位の処理のみ実行することで、様々な`Handlerクラス`から再利用できる仕組みです。
…が、振り返れば単純な`CRUD`しかしないアプリにこんな大層なアーキテクチャを採用する必要はなかったと感じています。

### テストをほぼ書いていない
先ほどの`ドメイン駆動開発(DDD)`で各処理を切り分けておきながら、テストを書いていません。何のためのDDDなんだ…
GitHubには制作初期に書いたRSpecのテスト(ほぼ通らない)が置いてあります。

### DockerとかCircleCIとか触ってみたかった
これは制作中に存在を知ったので手を出しませんでした。次のポートフォリオで採用してみようと思います。

# 今後の課題点
### HTTPS化
~~AWSの`ELB`や`CloudFront`を使ってサイトのHTTPS化を行いたいです(挫折済み)。~~

#### 2020/1/19 追記
[Let's Encrypt](https://letsencrypt.org/ja/)を利用してSSL対応しました。

### レスポンシブ対応
サービスの要件上、モバイルからのアクセスの方が多そうなので(実際アクセスされるかは別として)レスポンシブ対応したいです。

### 入力フォームのエラーメッセージの表示
<img width="1440" alt="スクリーンショット 2019-12-11 17.40.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449905/0732cff3-c66b-75ff-3e0b-45b5006aa14c.png">
現状だと新規登録や投稿画面の入力フォームのエラーメッセージが画面上部に表示される仕様です。
これもsubmitの前に、validationチェックし、エラーがあれば入力フォームの近くにそれぞれのエラーメッセージを表示するようにしたいです。

### ソーシャルログイン機能
Twitterログインを実装してもっと利用しやすい仕組みにしたいと考えています。

# 作ってみた感想
自分で考えたWebサービスを形にするということは予想以上に大変でした。
Railsチュートリアルと違って道筋がない(当たり前ですが)ので、「この機能で本当にいいのか?」「このUIでいいのか?」「この処理はどこに書けばいいんだ?」と常に迷子になりながらコーディングしていました。その分デプロイできた時は脳汁出まくりで気持ちよかったです。

振り返れば大変だった以上に楽しかったです。エラー出しまくりながらやりたいことが実装できた時の感動は他では味わえないです。
このWebサービスを最後まで作りきることが出来たのも、ひとえにQiitaやSlackやもくもく会でアドバイスを下さった皆様のおかげです。本当にありがとうございます。

今後もこのWebサービスを改修しながら別のWebサービスも作りつつ、就活に励む所存です。

### あとがき
名古屋でもくもく会を開催するのでよかったら参加してください。
https://connpass.com/event/158832/