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


はじめに

バックエンドにRuby on Rails,フロントエンドにNext.jsを用いてSPA型のポートフォリオを作成しました。
このポートフォリオは、自分がRailsAWS EC2を用いて開発した一つ前のポートフォリオをSPA化したものになります。
あくまでもSPA化を目的に作成したので、デプロイ先はvercelherokuをそれぞれの環境で使用しています。

アプリの概要(Git Hub repoのREAD MEと同じ)

各レポジトリー

Frondend repository
Backend repository

アプリ名: 住み心地.com

世界中の住み心地に対するレビューの投稿(user登録必要)、閲覧ができます。

使用技術

  • 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図

作成理由

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

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

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

開発環境について

開発環境は気軽に環境を変更できて、共有しやすいという点から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で一括管理ができる用にしています。

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も含む)
  • SSGSSRを両立できる

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

では次にこの2つの理由についてそれぞれ説明していきます。

router処理を完全にサポートしている(Dynamic Routingも含む)

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

SSGSSRを両立できる

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を設定したカスタムフックを作成して、直接エラー内容をそのまま表示させる仕組みに落ち着きました。

最も苦労したこと

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

  • 具体例
    • 実現したい機能
      • mapページ内の動作は全てページ間の遷移無しで実装したい
      • map内にレビュー情報を保持したピンを立てたい
    • 実現のために取り組んだこと
      • 同期処理と非同期処理の違いもわかっていない状態だったので、非同期処理に関連する記事をひたすら読み、なおかつ具体例として挙げられているコードを実践
      • mapを表示するために使用したライブラリー「google-map-react」のexamplesを読み、自分の実現したい機能を担っているコードを予想して再現する

自分が参考にした記事、サイトなど

環境開発編

Reactの環境開発をDockerで構築してみた
Rails6 + PostgresのDocker開発環境を構築
(上記2つの構築を結びつけて、現在の開発環境を作成しました。)

フロントエンド編

Next tutorial
(next.jsのtutorialです)

react-railsを使って、簡単なデータ表示+検索のサンプルを作ってみる
(この記事はRails APIから取得したデータを表示する時に参考にしました。)

google-map-react
(この中のExamplesを読みながら、mapコンポーネントの作成を行っていました。)

バックエンド編

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

Rails gem 'geocoder'を使って入力した住所をGoogleMapに表示させる
(geocoding APIを使用するためにこの記事を参考にしました。)

最後に

next.jsを使ったSPAを作りたくて今回のサイトをSPA化してみましたが、ただ今回の経験を通して感じたことは「next.js使いやす!」ですね。next.jsの実行環境が少々複雑で、AWS,GCPなどのクラウドで再現しようとするとまた少し難しさはありますが、いつか挑戦してみようと思います。
またデータベース構造があまりにもシンプルで面白みに欠けるので、もう少し複雑な構造も挑戦してみたいと思います。

これから実装したい機能など

  • oauth2認証を導入する(rails APIでのoauth2認証のやり方を勉強)
  • typescriptでリファクタリング
    typescriptでのリファクタリングが大方完了しました。
    Next.jsts.configを追加するだけで、typescriptが導入できるので、楽でいいですね!
    プロフィールや、ユーザーページのリファクタリングが一部残ってはいますが、typescriptの導入が出来てよかったです。
    ただ型強度をstrict: trueで作成することが出来なかったので、まだまだtypescriptの沼は深いです。

  • レビュー自体を検索できるシステム

  • 気になるレビュー投稿者に質問を送れる

  • 参考になったレビューにいいねを押せる

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

追記(判明したバグについて)

位置情報を持たないレビュー投稿が作成されていた

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

原因

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