Seed 파일 형식

이 페이지

Seed 파일은 EmDash 사이트를 초기화하는 JSON 문서입니다. 컬렉션, 필드, 분류체계, 메뉴, 리디렉션, 위젯 영역, 사이트 설정 및 선택적 샘플 컨텐츠를 정의합니다.

루트 구조

Seed 파일은 다음과 같은 최상위 구조를 갖습니다:

{
	"$schema": "https://emdashcms.com/seed.schema.json",
	"version": "1",
	"meta": {},
	"settings": {},
	"collections": [],
	"taxonomies": [],
	"bylines": [],
	"menus": [],
	"redirects": [],
	"widgetAreas": [],
	"sections": [],
	"content": {}
}
필드타입필수설명
$schemastring아니오에디터 유효성 검사를 위한 JSON 스키마 URL
version"1"Seed 형식 버전
metaobject아니오Seed에 대한 메타데이터
settingsobject아니오사이트 설정
collectionsarray아니오컬렉션 정의
taxonomiesarray아니오분류체계 정의
bylinesarray아니오바이라인 프로필 정의
menusarray아니오네비게이션 메뉴
redirectsarray아니오리디렉션 규칙
widgetAreasarray아니오위젯 영역 정의
sectionsarray아니오재사용 가능한 컨텐츠 블록
contentobject아니오샘플 컨텐츠 항목

Meta

meta 객체는 seed에 대한 선택적 설명 메타데이터를 포함합니다:

{
	"meta": {
		"name": "Blog Starter",
		"description": "A simple blog with posts, pages, and categories",
		"author": "EmDash"
	}
}

Settings

settings 객체는 사이트 전체 구성 값을 포함합니다:

{
	"settings": {
		"title": "My Site",
		"tagline": "A modern CMS",
		"postsPerPage": 10,
		"dateFormat": "MMMM d, yyyy"
	}
}

설정은 site: 접두사와 함께 options 테이블에 적용됩니다. 설정 마법사는 seed 파일에서 titletagline을 미리 채우며(제공된 경우), 사용자가 초기 설정 중에 이를 재정의할 수 있도록 합니다.

Collections

각 컬렉션 정의는 데이터베이스에 컨텐츠 유형을 생성합니다:

{
	"collections": [
		{
			"slug": "posts",
			"label": "Posts",
			"labelSingular": "Post",
			"description": "Blog posts",
			"icon": "file-text",
			"supports": ["drafts", "revisions"],
			"fields": [
				{
					"slug": "title",
					"label": "Title",
					"type": "string",
					"required": true
				},
				{
					"slug": "content",
					"label": "Content",
					"type": "portableText"
				},
				{
					"slug": "featured_image",
					"label": "Featured Image",
					"type": "image"
				}
			]
		}
	]
}

컬렉션 속성

속성타입필수설명
slugstringURL 안전 식별자 (소문자, 밑줄)
labelstring복수형 표시 이름
labelSingularstring아니오단수형 표시 이름
descriptionstring아니오관리 UI 설명
iconstring아니오Lucide 아이콘 이름
supportsarray아니오기능: "drafts", "revisions"
fieldsarray필드 정의

필드 속성

속성타입필수설명
slugstring열 이름 (소문자, 밑줄)
labelstring표시 이름
typestring필드 타입
requiredboolean아니오유효성 검사: 필드는 값이 있어야 함
uniqueboolean아니오유효성 검사: 값은 고유해야 함
defaultValueany아니오새 항목에 대한 기본값
validationobject아니오추가 유효성 검사 규칙
widgetstring아니오관리 UI 위젯 재정의
optionsobject아니오위젯별 구성

필드 타입

타입설명저장 형식
string짧은 텍스트TEXT
text긴 텍스트 (textarea)TEXT
number숫자 값REAL
integer정수INTEGER
boolean참/거짓INTEGER
date날짜 값TEXT (ISO 8601)
datetime날짜 및 시간TEXT (ISO 8601)
email이메일 주소TEXT
urlURLTEXT
slugURL 안전 문자열TEXT
portableText리치 텍스트 컨텐츠JSON
image이미지 참조JSON
file파일 참조JSON
json임의의 JSONJSON
reference다른 항목에 대한 참조TEXT

Taxonomies

분류체계는 컨텐츠를 위한 분류 시스템입니다:

{
	"taxonomies": [
		{
			"name": "category",
			"label": "Categories",
			"labelSingular": "Category",
			"hierarchical": true,
			"collections": ["posts"],
			"terms": [
				{ "slug": "news", "label": "News" },
				{ "slug": "tutorials", "label": "Tutorials" },
				{
					"slug": "advanced",
					"label": "Advanced Tutorials",
					"parent": "tutorials"
				}
			]
		},
		{
			"name": "tag",
			"label": "Tags",
			"labelSingular": "Tag",
			"hierarchical": false,
			"collections": ["posts"]
		}
	]
}

분류체계 속성

속성타입필수설명
namestring고유 식별자
labelstring복수형 표시 이름
labelSingularstring아니오단수형 표시 이름
hierarchicalboolean중첩된 용어 허용 (카테고리) 또는 평면 (태그)
collectionsarray이 분류체계가 적용되는 컬렉션
termsarray아니오사전 정의된 용어

용어 속성

속성타입필수설명
slugstringURL 안전 식별자
labelstring표시 이름
descriptionstring아니오용어 설명
parentstring아니오상위 용어 슬러그 (계층적인 경우만)

menus 배열은 관리자에서 편집 가능한 네비게이션 메뉴를 정의합니다:

{
	"menus": [
		{
			"name": "primary",
			"label": "Primary Navigation",
			"items": [
				{ "type": "custom", "label": "Home", "url": "/" },
				{ "type": "page", "ref": "about" },
				{ "type": "custom", "label": "Blog", "url": "/posts" },
				{
					"type": "custom",
					"label": "External",
					"url": "https://example.com",
					"target": "_blank"
				}
			]
		}
	]
}

메뉴 항목 타입

타입설명필수 필드
custom사용자 정의 URLurl
page페이지 항목에 대한 링크ref
post게시물 항목에 대한 링크ref
taxonomy분류체계 아카이브에 대한 링크ref, collection
collection컬렉션 아카이브에 대한 링크collection

메뉴 항목 속성

속성타입설명
typestring항목 타입 (위 참조)
labelstring표시 텍스트 (페이지/게시물 참조의 경우 자동 생성)
urlstring사용자 정의 URL (custom 타입의 경우)
refstringSeed의 컨텐츠 ID (page/post 타입의 경우)
collectionstring컬렉션 슬러그
targetstring새 창의 경우 "_blank"
titleAttrstringHTML title 속성
cssClassesstring사용자 정의 CSS 클래스
childrenarray중첩된 메뉴 항목

Bylines

바이라인 프로필은 소유권(author_id)과 분리되어 있습니다. 재사용 가능한 바이라인 ID를 한 번 정의한 다음 컨텐츠 항목에서 참조합니다.

{
	"bylines": [
		{
			"id": "editorial",
			"slug": "emdash-editorial",
			"displayName": "EmDash Editorial"
		},
		{
			"id": "guest",
			"slug": "guest-contributor",
			"displayName": "Guest Contributor",
			"isGuest": true
		}
	]
}
속성타입필수설명
idstringcontent[].bylines에서 사용하는 Seed 로컬 ID
slugstringURL 안전 바이라인 슬러그
displayNamestring템플릿 및 API에 표시되는 이름
biostring아니오선택적 프로필 약력
websiteUrlstring아니오선택적 웹사이트 URL
isGuestboolean아니오바이라인을 게스트 프로필로 표시

Redirects

redirects 배열은 마이그레이션 후 레거시 URL을 보존하는 리디렉션 규칙을 정의합니다:

{
	"redirects": [
		{ "source": "/old-about", "destination": "/about" },
		{ "source": "/legacy-feed", "destination": "/rss.xml", "type": 308 },
		{
			"source": "/category/news",
			"destination": "/categories/news",
			"groupName": "migration"
		}
	]
}

리디렉션 속성

속성타입필수설명
sourcestring소스 경로 (/로 시작해야 함)
destinationstring대상 경로 (/로 시작해야 함)
typenumber아니오HTTP 상태: 301, 302, 307 또는 308
enabledboolean아니오리디렉션이 활성화되어 있는지 여부 (기본값: true)
groupNamestring아니오관리자 필터링/검색을 위한 선택적 그룹화 레이블

Widget Areas

widgetAreas 배열은 구성 가능한 컨텐츠 영역을 정의합니다:

{
	"widgetAreas": [
		{
			"name": "sidebar",
			"label": "Main Sidebar",
			"description": "Appears on blog posts and pages",
			"widgets": [
				{
					"type": "component",
					"title": "Recent Posts",
					"componentId": "core:recent-posts",
					"props": { "count": 5 }
				},
				{
					"type": "menu",
					"title": "Quick Links",
					"menuName": "footer"
				},
				{
					"type": "content",
					"title": "About",
					"content": [
						{
							"_type": "block",
							"style": "normal",
							"children": [{ "_type": "span", "text": "Welcome to our site!" }]
						}
					]
				}
			]
		}
	]
}

위젯 타입

타입설명필수 필드
content리치 텍스트 컨텐츠content (Portable Text)
menu메뉴 렌더링menuName
component등록된 컴포넌트componentId

내장 컴포넌트

컴포넌트 ID설명
core:recent-posts최근 게시물 목록
core:categories카테고리 목록
core:tags태그 클라우드
core:search검색 양식
core:archives월별 아카이브

Sections

섹션은 편집자가 /section 슬래시 명령을 통해 Portable Text 필드에 삽입하는 재사용 가능한 컨텐츠 블록입니다:

{
	"sections": [
		{
			"slug": "hero-centered",
			"title": "Centered Hero",
			"description": "Full-width hero with centered heading and CTA button",
			"keywords": ["hero", "banner", "header", "landing"],
			"content": [
				{
					"_type": "block",
					"style": "h1",
					"children": [{ "_type": "span", "text": "Welcome to Our Site" }]
				},
				{
					"_type": "block",
					"children": [
						{ "_type": "span", "text": "Your compelling tagline goes here." }
					]
				}
			]
		}
	]
}

섹션 속성

속성타입필수설명
slugstringURL 안전 식별자
titlestring섹션 선택기에 표시되는 표시 이름
descriptionstring아니오이 섹션을 사용할 시기 설명
keywordsarray아니오섹션을 찾기 위한 검색어
contentarrayPortable Text 블록
sourcestring아니오"theme" (seed의 기본값) 또는 "import"

Seed 파일의 섹션은 source: "theme"으로 표시되며 관리 UI에서 삭제할 수 없습니다. 편집자는 자신만의 섹션(source: "user")을 만들 수 있으며 컨텐츠를 편집할 때 모든 섹션 타입을 삽입할 수 있습니다.

Content

content 객체는 컬렉션별로 구성된 샘플 컨텐츠 항목을 포함합니다:

{
	"content": {
		"posts": [
			{
				"id": "hello-world",
				"slug": "hello-world",
				"status": "published",
				"bylines": [
					{ "byline": "editorial" },
					{ "byline": "guest", "roleLabel": "Guest essay" }
				],
				"data": {
					"title": "Hello World",
					"content": [
						{
							"_type": "block",
							"style": "normal",
							"children": [{ "_type": "span", "text": "Welcome!" }]
						}
					],
					"excerpt": "Your first post."
				},
				"taxonomies": {
					"category": ["news"],
					"tag": ["welcome", "first-post"]
				}
			}
		],
		"pages": [
			{
				"id": "about",
				"slug": "about",
				"status": "published",
				"data": {
					"title": "About Us",
					"content": [
						{
							"_type": "block",
							"style": "normal",
							"children": [{ "_type": "span", "text": "About page content." }]
						}
					]
				}
			}
		]
	}
}

컨텐츠 항목 속성

속성타입필수설명
idstring참조를 위한 Seed 로컬 ID
slugstringURL 슬러그
statusstring아니오"published" 또는 "draft" (기본값: "published")
dataobject필드 값
bylinesarray아니오순서가 지정된 바이라인 크레딧 (byline, 선택적 roleLabel)
taxonomiesobject아니오분류체계 이름별 용어 할당

Content References

$ref: 접두사를 사용하여 다른 컨텐츠 항목을 참조합니다:

{
	"data": {
		"related_posts": ["$ref:another-post", "$ref:third-post"]
	}
}

$ref: 접두사는 시드 중에 Seed ID를 데이터베이스 ID로 해석합니다.

Media References

URL에서 이미지 포함:

{
	"data": {
		"featured_image": {
			"$media": {
				"url": "https://images.unsplash.com/photo-xxx",
				"alt": "Description of the image",
				"filename": "hero.jpg",
				"caption": "Photo by Someone"
			}
		}
	}
}

.emdash/media/에서 로컬 이미지 포함:

{
	"data": {
		"featured_image": {
			"$media": {
				"file": "hero.jpg",
				"alt": "Description of the image"
			}
		}
	}
}

미디어 속성

속성타입필수설명
urlstring예*다운로드할 원격 URL
filestring예*.emdash/media/의 로컬 파일 이름
altstring아니오접근성을 위한 대체 텍스트
filenamestring아니오파일 이름 재정의
captionstring아니오미디어 캡션

*url 또는 file 중 하나가 필요하며, 둘 다는 아닙니다.

Applying Seeds Programmatically

CLI 도구 또는 스크립트에 seed API 사용:

import { applySeed, validateSeed } from "emdash/seed";
import seedData from "./.emdash/seed.json";

// 먼저 유효성 검사
const validation = validateSeed(seedData);
if (!validation.valid) {
	console.error(validation.errors);
	process.exit(1);
}

// Seed 적용
const result = await applySeed(db, seedData, {
	includeContent: true,
	onConflict: "skip",
	storage: myStorage,
	baseUrl: "http://localhost:4321",
});

console.log(result);
// {
//   collections: { created: 2, skipped: 0 },
//   fields: { created: 8, skipped: 0 },
//   taxonomies: { created: 2, terms: 5 },
//   bylines: { created: 2, skipped: 0 },
//   menus: { created: 1, items: 4 },
//   redirects: { created: 3, skipped: 0 },
//   widgetAreas: { created: 1, widgets: 3 },
//   settings: { applied: 3 },
//   content: { created: 3, skipped: 0 },
//   media: { created: 2, skipped: 0 }
// }

Apply 옵션

옵션타입기본값설명
includeContentbooleanfalse샘플 컨텐츠 항목 생성
onConflictstring"skip""skip", "update" 또는 "error"
mediaBasePathstring로컬 미디어 파일의 기본 경로
storageStorage미디어 업로드를 위한 스토리지 어댑터
baseUrlstring미디어 URL의 기본 URL

Idempotency

시드는 여러 번 안전하게 실행할 수 있습니다. 엔티티 타입별 충돌 동작:

엔티티동작
컬렉션슬러그가 존재하면 건너뛰기
필드collection + 슬러그가 존재하면 건너뛰기
분류체계 정의이름이 존재하면 건너뛰기
분류체계 용어이름 + 슬러그가 존재하면 건너뛰기
바이라인 프로필슬러그가 존재하면 건너뛰기
메뉴이름이 존재하면 건너뛰기
메뉴 항목모두 교체 (메뉴가 다시 생성됨)
리디렉션소스가 존재하면 건너뛰기
위젯 영역이름이 존재하면 건너뛰기
위젯모두 교체 (영역이 다시 생성됨)
섹션슬러그가 존재하면 건너뛰기
설정업데이트 (설정은 변경되도록 의도됨)
컨텐츠컬렉션에 슬러그가 존재하면 건너뛰기

Validation

Seed 파일은 적용 전에 유효성이 검사됩니다:

import { validateSeed } from "emdash/seed";

const { valid, errors, warnings } = validateSeed(seedData);

if (!valid) {
	errors.forEach((e) => console.error(e));
}

warnings.forEach((w) => console.warn(w));

유효성 검사는 다음을 확인합니다:

  • 필수 필드가 있음
  • 슬러그가 해당 타입에 유효함 (컬렉션 및 필드 슬러그는 소문자, 숫자, 밑줄 허용; 다른 슬러그는 하이픈도 허용)
  • 필드 타입이 유효함
  • 참조가 기존 컨텐츠를 가리킴
  • 계층적 용어 부모가 존재함
  • 리디렉션 경로가 안전한 로컬 URL임
  • 리디렉션 소스가 고유함
  • 컬렉션 내에 중복된 슬러그가 없음

CLI Commands

.emdash/seed.json, package.json#emdash.seed 또는 seed/seed.json의 seed 파일은 빌드에 인라인되며 데이터베이스가 비어 있을 때 첫 번째 요청에 적용됩니다. 기존 사이트의 스키마(및 선택적으로 해당 컨텐츠)를 seed 파일로 내보내려면:

# 새 프로젝트에서는 .emdash/가 아직 존재하지 않을 수 있으므로 `mkdir -p`
mkdir -p .emdash

# 현재 스키마를 seed 파일로 내보내기
npx emdash export-seed > .emdash/seed.json

# 컨텐츠와 함께 내보내기
npx emdash export-seed --with-content > .emdash/seed.json

Next Steps