blocks 필드는 순서가 있는 페이지 구성을 저장합니다. 각 항목에는 블록 유형, 유지되는 스키마 버전, 안정적인 키, 그리고 해당 버전에서 선언한 필드가 기록됩니다. 편집자는 콘텐츠 편집기에서 블록을 추가하고 순서를 바꿉니다. Astro 라우트는 각 블록 유형을 컴포넌트에 매핑합니다.
블록 유형 정의하기
블록 유형은 이를 사용하는 컬렉션 필드보다 먼저 정의하세요. 시드는 번호가 매겨진 모든 버전과 활성 currentVersion 포인터를 보존합니다.
다음 시드는 Hero와 Feature grid 블록을 정의한 다음, 이를 Pages의 layout 필드에서 사용할 수 있게 합니다.
{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"blockTypes": [
{
"slug": "hero",
"label": "Hero",
"category": "Layout",
"currentVersion": 1,
"versions": [
{
"version": 1,
"fields": [
{ "slug": "heading", "label": "Heading", "type": "string", "required": true },
{ "slug": "body", "label": "Body", "type": "portableText" },
{ "slug": "image", "label": "Image", "type": "image" },
{ "slug": "link_label", "label": "Link label", "type": "string" },
{ "slug": "link_url", "label": "Link URL", "type": "url" }
]
}
]
},
{
"slug": "feature_grid",
"label": "Feature grid",
"category": "Layout",
"currentVersion": 1,
"versions": [
{
"version": 1,
"fields": [
{ "slug": "heading", "label": "Heading", "type": "string" },
{
"slug": "items",
"label": "Items",
"type": "repeater",
"validation": {
"subFields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{ "slug": "description", "label": "Description", "type": "text" }
]
}
}
]
}
]
}
],
"collections": [
{
"slug": "pages",
"label": "Pages",
"fields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{
"slug": "layout",
"label": "Layout",
"type": "blocks",
"validation": {
"allowedTypes": ["hero", "feature_grid"],
"maxItems": 20
}
}
]
}
]
}
allowedTypes의 순서가 블록 선택기의 순서를 결정합니다. 나중에 허용된 유형을 제거하면 EmDash는 이를 서버가 관리하는 retiredTypes 목록으로 옮깁니다. 기존 블록은 계속 편집할 수 있지만, 편집자는 해당 유형을 추가하거나 복제할 수 없습니다.
Astro 컴포넌트 만들기
각 컴포넌트는 value, index, blockKey를 받습니다. 값에는 _type, _version, _key가 포함되므로, 블록 스키마가 발전할 때 컴포넌트는 유지되는 버전을 좁혀서 처리할 수 있습니다.
Hero 컴포넌트는 표시되는 모든 값을 저장된 블록에서 읽습니다.
---
import { sanitizeHref } from "emdash";
import { Image, PortableText, type BlockComponentProps } from "emdash/ui";
import type { PageLayoutBlock } from "../../../emdash-env";
type HeroBlock = Extract<PageLayoutBlock, { _type: "hero" }>;
type Props = BlockComponentProps<HeroBlock>;
const { value } = Astro.props;
---
<section class="hero">
<div>
<h1>{value.heading}</h1>
{value.body && <PortableText value={value.body} />}
{value.link_url && <a href={sanitizeHref(value.link_url)}>{value.link_label}</a>}
</div>
{value.image && <Image image={value.image} />}
</section>
허용된 유형마다 컴포넌트를 만드세요. 마크업과 스타일은 컴포넌트가 제어하고, 콘텐츠와 미디어는 블록 값이 제공합니다.
구성 렌더링하기
defineBlockComponents를 사용하면 생성된 필드 유니온의 모든 _type에 대해 컴포넌트를 하나씩 요구할 수 있습니다. 그 맵을 페이지 라우트의 <Blocks>에 전달하세요.
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { Blocks, defineBlockComponents } from "emdash/ui";
import type { PageLayoutBlock } from "../../../emdash-env";
import FeatureGrid from "../../components/blocks/FeatureGrid.astro";
import Hero from "../../components/blocks/Hero.astro";
const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.rewrite("/404");
const { entry: page, cacheHint } = await getEmDashEntry("pages", slug);
if (!page) return Astro.rewrite("/404");
Astro.cache.set(cacheHint);
const components = defineBlockComponents<PageLayoutBlock>({
hero: Hero,
feature_grid: FeatureGrid,
});
---
<Blocks value={page.data.layout} components={components} />
<Blocks>는 콘텐츠, 스키마, 미디어, 네트워크 쿼리를 전혀 수행하지 않습니다. 전달받은 배열을 저장된 순서대로 렌더링합니다. 블록 컴포넌트가 다른 데이터를 필요로 하면, 그 컴포넌트가 명시적으로 애플리케이션 쿼리를 실행할 수 있습니다.
누락된 컴포넌트 처리하기
개발 중에는 매핑되지 않은 유형이 눈에 보이는 플레이스홀더와 콘솔 경고를 생성합니다. 플레이스홀더에는 _type 이름이 표시되지만 저장된 블록 값은 출력되지 않습니다.
프로덕션에서는 매핑되지 않은 유형이 fallback 컴포넌트가 제공된 경우 해당 컴포넌트를 렌더링합니다. 제공되지 않으면 아무것도 출력하지 않습니다.
---
import MissingBlock from "../../components/blocks/MissingBlock.astro";
---
<Blocks value={page.data.layout} components={components} fallback={MissingBlock} />
프로덕션 콘텐츠에서 사용하는 블록 유형을 활성화하기 전에 렌더러 지원을 먼저 배포하세요.
블록 스키마 변경하기
호환되는 변경은 활성 버전을 수정합니다. 선택적 필드 추가, 기본값 추가, 유효성 검사 완화는 같은 버전 번호를 유지합니다. 저장된 블록은 다음에 기록될 때 기본값을 받습니다.
호환되지 않는 변경은 비활성 버전을 만듭니다. 필드 제거, 필드 유형 변경, 필수 필드 추가, 유효성 검사 강화는 호환되지 않는 변경입니다.
-
스키마 API 또는 MCP를 통해 호환되지 않는 버전을 만듭니다. 비활성 상태로 둡니다.
-
유지되는 버전과 새 버전을 모두 처리하도록 렌더러를 업데이트합니다. 렌더러를 배포합니다.
-
새 버전을 활성화합니다. 활성화 이후 새 블록은 이 버전을 사용합니다.
-
migrateBlocks: true로 저장된 블록을 명시적으로 마이그레이션합니다._version과 해당 버전에 고유한 필드를 변경하는 동안에도 각 블록의_key는 유지합니다.
이전 버전은 리비전, 초안, 미디어 추적, 저장된 콘텐츠를 위해 계속 사용할 수 있습니다. 블록 유형과 유지되는 버전에는 완전 삭제 작업이 없습니다.
지원되는 중첩 필드
블록 정의는 string, text, url, number, integer, boolean, datetime, select, multiSelect, portableText, image, file, repeater를 지원합니다.
블록 정의 안에서는 참조, JSON, 슬러그, 중첩 블록, 사용자 지정 위젯, 물리적 인덱스, 고유성, 하위 필드별 현지화를 지원하지 않습니다. blocks 필드 자체는 필수, 고유, 검색 가능, 인덱싱 대상으로 설정할 수 없으며, 사용자 지정 필드 위젯을 지정할 수도 없습니다.
정확한 필드 유효성 검사와 저장되는 값의 규칙은 blocks 필드 레퍼런스를 참조하세요. 시드 충돌과 내보내기 동작은 시드 파일을 참조하세요.