본문으로 바로가기 본문 바로가기
WIKI 기술 지식 베이스

aws4fetch로 R2 사용하기

원문 보기 위키 갱신

aws4fetch 패키지로 R2를 사용하는 예시예요. fetch와 SubtleCrypto API를 사용해 S3 호환 API를 호출하고 버킷·객체를 나열하며 사전 서명 URL을 생성해요. 브라우저와 Workers에서 자연스럽게 동작해요.

출처: 문서

본문

JavaScript나 TypeScript 사용자는 평소처럼 aws4fetch ↗︎ npm 패키지를 계속 사용할 수 있어요. 이 패키지는 브라우저에서나 Cloudflare Workers에서 작업할 때 익숙한 fetch와 SubtleCrypto API를 사용해요.

S3 서비스 클라이언트를 만들 때 R2 구성 자격 증명을 반드시 전달해야 해요:

import { AwsClient } from "aws4fetch";

// Provide your Cloudflare account ID
const R2_URL = `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`;

const client = new AwsClient({
	// Retrieve your S3 API credentials for your R2 bucket via API tokens (see: https://developers.cloudflare.com/r2/api/tokens)
	accessKeyId: ACCESS_KEY_ID,
	secretAccessKey: SECRET_ACCESS_KEY,
});

const ListBucketsResult = await client.fetch(R2_URL);
console.log(await ListBucketsResult.text());
// <ListAllMyBucketsResult>
//     <Buckets>
//         <Bucket>
//             <CreationDate>2022-04-13T21:23:47.102Z</CreationDate>
//             <Name>user-uploads</Name>
//         </Bucket>
//         <Bucket>
//             <CreationDate>2022-05-07T02:46:49.218Z</CreationDate>
//             <Name>my-bucket</Name>
//         </Bucket>
//     </Buckets>
//     <Owner>
//         <DisplayName>...</DisplayName>
//         <ID>...</ID>
//     </Owner>
// </ListAllMyBucketsResult>

const ListObjectsV2Result = await client.fetch(
	`${R2_URL}/my-bucket?list-type=2`,
);
console.log(await ListObjectsV2Result.text());
// <ListBucketResult>
//   <Name>my-bucket</Name>
//   <Contents>
//     <Key>cat.png</Key>
//     <Size>751832</Size>
//     <LastModified>2022-05-07T02:50:45.616Z</LastModified>
//     <ETag>"c4da329b38467509049e615c11b0c48a"</ETag>
//     <StorageClass>STANDARD</StorageClass>
//   </Contents>
//   <Contents>
//     <Key>todos.txt</Key>
//     <Size>278</Size>
//     <LastModified> 2022-05-07T21:37:17.150Z</LastModified>
//     <ETag>"29d911f495d1ba7cb3a4d7d15e63236a"</ETag>
//     <StorageClass>STANDARD</StorageClass>
//   </Contents>
//   <IsTruncated>false</IsTruncated>
//   <MaxKeys>1000</MaxKeys>
//   <KeyCount>2</KeyCount>
// </ListBucketResult>

사전 서명 URL 생성 (Generate presigned URLs)

버킷에 대한 공개 읽기 또는 쓰기 접근 권한을 일시적으로 공유하는 데 사용할 수 있는 사전 서명 링크도 생성할 수 있어요.

import { AwsClient } from "aws4fetch";

const client = new AwsClient({
	service: "s3", // Required by SDK but not used by R2
	region: "auto", // Required by SDK but not used by R2
	// Retrieve your S3 API credentials for your R2 bucket via API tokens (see: https://developers.cloudflare.com/r2/api/tokens)
	accessKeyId: ACCESS_KEY_ID,
	secretAccessKey: SECRET_ACCESS_KEY,
});

// Provide your Cloudflare account ID
const R2_URL = `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`;

// Use the `X-Amz-Expires` query param to determine how long the presigned link is valid.
console.log(
	(
		await client.sign(
			new Request(`${R2_URL}/my-bucket/dog.png?X-Amz-Expires=${3600}`),
			{
				aws: { signQuery: true },
			},
		)
	).url.toString(),
);
// You can also create links for operations such as PutObject to allow temporary write access to a specific key.
// Specify Content-Type header to restrict uploads to a specific file type.
console.log(
	(
		await client.sign(
			new Request(`${R2_URL}/my-bucket/dog.png?X-Amz-Expires=${3600}`, {
				method: "PUT",
				headers: {
					"Content-Type": "image/png",
				},
			}),
			{
				aws: { signQuery: true },
			},
		)
	).url.toString(),
);
https://<ACCOUNT_ID>.r2.cloudflarestorage.com/my-bucket/dog.png?X-Amz-Expires=3600&X-Amz-Date=<timestamp>&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<credential>&X-Amz-SignedHeaders=host&X-Amz-Signature=<signature>
https://<ACCOUNT_ID>.r2.cloudflarestorage.com/my-bucket/dog.png?X-Amz-Expires=3600&X-Amz-Date=<timestamp>&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<credential>&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Signature=<signature>

PutObject 예시로 생성한 링크를 사용해서 사전 서명 링크가 만료되기 전까지 지정된 버킷과 키에 업로드할 수 있어요. Content-Type이 포함된 사전 서명 URL을 사용할 때는 클라이언트가 요청에 일치하는 Content-Type 헤더를 꼭 포함해야 해요.

curl -X PUT "https://<ACCOUNT_ID>.r2.cloudflarestorage.com/my-bucket/dog.png?X-Amz-Expires=3600&X-Amz-Date=<timestamp>&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<credential>&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Signature=<signature>" \
  -H "Content-Type: image/png" \
  --data-binary @dog.png

CORS와 Content-Type으로 업로드 제한하기 (Restrict uploads with CORS and Content-Type)

업로드용 사전 서명 URL을 생성할 때 다음 방법으로 남용과 오용을 제한할 수 있어요:

  • Content-Type 제한: 서명할 때 요청에 Content-Type 헤더를 지정해요. 클라이언트가 다른 Content-Type 헤더를 보내면 업로드는 실패해요.

  • CORS 구성: 버킷에 CORS 규칙을 설정해 어떤 오리진이 파일을 업로드할 수 있는지 제어해요. Cloudflare 대시보드 ↗︎에서 버킷 설정에 JSON 정책을 추가해 CORS를 구성할 수 있어요:

[
  {
    "AllowedOrigins": ["https://example.com"],
    "AllowedMethods": ["PUT"],
    "AllowedHeaders": ["Content-Type"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
  }
]

그다음 Content-Type 제한이 적용된 사전 서명 URL을 생성해요:

const signedRequest = await client.sign(
	new Request(`${R2_URL}/my-bucket/user-upload.png?X-Amz-Expires=${3600}`, {
		method: "PUT",
		headers: {
			"Content-Type": "image/png",
		},
	}),
	{
		aws: { signQuery: true },
	},
);
const putUrl = signedRequest.url.toString();

클라이언트가 이 사전 서명 URL을 사용할 때는 반드시 다음을 지켜야 해요:

  • 허용된 오리진에서 요청할 것 (CORS로 강제됨)

  • Content-Type: image/png 헤더를 포함할 것 (서명으로 강제됨)

더 알아보기 (Learn more)