大急ぎでRails+Reactのアプリケーションを作るときにやったこと後編


ある日突然、大慌てでWebアプリを作らなくてはいけなくなった

このエントリは、第二のドワンゴ Advent Calendar 2020の23日目です。

このエントリは、大急ぎでRails+Reactのアプリケーションを作るときにやったこと前編の続きです

このエントリは、自分は仕事でRails+ReactのAPI+SPAプロジェクトをいくつか経験してきたが、0からその環境を作ったことがないということに気づいてしまった私の記録です。多くの躓きを経て、非常に非常にかんたんな機能しか持たないアプリをつくるのにのべ20時間ほどの時間を要しまし、自分はReact+Railsエンジニアになったつもりでいたという反省文、その話の後編です。これから書くことは、0からRails+React環境を用意したことのない人に向けて書く、まさに書くは一時の恥、書かぬは一生の恥のエントリです。

前回までにやったこと

前編では、サンプルアプリ「ねこねこかわいい」を作るために以下の工程を説明しました。

  • rails new
  • DBの用意
  • Webpackerの設定
  • RailsとReactのインテグレーション
  • UI FrameworkとしてSmartHR UIの導入

後編のこのエントリでは

  • axiosのクライアントを用いてReactからRailsに通信
  • Google OAuthでユーザー登録
  • S3に画像の投稿
  • Herokuへのデプロイ

までを解説します。

axiosクライアントの作成

ReactからRailsのAPIに通信するためには、axiosを使う。なんでaxioかというと、手でFetchAPIを書くよりはだいぶ書き心地が良いことと、将来的にOpenAPIなどでスキーマ定義を作成して通信クライアントを自動生成するときにaxiosを使ったクライアントを選択できるからと考えたから。自動生成されたaxiosのクライアントに切り替えるときに、大きなショックが無いようにということですね。それ以外は特に積極的な理由はないです。

axiosをインストール。ついでに、Cookieもいい感じに処理してほしいので、axios-cookiejar-supportとtouch-cookieも入れる。ただ、入れてから気づいてまだ検証してないけど、Cookie系はもしかしたら必要ない可能性もある(とはいえ、最終的にSPAに持っていくときにはいると思うので入れておいて悪いことはないんじゃないかな、くらいに思っている)。

[yarn add axios @types/axios · kinoppyd/nekonekokawaii@8a9e5fd]

[yarn add axios-cookiejar-support tough-cookie @types/tough-cookie · kinoppyd/nekonekokawaii@6c0a6cd]

入れたら、どこのコンポーネントからでも使えるClientクラスをつくておく。また、Railsとの通信ではCSRFで保護してほしいので、CSRFトークンも送れるようにしておく。ここでのCSRFトークンは、Rails側のViewレンダリングでheaderに埋め込まれたものを取得して使う。つまり、いまはRailsのViewにReactコンポーネントを埋め込んでいるので対応可能だが、完全SPAなどにシフトしていくためには、また違う方法をとる必要がでてくるということになる。が、とりあえず今はこれで十分。

これを実際に利用するときには、各API用のクラスを作成して分割しておくと、後々ごちゃごちゃしなくて良いと思う。ひとまず簡単に、/postsにPOSTするだけのクライアントを作る。

[Add api client · kinoppyd/nekonekokawaii@56b9d11]

もちろん、Rails側にこのリクエストに対応するエンドポイントもはやしておく必要がある。

[Add Create post action · kinoppyd/nekonekokawaii@cb536da]

最後に、フロント側に非常に雑ではあるがAPI Clientを使ってPOSTを実行するコードを追加して、動作を確認する。非常に雑な感じではあるが、フォームの内容はStateで覚えておき、ボタンのクリックハンドラでAPIを呼び出すときに投げる。成功したらページリロード。

[Add new post form for index · kinoppyd/nekonekokawaii@eca716b]

ここまでで、axiosを使ってRails側のAPIを呼び出して、Reactから新しいPOSTを作れるまでの一連の流れが完成した。

GoogleのOAuthでユーザー登録

ここはあまり真面目に読む必要はない。Google API ConsoleからOAuth2の認証情報を追加して、OmniAuthを使ってGoogleアカウントからEmailなどの情報の認可を受け、それをもとにユーザー登録などを行うだけだ。正直、ググったほうがここより詳しい解説が出てくると思う。

ここでは、単にエッセンシャルなユーザーログインだけを書いていく。

まずは、email, display_name, avator の3つのフィールだけを持ったUserモデルを作成する。

[rails g model user email:string display_name:string avatar:string · kinoppyd/nekonekokawaii@8ba68dc]

Userモデルは、OAuthのコールバックのハッシュをもとにユーザーを作成するつもりで、次のようなユーザー作成ヘルパを書いておく。

[Create User from oauth hash · kinoppyd/nekonekokawaii@891b186]

次に、omniauthとomniauth-google-oauth2を導入する

[bundle add omniauth omniauth-google-oauth2 · kinoppyd/nekonekokawaii@22afc7c]

Omniauthの設定は、config/initializers/omniauth.rb 内に記述する。

[Add omniauth initializer · kinoppyd/nekonekokawaii@70880d6]

そして、OAuthからのコールバックを受けてUserの作成を行う、セッションコントローラの作成と、ルーティングの追加を行う。

[Add sessions controller and routes · kinoppyd/nekonekokawaii@a1ef697]

最後に、React側にログインボタンを追加して、 /auth/google_oauth2/ にリンクを貼れば、ユーザーの作成とログインまでの流れが完成する。

簡単に言ったが、これはController側でユーザーのログインをチェックしたり、状態によってUIを出し分けたりする処理が入るので、そう簡単にはできないし分量が多い。下記のコミットを参照してほしい。

[User login interface · kinoppyd/nekonekokawaii@410e365]

何をやったかを簡単にまとめておくと、OAuthの認可情報を取れていればUserモデルをセッションという名前でReact側に渡して、それによって処理を切り分けるようにしている。Sessionがなければログインボタンを出し、あればユーザーアイコンとPostようのフォームを表示している。とにかくしちめんどうなことが書かれているので、コミットの方を参考にしてほしい。

S3に画像の投稿

ねこが可愛いことを伝えるために、画像も投稿したい。当然そう思うので画像も投稿できるようにするには、ActiveStorageを使う。

まず、S3のバケット作成とIAM設定を行う。これは手順が本質的じゃない話なので、いくつか参考になるブログを見てほしい。

[ActiveStorageでファイルの保存先にAWS S3を利用するための準備 – Qiita]

この準備ができたら、まずActiveStorageを使えるようにする。今回はあえてrails newするときに省いたので、次のコマンドで使えるようにしていく。

[rails active_storage:install · kinoppyd/nekonekokawaii@46ae493]

[bundle add aws-sdk-s3 · kinoppyd/nekonekokawaii@9ac7424]

これにによって、ActiveStorageの使うDBのテーブルが作成され、S3と通信するようのGemも入る。

次に、config/storage.ymlを編集してS3を使うようにする。

面倒なので本番環境でも開発環境でもS3を触るようにする。本来ならばバケットを分けるべきだが、これはチュートリアルなので気にしない。

さらに、PostモデルにもActiveStorageのBlobを扱えるようにリレーションを書いておく。

[Add ActiveStorage configures and relations · kinoppyd/nekonekokawaii@8554224]

これで、PostにActiveStorageで保存した画像を紐付ける準備ができた。

最後に、Reactから画像を受け取りそれを保存するControllerと、更にReact側からどうやって画像を送るのかのコードを追加していく。ActiveStorageは、Railsのエコシステムとがっちり組み合わさって動くため、本来であればViewHelperを使ってファイルをアップロードする専用のフォームを作るのだが、フロントは全部Reactで書きたい。であれば、どのようにしてReactから送られてくるリクエストをもとに、ActiveStorageでBlobを作成すればよいのか? 最も手っ取り早い方法は、input type=”file” のフォームを用意し、Base64をJSONに入れて送り、それをController内でデコードしてStringIOに詰め直すことだ。

このように、リクエストパラメータにpictureが詰まっていれば、その中のdataというBase64エンコードされた文字列を、Base64.decode64でもとに戻してStringIOに詰める。それだけだ。フロントから送られてくるときは、Base64の文字列の先頭にファイル属性などの文字列が付いているので、カンマでスプリットしてデータ部分のみを取り出す(これは別にフロントでやっても良い処理だが、Rubyのほうが楽だった)。

フロント側は、このように変更を加えている。

FileReaderを使って、inputから渡されたファイルをBase64化した上でStateに保持し、リクエスト時にクライアントに渡している。クライアントはコードを次のように変えた。

また、Postモデルが画像を扱えるようになったことで、Rails側のViewではこうやって画像情報を渡している。

React側では、受け取ったURLを表示する。

全体的にやや雑な実装だが、これはプロトタイプなので気にしない。本気になったときにブラッシュアップしてほしい。

ここまでの流れで、Reactから渡した画像はRails経由でS3にアップロードされ、それを参照することも可能になった。

[Implements API and Front · kinoppyd/nekonekokawaii@2ae89d0]

Herokuにデプロイ

ここまでくれば、あとはアプリケーションをデプロイするだけだ。といっても、Herokuを使えば何も問題なくすべてが終わる。ちょっともう時間がないので、こればっかりは自分で調べてほしい。GCPのキーとAWSのキーを設定するのを忘れないように。

完成したアプリ

https://nekonekokawaii.herokuapp.com/posts

おわりに

React+Railsのアプリケーションを0から作って形にする一連の流れを解説した。なるべく、いくらでも拡張が効くようにしっかりとしたベースを作るつもりでコードを書けたと思う。これさえわかれば、あとはいつも仕事でやっているように、自分の好きなようにアプリを拡張できるようになると思う。

これでようやく、RailsとReactは書けるけど自分では何も生み出せないという疑念から自分を開放できた気がする。気持ちが凄く楽になった。


この記事は未分類に投稿されました. このパーマリンクをブックマークする。 コメントを投稿するか、トラックバックをどうぞ: トラックバック URL.

コメント: 1

  1. 通りすがり
    公開日時: 2021年8月12日 - 13:43 | パーマリンク

    分かりやすかったです!

コメントする

あなたのメールは 絶対に 公開されたり共有されたりしません。 * が付いている欄は必須項目です

次の HTML タグと属性が使用できます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

*
*