EmDash의 주요 설정은 astro.config.mjs에 있으며, src/live.config.ts가 콘텐츠 로더를 등록합니다. 배포별 값은 환경 변수에서도 올 수 있습니다. package.json의 작은 emdash 메타데이터 블록은 템플릿 레이블과 레거시 로컬 CLI 흐름을 지원합니다.
Astro 통합
astro.config.mjs에서 EmDash를 Astro 통합으로 구성합니다:
import { defineConfig } from "astro/config";
import emdash, { local, s3 } from "emdash/astro";
import { sqlite, libsql } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
plugins: [],
}),
],
});
통합 옵션
database
필수. 데이터베이스 어댑터 구성입니다. 어댑터 중 하나를 선택하세요:
// SQLite (Node.js)
database: sqlite({ url: "file:./data.db" });
// PostgreSQL
database: postgres({ connectionString: process.env.DATABASE_URL });
// libSQL
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
// Cloudflare D1 (import from @emdash-cms/cloudflare)
database: d1({ binding: "DB" });
자세한 내용은 데이터베이스 옵션을 참조하세요.
migrations
선택. EmDash 내부 데이터베이스 마이그레이션의 런타임 처리 방식을 제어합니다. 이 옵션을 생략하면 기본값은 { runtime: "auto" }입니다.
migrations: {
runtime: "check", // "auto" | "check" | "manual"
dev: "auto", // optional development override
}
auto는 대기 중인 마이그레이션을 확인하고 적용하고, check는 실행 중인 빌드에 알려진 마이그레이션이 대기 중이면 503을 반환하며, manual은 런타임 마이그레이션 쿼리를 수행하지 않습니다. EMDASH_MIGRATIONS_MODE가 실제 런타임 모드를 덮어씁니다. check 또는 manual을 채택하기 전에 코어 데이터베이스 마이그레이션 관리를 참조하세요.
storage
선택. 미디어 스토리지 어댑터 구성입니다. 이 옵션을 생략하면 EmDash가 ./.emdash/uploads에 파일을 저장하고 /_emdash/api/media/file을 통해 제공합니다. 기본 로컬 디렉터리가 적합하지 않을 때 어댑터를 선택하세요:
// Local filesystem (development)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2 binding (Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // optional
});
// S3-compatible (any platform) — all fields from S3_* environment variables
storage: s3()
// Or with explicit values
storage: s3({
endpoint: "https://s3.amazonaws.com",
bucket: "my-bucket",
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: "us-east-1", // optional, default: "auto"
publicUrl: "https://cdn.example.com", // optional
});
자세한 내용은 스토리지 옵션을 참조하세요.
images
선택. EmDash가 저장된 미디어를 Astro 이미지 최적화와 통합할지 제어합니다. 기본값은 true입니다.
활성화하면 EmDash가 Astro 이미지 엔드포인트를 감싸 <Image>와 getImage()가 구성된 스토리지 어댑터에서 소스 바이트를 직접 읽을 수 있습니다. 원본 미디어 URL이 Cloudflare Access 뒤에 있어도 동작합니다. 다른 이미지 서비스가 미디어를 처리하거나 모든 이미지를 EmDash 엔드포인트 래퍼 없이 렌더링해야 할 때 images: false로 설정하세요.
emdash({
images: false,
});
mediaProviders
선택. 미디어 라이브러리에 미디어 서비스를 추가합니다. 스토리지 기반 로컬 제공자는 자동으로 계속 사용할 수 있으며, 이 배열의 각 디스크립터는 편집자가 미디어를 탐색하거나 업로드할 수 있는 추가 위치를 만듭니다.
다음 예는 Cloudflare Images와 Cloudflare Stream을 추가합니다:
import { cloudflareImages, cloudflareStream } from "@emdash-cms/cloudflare";
emdash({
mediaProviders: [cloudflareImages({}), cloudflareStream({})],
});
제공자 자격 증명은 런타임에 해석됩니다. 위의 빈 구성은 cloudflareImages(config) 및 cloudflareStream(config) 어댑터 절에 설명된 기본 Cloudflare 환경 변수를 사용합니다. 바인딩 및 렌더링 설정은 미디어 라이브러리: 미디어 제공자를 참조하세요.
objectCache
선택. 콘텐츠 및 구성 쿼리 결과를 키/값 저장소에 캐시하여 매 요청마다 데이터베이스를 조회하지 않고 읽기를 제공합니다. 생략하면 비활성화됩니다. 어댑터 중 하나를 선택하세요:
// Cloudflare KV (shared across all isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });
// In-memory (Node.js / development)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();
설정 및 옵션은 객체 캐시를 참조하세요.
middleware.outer
선택. 전체 EmDash 미들웨어 스택 바깥에 Astro 미들웨어 모듈을 등록합니다. 통합이 Astro order: "pre"로 등록하므로 src/middleware.ts에 정의된 미들웨어보다 먼저 실행됩니다. 캐시 히트 시 런타임 및 데이터베이스 초기화를 피해야 하는 요청 게이트나 전체 응답 캐시, 또는 EmDash 최종 HTML에 의존하는 응답 헤더에 사용하세요.
emdash({
middleware: {
outer: "./src/outer-middleware.ts",
},
});
실행 순서는 다음과 같습니다.
- 외부 미들웨어가
await next()까지 실행됩니다. - EmDash가 런타임과 데이터베이스를 초기화한 뒤 설정, 인증, 요청 컨텍스트 미들웨어를 실행합니다.
- Astro 라우트가 렌더링됩니다.
- EmDash가 시각 편집 HTML 및 보안/타이밍 헤더 등 응답 변경을 적용합니다.
next()가 최종 응답과 함께 외부 미들웨어로 돌아갑니다.
next()를 호출하기 전에는 일반 Astro 요청 및 플랫폼 실행 컨텍스트는 있지만 locals.emdash, locals.user, 데이터베이스, 요청 범위 EmDash 상태는 사용할 수 없습니다. 조기 Response는 EmDash를 완전히 건너뛰므로 필요한 보안 및 캐시 헤더를 포함해야 합니다. next()가 해결된 후에는 CSP nonce를 확정하거나, 전체 본문을 캐시하거나, Content-Length를 설정해도 됩니다. 미들웨어가 본문을 변경하면 기존 Content-Length 헤더를 제거하거나 다시 계산하세요.
이 훅은 Node와 Cloudflare 모두에서 Astro 미들웨어 API를 사용합니다. 다음은 익명 HTML 응답만 캐시하고 EmDash 초기화 전에 히트를 반환하는 최소 Cloudflare Cache API 예입니다:
import { waitUntil } from "cloudflare:workers";
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async ({ request }, next) => {
if (request.method !== "GET" || request.headers.has("cookie")) {
return next();
}
const cacheKey = new Request(request.url, { method: "GET" });
const cached = await caches.default.match(cacheKey);
if (cached) return cached;
const response = await next();
const isHtml = response.headers.get("content-type")?.includes("text/html");
const isPrivate = response.headers.get("cache-control")?.includes("no-store");
if (response.ok && isHtml && !isPrivate) {
waitUntil(caches.default.put(cacheKey, response.clone()));
}
return response;
});
Node에서는 Redis 같은 Node 호환 캐시로 동일한 미들웨어 형태를 사용하세요. 캐시 키와 우회 규칙에는 렌더링된 응답을 바꾸는 모든 요청 속성이 포함되어야 합니다.
playground
선택. 일회성 브라우저 기반 EmDash 플레이그라운드에 사용되는 미들웨어를 활성화합니다. 세션마다 쓰기 가능한 Durable Object 데이터베이스를 만들고, 구성된 시드를 적용한 뒤 일반 EmDash 미들웨어가 실행되기 전에 방문자를 익명 관리자로 로그인시킵니다.
import { playgroundDatabase } from "@emdash-cms/cloudflare";
emdash({
database: playgroundDatabase({ binding: "PLAYGROUND_DB" }),
playground: {
middlewareEntrypoint: "@emdash-cms/cloudflare/db/playground-middleware",
},
});
이 모드는 @emdash-cms/cloudflare와 Durable Object 바인딩이 필요합니다. 일반 설정 및 인증 미들웨어를 우회하므로 프로덕션 CMS가 아닌 임시 데모 사이트에만 사용하세요.
plugins
선택. Astro 사이트와 동일한 프로세스에서 실행되는 플러그인 배열입니다. 네이티브 플러그인은 여기에 둡니다. 전체 프로세스 접근을 신뢰하고 격리가 필요 없을 때 샌드박스 호환 플러그인도 여기에서 실행할 수 있습니다.
다음 예는 네이티브 플러그인을 등록합니다:
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
네이티브 플러그인은 서버 및 프레임워크 API를 직접 사용할 수 있으므로, 패키지가 샌드박스 호환 플러그인 진입점도 제공하지 않는 한 sandboxed로 옮길 수 없습니다. 작성 및 배포 차이는 플러그인 형식 선택을 참조하세요.
sandboxed
선택. EmDash가 선언한 플러그인 API를 사용하고 격리된 런타임에서 실행되는 샌드박스 호환 플러그인 배열입니다. 네이티브 플러그인은 여기에 두지 마세요. 네이티브 코드는 샌드박스가 제공하지 않는 프로세스 및 프레임워크 접근에 의존할 수 있습니다.
import thirdPartyPlugin from "third-party-emdash-plugin";
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxed: [thirdPartyPlugin()],
sandboxRunner: sandbox(),
});
사용 가능한 샌드박스 러너가 구성되지 않으면 샌드박스 플러그인은 건너뜁니다. Cloudflare 및 Node.js 러너 설정은 플러그인 샌드박스를 참조하세요.
sandboxRunner
선택. 격리된 플러그인 런타임을 시작하는 팩토리의 모듈 지정자입니다. sandboxed의 플러그인과 마켓플레이스 또는 레지스트리 플러그인에 필요합니다.
Cloudflare Workers에서는 sandbox() 어댑터를 사용합니다:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
Node.js 배포는 플러그인 샌드박스: Node.js에 문서화된 workerd 러너 모듈을 사용합니다.
sandbox
선택. 구성된 샌드박스 러너가 플러그인을 격리할지 제어합니다. sandboxRunner가 구성되면 샌드박싱이 활성화됩니다. 문제가 플러그인인지 샌드박스 런타임인지 진단할 때만 sandbox: false로 설정하세요:
emdash({
sandboxRunner: sandbox(),
sandbox: false,
});
false이면 sandboxed에 선언되고 마켓플레이스에서 설치된 플러그인이 격리나 리소스 제한 없이 메인 서버 프로세스에서 실행됩니다. 진단 후 샌드박싱을 복원하세요.
registry
선택. 플러그인 레지스트리 집계기 및 정책을 구성합니다. 명시적 값이 없으면 sandboxRunner가 구성되고 sandbox가 false가 아닐 때 EmDash는 https://registry.emdashcms.com을 사용합니다.
registry: false로 레지스트리 검색 및 레지스트리 설치 플러그인을 비활성화하면서, sandboxed에 선언된 플러그인과 레거시 Marketplace 플러그인용 샌드박스 러너는 유지할 수 있습니다:
emdash({
sandboxRunner: sandbox(),
registry: false,
});
레지스트리 서비스 URL을 문자열로 전달하거나, 사이트에 중재 소스나 릴리스 연령 정책이 필요할 때 객체를 사용합니다. 다음 예는 객체 형태를 사용합니다:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
registry: {
aggregatorUrl: "https://registry.emdashcms.com",
acceptLabelers: "did:web:labels.emdashcms.com",
policy: {
minimumReleaseAge: "48h",
minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
},
},
});
| 옵션 | 타입 | 설명 |
|---|---|---|
aggregatorUrl | string | 레지스트리 서비스의 기본 URL. 프로덕션에서는 HTTPS를 사용하세요. |
acceptLabelers | string | 요청에서 허용하는 중재 서비스의 선택적 쉼표 구분 분산 식별자(DID). DID는 안정적인 Atmosphere 계정 식별자입니다. 이 설정은 레지스트리 서비스 정책을 덮어쓸 수 없습니다. |
policy.minimumReleaseAge | string | number | 이 연령보다 새로운 릴리스를 보류합니다. 기간 문자열("48h", "7d") 또는 초. |
policy.minimumReleaseAgeExclude | string[] | 보류에서 제외되는 게시자 DID 또는 <did>/<plugin-slug> 쌍. |
릴리스 연령 정책은 레지스트리가 보존된 릴리스가 하나이고 패키지를 지속적으로 관찰했음을 확인할 때만 패키지의 첫 릴리스를 면제합니다. 백필된 패키지, 삭제된 이전 릴리스, 또는 이력 증거 부재는 보류를 유지합니다. 명시적 게시자 및 패키지 면제는 이력과 관계없이 적용됩니다.
설치 워크플로 및 신뢰 모델은 플러그인 레지스트리를 참조하세요.
marketplace
사용 중단. 레거시 Marketplace에서 설치된 플러그인 업데이트에 사용되는 기본 URL. Marketplace 탐색 및 새 설치는 관리자에 표시되지 않습니다. 이 옵션이 구성된 동안 기존 Marketplace 플러그인은 계속 업데이트 및 제거할 수 있습니다.
emdash({
marketplace: "https://marketplace.emdashcms.com",
sandboxRunner: sandbox(),
});
프로덕션 URL은 HTTPS를 사용해야 합니다. HTTP는 개발 중 localhost와 127.0.0.1에만 허용됩니다. 모든 Marketplace 플러그인을 교체하거나 제거할 때까지 이 옵션을 유지한 뒤 제거하세요. 전체 절차는 Marketplace에서 마이그레이션을 따르세요.
fonts
선택. 관리자 UI 글꼴 구성.
기본적으로 EmDash는 Astro Font API를 통해 Noto Sans를 로드합니다. 글꼴은 빌드 시 Google에서 다운로드되어 자체 호스팅되므로 런타임 CDN 요청이 없습니다. 기본 글꼴은 라틴, 키릴, 그리스, 데바나가리, 베트남 문자 체계를 포함합니다.
추가 문자 체계 지원을 위해 스크립트 이름을 전달하세요. 다음 예는 아랍어와 일본어를 추가합니다:
emdash({
fonts: {
scripts: ["arabic", "japanese"],
},
})
사용 가능한 스크립트는 arabic, armenian, bengali, chinese-simplified, chinese-traditional, chinese-hongkong, devanagari, ethiopic, farsi, georgian, gujarati, gurmukhi, hebrew, japanese, kannada, khmer, korean, lao, malayalam, myanmar, oriya, sinhala, tamil, telugu, thai, tibetan입니다.
각 스크립트는 Google Fonts의 해당 Noto Sans 변형에 매핑됩니다(예: "arabic"은 Noto Sans Arabic 로드). 모든 글꼴 면은 단일 font-family 이름을 공유하고 unicode-range를 사용해 브라우저가 페이지 문자에 필요한 파일만 다운로드합니다.
글꼴 주입을 완전히 비활성화하고 시스템 글꼴을 사용하려면 false로 설정하세요:
emdash({
fonts: false,
})
관리자 CSS는 --font-emdash CSS 변수를 사용합니다. 위 글꼴 구성으로 자동 설정됩니다.
auth
선택. 인증 어댑터입니다. EmDash 기본 로그인은 패스키이며, auth를 설정하면 외부 제공자로 대체됩니다. Cloudflare Access 어댑터 access()는 @emdash-cms/cloudflare에서 제공합니다:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
Admins: 50,
Editors: 40,
},
}),
});
access() 옵션:
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
teamDomain | string | 필수 | Cloudflare Access 팀 도메인 |
audience | string | — | Application Audience(AUD) 태그. Workers에서는 audienceEnvVar를 선호하세요. |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | 런타임에 audience 태그를 읽을 환경 변수 |
autoProvision | boolean | true | 첫 로그인 시 EmDash 사용자 생성 |
defaultRole | number | 30 | roleMapping에 매칭되지 않은 사용자의 역할 수준(사용자 역할 참조) |
syncRoles | boolean | false | 프로비저닝 시가 아니라 매 로그인마다 roleMapping 재적용 |
roleMapping | object | — | IdP 그룹 이름을 EmDash 역할 수준에 매핑. 첫 매칭이 적용됨 |
authProviders
선택. 플러그형 로그인 제공자 배열(최상위, auth와 함께). 각 항목은 아래처럼 제공자 팩토리를 호출한 결과입니다:
import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";
emdash({
authProviders: [github(), google(), atproto()],
});
내장 제공자:
github()—EMDASH_OAUTH_GITHUB_CLIENT_ID/EMDASH_OAUTH_GITHUB_CLIENT_SECRET(또는 접두사 없는 대체)를 읽습니다.google()—EMDASH_OAUTH_GOOGLE_CLIENT_ID/EMDASH_OAUTH_GOOGLE_CLIENT_SECRET를 읽습니다.atproto()— Atmosphere 계정 로그인(Bluesky 및 더 넓은 AT Protocol 네트워크). 환경 변수 불필요.{ allowedDIDs, allowedHandles, defaultRole }허용. Atmosphere 로그인 가이드 참조.
서드파티 패키지는 동일한 AuthProviderDescriptor 형태로 자체 제공자를 등록할 수 있습니다 — 로그인 제공자 참조.
mcp
선택. /_emdash/api/mcp에서 Model Context Protocol(MCP) 엔드포인트를 활성화합니다. 엔드포인트는 기본적으로 활성화되며 bearer 토큰이 필요하므로, 활성화해도 익명 접근은 허용되지 않습니다.
사이트가 MCP 엔드포인트를 노출하면 안 될 때 옵션을 false로 설정하세요:
emdash({
mcp: false,
});
토큰 생성 및 클라이언트 구성은 MCP 서버 참조를 참조하세요.
siteUrl
사이트의 공개 브라우저 대면 origin(스킴 + 호스트 + 선택적 포트, 경로 없음). 프로덕션 설정을 실행하기 전에 설정하세요. 구성된 origin 없이 설정을 완료할 수 있는 것은 루프백 개발 호스트뿐입니다.
TLS 종료 리버스 프록시 뒤에서는 Astro.url이 공개 주소(https://cms.example.com) 대신 내부 주소(http://localhost:4321)를 반환합니다. 이는 패스키, CSRF origin 매칭, OAuth 리디렉션, 로그인 리디렉션, MCP 검색, 스냅샷 내보내기, 사이트맵, robots.txt, JSON-LD 구조화 데이터를 깨뜨립니다. siteUrl을 설정하면 이를 한 번에 수정합니다.
통합은 로드 시 이 값을 검증합니다. http: 또는 https: 프로토콜의 유효한 URL이어야 하며 origin으로 정규화됩니다(경로는 제거).
다음 예는 공개 origin을 설정합니다:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
});
구성에 siteUrl이 없으면 EmDash는 순서대로 환경 변수 EMDASH_SITE_URL, 그다음 SITE_URL을 확인합니다. 공개 URL이 런타임에 설정되는 컨테이너 배포에 유용합니다.
두 소스 모두 없으면 비루프백 호스트에서 설정은 SITE_URL_REQUIRED로 실패합니다. 이는 첫 미인증 설정 요청이 이후 인증 이메일에 사용되는 origin을 선택하는 것을 방지합니다.
Cloudflare Workers에서는 env var 대체가 process.env를 읽습니다. nodejs_compat와 함께 Cloudflare는 2025-04-01 이후 호환 날짜에서 기본적으로 process.env를 채웁니다. 더 이른 날짜에 고정된 프로젝트는 nodejs_compat_populate_process_env도 추가해야 합니다.
// wrangler.jsonc
{
"compatibility_date": "2026-02-24",
"compatibility_flags": ["nodejs_compat"],
"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}
allowedOrigins
선택. 둘 이상의 호스트명으로 접근 가능한 배포에서 패스키 검증이 허용하는 추가 브라우저 origin.
siteUrl은 단일 canonical origin을 정의합니다. 동일 EmDash 배포가 등록 가능한 상위 도메인을 공유하는 여러 호스트명(예: https://example.com과 https://preview.example.com)으로 접근 가능할 때, WebAuthn이 동일 rpId 하위 도메인에서 패스키를 허용해도 origin이 siteUrl과 정확히 일치하지 않는 assertion은 패스키 검증에서 거부됩니다.
astro.config.mjs의 allowedOrigins 또는 EMDASH_ALLOWED_ORIGINS env var로 추가 허용 origin을 선언합니다. canonical siteUrl은 rpId의 출처로 유지되며, 여기 나열된 항목은 검증 시 허용됩니다. 두 소스는 런타임에 병합되므로 구성은 안정 origin(버전 관리, 코드 리뷰)을 선언하고 env는 환경별 추가(예: 임시 PR 미리보기)를 더할 수 있습니다.
다음 예는 구성에서 origin을 하나 더 선언합니다:
emdash({
siteUrl: "https://example.com",
allowedOrigins: ["https://preview.example.com"],
})
동일한 값은 환경 변수에서도 올 수 있습니다:
EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
유효성 검사
EmDash는 브라우저가 절대 따르지 않을 죽은 구성을 방지하기 위해 이를 검증합니다:
- 각 항목은 후행 점이 없고 호스트명에 빈 레이블이 없는 파싱 가능한
http:또는https:URL이어야 합니다. allowedOrigins가 비어 있지 않으면siteUrl이 설정되어야 하며(어느 소스든) IP 리터럴이거나 후행 점 호스트명이면 안 됩니다.- 각 origin은
siteUrl과 동일한 호스트명이거나 그 하위 도메인이어야 합니다.(WebAuthn은rpId가 모든 origin의 등록 가능 접미사여야 합니다.)
검증이 실패하면 EmDash config error in EMDASH_ALLOWED_ORIGINS: "https://other-site.com" is not a subdomain of siteUrl "https://example.com". Allowed origins must be the same hostname as siteUrl or a subdomain of it. 같은 소스가 표시된 오류가 나타납니다.
오류가 표면화되는 위치는 값이 선언된 곳에 따라 다릅니다:
astro.config.mjs에서config.allowedOrigins와config.siteUrl이 모두 올 때 Astro 시작 시 — 코드 오타는 빌드 실패.EMDASH_ALLOWED_ORIGINS또는EMDASH_SITE_URL에서 값이 올 때 첫 패스키 검증 시 — env 불일치는 첫 verify 시도에서 500으로 표면화.
리버스 프록시 설정
Astro는 공개 호스트가 허용될 때만 **X-Forwarded-***를 반영합니다. 사용자가 접속하는 호스트명(및 스킴)에 security.allowedDomains를 구성하세요. **astro dev**에서는 Vite가 프록시 Host 헤더를 받도록 일치하는 **vite.server.allowedHosts**를 추가하세요.
먼저 allowedDomains(및 전달 헤더) 수정을 선호하고, 재구성된 URL이 여전히 브라우저 origin과 다를 때 **siteUrl**을 사용하세요(TLS가 앞에서 종료되고 업스트림 요청이 **http://**로 남는 경우가 일반적).
앞에 TLS가 있으면 dev 서버를 루프백에 바인딩(astro dev --host 127.0.0.1)하는 것만으로 충분한 경우가 많습니다. 프록시는 로컬로 연결하고 **siteUrl**은 공개 HTTPS origin과 일치합니다.
프록시가 클라이언트 IP 헤더를 쓰면 trustedProxyHeaders를 설정해 EmDash rate limit이 공유 “unknown” 키 아래 모든 요청을 묶지 않고 실제 클라이언트 IP를 사용하게 하세요.
다음 구성은 리버스 프록시 배포를 위해 allowedDomains, vite.server.allowedHosts, siteUrl을 함께 설정합니다:
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
security: {
allowedDomains: [
{ hostname: "cms.example.com", protocol: "https" },
{ hostname: "cms.example.com", protocol: "http" },
],
},
vite: {
server: {
allowedHosts: ["cms.example.com"],
},
},
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
}),
],
});
trustedProxyHeaders
선택. 제어하는 리버스 프록시 뒤에서 실행할 때 클라이언트 IP 해석에 신뢰할 헤더. 인증 rate limit(매직 링크, 가입, 패스키, OAuth 디바이스 플로) 및 공개 댓글 엔드포인트에서 사용됩니다.
Cloudflare에서는 요청에 붙은 cf 객체가 자동으로 사용됩니다 — 보통 이를 설정할 필요가 없습니다. nginx, Caddy, Traefik, Fly, Railway 등 뒤의 자체 호스팅 배포에서는 프록시가 쓰는 헤더로 설정해 rate limit이 모든 요청을 “unknown”으로 처리하지 않고 실제 클라이언트 IP로 버킷팅하게 하세요.
다음 예는 nginx, Caddy 또는 Traefik이 설정하는 x-real-ip 헤더를 신뢰합니다:
emdash({
database: sqlite({ url: "file:./data.db" }),
trustedProxyHeaders: ["x-real-ip"],
});
헤더는 순서대로 시도됩니다. *-forwarded-for와 일치하는 값은 쉼표 구분 목록으로 파싱되며 첫 항목이 사용됩니다. 다음 예는 Fly.io 헤더를 선호하고 x-forwarded-for로 대체합니다:
emdash({
trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});
구성에 없으면 EmDash는 EMDASH_TRUSTED_PROXY_HEADERS env var(쉼표 구분)를 읽습니다. 구성의 명시적 빈 배열이 env var를 덮어씁니다.
maxUploadSize
선택. 허용되는 최대 미디어 파일 업로드 크기(바이트). 직접 multipart 업로드와 signed-URL 업로드 모두에 적용됩니다. 기본값 52_428_800(50 MB). 다음 예는 한도를 100 MB로 올립니다:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
| 값 | 설명 |
|---|---|
number(바이트) | 양의 유한 정수여야 함 |
| 생략 | 기본 50 MB |
구성된 한도를 초과하는 업로드는 직접 업로드 경로에서 413 Payload Too Large, signed-URL 경로에서 400 Validation Error로 거부됩니다.
admin
선택. 관리자 인터페이스의 EmDash 브랜딩을 대체합니다. 이 값은 공개 사이트의 제목, 로고, favicon은 변경하지 않습니다.
emdash({
admin: {
logo: "/images/agency-logo.webp",
siteName: "Agency CMS",
favicon: "/favicon.ico",
},
});
| 옵션 | 타입 | 설명 |
|---|---|---|
logo | string | 로그인 페이지 및 사이드바용 로고 URL 또는 경로 |
siteName | string | 사이드바 및 브라우저 제목에 표시되는 이름 |
favicon | string | 관리자 페이지 favicon URL 또는 경로 |
toolbar
선택. 편집자 도구 모음(공개 페이지의 떠 있는 pill) 전달 방식을 제어합니다. 기본값 "server".
| 값 | 동작 |
|---|---|
"server"(기본) | 인증된 편집자용으로 렌더링된 모든 HTML 응답에 서버 측에서 도구 모음이 주입됩니다. |
"client" | 공개 HTML은 모든 방문자에게 동일합니다. 작은 부트스트랩 스크립트가 관리자에 로그인한 브라우저에 “Edit” pill을 표시합니다. 클릭하면 세션을 검증하고 _edit 쿼리 매개변수로 페이지를 다시 로드하며, 전체 도구 모음과 함께 항상 새로 렌더링됩니다(캐시 안 됨). |
false | 도구 모음 또는 부트스트랩 스크립트를 렌더링하지 않습니다. |
emdash({
toolbar: "client",
})
공개 HTML이 공유 캐시(Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …)를 통해 제공될 때 "client"를 사용하세요. 서버 측 주입에서는 익명 방문자가 먼저 캐시를 채우면 공개 사이트를 보는 편집자가 도구 모음 없는 캐시된 익명 변형을 받아 도구 모음이 캐시 상태에 따라 나타났다 사라집니다. 클라이언트 모드에서는 공유 가능 HTML에 세션별 내용이 주입되지 않아 캐시가 완전히 유효하고 도구 모음이 안정적입니다.
"client" 모드 참고:
- 로그아웃 방문자가 공유
?_editURL을 열면 canonical URL로 리디렉션되어 매개변수가 초안을 유출하거나 페이지 내용으로 추가 캐시 항목을 채우지 못합니다. - “로그인됨” 신호는 관리자가 설정하는 비밀 아닌
localStorage플래그입니다. pill은 편집 보기 진입 전 실제 세션을 검증합니다. - 부트스트랩은 작은 인라인
<script>입니다. 사이트가'unsafe-inline'없이 엄격한Content-Security-Policy를 보내면 해시를 추가하세요 — 서버 주입 도구 모음에도 동일합니다. - EmDash는 세션별 내용을 주입하지 않지만, 자체 템플릿이
Astro.locals.user에 분기하면(예: 로그인 사용자용 “Admin” nav 링크) 그 차이는 HTML에 남아 캐시를 여전히 분할합니다.
모든 모드에서 도구 모음은 × 버튼으로 브라우저에서 닫을 수 있습니다(브라우저별, 다음에 편집자가 관리자를 열 때까지). 미리보기 및 편집 모드 응답은 항상 Cache-Control: private, no-store로 서버 측 렌더링됩니다.
experimental
선택. 마이너 릴리스에서 동작 또는 wire format이 바뀌거나 제거될 수 있는 옵트인 기능. 각 필드는 독립적으로 활성화됩니다.
experimental.registry
사용 중단. 최상위 registry 옵션을 사용하세요. 최상위 옵션이 생략되면 기존 experimental.registry 구성은 계속 동작합니다. 둘 다 있으면 최상위 값이 우선합니다.
다음 변경은 기존 레지스트리 URL을 최상위로 옮깁니다:
emdash({
experimental: {
registry: "https://registry.example.com",
},
registry: "https://registry.example.com",
});
데이터베이스 어댑터
emdash/db에서 어댑터를 가져옵니다:
import { sqlite, libsql, postgres } from "emdash/db";
sqlite(config)
Node.js 내장 데이터베이스 드라이버를 사용하는 SQLite 데이터베이스. 다음 예는 로컬 파일에 연결합니다:
| 옵션 | 타입 | 설명 |
|---|---|---|
url | string | file: 접두사가 있는 파일 경로 |
sqlite({ url: "file:./data.db" });
libsql(config)
libSQL 데이터베이스. 다음 예는 원격 libSQL 데이터베이스에 연결합니다:
| 옵션 | 타입 | 설명 |
|---|---|---|
url | string | 데이터베이스 URL |
authToken | string | 런타임 auth 토큰(로컬 파일은 선택) |
migrationAuthTokenEnv | string | 마이그레이션 토큰 변수 이름(기본 TURSO_AUTH_TOKEN) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
연결 풀링이 있는 PostgreSQL 데이터베이스.
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionString | string | PostgreSQL 연결 URL |
host | string | 데이터베이스 호스트 |
port | number | 데이터베이스 포트 |
database | string | 데이터베이스 이름 |
user | string | 데이터베이스 사용자 |
password | string | 데이터베이스 비밀번호 |
ssl | boolean | SSL 활성화 |
pool.min | number | 최소 풀 크기(기본: 0) |
pool.max | number | 최대 풀 크기(기본: 10) |
pool.connectionTimeoutMillis | number | 최대 연결 대기(pg 기본: 0, 타임아웃 없음) |
pool.idleTimeoutMillis | number | 유휴 클라이언트 수명(pg 기본: 10,000 ms) |
migrationConnectionStringEnv | string | 마이그레이션 connection-string 변수 이름(기본 DATABASE_URL) |
다음 예는 connection string으로 연결합니다:
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Cloudflare D1 데이터베이스. @emdash-cms/cloudflare에서 가져옵니다.
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
binding | string | — | wrangler.jsonc의 D1 바인딩 이름 |
session | string | "disabled" | 읽기 복제 모드: "disabled", "auto", "primary-first" |
bookmarkCookie | string | "__em_d1_bookmark" | 세션 bookmark용 쿠키 이름 |
coalesce | boolean | false | 동일 이벤트 루프 턴에서 동시 읽기 배치. "disabled"가 아닌 session 모드 필요 |
다음 예는 기본 바인딩과 읽기 복제본이 활성화된 구성을 보여줍니다:
// Basic
d1({ binding: "DB" });
// With read replicas
d1({ binding: "DB", session: "auto" });
session이 "auto" 또는 "primary-first"이면 EmDash는 D1 Sessions API로 읽기 쿼리를 근처 복제본으로 라우팅합니다. 인증 사용자는 bookmark 기반 read-your-writes 일관성을 받습니다. 자세한 내용은 데이터베이스 옵션 — 읽기 복제본을 참조하세요.
hyperdrive(config?)
Cloudflare Hyperdrive 바인딩을 통한 PostgreSQL. @emdash-cms/cloudflare에서 이 어댑터를 가져옵니다.
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
binding | string | "HYPERDRIVE" | 쿼리 캐싱이 비활성화된 기본 Hyperdrive 바인딩 |
cachedBinding | string | — | 익명 공개 읽기용 선택적 두 번째 캐싱 활성 바인딩 |
preferUncachedAfterWriteMs | number | 60_000 | cachedBinding 설정 시 콘텐츠 쓰기 후 공개 읽기가 primary를 사용하는 시간(ms) |
migrationConnectionStringEnv | string | primary 바인딩에서 파생 | emdash migrate에 사용되는 직접 PostgreSQL URL이 있는 환경 변수 |
max | number | 5 | 하나의 Worker isolate에서 Hyperdrive로의 최대 연결 수 |
다음 예는 인증 요청과 쓰기는 uncached 바인딩으로, 익명 공개 읽기는 cached 바인딩을 사용할 수 있게 라우팅합니다:
hyperdrive({
binding: "HYPERDRIVE",
cachedBinding: "HYPERDRIVE_CACHED",
preferUncachedAfterWriteMs: 60_000,
});
두 바인딩은 동일한 데이터베이스를 가리켜야 합니다. pg 8.16.3 이상을 설치하고 nodejs_compat 호환 플래그를 활성화하며 배포 마이그레이션용 직접 데이터베이스 URL을 구성하세요. 전체 Worker 및 마이그레이션 설정은 데이터베이스 옵션: Hyperdrive를 참조하세요.
durableObjects(config)
CMS를 SQLite 기반 Durable Object 하나에 저장합니다. @emdash-cms/cloudflare에서 이 어댑터를 가져옵니다.
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
binding | string | 필수 | EmDashDB 클래스용 Durable Object namespace 바인딩 |
name | string | "emdash" | 싱글톤 객체 이름. 하나의 바인딩 뒤 여러 DB를 격리할 때만 변경 |
session | string | "disabled" | "auto"는 익명 읽기를 복제본으로, 쓰기를 primary로 라우팅 |
bookmarkCookie | string | "__em_do_bookmark" | "auto" 모드에서 read-your-writes 일관성에 사용하는 쿠키 |
durableObjects({ binding: "DB_DO", session: "auto" });
복제본 라우팅에는 experimental 및 replica_routing 호환 플래그와 wrangler.jsonc의 Durable Object 클래스 및 마이그레이션 항목이 필요합니다.
previewDatabase(config)
미리보기 세션마다 Durable Object에 격리된 스냅샷 데이터베이스를 하나 만듭니다. 유일한 옵션은 필수 binding 이름입니다:
previewDatabase({ binding: "PREVIEW_DB" });
이 어댑터는 미리보기 인프라용이며 프로덕션 사이트의 primary 데이터베이스가 아닙니다.
playgroundDatabase(config)
플레이그라운드 세션마다 Durable Object에 쓰기 가능한 시드 데이터베이스를 하나 만듭니다. playground 통합 옵션과 함께 사용하세요:
playgroundDatabase({ binding: "PLAYGROUND_DB" });
필수 binding은 playground Durable Object namespace를 식별합니다. 일회성 데모 사이트에만 이 어댑터를 사용하세요.
스토리지 어댑터
local과 s3는 emdash/astro에서, r2 어댑터는 @emdash-cms/cloudflare에서 가져옵니다:
import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
local(config)
로컬 파일 시스템 스토리지. 다음 예는 로컬 디렉터리에서 업로드를 제공합니다:
| 옵션 | 타입 | 설명 |
|---|---|---|
directory | string | 디렉터리 경로 |
baseUrl | string | 파일 제공용 기본 URL |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Cloudflare R2 바인딩. 다음 예는 공개 URL이 있는 R2 바인딩을 사용합니다:
| 옵션 | 타입 | 설명 |
|---|---|---|
binding | string | R2 바인딩 이름 |
publicUrl | string | 선택적 공개 URL |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
S3 호환 스토리지. 모든 구성 필드는 선택입니다. s3({...})에서 생략된 필드는 Node 프로세스 시작 시 일치하는 S3_* 환경 변수에서 해석됩니다. 명시적 값이 항상 우선합니다.
구성과 환경 값 병합 후 endpoint와 bucket은 필수입니다. 자격 증명 중 하나라도 설정되면 accessKeyId와 secretAccessKey 모두 필수입니다. 누락 시 MISSING_S3_CONFIG 오류 코드로 시작이 실패합니다.
전제 조건: 프로젝트에 @aws-sdk/client-s3와 @aws-sdk/s3-request-presigner를 설치하세요. EmDash 코어는 AWS SDK를 번들하지 않습니다. 자세한 내용은 스토리지 옵션: S3 호환 스토리지를 참조하세요.
| 옵션 | 타입 | 설명 |
|---|---|---|
endpoint | string | S3 엔드포인트 URL(S3_ENDPOINT) |
bucket | string | 버킷 이름(S3_BUCKET) |
accessKeyId | string | 액세스 키(S3_ACCESS_KEY_ID) |
secretAccessKey | string | 시크릿 키(S3_SECRET_ACCESS_KEY) |
region | string | 리전, 기본 "auto"(S3_REGION) |
publicUrl | string | 선택적 CDN URL(S3_PUBLIC_URL) |
다음 예는 환경에서 모든 필드를 해석하거나, 구성과 환경을 혼합하거나, 모든 필드를 명시합니다:
// All fields from S3_* environment variables (Node container deployments)
s3()
// Mix: CDN from config, rest from environment
s3({ publicUrl: "https://cdn.example.com" })
// All explicit
s3({
endpoint: "https://xxx.r2.cloudflarestorage.com",
bucket: "media",
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
publicUrl: "https://cdn.example.com",
})
런타임 환경 변수 해석은 Node 전용 기능입니다. Cloudflare Workers에서는 시크릿과 변수가 process.env가 아니라 fetch 핸들러의 env 매개변수로 노출되므로 S3_* 환경 변수는 읽히지 않습니다. Workers 배포는 r2(config) 어댑터를 사용하거나 s3({...})에 명시적 값을 전달해야 합니다. 스토리지 옵션을 참조하세요.
객체 캐시 어댑터
다음 중 하나를 objectCache 옵션에 전달합니다.
kvCache(config)
모든 isolate에서 공유되는 Cloudflare KV 백엔드. @emdash-cms/cloudflare에서 가져옵니다.
kvCache({
binding: "CACHE", // KV binding name (required)
defaultTtl: 3600, // entry TTL in seconds (optional, KV minimum 60)
revalidate: 1000, // cross-isolate staleness window in ms (optional)
timeout: 2000, // per-op timeout in ms before a miss (optional, 0 disables)
keyPrefix: "em", // cache key prefix (optional)
})
memoryCache(config?)
Node.js 및 개발용 인프로세스 백엔드. emdash/astro에서 가져옵니다.
memoryCache({
defaultTtl: 3600, // entry TTL in seconds (optional)
revalidate: 1000, // staleness window in ms (optional)
maxEntries: 1000, // max cached keys before eviction (optional)
keyPrefix: "em", // cache key prefix (optional)
})
설정 및 동작은 객체 캐시를 참조하세요.
인증 및 샌드박스 어댑터
이 어댑터는 auth 및 sandboxRunner 통합 옵션에 사용할 값을 반환합니다.
access(config)
내장 패스키 로그인을 Cloudflare Access 인증으로 대체합니다. @emdash-cms/cloudflare에서 가져와 결과를 auth에 전달합니다:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audienceEnvVar: "CF_ACCESS_AUDIENCE",
}),
});
teamDomain은 필수입니다. 어댑터는 audience 또는 audienceEnvVar가 이름하는 변수에서 application audience를 읽을 수 있으며, autoProvision, defaultRole, syncRoles, roleMapping도 허용합니다. auth 옵션에 기본값 및 역할 동작이 문서화되어 있습니다.
sandbox()
플러그인 샌드박스 러너로 Cloudflare Worker Loader를 선택합니다. @emdash-cms/cloudflare에서 가져와 반환 값을 sandboxRunner에 전달합니다:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
사이트에는 Worker Loader 바인딩과 플러그인 브리지 진입점도 필요합니다. 해당 배포 설정은 플러그인 샌드박스: Cloudflare Workers를 참조하세요.
미디어 제공자 어댑터
미디어 제공자 디스크립터를 mediaProviders에 전달합니다. 내장 Cloudflare 제공자는 모두 @emdash-cms/cloudflare에서 가져옵니다.
아래 각 *EnvVar 옵션은 환경 변수 이름입니다. 제공자는 다음 순서로 읽습니다: 해당 이름의 Cloudflare Workers 바인딩, 그다음 Node 어댑터의 process.env. 일치하는 직접 옵션(accountId, accountHash, apiToken)이 항상 둘 다보다 우선합니다.
cloudflareImages(config)
이미지 자산 탐색, 업로드, 삭제, 전달을 위해 Cloudflare Images를 추가합니다.
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
accountId | string | CF_ACCOUNT_ID에서 | Cloudflare 계정 ID |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | accountId 생략 시 사용하는 변수 |
accountHash | string | CF_IMAGES_ACCOUNT_HASH에서 | 전달 URL에 사용되는 계정 해시 |
accountHashEnvVar | string | "CF_IMAGES_ACCOUNT_HASH" | accountHash 생략 시 사용하는 변수 |
apiToken | string | CF_IMAGES_TOKEN에서 | Cloudflare Images 읽기/편집 권한 토큰 |
apiTokenEnvVar | string | "CF_IMAGES_TOKEN" | apiToken 생략 시 사용하는 변수 |
deliveryDomain | string | imagedelivery.net | 사용자 지정 이미지 전달 호스트명 |
defaultVariant | string | "public" | 표시에 사용하는 이미지 variant |
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];
cloudflareStream(config)
동영상 자산 탐색, 검색, 업로드, 삭제, 재생을 위해 Cloudflare Stream을 추가합니다.
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
accountId | string | CF_ACCOUNT_ID에서 | Cloudflare 계정 ID |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | accountId 생략 시 사용하는 변수 |
apiToken | string | CF_STREAM_TOKEN에서 | Cloudflare Stream 읽기/편집 권한 토큰 |
apiTokenEnvVar | string | "CF_STREAM_TOKEN" | apiToken 생략 시 사용하는 변수 |
customerSubdomain | string | Cloudflare 기본값 | 사용자 지정 Stream 전달 호스트명 |
controls | boolean | true | 플레이어 컨트롤 표시 |
autoplay | boolean | false | 자동 재생 시작 |
loop | boolean | false | 반복 재생 |
muted | boolean | false, autoplay 시 true | 음소거 재생 |
mediaProviders: [cloudflareStream({ controls: true })];
필요한 바인딩 및 렌더링 컴포넌트는 미디어 라이브러리: 미디어 제공자를 참조하세요.
Astro 캐시 어댑터
cloudflareCache(config?)
레거시 어댑터는 Workers Cache API에 응답을 저장하고 Cloudflare REST API로 캐시 태그를 purge하는 Astro cache.provider를 반환합니다:
import { cloudflareCache } from "@emdash-cms/cloudflare";
export default defineConfig({
cache: {
provider: cloudflareCache(),
},
});
cacheName(기본 "emdash")과 bookmarkCookie(기본 "__em_d1_bookmark"), 태그별 purge 요청용 zoneId 또는 zoneIdEnvVar 및 apiToken 또는 apiTokenEnvVar를 허용합니다. 기본 변수 이름은 CF_ZONE_ID와 CF_CACHE_PURGE_TOKEN입니다.
라이브 컬렉션
src/live.config.ts에서 EmDash 로더를 구성합니다:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};
로더 옵션
emdashLoader() 함수는 인수를 받지 않습니다:
emdashLoader();
환경 변수
EmDash는 다음 환경 변수를 따릅니다:
| 변수 | 설명 |
|---|---|
EMDASH_SITE_URL | 공개 브라우저 대면 origin(SITE_URL로 대체) |
EMDASH_ALLOWED_ORIGINS | 패스키 검증이 허용하는 추가 origin의 쉼표 구분 목록(다중 서브도메인 배포). |
EMDASH_DATABASE_URL | 데이터베이스 URL 재정의 |
EMDASH_ENCRYPTION_KEY | 저장 시 플러그인 시크릿 암호화 키. 운영자 제공 — DB에 저장되지 않음. |
EMDASH_PREVIEW_SECRET | 미리보기 HMAC 시크릿 선택적 재정의. 미설정 시 사이트별 안정 값이 생성되어 DB에 저장됨. |
EMDASH_IP_SALT | 댓글 작성자 IP 해시 salt 선택적 재정의. 미설정 시 사이트별 안정 값이 생성되어 DB에 저장됨. |
EMDASH_AUTH_SECRET | 레거시. 설정 시 IP salt 소스로 사용. 기존 설치는 업그레이드 후에도 안정적인 댓글 작성자 IP 해시를 위해 유지해야 함. |
EMDASH_TURNSTILE_SECRET_KEY | Cloudflare Turnstile 시크릿 키(TURNSTILE_SECRET_KEY로 대체). 설정 시 댓글 제출에 유효한 Turnstile 토큰 필요 — <CommentForm>의 turnstileSiteKey prop과 함께 사용. |
EMDASH_URL | 스키마 동기화용 원격 EmDash URL |
다음 명령으로 암호화 키를 생성합니다:
npx emdash secrets generate
package.json 구성
템플릿과 사이트는 package.json의 emdash 키 아래에 선택적 메타데이터를 선언할 수 있습니다:
{
"emdash": {
"label": "My Blog Template",
"schema": ".emdash/schema.sql",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev"
}
}
| 옵션 | 설명 |
|---|---|
label | 표시용 템플릿 이름 |
schema | emdash init이 읽는 선택적 SQL 스키마 |
seed | 시드 JSON 파일 경로 |
url | 사용 중단된 emdash dev --types 흐름에 사용되는 원격 URL |
TypeScript 구성
로컬 개발 중 Astro 통합은 프로젝트 루트에 emdash-env.d.ts를 생성하고 스키마 변경 후 갱신합니다. 이 파일은 emdash 모듈을 확장하므로 표준 getEmDashCollection() 및 getEmDashEntry() import가 경로 별칭 없이 로컬 컬렉션 필드를 추론합니다.
별도 emdash types 명령은 실행 중인 로컬 또는 원격 인스턴스에서 스키마를 가져와 기본적으로 .emdash/types.ts에 씁니다. 애플리케이션 코드가 해당 독립 출력을 직접 import할 때만 별칭을 추가하세요:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
다음 명령으로 독립 원격 스키마 타입을 생성합니다:
npx emdash types