미디어 스토리지 선택

이 페이지

업로드된 미디어용 스토리지 어댑터를 하나 선택하세요. 데이터베이스 백업에는 미디어 메타데이터만 포함되고 저장된 파일은 포함되지 않으므로, 스토리지 백엔드는 별도로 백업하세요.

개요

스토리지사용 시기서명된 업로드
R2 바인딩사이트가 Cloudflare Workers에서 실행될 때아니오
S3Node.js 사이트가 AWS S3, R2의 S3 API, MinIO 또는 호환 스토리지를 사용할 때
로컬Node.js 사이트에 쓰기 가능한 영구 볼륨이 있을 때아니오

Cloudflare R2 바인딩

Cloudflare Workers에서 R2 바인딩 어댑터를 사용합니다. 바인딩이 런타임에 접근을 제공하므로, 사이트는 R2 액세스 키가 필요하지 않습니다.

import emdash from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";

export default defineConfig({
	integrations: [
		emdash({
			storage: r2({ binding: "MEDIA" }),
		}),
	],
});

구성

옵션타입설명
bindingstringwrangler.jsonc의 R2 바인딩 이름
publicUrlstring버킷의 선택적 공개 URL

설정

Wrangler 구성에 R2 바인딩을 추가합니다:

wrangler.jsonc

{
  "r2_buckets": [
    {
      "binding": "MEDIA",
      "bucket_name": "emdash-media"
    }
  ]
}

wrangler.toml

[[r2_buckets]]
binding = "MEDIA"
bucket_name = "emdash-media"

공개 접근

공개 버킷에서 미디어를 제공하려면 Cloudflare API로 커스텀 도메인을 연결한 다음 해당 오리진을 publicUrl로 설정합니다. Cloudflare의 r2.dev 개발 URL은 레이트 리밋이 있으며 프로덕션 트래픽용이 아닙니다.

storage: r2({
	binding: "MEDIA",
	publicUrl: "https://media.example.com",
});

같은 버킷이 자동 JSON 백업을 저장하는 경우, 공개 버킷 오리진이 backups/ 아래의 객체를 노출할 수 있습니다. 프라이빗 버킷과 EmDash의 미디어 라우트를 사용하거나, 공개 오리진을 미디어 객체로 제한하세요. 백업을 참조하세요.

S3 호환 스토리지

S3 어댑터는 Node.js에서 Cloudflare R2의 S3 API, AWS S3, MinIO 및 호환 서비스와 작동합니다.

다음 구성은 Node.js 프로세스 시작 시 S3_* 변수에서 엔드포인트, 버킷, 자격 증명, 리전 및 선택적 공개 URL을 해석합니다:

import emdash, { s3 } from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			storage: s3(),
		}),
	],
});

구성

옵션타입필수설명
endpointstringS3 엔드포인트 URL
bucketstring버킷 이름
accessKeyIdstring아니오*액세스 키
secretAccessKeystring아니오*시크릿 키
regionstring아니오리전 (기본값: "auto")
publicUrlstring아니오선택적 CDN 또는 공개 URL

* accessKeyIdsecretAccessKey는 함께 제공되거나 둘 다 생략되어야 합니다.

환경 변수에서 S3 구성 해석

s3({...})에서 생략된 모든 필드는 프로세스 시작 시 해당 S3_* 환경 변수에서 읽힙니다. 이를 통해 컨테이너 이미지를 한 번 빌드하고 재빌드 없이 부팅 시 자격 증명을 주입할 수 있습니다. s3({...})의 명시적 값은 항상 환경 변수보다 우선합니다.

환경 변수필드참고
S3_ENDPOINTendpoint유효한 http/https URL이어야 함
S3_BUCKETbucket
S3_ACCESS_KEY_IDaccessKeyId
S3_SECRET_ACCESS_KEYsecretAccessKey
S3_REGIONregion기본값 "auto"
S3_PUBLIC_URLpublicUrl선택적 CDN 접두사

환경 변수는 프로세스 시작 시 process.env에서 읽힙니다. 이것은 Node 전용 기능입니다.

인수 없이 s3()를 호출하면 S3_* 환경 변수에서 모든 필드를 읽습니다:

import emdash, { s3 } from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			// 인수 없는 s3(): 모든 필드를 S3_* 환경 변수에서
			storage: s3(),

			// 또는 혼합: 하나의 필드를 오버라이드, 나머지는 환경에서
			// storage: s3({ publicUrl: "https://cdn.example.com" }),
		}),
	],
});

S3 API를 통한 R2

R2로의 직접 서명된 업로드가 필요할 때 Node.js에서 S3 어댑터를 사용합니다. Cloudflare API 또는 CLI로 범위가 제한된 R2 API 자격 증명을 만든 후 다음 런타임 변수를 설정하세요:

S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
S3_BUCKET=emdash-media
S3_ACCESS_KEY_ID=<r2-access-key-id>
S3_SECRET_ACCESS_KEY=<r2-secret-access-key>
S3_REGION=auto
S3_PUBLIC_URL=https://media.example.com

실제 값은 Node.js 호스팅 플랫폼의 시크릿 매니저에 보관하세요. 공개 URL은 선택사항이며 업로드에 사용되는 S3 API 엔드포인트를 대체하지 않습니다.

MinIO

같은 런타임 변수를 MinIO로 지정합니다. S3_ENDPOINT를 MinIO API 오리진으로, S3_BUCKET을 버킷 이름으로, 두 자격 증명 변수를 범위가 제한된 MinIO 액세스 키로 설정합니다. 해당 오리진이 버킷의 객체를 공개적으로 제공하는 경우에만 S3_PUBLIC_URL을 설정하세요.

로컬 파일 시스템

개발 또는 영구 디스크가 있는 단일 Node.js 서버에 로컬 스토리지를 사용합니다. 파일은 해당 디스크의 디렉토리에 저장됩니다.

import emdash, { local } from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			storage: local({
				directory: "./uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
		}),
	],
});

구성

옵션타입설명
directorystring파일 저장 디렉토리 경로
baseUrlstring파일 제공 기본 URL

baseUrl은 커스텀 정적 파일 서버를 구성하지 않는 한 EmDash의 미디어 파일 엔드포인트 (/_emdash/api/media/file)와 일치해야 합니다.

환경별로 다른 스토리지 사용

다음 구성은 개발 중에 로컬 디렉토리를 사용하고 Cloudflare 프로덕션 빌드에서 R2를 사용합니다:

import emdash, { local } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";

const storage = import.meta.env.PROD
	? r2({ binding: "MEDIA" })
	: local({
			directory: "./uploads",
			baseUrl: "/_emdash/api/media/file",
		});

export default defineConfig({
	integrations: [emdash({ storage })],
});

서명된 업로드

S3 어댑터는 서명된 업로드 URL을 지원하여 클라이언트가 서버를 거치지 않고 스토리지에 직접 업로드할 수 있습니다. 이는 큰 파일의 성능을 향상시킵니다.

S3 어댑터 사용 시 서명된 업로드는 자동입니다. 관리자 인터페이스는 사용 가능할 때 이를 사용합니다.

서명된 업로드를 지원하는 어댑터:

  • S3 (S3 API를 통한 R2 포함)

서명된 업로드를 지원하지 않는 어댑터:

  • R2 바인딩 (대신 R2 자격 증명을 사용한 S3 어댑터 사용)
  • 로컬

스토리지 인터페이스

모든 스토리지 어댑터는 동일한 인터페이스를 구현합니다:

interface Storage {
	upload(options: {
		key: string;
		body: Buffer | Uint8Array | ReadableStream;
		contentType: string;
	}): Promise<UploadResult>;

	download(key: string): Promise<DownloadResult>;
	delete(key: string): Promise<void>;
	exists(key: string): Promise<boolean>;
	list(options?: ListOptions): Promise<ListResult>;
	getSignedUploadUrl(options: SignedUploadOptions): Promise<SignedUploadUrl>;
	getPublicUrl(key: string): string;
}

이 일관성으로 애플리케이션 코드를 변경하지 않고 스토리지 백엔드를 전환할 수 있습니다.