SQLite
sqlite({ url: "file:./data.db" })를 사용한 로컬 개발
EmDash는 Astro와 깊게 통합되어 완전한 CMS 경험을 제공합니다. 이 페이지는 주요 아키텍처 결정과 각 구성 요소가 어떻게 조화를 이루는지 설명합니다.
┌──────────────────────────────────────────────────────────────────┐│ 내 Astro 사이트 ││ ││ ┌────────────────────────────────────────────────────────────┐ ││ │ EmDash 통합 │ ││ │ │ ││ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │ ││ │ │ 콘텐츠 API │ │ 관리자 │ │ 플러그인 │ │ ││ │ │ │ │ 패널 │ │ │ │ ││ │ └──────────────┘ └──────────────┘ └───────────────┘ │ ││ │ │ ││ │ ┌──────────────────────────────────────────────────────┐ │ ││ │ │ 데이터 계층 │ │ ││ │ │ 데이터베이스 (D1/SQLite) + 스토리지 (R2/S3) │ │ ││ │ └──────────────────────────────────────────────────────┘ │ ││ └────────────────────────────────────────────────────────────┘ ││ ││ ┌────────────────────────────────────────────────────────────┐ ││ │ Astro 프레임워크 │ ││ │ Live Collections · Middleware · Sessions │ ││ └────────────────────────────────────────────────────────────┘ │└──────────────────────────────────────────────────────────────────┘EmDash는 Astro 통합으로 실행됩니다. 관리자 패널과 REST API를 위한 라우트를 주입하고, 라이브 컬렉션을 위한 콘텐츠 로더를 제공하며, 데이터베이스 마이그레이션과 스토리지 연결을 관리합니다.
코드에서 스키마를 정의하는 전통적인 CMS와 달리, EmDash는 스키마 정의를 데이터베이스 자체에 저장합니다. 두 개의 시스템 테이블이 콘텐츠 구조를 추적합니다:
_emdash_collections — 컬렉션 메타데이터 (슬러그, 레이블, 기능)_emdash_fields — 각 컬렉션에 대한 필드 정의관리자 UI를 통해 ‘products’ 컬렉션을 title과 price 필드와 함께 생성하면, EmDash는 다음을 수행합니다:
_emdash_collections 및 _emdash_fields에 레코드를 삽입합니다.ec_products를 생성하기 위해 ALTER TABLE을 실행합니다.이 설계는 다음을 가능하게 합니다:
각 컬렉션은 ec_ 접두사가 붙은 자체 SQLite 테이블을 가집니다:
-- Created when "posts" collection is addedCREATE TABLE ec_posts ( -- System columns (always present) id TEXT PRIMARY KEY, slug TEXT UNIQUE, status TEXT DEFAULT 'draft', -- draft, published, scheduled author_id TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), published_at TEXT, deleted_at TEXT, -- Soft delete version INTEGER DEFAULT 1, -- Optimistic locking
-- 콘텐츠 컬럼 (필드 정의에서 가져옴) title TEXT NOT NULL, content JSON, -- Portable Text excerpt TEXT);JSON을 사용하는 단일 콘텐츠 테이블 대신 컬렉션별 테이블을 사용하는 이유는 무엇인가요?
EmDash는 Astro 6의 라이브 컬렉션을 사용하여 런타임에 콘텐츠를 제공합니다. 콘텐츠 변경 사항은 정적 재빌드 없이 즉시 사용 가능합니다.
emdashLoader()는 Astro의 LiveLoader 인터페이스를 구현합니다:
import { defineLiveCollection } from "astro:content";import { emdashLoader } from "emdash/runtime";
export const collections = { _emdash: defineLiveCollection({ loader: emdashLoader() }),};제공된 래퍼 함수를 사용하여 콘텐츠를 쿼리하세요:
import { getEmDashCollection, getEmDashEntry } from "emdash";
// 모든 게시된 게시물 가져오기const { entries: posts } = await getEmDashCollection("posts");
// 초안 가져오기const { entries: drafts } = await getEmDashCollection("posts", { status: "draft",});
// 슬러그로 단일 항목 가져오기const { entry: post } = await getEmDashEntry("posts", "my-post-slug");EmDash 통합은 Astro의 injectRoute API를 사용하여 관리자 및 API 라우트를 추가합니다:
| 경로 패턴 | 목적 |
|---|---|
/_emdash/admin/[...path] | 관리자 패널 SPA |
/_emdash/api/manifest | 관리자 매니페스트 (컬렉션, 플러그인) |
/_emdash/api/content/[collection] | 콘텐츠 항목에 대한 CRUD |
/_emdash/api/media/* | 미디어 라이브러리 작업 |
/_emdash/api/schema/* | 스키마 관리 |
/_emdash/api/settings | 사이트 설정 |
/_emdash/api/menus/* | 탐색 메뉴 |
/_emdash/api/taxonomies/* | 카테고리, 태그, 사용자 정의 분류법 |
라우트는 emdash 패키지에서 주입됩니다—프로젝트에 아무것도 복사되지 않습니다.
EmDash는 지원되는 모든 데이터베이스에서 타입 안전한 SQL 쿼리를 위해 Kysely를 사용합니다:
SQLite
sqlite({ url: "file:./data.db" })를 사용한 로컬 개발
D1
d1({ binding: "DB" })를 사용한 Cloudflare의 서버리스 SQL
libSQL
libsql({ url: "...", authToken: "..." })를 사용한 원격 SQLite
데이터베이스 구성은 astro.config.mjs에서 통합에 전달됩니다:
import { defineConfig } from "astro/config";import emdash from "emdash/astro";import { sqlite } from "emdash/db";import { local } from "emdash/storage";
export default defineConfig({ integrations: [ emdash({ database: sqlite({ url: "file:./data.db" }), storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file", }), }), ],});미디어 파일은 데이터베이스와 별도로 저장됩니다. EmDash는 다음을 지원합니다:
업로드는 Workers 본문 크기 제한을 우회하기 위해 클라이언트에서 스토리지로 직접 업로드하는 서명된 URL을 사용합니다.
플러그인은 WordPress에서 영감을 받은 훅 시스템을 통해 EmDash를 확장합니다:
content:beforeSave, content:afterSave, content:beforeDelete, content:afterDeletemedia:beforeUpload, media:afterUpload플러그인은 두 가지 모드로 실행될 수 있습니다:
import { seoPlugin } from "@emdash-cms/plugin-seo";
emdash({ plugins: [seoPlugin({ maxTitleLength: 60 })],});일반적인 콘텐츠 요청은 다음 경로를 따릅니다:
getEmDashCollection()이 Astro의 getLiveCollection()을 호출 3. 로더 실행 —
emdashLoader가 Kysely를 통해 적절한 ec_* 테이블을 쿼리 4. 데이터 반환 — 항목이
id, slug, data와 함께 Astro의 항목 형식으로 매핑됨 5. 페이지 렌더링 — 컴포넌트가
콘텐츠를 수신하고 HTML을 렌더링관리자 요청의 경우:
beforeCreate, afterUpdate 등 4. 데이터베이스
업데이트 — Kysely가 SQL 실행 5. 응답 반환 — 관리자 SPA에 JSON 응답 반환EmDash는 빌드 시 가상 모듈을 생성하여 런타임을 구성합니다:
| 모듈 | 목적 |
|---|---|
virtual:emdash/config | 데이터베이스 및 스토리지 구성 |
virtual:emdash/dialect | 데이터베이스 방언 팩토리 |
virtual:emdash/plugin-admins | 플러그인 관리자 UI를 위한 정적 임포트 |
이 접근 방식은 번들러가 플러그인 코드를 올바르게 해석하고 트리 쉐이킹할 수 있도록 보장합니다.