Railsで動く既存サービスを全面React化した理由と方法
Rep立教大学シラバス検索システムというサービスを3年間運営してきました。開発者はほぼ僕一人で、卒業した今も細々と続けています。
Repはその名の通り立教大学のシラバスを検索できるサードパーティなサービスで、学生が授業のレビューを共有したり、学生だけが参加できる掲示板などがあるサービスです。
多くの立教生に利用され、今では立教生にとってなくてはならないサービスと小声で言えるくらいにはなりました。
今月上旬(2020年9月)様々な思惑でようやくRepにReactを導入したので、それについてまとめます。
どうやったか
どういう規模で導入するか
Repでは一気にすべてをReact化しました。
理由は動くものを目指して開発を始めたら開発のテンションが上がっちゃったからです。
すべてをAPI化して、すべてのフロントを書き直す時間があったので出来たことだとは思います。
既存のサービスのフロントエンドを大きく変更する場合、部分的に導入するという選択肢があると思います。
そしてちょっとググると、Railsに部分的にReactを導入する方法はかなり出てきます。
- Railsに元々あるSprocketsに乗っかる
- webpackerを入れる
-
react-rails
やreact_on_rails
gemの導入
ちょっと粒度は異なりますが、大体以上のような感じで どこでフロントのコードを書くのか(管理するのか) 、様々な選択肢がありました。
最終的には上述の通り、とりあえず動くものをと create-react-app
使って小さな規模で書いていたら調子に乗ってきて、部分導入なぞは考えずに一気にReact化する方針にしました。
時間に余裕があり、最終的にはフルReact化をしたいと思っていたので後悔はしていません。また今後Reactの規模を広げる際にRailsとの兼ね合いを考慮する必要がなくなったのも頑張った甲斐があります。
どういう技術を使ったか
フロント側
- TypeScript
- React(16.13.0)
- react-router
- axios
- material-ui
- Firebase hosting(ほぼ無料枠で足りそう)
- Firebase firestore(リアルタイムチャットを新たに追加しました)
Flux的なものは導入しませんでした。
当初は導入を前提に考えてコードを試し書きしたこともありましたが、hooksやcontextの利用で十分なstate管理ができると考えて、Repの規模では工数増加のデメリットのほうが大きいと考えて必要ないと判断しました。
おそらく今後も小さく作る場合は導入しないです。それくらい hooks, contextがとても使いやすいです。
サーバーサイド
- devise-token-auth(deviseに乗っかってtoken認証を可能にする)
新たに導入したのは devise-token-auth
だけですが、同時に一番つらかったのが認証の仕組みです。
フロントではCookieにtokenなどを保存し、request headerに含める方式にしました。
基本のメールアドレス認証は devise-token-auth を利用すれば容易なのですが、LINEログインをすでに導入していて、そのOAuthの実装に頭を悩ませました(また記事にします)。
なぜやったか
これまでのRepのフロント環境は、Railsだけ、つまりActionView(slim)とjQueryをゴリゴリ書いていました。
これに対していくつかの不満/願望が出てきました。
1.[不満] インタラクティブな事ができない/しづらい
当初はただのシラバス検索とレビューの読み書きだけだったRepもやりたいことが増えてきました。
- 柔軟な履修組み機能
- 学生間のチャット
いずれもすでにある機能でした。
ただjQueryでの表現に限界が来ていて、例えば時間割の空きコマをクリックしたらそのコマの授業が一覧され、さらにユーザー操作で表示内容(授業内容やレビューなど)を切り替える..。
みたいなことをしようとすると実装がかなり辛い感じになります。
こういったメディアよりもツールとしての側面を強めていき、ユーザーが直感的にサービスを使えるようにするのが目的にありました。
2.[不満] jQueryによるDOM制御辛い
これは様々な方が言っているし、もはや常識になってきている(?)ので割愛します。
またjQueryの問題ではないですが、Railsの便利な使い方で、 create.js.erb
といったjsをresponseで渡して実行するというのがありますが、これを多用しすぎていました。
お気に入りボタンの create.js.erb がこれです。
$(".Favorite__button--<%= @lesson.id %>").html('<%= j(render "favorite_button_for_destroy", favo: @favorite, lesson: @lesson) %>')
一行だけで表示が変わるのは魅力的ですが、可読性は低いですしロジックと見た目が密に結合していて再利用がしづらいです。
例えばお気に入りボタンのスタイルを変えたい画面が増えたときに、ゴニョゴニョしなくてはいけません(できなくはない)。
3.[願望] React使ってSPAなサービスを0から作りたい
仕事ではReactを使い、個人的にはjQuery。
どちらも素敵なライブラリですが、個人的にはReactのほうが人気があり、webエンジニアとして1つもサービスを作ったことないのはヤバいんじゃないかっていう危機感がありました。
実際0から作ると辛いところや、慎重に考えるべきところなどの勘所を得ることができました。
結果
不満・願望の大半は解消・叶いました。
componentに適切にProps渡しさえすれば、component単位でちゃんと動いてくれるの感動です。
何よりナウくなって、開発意欲が増します。
技術力不足で2年以上かかってようやくReactを導入できたので感無量です😇
今後記事にしたいこと
長くなりそうなので、ここで終わりにします。
今回はふんわりした感じになってしまいました。
もはや何番煎じか分かりませんが、モチベがあったらこの辺りを書こうかなって思っています。
書いた記事のリンク貼っていきます。
- React から axios で Rails API を叩く実装
- useContextを用いたメッセージUIの表示
- 認証状態とreact-router
- create-react-appで作ったプロジェクトをFirebaseにhostingするためにしたこと
- SPA + RailsでOAuth(LINEログイン)をどう実装したか
- GoogleAnalyticsからFirebaseAnalyticsへの移行(同時にSPA化)
Discussion
授業名/教授名フォームにおいて、『神』について検索すると、通常の検索結果無しとは異なる挙動をするのは仕様ですか?(Chrome ver.85.0.4183.121)