[Rails-API,next-jsを使用]　自分が作成したポートフォリオを紹介させてください。

#はじめに
バックエンドにRuby on Rails,フロントエンドにNext.jsを用いて**SPA**型のポートフォリオを作成しました。
このポートフォリオは、自分が**Rails**と**AWS EC2**を用いて開発した一つ前の[ポートフォリオ](https://github.com/takaya787/rails_app)をSPA化したものになります。
あくまでもSPA化を目的に作成したので、デプロイ先は**vercel**と**heroku**をそれぞれの環境で使用しています。

##アプリの概要（Git Hub repoのREAD MEと同じ)
####各レポジトリー
[**Frondend repository**](https://github.com/takaya787/ts-frontend)
[**Backend repository**](https://github.com/takaya787/backend)

####アプリ名: [**住み心地.com**](https://frontend.takaya787.vercel.app/)
世界中の住み心地に対するレビューの投稿(user登録必要)、閲覧ができます。
![スクリーンショット 2021-01-30 11.37.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754958/5fe6d179-12ee-54d9-2111-4a73806a17a1.png)

####使用技術
* React
* Next.js（フロント部分)
* Typescript
* SCSS
* Ruby 2.7.2
* Rails 6.1.1(サーバー部分)
* postgres SQL
* Docker, Docker-compose (開発環境)
* Google-Map API
* Geocoding API

####機能一覧

◆　ユーザー機能 
* 新規登録、ログイン、ログアウト
* マイページ、登録情報を変更

◆　レビュー機能 
* 新規作成、変更、削除(削除はユーザーページからのみ可能)

◆　Map機能( google-map-react 使用)
* 位置情報検索から、地図を移動( geocoding API使用)
* レビュー新規作成、変更機能
* レビューを読み込み、地図内で表示
* クリック時にクリックされたレビュー内容を表示

####ERD図
![スクリーンショット 2021-02-01 23.08.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754958/6b5b4a95-08d5-f42a-8879-880e0b446b02.png)

##作成理由
自分は留学経験があるのですが、留学に出発する前は自分が知らない土地で生活することに対して、不安を感じました。
そこで、海外生活への不安を経験者の話を聞いて、少しでも解消できればと思い、今回のサイトを作成しました。

##技術選定やこだわった点について

ここからは自分なりの技術選定について記載していきますので、もしおかしいところがあったり、ここの技術はこれがオススメなどのアドバイスがあれば、気軽に送って頂けると嬉しいです。

###開発環境について
開発環境は**気軽に環境を変更できて、共有しやすい**という点から**Docker**,**docker-compose**を使用しています。
**ファイル構造**

```
├── back
│   ├── Dockerfile
│   ├── Gemfile
│   ├── Gemfile.lock
│   ├── README.md
│   ├── Rakefile
│   ├── app
│   ├── bin
│   ├── config
│   ├── config.ru
│   ├── db
│   ├── lib
│   ├── log
│   ├── public
│   ├── storage
│   ├── test
│   ├── tmp
│   └── vendor
├── docker-compose.yml
└── front
    ├── Dockerfile
    ├── README.md
    └── app
```

次のように各ディレクトリ毎にDockerfileを設定して、docker-composeで一括管理ができる用にしています。

```yml:docker-compose.yml
version: "3"
services:
  frontend:
    build:
      context: front/
      dockerfile: Dockerfile
    container_name: frontend
    volumes:
      - ./front/app:/usr/src/app
    command: 'npm run dev'
    ports:
      - "8080:3000"

  db:
    container_name: psql_server
    image: postgres
    volumes:
      - psgl_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: password
    ports:
      - 3308:3306

  backend:
    container_name: backend
    build:
      context: back/
      dockerfile: Dockerfile
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    depends_on:
      - db
    ports:
      - "3000:3000"
    volumes:
      - ./back:/api

volumes:
  psgl_data:
```

###フレームワークについて

今回はフロントエンドに**Next.js**,バックエンドに**Rails API**を使用しています。

####Next.jsを選択した理由について
* router処理を完全にサポートしている（**Dynamic Routing**も含む)
* **SSG**と**SSR**を両立できる

他のReactのフレームワークとして、同じ**JAMstack**な**Gatsby**もありますが、**Dynamic Routing**による動的なコンテンツが作成できる点で**next.js**を採用しました。

###### では次にこの２つの理由についてそれぞれ説明していきます。
>router処理を完全にサポートしている（**Dynamic Routing**も含む)

**next.js**ではrouting用のpackageを導入せずに、routing設定をすることができます。
**create-react-app**では**react-router-dom**などを導入し、なおかつ**Router Component**を用いて各routingを設定する必要があります。
ですが、**next.js**では**pages**ディレクトリーにfileを設置するだけで可能で、これは非常に効率的だなと思います。

>**SSG**と**SSR**を両立できる

next.jsでは、**getStaticProps(SSG)**,使用して動的に取得したdataを用いて、ページを静的に作成することができます。
つまり、build時にページを静的に作成していますが、ユーザーページなど日々新しく作成されるページは、
ユーザーが登録される毎に、動的にserver側が作成できるので、**静的なページサイトの高速性**と**動的ページ作成の利便性**を得られます。
この点はとても素晴らしい技術だなと使ってみて、実感しました。

####Rails APIを選択した理由について
**Rails API**を選択した理由は、僕が使える**バックエンド**が**Rails**だけだったということが正直な理由です。(笑)
ですが、Railsはその**MVCアーキテクチャー**により、ウェブページをこのフレームワークだけで作成できるという点だけでなく、APIモードでも、データベースも含めて一箇所で作成できるので、凄く開発しやすくて個人的に好きです。

ただ、速度が非常に求められる大規模なサービスなら、Railsではなく他の技術で作成したほうが良いとは思います。
###外部APIについて

####Google-Map API
**このサイトのコンセプト**が、海外の場所へ**住み心地**という評価と共にレビューを投稿できることなので,海外の場所に直接リンクさせながらレビューを探したり、投稿できるようにしたいとは考えていました。
ですので、地図を表示させるのに最も適している**Google-Map API**を採用しようと決めました。

####Geo-coding APIについて
ユーザーがそれぞれの目的地へ直接レビューを検索、投稿できる仕組みにしたかったので、検索キーワードから地図の位置情報を検索システムと同期させるために使用しました。

###機能でこだわった点
* Map内でレビューを閲覧、投稿、編集ができること
* 入力フォームでserver側のvalidationエラーを表示させる

>Map内でレビューを閲覧、投稿、編集ができること

できる限りページ間の遷移を少なくするために、**Mapコンポーネント**の必要な機能を、それぞれ**子コンポーネント**として作成することにこだわって、その結果、目的の場所を調べて、レビューを投稿するだけで、レビューに**位置情報**を自動的に設定でき、また地図上に投稿結果がリアルタイムで反映できるようになりました。
**レビューのデータ**は**Mapコンポーネント**を**render**する時に**バックエンド**から取得するように設定し、取得した**データ**を、`use Context`を用いて**子コンポーネント**内で簡単に共有できる構造にしています。
**編集リンク**はレビューの投稿者がレビューを開いた場合のみ、表示されるように設定しています。

>入力フォームで直接server側のvalidationを反映させる

実は、自分がSPAとして作成時に困ったのが、「ユーザー登録時のバックエンドで設定した**validation**をどのようにフロントで反映させよう」という点でした。
それで今回は、**errors state**を設定した**カスタムフック**を作成して、直接エラー内容をそのまま表示させる仕組みに落ち着きました。
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754958/e20b8d56-7e82-fef3-2c22-6b2ec2d0e1bc.gif)

###最も苦労したこと
自分がこのポートフォリオを作成する際に最も苦労したことは、当たり前なのですが、**自分が実現したい機能の実装方法を全て自分で調べ、候補を一つずつ試していきながら実装したこと**です。

- 具体例
    - **実現したい機能**
        - mapページ内の動作は全てページ間の遷移無しで実装したい
        - map内にレビュー情報を保持したピンを立てたい
    - **実現のために取り組んだこと**
        - **同期処理と非同期処理**の違いもわかっていない状態だったので、非同期処理に関連する記事をひたすら読み、なおかつ具体例として挙げられているコードを実践
        - mapを表示するために使用したライブラリー**「google-map-react」**のexamplesを読み、自分の実現したい機能を担っているコードを予想して再現する
 
###自分が参考にした記事、サイトなど
#####環境開発編
[Reactの環境開発をDockerで構築してみた](https://blog.web.nifty.com/engineer/2714)
[Rails6 + PostgresのDocker開発環境を構築](https://qiita.com/d0ne1s/items/f724a08119bad2973e46)
(上記2つの構築を結びつけて、現在の開発環境を作成しました。)
#####フロントエンド編
[Next tutorial](https://nextjs.org/learn/basics/create-nextjs-app) 
(next.jsのtutorialです)

[react-railsを使って、簡単なデータ表示＋検索のサンプルを作ってみる
](https://qiita.com/laser_beam/items/97483176c712e408907f)(この記事はRails APIから取得したデータを表示する時に参考にしました。)

[google-map-react](https://github.com/google-map-react/google-map-react)
(この中の**Examples**を読みながら、mapコンポーネントの作成を行っていました。)

#####バックエンド編
[Ruby on Rails チュートリアル ver6.0](https://railstutorial.jp/)
(Railsのバックエンドの基礎を学ぶには、この一冊が最適だと思います。)

[Rails gem 'geocoder'を使って入力した住所をGoogleMapに表示させる](https://qiita.com/ashketcham/items/9baf8bc407cea45ba833)
(geocoding APIを使用するためにこの記事を参考にしました。)



###最後に
**next.js**を使った**SPA**を作りたくて今回のサイトを**SPA化**してみましたが、ただ今回の経験を通して感じたことは**「next.js**使いやす！」ですね。**next.js**の実行環境が少々複雑で、**AWS,GCP**などのクラウドで再現しようとするとまた少し難しさはありますが、いつか挑戦してみようと思います。
またデータベース構造があまりにもシンプルで面白みに欠けるので、もう少し複雑な構造も挑戦してみたいと思います。
####これから実装したい機能など
* oauth2認証を導入する(rails APIでのoauth2認証のやり方を勉強)
* ~~**typescript**でリファクタリング~~
 **typescript**でのリファクタリングが大方完了しました。
**Next.js**は**ts.config**を追加するだけで、**typescript**が導入できるので、楽でいいですね！
プロフィールや、ユーザーページのリファクタリングが一部残ってはいますが、typescriptの導入が出来てよかったです。
ただ型強度を`strict: true`で作成することが出来なかったので、まだまだ**typescript**の沼は深いです。

* レビュー自体を検索できるシステム
* 気になるレビュー投稿者に質問を送れる
* 参考になったレビューに**いいね**を押せる

などなどです。
大規模なサービスでは、**typescript**で作成することが当たり前だと思うので、今から練習は必要ですね。
初めて自分が１から作成したアプリなので、適当に遊びながら勉強していきます。
ここまで長い記事を読んで頂き、ありがとうございました。

### 追記（判明したバグについて)
##### 位置情報を持たないレビュー投稿が作成されていた
投稿情報を持つ**Reviewモデル**と位置情報を持つ**Spotモデル**を一対一結びつけて、レビューを送信する際に自動的に位置情報も送信する設定にしていたのですが、**位置情報を持たないレビュー**が作成されていることがわかりました。
その結果、レビューを表示する際にあるはずの位置情報を取得できず、エラーが発生していることが見つかったので、正しい位置情報を持っているかを検査し、正しくない場合は**Bad reques**tとしてレビューを作成しないように書き換えました。

##### 原因
このエラーの原因は、サーバーサイドできっちり**validation**を設定していないことが原因でした。
**validation**を設定する必要性が実感できた気がします。 （笑）