Railsガイド -> 現場Rails -> パRails を一通り眺めて考えた次の学習方針

来月からRails(& Vue)をガッツリ触ることになりそうなので、錆びついたRailsのスキルを磨き直すために、Railsガイド -> 現場Rails -> パRails を一通り眺めて今後の学習方針を検討した。 まぁよく考えると実務でガッツリRails使ってたのって半年くらいなので、錆びついたというか元から大したスキルが無いだけじゃね?と思わなくも無いが、 やることは変わらないので良しとする。

ひとまずそれぞれを眺めた(読んだとは言えないレベル)所感を記載の上、今後の学習方針を考えていく。

ざっと眺めた

Railsガイド

  • 網羅的に書かれているが、それぞれの重要度はなんともわからん
  • しかしモデル関連(model, マイグレーション, バリデーション, コールバック, 関連, クエリ)あたりはめちゃめちゃ重要だろう。一通り手を動かしてみていい気がする。
  • あとはコントローラー&ルーティング周りも同様だろう。こちらも手を動かして良い
  • その他は。。。
    • Rails全体の環境構築関連は重要そうだが、他(現場、パRails)ではそれほど出てこなかったので保留で良いかもしれない
    • セキュリティは重要だが、網羅的に書かれすぎている。他で良いかも
    • zeitwerkは悩みどころ。パRailsにもろくな記載はなかったのでひとまず保留でいいかもな?
  • 普段あまり触れないが重要そうな話題も多数あるが、詳細に書かれすぎていて基礎知識の無い段階で手を出すのは危険かも
  • 結論、モデル・コントローラー・ルーティング関連のみガッツリ触れる

現場Rails

  • 5章くらいまでは、「まぁ知ってるな」というレベルの話
  • 6章くらいから、トピック単位であまり馴染みの無い内容が増えてくる。
  • Action Jobとかはこの本の内容から手をつけるのも悪くないかもしれない。
  • 9章・10章はノウハウっぽい箇所で個人的には弱いので、真面目に読むのも良さそう。
  • 結論、ガッツリやる必要はないがトピック毎に読みこなすのが良いかもしれない。

Rails

  • 前半3章くらいまでは個々で気になるトピックがあった。拾い読みしたい。
  • 6章は全体的に手を動かしてやっておきたい
  • 8.3とかもトピックとして良い
  • 10はやりたいが、、、うーんといったところ。
  • 11章以降は宝の山。ガッツリ読みたい。が、最後で良いのかもしれない。
  • 結論: 全体的に有用な情報が多い。特に11章以降は必見。また、6章で手を動かしながら、勘を取り戻す感じが良いかも?

今後の学習方針

  • ずっと文字を眺めていて疲れたのでまずは手を動かしていきたい。
  • ということでパRailsの第6章を進める。模写で。
  • その後作成したアプリベースで、Railsガイドのモデル・コントローラー・ルーティング関連をやっていく
  • 合間合間で飽きたら上で興味深いと感じた章を読んでみる。

React Testing Library を色々試したので備忘録

色々試したので基本方針、実装上の注意点、その他ハマりどころ等を記録に残す。

基本方針

構成

  • 大まかな構成はRSpecと似た形でいけそう。
  • テストケースの構成は基本的にはRSpecのdescribe/context/itの考え方でいける。
    • ただしJestではdescribe/testしかないので、describeを入れ子にしてcontextの代用にする(わざわざcontext相当の層を作る必要はないかもしれない)
    • ここは今後の要調査ポイントの1つ
  • 各テストケース内の構成も、"データの用意" -> "処理の実行" -> "結果の検証"のRSpecでよく見る流れでいける。
    • 共通処理をメソッド化するべきか?などは今後の要調査ポイント。
    • 基本的にはベタがき & コメントで補足 の形で良いのでは?という気がしている。共通化は最小限で。

Testing Libraryについて

  • 公式サイト(https://testing-library.com/docs/intro)にある以下の記載の通り、ソフトウェアを実際に利用するようにテストことを常に認識しておく。つまりどんな機能をテストする場合も、「表示されるテキストやアイコン」で操作対象を特定し、「フォームへの入力」や「ボタン押下」を行う。

    We try to only expose methods and utilities that encourage you to write tests that closely resemble how your web pages are used.

  • ただしあくまでインテグレーションテストであるため、外部のモジュール等と連携している箇所は、モックやスタブを用いる。例えばページ遷移が発生する操作をした場合は、「ページ遷移すること」の確認ではなく、「ページ遷移を実現する(おそらく外部の)モジュールにどんなパラメータを渡したか?」をモック等を利用しつつテストすることになる

  • 公式ドキュメントが充実しているので、基本は公式ドキュメントを見れば良い。ただ次の翻訳記事も良い。 React Testing Libraryの使い方 - Qiita

実装時の注意点

"データの用意" -> "処理の実行" -> "結果の検証"のそれぞれに関して、実装時に注意する内容を記載する。

データの用意

処理の実行

  • React Testing Libraryの userEventを利用して、フォームの入力/submitやボタンのクリックを行う。
  • 操作対象となる要素の選択は React Testing Libraryのscreen.getByText() 等のクエリで取得する。
  • userEventを利用した操作はあくまで イベントの発火処理であって、htmlのDOM要素が何であれ関係はない。
    • セレクトボックスがoptionでなくWAI-ARIAの謎機能で実現されていようが、対象の要素を選択してuserEventをかませばOK。
  • フォーム送信時のパラメータを検証する等の必要がある場合は、この時点でMock Objectを、JestのMock関数を用いて作成しておく。詳細は後述。
  • あくまでインテグレーションテストなので、処理結果に外部のライブラリが挟まる場合は、そこに渡すパラメータの検証までしかできない。
  • クエリは以下参照
  • userEventは以下参照

結果の検証

  • クエリを用いるだけでも検証になる。
  • 必要に応じてexpectとカスタムマッチャを組み合わせる。
  • モックを利用して間接出力の検証を行う場合は、mockプロパティを利用する。
  • userEventの後に非同期処理が走る場合は、waitForを用いる必要もあるので注意。

ハマりどころ, 注意点

Mock Object/Test Stubの作成

一番ハマるのがここ。コンポーネントに合わせて適切なMock関数を用意する必要がある。大抵の場合、外部モジュールのモック化が必要になるので、ここではその点に絞って注意点を記載する。恐らくもっと良いやり方はあるが、ひとまず暫定出来るやり方という位置付け。

  • 基本的に以下の形でモジュールのmock化と処理の実装ができる。引数のチェックや戻り値が必要ない場合は、jest.mock('axios')だけでも可。jest.mock('XXX')でモック化したmodule内のメソッド等は、全てjest.fn()で置き換えられる。
jest.mock('axios')
axios.get.mockImplementationOnce(() =>
      Promise.resolve({ data: stocks })
)
jest.mock('axios')
const mockedAxios = axios as jest.Mocked<typeof axios>
mockedAxios.get.mockImplementationOnce(() =>
      Promise.resolve({ data: stocks })
)
  • モジュールの構成によっては
import * as reactRouterDom from 'react-router-dom'

のようにする必要もあるかもしれない。

  • 間接出力を検証するためのモック(= Mock Object)の場合は、mockプロパティを用いるために、モックの実装時にjest.fnを利用する必要がある。ただし特に戻り値等の実装をする必要がない場合は、mockImplementationOnce等を利用せずjest.mock('axios')だけでmockプロパティの利用は可能。
jest.mock('axios')
const mockedAxios = axios as jest.Mocked<typeof axios>
const myGetMock = jest.fn(() =>
      Promise.resolve({ data: stocks }))
mockedAxios.get.mockImplementationOnce(myGetMock)
  • デフォルトの実装を加えたいときは、jest.mockの第2引数を以下の形で用いる。これはあくまでデフォルトなので、別途mockImplementationOnce等で書き換え可能。
jest.mock('react-redux', () => {
  return {
    useDispatch: () => () => {
      // do nothing
    },
    useSelector: jest.fn(() => ({
      errors: [],
      stockDetail: {},
    })),
  }
})
  • mockImplementationOnce等に渡すmockの実装は、引数・戻り値の型をモック対象のメソッドに合わせないと型エラーが起きる点に注意

TypeError: window.matchMedia is not a function

というエラーが出ることがある。これはJestの問題のようだが、以下の記事を参考にすると解決できる。 reactjs - Jest test fails : TypeError: window.matchMedia is not a function - Stack Overflow

MockとかSpyとかStubとかの話と、Jest のMock機能

モックとスタブの違いがわからん!かったが今回ある程度理解したので書き記す。 また Jestの公式ドキュメントのMock機能のページを一通り触りつつ、それぞれがモック・スタブのどちらに当たるか考えていく。

モックとスタブの違い

XUnitPatterns

前提として、モックやスタブの語源、というかネタ元に関して理解する必要がある。 きっと諸説ある類の話なんだろうが、私としては、XUnitPatterns(http://xunitpatterns.com)が元だと考えている。 それを踏まえるとモックとスタブは正確に表現するなら、

  • Mock Object
  • Test Stub

ということになる。まずこの内容を理解するのが良さそうだ。

XUnitPatternsのサイトから理解するのは結構きつい。(英語だし量が多いし。。。) 以下のブログがすごくいい感じにまとめてくれているので参考になる。
xUnit Test PatternsのTest Doubleパターン(Mock、Stub、Fake、Dummy等の定義) - 千里霧中

以降このブログを参考に要点を書き出す。

間接入力と間接出力

理解のためには以下2つの用語がキーになりそうだ。

  • 間接入力
    • 間接入力はテストコードから見えないテスト対象への入力(fetchでhttp GETした場合とか)
  • 間接出力
    • テストコードから見えないテスト対象の出力(fetchでhttp POSTした場合とか)

詳細は上記記事参照のこと

Mock Objectと Test Stub

その上でそれぞれを大体以下の形で理解した。 話の都合でTest Stubから説明する。 どちらもハリボテとして動くモジュールという意味では似ているが、果たす役割は全く逆と言ってもいいかもしれない。

Test Stub

  • Test Stub は SUT(System Under Test = テスト対象)への間接入力を行うためのモジュール。
  • 任意の値をSUTに入力することで、テストの目的に応じてSUTの振る舞いを制御する。
  • どちらかというとSUTの一部として機能するモジュール。

Mock Object

  • Mock Objectは、間接出力を検証するためのモジュール。
  • SUT(System Under Test = テスト対象) からの間接出力の値が妥当かどうかをチェックして、結果をテストコードに返す。
  • Test Stubの機能を包含することもある。
  • どちらかというとテストコードの一部として機能するモジュール。

(おまけ) Test Spy

  • Mockオブジェクトから検証機能を抜いたやつ(雑な説明)
  • 間接出力を受け取りテストコードに投げる。検証はテストコードで行う

注意点

この分類はあくまでXUnitPatterns における分類。世間一般ではモックやスタブは結構色々な意味で使われている可能性がある。 Mock ObjectとTest Stubをまとめてモックと呼ぶ場合もありそう。 そのため人と話す際には認識をきっちり合わせた方が良い。

JestのMock機能の話

上を踏まえた上でJestのMock機能を触ってみた。 Mock Functions · Jest

とりあえず章ごとに軽く所感を残す。 注意点はMock Object(相当) も Test Stub(相当) もどちらもモック機能 で通していること。

「モック関数を利用する」

ここは正しくMockObjectとしての使い方っぽい。forEachのコールバックの部分をMockにして、forEachから コールバックに渡すパラメータを中心に検証している。 うん、間接出力を検証している。まさにMock Objectだな!

「モックの戻り値」

本文に以下のような記載がある通り、これは間接入力を実現するための使い方であり、Test Stub っぽい。

モック関数は、テスト中のコードにテスト用の値を注入するのにも利用できます。

正直こっちの使い方の方が出番が多い気がしてならないので、Test Stubとして使うにもかかわらず mockXXXといった名前の関数を使うことになるので、正直混乱をきたしそうだ。まぁJest的にはモックということなんだろうから受け入れる。

「モジュールのモック」

この機能もかなり出番が多そうだ。また、Mock Object, Test Stubという観点だと、モジュール次第でどちらもあり得る。

例えばドキュメントの例にもある axios であれば、get の場合は値をSUTに渡すだけなので完全に Test Stubだが、postの場合は まず値を渡すのでその検証を行えばMock Objectだが、postのレスポンスをSUTに渡せばTest Stubを包含していることにもなり、まさしく定義通りのMockObjectだ。

最後に

Jestのドキュメントと照らし合わせて、なんとなーくモックとかスタブとかを理解した気持ちになった!☝ ՞ਊ ՞)☝ウェーイ(古)

掴めばなかなか使いやすそうな github actions

githubでCIと言えば少し前まではcircle CIだったが、どうやら今はgithub actionsが熱いらしい。使ってみたところ最初こそ雰囲気を掴めず少し苦労したが、ある程度ルールが分かったらかなり使いやすいと感じたので、今後は積極的に使っていこうと思っている。が、まぁその感覚に到るまではそこそこ苦労した部分もあったので、その辺を忘備録として残す。

ワークフローの基本構成と、service, containerあたりを抑えておけば、ひとまずだいたい読める。

ただある程度高度になると、github actionsそのもの以外の要因でハマりそうな予感はある。 特にdocker周りは知識の強化が必要かも。。。  

基本的な内容

公式ドキュメント

日本語でかつ流石の充実具合なので困ったら参照するのが良い docs.github.com

最初にやること

githubのActionsからworkflow ファイルのテンプレート選ぶことができるのでまずはそこから始めると良い。rubyのものやnodeのものなどをよく使うか?

ワークフローとかジョブとかステップとかアクションとか

ワークフローファイルの基本構成は以下。それぞれの名前とファイル内の対応する箇所は抑えておけばだいたい読める。

ワークフローを設定する - GitHub Docs

ワークフローには、少なくとも1つのジョブが必要であり、ジョブには個々のタスクを実行する一連のステップが含まれます。 ステップでは、コマンドを実行するか、アクションを使用することができます。 独自のアクションを作成することも、GitHubコミュニティで共有されているアクションを使用し、必要に応じてカスタマイズすることもできます。

 ## サービスとコンテナ

- Containerの話 container を設定すると、stepsがその環境で実行されるらしい。serviceと組み合わせて使うと、ポート等ネットワーク設定を気にしなくてよくなるので、基本これで良い気がする。

サービスコンテナについて - GitHub Docs

コンテナ内でジョブとサービスを実行すれば、ネットワークアクセスはシンプルになります。 サービスコンテナへは、ワークフロー中で設定したラベルを使ってアクセスできます。 サービスコンテナのホスト名は、自動的にラベル名にマップされます。 たとえばredisというラベルでサービスコンテナを作成したなら、そのサービスコンテナのホスト名はredisになります。

サービスコンテナでポートを設定する必要はありません。 デフォルトで、すべてのコンテナは同じDockerネットワークの一部となってお互いにすべてのポートを公開し合い、Dockerネットワークの外部へはポートは公開されません。

ハマった箇所

作業ディレクトリの変更

トップディレクトリをapi/clientの形にしているので、この処理が必要だった。

GitHub Actions で作業ディレクトリを変更したい https://blog.takuchalle.dev/post/2020/02/20/github_actions_change_directory/

Rails環境で必要な設定

MySQL 8.0

MySQL 8.0での認証方式にgithub actions側が付いてこれてないらしい?以下の設定が必要 docker - How to pass "mysql_native_password" to MySQL service in GitHub Actions? - Stack Overflow

別解。試しはしたがうまくいかなかった。 GithubActionsでMySQL8.0のRSpecを動かす - Qiita

その他

  • キャッシュを利用するとCIのスピードを改善できるらしい、がひとまず保留
  • シークレットとは? GitHubのSecretsを活用して、GitHub Actionsで安全に機密情報を扱う - Qiita

    GitHub repositoryにyamlファイルを書くことで、Commit時やPR反映時等色々な場面で利用できるGitHub Actions。 そんなGitHub Actionsに処理を書いていると、外部サービスと連携するためにアクセストークンのような機密情報を保持したくなります。 この記事では、その機密情報の扱うためのSecretsの使い方について記載します。

  • ヘルスチェックの意味が分からんが、どうやらこれはdocker周りの話 - withはactionのパラメータ
  • ワークフローをスケジュールするときは、ワークフローファイルで POSIX cron 構文を使用できる ワークフローを設定する - GitHub Docs

 

Rails製 APIにおける Request Spec(RSpec)の作成方針

未だ書き慣れたとは言い難いRSpecだが、APIのSpecを改めて書いてみたところ、ある程度方針が固まってきたのでまとめてみる。

RSpec共通

describe, it, contextを用いて test caseを羅列する

なにはともかく枠から作る。ここさえ固まれば以降大きくはブレない。出来るだけこの時点でレビューに出すのが望ましい。 この際contextはなるべく浅い階層になるよう作成する。

具体的には以下記事参考

RSpecえかきうた|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

簡単なアイテムから1コンテキスト分作り込む

ここからは具体的にSpecの中身を作り込んで行くが、基本的には「簡単なテストから1つずつ満足のいくように作り込む」方針を取るのが望ましいと考えている。

Specは各Context(なりit)で書き方が共通するパターンが多い。そのため途中で書き方を変えるとそれが広い範囲に波及し面倒なことになる。 それを避けるため、まずは1つ満足の行く形に1つテストを作り込み、そのやり方を他に適用して行く形が効率が良いと考えている。 またその際なるべく簡単なケースから作り込んでいけば、より難しいケースを作成する際に(部分的に作成方針が決まっているため)作成の難易度が下がる。

なるべくDLS等のRSpec固有の書き方は避ける

これは諸説あると思うが、私個人としてはなるべくRSpec固有の書き方は使わないようにしている。具体的には before do, subject, let, 各種マッチャ がそれに該当する。

理由としては、まぁ一言で表せばめんどくさいからだ。RSpecは様々な便利なツールを用意してくれてはいるが特に推奨はしていない。(推奨しているのはdescribeやitを使ったテストケースの分け方くらいだろうか) 公式があまりopinionatedで無い分、いわゆる「良い書き方」には色々な意見がある。以前は「Better Specs」と呼ばれるRSpecの機能をフル活用した書き方が推奨されていたようだが、昨今ではよりシンプルな書き方も支持されてきている。

サヨナラBetter Specs!? 雑で気楽なRSpecのススメ - Qiita

その上で私は「シンプルな書き方」を支持する。正直あまりRSpecでしか役に立たないような内容をガッツリ勉強しようとは思わないので。。。RubyRailsの書き方で実現できるならそっちの方が綺麗かなぁとすら思う。

基本シンプルに記載し、可読性やメンテナンス性の向上が非常に高く見込める時だけRSpec固有の書き方を利用する方針だ。

その上で具体的に使うものは

  • describe, context, it
  • expect , eq
  • let!(出来るだけ避ける)

このくらい。種々の便利なマッチャは出来るだけ使わず、ほぼ eq で通す(例外としてはraise_errorなどは使うけれども)

他には共通処理はbeforeを使わずdefを使う。問題なくテストは書ける。

FactoryBotはフル活用する。

こいつは単純に有能で利用することで可読性やメンテナンス性の向上が非常に高く見込めるのでフル活用していく。

AAA(Arange Act Assert)を意識する

itの中身はおおよそ、データの準備・テスト対象処理の実行・結果の検証(テスト)の3つに分けられる。itの中身もこの3種類が明確に分かれるように記載する方がわかりやすい。 3,4 行程度ならベタに書くで良いと思うが、長くなるようなら改行を挟んだり、defなりヘルパーなりを作成することで見やすくする等の工夫も必要になってくるかもしれない。

参考: 【アンチパターン】Arrange、Act、Assert(AAA)を意識できていないRSpecのコード例とその対処法 - Qiita

Request Spec 固有

ここからはRequest Spec固有の作成方針を記載する。

describe, context, itの書き方

describe

"GET /api/v1/stocks"の形で、httpsのメソッドとURIを記載する。

context

正常系や異常系、ログイン・未ログインなど階層化せず、利用ケースをフラットに記載する。

it

context毎におおよそ以下3種類を作成する。それぞれitを分ける。itを分けるかは諸説あるかもしれないが、私としては確認する内容が明確になるためこの形が好き。パフォーマンスの影響は考慮する必要があるかもしれない。

  • httpのステータスコードの確認
  • レスポンスの内容(bode)を確認
  • (post/patch/delete等の場合) 実データへの影響を確認

パラメータのチェック

APIから取得したパラメータのチェックはおそらくrequest specで頻出の内容と思うが、私としてはパラメータを1つ1つexpectを切って確認するやり方を取っている。 例えば以下のようなやり方だ。

        expect(actual[:name]).to eq params[:name]
        expect(actual[:colorNumber]).to eq params[:colorNumber]
        expect(Time.zone.parse(actual[:manufacturingDate])).to eq Time.zone.parse(params[:manufacturingDate])
        expect(actual[:quantity]).to eq params[:quantity]
        expect(actual[:condition]).to eq params[:condition]

明らかに冗長ではあるため改善の余地はある。

例えばhave_attributes を使うことが考えられる。ただhave_attributesは全てのパラメータが一致していないとテストがパスしないため、レスポンスにidが含まれる場合等も考慮すると柔軟性に欠ける。 もっと良い使い方もあるかもしれないが、、、 (別の記事の一部の記載では部分的なチェックもできると書かれていたが、私の環境ではうまくいかなかった。。。)
週刊Railsウォッチ(20200629前編)RSpecをメンテしやすくする9つのコツ、application.jsのrequireをimportに置き換え、HTTP 308 Permanent Redirectとはほか|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

またrspec-parameterizedの利用もありそうではある。折を見て今後検討したい。 github.com

ひとまずこのやり方でも最低限は要求を満たせると考えている。しばらくはこれで行く。

その他参考サイト

【翻訳】RSpecのリードメンテナだけど何か質問ある? - Qiita

React + Railsでのエラー周り実装の基本方針

タイトルの通り。そんな大した話ではないが今後の基本方針としてメモ。まぁ浅い内容。

動くコードは以下参照。

github.com

気にするべきエラーをひとまず以下の形で分類。

  • フロント: 入力値のValidation
  • フロント: APIレスポンス
  • API: サーバ側で発生する種々のエラー

以下詳細

フロント: 入力値のValidation

入力値のValidationはコンポーネントライブラリ(ant design)に任せる形をとる。 自分で実装する場合はValidation結果はなるべくコンポーネントローカルのstateにエラーの情報を持たせる方向を取りたい(reduxのstoreは使わない)

フロント: APIレスポンス

fetch等でAPIを叩いた結果、返ってきたレスポンスがエラーだった(4XX等)の場合の処理。 axiosであれば2XX以外は例外として処理されるので、catchして受け取ったエラーの中身をグローバルのstoreに反映させる。 反映結果を元に必要に応じてエラーメッセージを表示する

axiosでcatchする場合の注意点

axiosを使った場合、catchで受け取ったエラー情報は以下のようにerror.responseの形を取らないと参照できない。

axios.post('/test/create', this.formData)
  .then(response => { 
    console.log(response)
  })
  .catch(error => {
    console.log(error.response)
  });

async/awaitでエラー処理を書く際のパターン

以下の参考記事の通りいくつかパターンがあるので状況に応じて好きなものを使う。

qiita.com

個人的にお気に入りなのは、以下の「3. async function 内でエラーハンドリングしない」パターン。

const asyncFuncHandleErrorOut = async () => {
  const result = await errorPromise()
  console.log(result) // unreachable
}

// async function を呼ぶ時にエラーハンドリング
asyncFuncHandleErrorOut()
  .catch(err => console.log(err)) // Some error occured

成功時、失敗の処理をキッチリ分けられる & 個人的にtry-catchの形があまり好きでない という理由から推しの手法。

参考サイト:

[フロントエンド] axiosライブラリを使って、柔軟にHTTP通信を行う - YoheiM .NET
axiosのcatchでerror objectの中身を見れない - Qiita
Axios catch error returns javascript error not server response · Issue #960 · axios/axios · GitHub

API: サーバ側で発生する種々のエラー

サーバ側ではDBアクセスや外部サービスへのアクセス等、種々のエラーが発生し得る。発生し得るエラーの種類はとても書ききれないので以下記事参照。

qiita.com

エラー毎に対応するhttpステータスコードを設定してレスポンスを返す。エラー毎にどのステータスコードが適切か?などあまり理解していない。

参考サイト:

qiita.com morizyun.github.io

rescue_from

様々な箇所で共通で発生するようなエラー(例えば ActiveRecord::RecordNotFound など)は、rescue_fromを用いることで共通で処理できる。

参考サイト:

Rails tips: rescue_fromでコントローラのエラーをrescueする(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社
RailsのAPIサーバーのエラーレスポンスで例外に対応するエラーコードを返却する - Timee Product Team Blog

最後に

全体的にAPI(RESTful API)の設計周りの知識が明らかに不足している。今後以下の書籍を読んで基礎的な知識を固めて行きたいところ。

書評: RESTful Webサービス - t-wada の日記(旧)
Web API: The Good Parts | 水野 貴明 |本 | 通販 | Amazon

SPAのログインチェック機能がいい感じに作れたので実装の流れを軽くメモる

SPAのログインチェック機能が自分としてはいい感じに出来たので記録に残す。

React Router v5までであれば、いわゆるPrivateRouteのやり方を用いれば良いのだが、今回はv6 のObject-based Routesを用いたのでやり方を自分で考える必要があった。出来てしまえば大したことはなかったのだが、微妙に紆余曲折あったのでメモ。

実装の流れ

  • (1). Privateコンポーネントの作成 & route objectへの反映

    • 認証が必要な機能のルーティングを統括するPrivateコンポーネントを作成し、route objectに反映する。
  • (2). ログインチェック機能の有効・無効を表すグローバルなステート(loginCheck, 初期値 true) & ステートを変更するためのaction等々を作成する

  • (3). ログインチェック用のhooksを作成し、Privateコンポーネントに組み込む
    • ログインチェック用のAPIを叩く
    • loginCheckステートをfalseにする
    • 失敗した場合はログイン画面に遷移する
  • (4). Privateコンポーネントレンダリングで、「loginCheck = trueの時にはローディング画面を表示」する処理を追加する

コード

動くコードは下記リポジトリ参照。 github.com

特に関連するのは以下のファイル。

  • client/src/layouts/Private.tsx
  • client/src/common/hooks/useLoginCheck.ts
  • client/src/domains/Login/authSlice.ts
  • client/src/routes.tsx