【Rails7】HerokuでActiveStorageとS3を連携させる方法

## はじめに
この記事では、RailsのActiveStorageを用いてAmazon S3に画像をアップロードし、その機能をHerokuで正常に動作させる方法を説明します。
S3を活用することで、アプリケーション側の負担を減らし、より高いパフォーマンスを発揮することができます。

## 前提条件
:::note info 
Rails 7.0.6
Ruby 3.2.2
:::
アプリケーションがすでにHerokuにデプロイされており、AWSアカウントは作成済みとします。


## ActiveStorage
ActiveStorageは、`Rails 5.2`から導入された、アプリケーション内でファイルアップロードを簡単に扱うことができる機能です。
ユーザーからアップロードされたファイルを、データベースではなくクラウドストレージやローカルのファイルシステムに保存することができます。

## ActiveStorageのセットアップ
### テーブル作成
ActiveStorageはアップロードされたファイルを直接モデルのテーブルに保存しません。
代わりに、`active_storage_blobs`テーブルにファイルのメタデータを保存し、`active_storage_attachments`テーブルを使用して該当するモデルとの関連を作ります。
なので、まずは代わりになるテーブルを作成していきます。
```
rails active_storage:install
```
このコマンドを実行すると代わりのテーブルを作成するためのマイグレーションが生成されます。
```
rails db:migrate
```
コマンドを実行し、テーブルを作成します。

### マッピング
ファイルを添付したいテーブルのモデルファイルに、作成されたテーブルとの紐づけを行います。
たとえば、Userモデルがあるとして、各ユーザにアバター画像を添付したい場合は、以下のように定義します。
こうすることで、レコード1件ごとに1個のファイルを添付できます。
```erb
class User < ApplicationRecord
  has_one_attached :avatar
end
```
複数添付したい場合は、以下のように記述します。
```erb
class User < ApplicationRecord
  has_many_attached :avatars
end
```
これでセットアップは完了となり、ファイルが作成されたテーブルに保存されるようになります。

## S3のセットアップ

### バケット作成
S3からバケットを作成します。
設定はリージョンが自分の地域に設定されているかどうかを確認し、それ以外はデフォルトのままで問題ありません。
![スクリーンショット 2023-08-04 13.04.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849608/b97cc4e5-0929-7c3d-98c1-e6b6d679ff2b.png)
バケットは`開発`、`テスト`、`本番`用の全部で３つ設定します。
![スクリーンショット 2023-08-04 13.06.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849608/51b44771-fa81-2502-f8e7-dc81115b54f5.png)
このとき、名前の付け方は下記のようにしてください。
* myawsbucket-development
* myawsbucket-production
* myawsbucket-test

### IAMポリシーの作成
IAMユーザでの必要なアクセス制御を追加したポリシーを作成します。
IAM＞ポリシー＞ポリシー作成に進んでいき、S3を選択します。![スクリーンショット 2023-08-04 13.15.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849608/34b652a5-d152-9d1c-69cd-6ca8de3d9bef.png)
選択したら、必要なアクションにチェックをつけます。
必要なアクションは全部で４つあります。
* ListBucket
* PutObject
* GetObject
* DeleteObject

この４つにチェックをつけたうえで、ポリシーを作成してください。

### IAMユーザの作成
IAM＞ユーザ＞ユーザの作成に進みます。
許可を設定する際には、『ポリシーを直接アタッチする』を選択し、作成したポリシーを選択してください。
![スクリーンショット 2023-08-04 13.26.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849608/699af130-d710-45f0-851f-d469b26aab57.png)
以降はデフォルトのままで問題ありません。
ユーザを作成したら、アクセスキーを作成します。
作成したユーザの『セキュリティ認証情報』タブに切り替え、アクセスキーを作成を押します。![スクリーンショット 2023-08-04 13.30.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849608/5570db20-6800-ae43-493b-dc126ec7cb4d.png)
ユースケースには『AWSの外部で実行されるアプリケーション』を選択します。
![スクリーンショット 2023-08-04 14.16.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849608/a552f240-e793-8f41-dcc9-077eeb70ea15.png)

アクセスキーを作成したら、アクセスキーとシークレットアクセスキーをコピーしておきます。
ここまでがS3のセットアップになります。

## ActiveStorageとS3の連携

### `config/storage.yml`の編集
コメントアウトを外してregionを自分の地域に変更します。
```erb:storage.yml
service: S3
    access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
    secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
    region: ap-northeast-1
    bucket: myawsbucket-<%= Rails.env %>
```
bucketに<%= Rails.env %>を使うことで、開発環境では『development』、テスト環境では『test』、本番環境では『production』となり、それぞれの環境ごとのバケットにファイルがアップロードされます。

それからターミナルを開き、VScodeでcredentials.ymlファイルを開くコマンドを実行します。
```console
EDITOR="code --wait" rails credentials:edit
```
このファイルにawsのアクセスキーとシークレットアクセスキーを追加します。
```yml
# aws:
#   access_key_id: 123
#   secret_access_key: 345

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: ********

aws:
  access_key_id: ********
  secret_access_key: ********
```
ちなみにcredentialsとは、Railsが提供する暗号化された設定ファイルなので、シークレットキーなどの重要な情報はここに設定しましょう。

### 環境ファイルの編集
config/environments/development.rb
config/environments/production.rb
それぞれのファイルの下記を変更します。
```erb
config.active_storage.service = :amazon
```

### Gemのインストール
Gemfileに下記を追加します
```
gem "aws-sdk-s3", require: false
```
追加したら`bundle install`を忘れずに行いましょう。


## Herokuのセットアップ

設定したファイルをすべてpushし、デプロイします。
デプロイが終わったら、環境変数を設定します。
ターミナルに下記のコマンドを入力してください。
```
heroku config:set AWS_ACCESS_KEY_ID="********"
heroku config:set AWS_SECRET_ACCESS_KEY="="********""
```
設定し、`heroku config`でキーが確認できれば連携は完了し、ファイルがS3にアップロードされるようになります。


## 参考資料
https://railsguides.jp/active_storage_overview.html

https://zenn.dev/redheadchloe/articles/e924ab767b40d5

https://qiita.com/toto_public/items/b9c9bc93a1dfd03a2cbb

https://qiita.com/daichi41/items/af2a56ea46c13ca55fd3