Rust(axum, sqlx)とReactでWebアプリ開発 - 備忘録2 デプロイ&機能追加編

## はじめに
前回の記事の続き。
長くなりそうだったので分割。

https://qiita.com/KMMT_h_m/private/1d7a9dccafa973812ed5

:::note warn
本記事は筆者の備忘録的な側面が多いため、読みにくい部分があります。
また、必ずしも正しい情報ではなかったり、実装における最適解ではない可能性があります。
:::
※もしより正確な情報だったり詳細な内容をご存じの方はコメントで教えていただけると幸いです。

## 概要
前回の記事の要約も兼ねて、ざっと今回制作しているWebアプリの概要を記載する。

- バックエンド
    - Rust
        - axum
        - sqlx
- フロントエンド
    - React

## バックエンドのデプロイ
今回は shuttle にデプロイする。
「Rust ネイティブのクラウド開発プラットフォーム」とのことで、いくつか記事も出てきたので[公式ドキュメント](https://docs.shuttle.rs/introduction/welcome)と並べながら進めることにした。

参考にした記事

https://qiita.com/kapiyva/items/e6572c217a45aca7bc0d

https://zenn.dev/monicle/articles/fcfd4f374456e5


### `cargo shuttle run`まで
まずはローカルでのshuttle対応を目指す。
shuttleにデプロイするためには、対応するようにコードを書き換えないといけないらしい。

shuttleのプロジェクト作成方法などはほかの資料にあるため省略。

---
進めていくと以下のようなエラーが発生。
```console
error: failed to select a version for `sqlx`.
    ... required by package `shuttle-shared-db v0.47.0`
    ... which satisfies dependency `shuttle-shared-db = "^0.47.0"` of package `todo v0.1.0 (C:\Users\gravi\Desktop\dev\rust\rust_todo)`
versions that meet the requirements `^0.7.1` are: 0.7.4, 0.7.3, 0.7.2, 0.7.1
```

ローカルではsqlx 0.8.0で進めていたのだが、どうやらshuttle側が未対応らしい。
ということで0.7.1に落としてみるとうまくいった様子。

`cargo shuttle run`を打つと、shuttleの設定を含んだDockerコンテナが起動する。
今までのローカル開発で動かしていたDockerコンテナとは別のコンテナが起動しているみたいだった。

ここで困ったのが、ローカルサーバーを立ち上げるとブラウザからDBに繋がらなくなってしまった。
いろいろ調べてみると、shuttleの公式ドキュメントサイトにある「Search or ask...」で調べると、デフォルトポートは8000番で立ち上がっていることが分かった。
（これはshuttleが用意したAIが回答を生成しているっぽい）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/78c3a8ec-43b8-5da4-6b5d-cb0bf089ad52.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/e767ef52-f15b-752c-ce86-9cd0140cff8e.png)

ローカルでは3000番を指定していたので、8000番に直すと繋がった。

### `cargo shuttle deploy`

デプロイ用のコマンドを打つとまたエラーが発生。

```
2024-09-12T16:21:54.018+09:00 [Deployer] error: test failed, to rerun pass `--bin todo`
2024-09-12T16:21:54.022+09:00 [Deployer]  INFO Releasing queue slot
2024-09-12T16:21:54.022+09:00 [Deployer]  INFO Entering Crashed state
2024-09-12T16:21:54.022+09:00 [Deployer]  INFO Build failed
2024-09-12T16:21:54.022+09:00 [Deployer] ERROR shuttle_deployer::deployment::queue: {error="Pre-deployment test failure: The deployed application's tests failed. HINT: re-run deploy with `--no-test` to skip tests."} Service build encountered an error
```

テストが通らないので怒られている。
そしてテストを無視してデプロイする方法も教えてくれている。（`--no-test`）

確かにローカルサーバーのポート番号を変えたのでテストが通らなくなる。
DBの接続先をshuttleのコンテナに変えてみたがうまくいかない。
あまりよろしくないと思うが、今回はこのままデプロイすることにする。

```
cargo shuttle deploy --no-test
```

うまくいくとコンソールにはデプロイ成功したログと、WEB上には _Running_ が表示される。

```
2024-09-12T16:22:53.476+09:00 [Deployer]  INFO Entering Running state
2024-09-12T16:22:53.476+09:00 [Deployer]  INFO Starting service
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/adf6147b-85e6-974a-d43a-11d76af5ec76.png)

ちなみにデプロイ成功すると本番用のURLがshuttleから発行されるので、
テスト用のコードに本番用のURLを入れるとテストが通った。（しかしデプロイ時にはやはり`--no-test`をつけないとデプロイできなかった）

バックエンドのデプロイまで終わった時点のコードはこちら

https://github.com/KMMThmurakami/rust_todo_public/tree/ChapterEX1_deploy_BE

## フロントエンドのデプロイ

フロントエンドはReactなので選択肢は多そうだが、今回はCloudflareを選択。
以下の記事を参考にした。

https://zenn.dev/kat/articles/ce1b0bb2d037ae

特に困ったことはなかった。
package.jsonにdeployコマンドがなかったので追加したぐらい。


### deploy.yml

次にGithub Actionsを使って、自動デプロイ環境を作ることにした。
Cloudflareにデプロイしたので以下の記事を参考に進めた。
トークン発行などまでの流れは以下の記事の通りなので省略。

https://zenn.dev/monicle/articles/4613eb4064dc40

困ったのは`deploy.yml`の設定だ。
成功するまで11回失敗した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/8290734c-94d6-9df6-1915-1593d25f7cce.png)

原因はバージョンが違ったり(記事はv3で書かれているが現在はv4が最新)、デプロイディレクトリが違ったり（ルート直下の一つ下にReactのソースを置いている）、様々だった。

Github上でエラーメッセージが出ているので、読み込んで調べて修正して...を繰り返して試行錯誤した結果、何とか自動デプロイに成功した。

ここまでのソースは以下。

https://github.com/KMMThmurakami/rust_todo_public/tree/ChapterEX3_github_actions

自動デプロイについて調べた際に見たりした記事。

https://qiita.com/taptappun/items/4a8f7ecaa3f91cb21be9

https://qiita.com/tommy_aka_jps/items/4713182b573b298ccb26

## リファクタと機能追加

デプロイした後に見直していて、少し動作が気になったところがあったので修正を加えた。

- `useCallback`,`useMemo`,遅延読み込みなどの追加
- ローディング中のUIを追加
- Labelフィルター時のUI変更
- CSS Modulesに変更

最終的なコードは以下。

https://github.com/KMMThmurakami/rust_todo_public/tree/ChapterEX4_refactor_and_feature

### 完成したアプリケーションのデモ画面

- 初期表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/6a4065e1-32fb-9fb8-ad95-a3f0964a2e4d.png)

- ローディング中
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/23e038c2-16a7-325b-0361-1d121abe11c7.png)

- Labelフィルター時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/fbcb4ef3-2ac1-5b63-96e4-e23b28c45c6e.png)

- データ更新時（これもう少し見せ方工夫したかった...）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/39cbfe6a-e30c-4104-c35c-f18f193cca06.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487953/0e86c9ba-12db-9f13-6b65-75203cbbecce.png)

## 最後に

前回の記事で紹介した[書籍](https://www.shuwasystem.co.jp/book/9784798067315.html)をベースに、本番環境にデプロイしたり、自分なりにアップデートをすることができた。

ユーザー認証機能はないため、アプリケーションのURLを記載するのは控えるが（全世界に公開したら誰に何を書き込まれるかわからない）、身内の中での掲示板的な使い方はできるかもしれない。

次はユーザー認証を入れたり、今回やっていない[フロントエンドもRustで書く(追記：挑戦してみました)](https://qiita.com/KMMT_h_m/private/33df79eaed29dd1a57a2)ということに挑戦してみたい。

## 追記
実は途中までハードコーディングでWEBアプリケーションの画面URLをコードに記載していたので、[shuttleのsecrets機能を使って環境変数からURLを取得](https://qiita.com/KMMT_h_m/private/10616daa60ac98bf2740)するようにした。

## 派生記事リンク
https://qiita.com/KMMT_h_m/private/1d7a9dccafa973812ed5

https://qiita.com/KMMT_h_m/private/33df79eaed29dd1a57a2

https://qiita.com/KMMT_h_m/private/10616daa60ac98bf2740