콘텐츠로 이동

시드 파일 형식

시드 파일은 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": "내 사이트",
"tagline": "현대적인 CMS",
"postsPerPage": 10,
"dateFormat": "MMMM d, yyyy"
}
}

설정은 site: 접두사와 함께 options 테이블에 적용됩니다. 설정 마법사는 사용자가 title과 tagline을 재정의할 수 있게 합니다.

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

{
"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"
}
]
}
]
}
속성타입필수 여부설명
slugstring예URL-안전 식별자 (소문자, 밑줄)
labelstring예복수 표시 이름
labelSingularstring아니요단수 표시 이름
descriptionstring아니요관리자 UI 설명
iconstring아니요Lucide 아이콘 이름
supportsarray아니요기능: "drafts", "revisions"
fieldsarray예필드 정의
속성타입필수 여부설명
slugstring예열 이름 (소문자, 밑줄)
labelstring예표시 이름
typestring예필드 타입
requiredboolean아니요유효성 검사: 필드에 값이 있어야 함
uniqueboolean아니요유효성 검사: 값이 고유해야 함
defaultValueany아니요새 항목에 대한 기본값
validationobject아니요추가 유효성 검사 규칙
widgetstring아니요관리자 UI 위젯 재정의
optionsobject아니요위젯별 구성
타입설명저장 형식
string짧은 텍스트TEXT
text긴 텍스트 (텍스트 영역)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": "카테고리",
"labelSingular": "카테고리",
"hierarchical": true,
"collections": ["posts"],
"terms": [
{ "slug": "news", "label": "뉴스" },
{ "slug": "tutorials", "label": "튜토리얼" },
{
"slug": "advanced",
"label": "고급 튜토리얼",
"parent": "tutorials"
}
]
},
{
"name": "tag",
"label": "Tags",
"labelSingular": "Tag",
"hierarchical": false,
"collections": ["posts"]
}
]
}
속성타입필수 여부설명
namestring예고유 식별자
labelstring예복수 표시 이름
labelSingularstring아니요단수 표시 이름
hierarchicalboolean예중첩된 용어 허용 (카테고리) 또는 평면적 (태그)
collectionsarray예이 분류 체계가 적용되는 컬렉션
termsarray아니요사전 정의된 용어
속성타입필수 여부설명
slugstring예URL-안전 식별자
labelstring예표시 이름
descriptionstring아니요용어 설명
parentstring아니요상위 용어 슬러그 (계층적 전용)

관리자에서 편집 가능한 네비게이션 메뉴:

{
"menus": [
{
"name": "primary",
"label": "기본 내비게이션",
"items": [
{ "type": "custom", "label": "홈", "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 타입용)
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
}
]
}
속성타입필수 여부설명
idstring예content[].bylines에서 사용하는 시드-로컬 ID
slugstring예URL-안전 바이라인 슬러그
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": "메인 사이드바",
"description": "블로그 게시물과 페이지에 표시됩니다",
"widgets": [
{
"type": "component",
"title": "최근 게시물",
"componentId": "core:recent-posts",
"props": { "count": 5 }
},
{
"type": "menu",
"title": "빠른 링크",
"menuName": "footer"
},
{
"type": "content",
"title": "소개",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "우리 사이트에 오신 것을 환영합니다!" }]
}
]
}
]
}
]
}
타입설명필수 필드
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": "우리 사이트에 오신 것을 환영합니다" }]
},
{
"_type": "block",
"children": [
{ "_type": "span", "text": "Your compelling tagline goes here." }
]
}
]
}
]
}
속성타입필수 여부설명
slugstring예URL-안전 식별자
titlestring예섹션 선택기에 표시되는 표시 이름
descriptionstring아니요이 섹션을 사용할 시기를 설명
keywordsarray아니요섹션을 찾기 위한 검색어
contentarray예Portable 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": "안녕하세요",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "환영합니다!" }]
}
],
"excerpt": "첫 게시물입니다."
},
"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
slugstring예URL 슬러그
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 "../../themes/.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

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

엔티티동작
Collection슬러그가 존재하면 건너뛰기
Field컬렉션 + 슬러그가 존재하면 건너뛰기
Taxonomy definition이름이 존재하면 건너뛰기
Taxonomy term이름 + 슬러그가 존재하면 건너뛰기
Byline profile슬러그가 존재하면 건너뛰기
Menu이름이 존재하면 건너뛰기
Menu items모두 교체 (메뉴가 재생성됨)
Redirect소스가 존재하면 건너뛰기
Widget area이름이 존재하면 건너뛰기
Widgets모두 교체 (영역이 재생성됨)
Section슬러그가 존재하면 건너뛰기
Settings업데이트 (설정은 변경되도록 의도됨)
Content컬렉션 내 슬러그가 존재하면 건너뛰기

시드 파일은 적용 전에 검증됩니다:

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임
  • 리디렉션 소스가 고유함
  • 컬렉션 내 중복 슬러그 없음
Terminal window
# Apply seed file
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