시드 파일 형식

이 페이지

시드 파일은 EmDash 사이트를 부트스트랩하는 JSON 문서입니다. 컬렉션, 필드, 택소노미, 메뉴, 리디렉션, 위젯 영역, 사이트 설정, 선택적 샘플 콘텐츠를 정의합니다.

루트 구조

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

메타

시드에 대한 선택적 메타데이터:

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

설정

사이트 전체 구성 값:

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

설정은 site: 접두사와 함께 options 테이블에 적용됩니다. 설정 마법사에서 사용자가 titletagline을 재정의할 수 있습니다.

컬렉션

컬렉션 정의는 데이터베이스에 콘텐츠 타입을 생성합니다:

{
	"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": [
		{
			"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": [
		{
			"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표시 텍스트 (page/post 참조의 경우 자동 생성)
urlstring사용자 정의 URL (custom 타입용)
refstring시드의 콘텐츠 ID (page/post 타입용)
collectionstring컬렉션 슬러그
targetstring새 창을 위한 "_blank"
titleAttrstringHTML title 속성
cssClassesstring사용자 정의 CSS 클래스
childrenarray중첩된 메뉴 항목

바이라인

바이라인 프로필은 소유권(author_id)과 별개입니다. 재사용 가능한 바이라인 아이덴티티를 한 번 정의하고 콘텐츠 항목에서 참조합니다.

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

리디렉션

마이그레이션 후 레거시 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아니오관리 필터링/검색을 위한 선택적 그룹 라벨

위젯 영역

구성 가능한 콘텐츠 영역:

{
	"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월별 아카이브

섹션

편집자가 /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" (시드 기본값) 또는 "import"

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

콘텐츠

컬렉션별로 정리된 샘플 콘텐츠:

{
	"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참조를 위한 시드 로컬 ID
slugstringURL 슬러그
statusstring아니오"published" 또는 "draft" (기본값: "published")
dataobject필드 값
bylinesarray아니오정렬된 바이라인 크레딧 (byline, 선택적 roleLabel)
taxonomiesobject아니오택소노미 이름별 용어 할당

콘텐츠 참조

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

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

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

미디어 참조

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 중 하나가 필수이며, 둘 다는 안 됩니다.

프로그래밍 방식 시드 적용

CLI 도구나 스크립트를 위한 시드 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);
}

// 시드 적용
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 }
// }

적용 옵션

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

멱등성

시딩은 여러 번 실행해도 안전합니다. 엔티티 타입별 충돌 동작:

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

유효성 검사

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

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 명령

# 시드 파일 적용
npx emdash seed .emdash/seed.json

# 샘플 콘텐츠 없이 적용
npx emdash seed .emdash/seed.json --no-content

# 유효성 검사만
npx emdash seed .emdash/seed.json --validate

# 현재 스키마를 시드로 내보내기
npx emdash export-seed > seed.json

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

다음 단계