AmplifyGen2でS3にCORSポリシーを設定する方法を紹介

# AmplifyGen2でS3にCORSポリシーを設定する方法を紹介

## 1. はじめに

本記事は、自分用の備忘録として書いています。  
現在、[Amplify Gen2](https://docs.amplify.aws/) を使用してFitness向けSNSを遊びで開発しています。その過程で、S3の署名付きURLを発行しようとした際に遭遇したエラーについてまとめました。
同じエラーにぶつかる人のためにでもなれば嬉しいです。

Amplify Gen2の署名付きURLについてはこちら
- [Build a Backend with Amplify - Storage](https://docs.amplify.aws/react/build-a-backend/storage/download-files/)

---

## 2. やろうとしていたこと
遊びでボディメイク頑張っている人向けのSNSを作成しようとしていました。
![スクリーンショット 2024-11-24 20.58.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3349905/96e23008-c69a-5c29-c8f1-d723018cdb6c.png)
投稿と一緒に写真をアップロードして投稿一覧で見えるようにしています。せっかくなので、Amplify UIのStorageImageを使っています。
参考：[Storage Image](https://ui.docs.amplify.aws/react/connected-components/storage/storageimage)
※写真は適当に作成したものです。
※SageMaker Ground Truthをイメージしたキャラです。

### もっとアップで筋肉がみたい。
このアプリはボディメイク用を想定しているので、筋肉の画像がアップロードされる予定です。であれば、もっとズームで見たいです。お気に入りの筋肉はなんならダウンロードしたいです。
StorageImageにはデフォルトでは、クリックした際のアクションはないので独自実装する必要がありました。
そこで利用したのが、onClickとS3の **署名付きURL** を組み合わせる方法です。

`StorageImage` で表示している画像をクリックした際に、署名付きURLを利用してフルサイズの画像を取得・表示しようとしていました。

---

## 3. CORSエラー発生

署名付きURLを取得しようとする以下のコードを実装したところ、**CORSエラー** が発生しました。
```typescript
# (投稿リストのコンポーネント)
import { useEffect, useState } from 'react';
import { generateClient } from 'aws-amplify/data';
import { getUrl } from 'aws-amplify/storage';
import { StorageImage } from '@aws-amplify/ui-react-storage';
import type { Schema } from '../../../amplify/data/resource';
(省略)
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <p>{post.content}</p>
          {post.image && (

              <StorageImage
                alt="Post image"
                path={post.image}
                className="justify-center w-24 h-auto"
                onClick={async () => {
                    if (post.image) { // Check if post.image is not null or undefined
                      const url = await generatePresignedUrl(post.image);
                      window.open(url, '_blank');
                    } else {
                      console.error('Image path is not defined.');
                    }
                  }}
              />

          )}
        </li>
      ))}
    </ul>
  );
};

(省略)
```
省略していますが、画像がクリックされたら、generatePresignedUrl(post.image)で署名付きURLを生成して別のタブを開いて表示します。
しかし、ここでお馴染みのCORSエラーが表示されました。


## 4. Amplify Gen2で設定する方法
CORSの設定をすれば良いんですが、Amplify Gen2はリソースの定義を簡素化しているので、どうすれば良いのか分かりにくいです。

そこで、公式ドキュメントを参考に実際に設定をやってみました。
参考：[Extend S3 resources](https://docs.amplify.aws/react/build-a-backend/storage/extend-s3-resources/)

の環境でこれを行うには、backend.ts を編集します。

設定手順
Amplifyプロジェクトのディレクトリに移動します。
S3のリソース設定を拡張します。
公式ドキュメント：Extend S3 Resources

以下が設定例です：

```typescript
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
import { storage } from './storage/resource';

import * as s3 from 'aws-cdk-lib/aws-s3';

const backend= defineBackend({
  auth,
  data,
  storage
});


const s3Bucket = backend.storage.resources.bucket;

const cfnBucket = s3Bucket.node.defaultChild as s3.CfnBucket;

// Define CORS rules
cfnBucket.corsConfiguration = {
  corsRules: [
    {
      allowedHeaders: ['*'],
      allowedMethods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD'], // S3 method strings
      allowedOrigins: ['http://localhost:5173'], // Replace with your application's URL
      exposedHeaders: ['ETag'],
      maxAge: 3000,
    },
  ],
};
```
Amplifyが生成したS3リソースはL2（高レベル）CDKコンストラクトで管理されているので、詳細設定のためにはL1コンストラクトを利用します。上記の例のように設定し、CORSエラーを解決する事ができました！
※URLはローカル開発のものになっています。こちら参考にする際は適切なURLに置き換えてください。

![スクリーンショット 2024-11-24 21.26.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3349905/66607cd8-d4bd-3dd5-49d4-e1b304c7f0f7.png)

Amplify Gen2の利便性を活かしつつ、トラブルシューティングも楽しんでいきましょう！

参考になれば幸いです