Astro는 EmDash 사이트의 페이지, 레이아웃, 컴포넌트, 서버 렌더링을 제공합니다. 이 가이드는 현재 EmDash 템플릿이 사용하는 Astro 개념을 다룹니다. WordPress 테마와 PHP 템플릿을 이미 이해하고 있다고 가정합니다.
EmDash에만 해당하지 않는 프레임워크 기능은 Astro 문서를 참조하세요.
프로젝트 구조
Astro 사이트는 파일 종류마다 명시적인 디렉터리를 둡니다. 현재의 EmDash 템플릿은 다음과 같은 구조를 사용합니다.
| WordPress | Astro | 용도 |
|---|---|---|
index.php, single.php, page.php | src/pages/ | URL 라우트 |
template-parts/ | src/components/ | 재사용 가능한 마크업 |
header.php와 footer.php | src/layouts/ | 공유되는 페이지 셸 |
style.css | src/styles/ | 사이트 스타일 |
| 플러그인 및 데이터베이스 설정 | astro.config.mjs | 통합 및 서버 어댑터 |
| 테마 설정 데이터 | seed/seed.json | 컬렉션, 메뉴, 샘플 콘텐츠 |
블로그 템플릿은 공개 URL과 일치하는 라우트 디렉터리를 사용합니다.
src/
├── components/
│ └── PostCard.astro
├── layouts/
│ └── Base.astro
├── pages/
│ ├── index.astro
│ ├── pages/
│ │ └── [slug].astro
│ └── posts/
│ ├── index.astro
│ └── [slug].astro
└── live.config.ts
Astro 컴포넌트
.astro 컴포넌트는 서버 측 TypeScript와 HTML 템플릿을 결합한 것입니다. --- 펜스 사이의 코드는 서버에서 실행됩니다. 두 번째 펜스 아래의 마크업이 응답 HTML이 됩니다.
다음 컴포넌트는 프런트매터에서 props를 선언하고 템플릿에서 렌더링합니다.
---
interface Props {
title: string;
excerpt?: string;
href: string;
}
const { title, excerpt, href } = Astro.props;
---
<article>
<h2><a href={href}>{title}</a></h2>
{excerpt && <p>{excerpt}</p>}
</article>
Astro는 {value}로 렌더링되는 값을 이스케이프합니다. import, 데이터베이스 쿼리, 그 밖의 서버 작업은 프런트매터에 둡니다.
템플릿 표현식
Astro 템플릿은 PHP 템플릿이라면 <?php ?>로 전환했을 자리에서 중괄호를 사용합니다. EmDash 템플릿에서 가장 흔한 패턴은 값 출력, 조건, 배열 매핑입니다.
| 목적 | Astro 구문 |
|---|---|
| 값 출력 | {post.data.title} |
| 값이 있을 때 렌더링 | {post.data.excerpt && <p>{post.data.excerpt}</p>} |
| 두 결과 중 선택 | {posts.length === 0 ? <p>No posts yet.</p> : <PostList />} |
| 목록 렌더링 | {posts.map((post) => <PostCard title={post.data.title} excerpt={post.data.excerpt} href={"/posts/" + post.id} />)} |
표현식에는 프런트매터에서 준비한 변수, Astro.props의 값, 또는 EmDash 쿼리가 반환한 데이터를 사용할 수 있습니다. Astro는 기본적으로 문자열 값을 이스케이프합니다. 구조화된 리치 텍스트에는 HTML을 주입하는 대신 <PortableText /> 같은 렌더러를 사용하세요.
Props와 슬롯
props는 get_template_part()에 전달하는 $args와 비슷합니다. 각 입력을 명시적으로 만들고, TypeScript로 검사할 수 있습니다.
슬롯을 사용하면 부모가 컴포넌트에 마크업을 전달할 수 있습니다. 기본 슬롯은 페이지 콘텐츠에 유용하고, 이름 있는 슬롯은 추가 삽입 지점을 제공합니다.
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<article>
<h2>{title}</h2>
<slot />
<footer><slot name="footer" /></footer>
</article>
다음 페이지는 두 슬롯을 모두 채웁니다.
---
import Card from "../components/Card.astro";
---
<Card title="Latest post">
<p>The main card content.</p>
<a slot="footer" href="/posts/latest">Read the post</a>
</Card>
슬롯은 컴포넌트 호출 안에서만 유효합니다. 다른 곳에 등록된 콜백을 받을 수 있는 WordPress 액션처럼 동작하지 않습니다.
레이아웃
레이아웃은 WordPress 테마가 흔히 header.php와 footer.php로 나누는 공통 문서 구조를 담당합니다. 페이지는 레이아웃을 import하고 그 슬롯을 통해 콘텐츠를 전달합니다.
다음 레이아웃은 문서 셸을 제공합니다.
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{title}</title>
</head>
<body>
<header><a href="/">My site</a></header>
<main><slot /></main>
</body>
</html>
다음 페이지는 레이아웃의 제목과 메인 콘텐츠를 제공합니다.
---
import Base from "../layouts/Base.astro";
---
<Base title="Home">
<h1>Latest posts</h1>
</Base>
파일 기반 라우팅
src/pages/의 파일이 라우트를 정의합니다. 대괄호가 있는 파일 이름은 동적 세그먼트를 만듭니다.
| 파일 | URL |
|---|---|
src/pages/index.astro | / |
src/pages/posts/index.astro | /posts |
src/pages/posts/[slug].astro | /posts/hello-world |
src/pages/pages/[slug].astro | /pages/about |
src/pages/posts/[slug].astro 안에서 Astro.params.slug는 URL의 값을 담고 있습니다. 나머지 매개변수, 리디렉션 및 그 밖의 라우팅 기능은 Astro 라우팅을 읽어 보세요.
서버 렌더링
현재의 EmDash 템플릿은 astro.config.mjs에서 output: "server"를 사용합니다. 따라서 페이지는 요청마다 데이터베이스를 쿼리할 수 있고, 게시된 콘텐츠는 새로운 정적 빌드에 의존하지 않습니다.
사이트가 의도적으로 EmDash를 빌드 시점의 데이터 소스로 취급하는 경우가 아니라면, EmDash 테마 라우트에 getStaticPaths()를 추가하지 마세요. 제공되는 테마는 서버 렌더링됩니다.
프레임워크 수준의 동작은 Astro 온디맨드 렌더링을 읽어 보세요.
EmDash 콘텐츠 쿼리
EmDash는 getEmDashCollection()과 getEmDashEntry()로 Astro 라이브 콘텐츠 컬렉션을 감쌉니다. 컬렉션 결과에는 entries 배열이 들어 있습니다. 단일 항목 결과에는 entry가 들어 있으며, 일치하는 게시된 항목이 없으면 null입니다.
다음 아카이브는 현재 블로그 템플릿과 같은 정렬과 식별자를 사용합니다.
---
import { getEmDashCollection } from "emdash";
import Base from "../../layouts/Base.astro";
const { entries: posts, error } = await getEmDashCollection("posts", {
orderBy: { published_at: "desc" },
});
if (error) {
return new Response("Could not load posts", { status: 500 });
}
---
<Base title="Posts">
{posts.map((post) => (
<article>
<h2><a href={`/posts/${post.id}`}>{post.data.title}</a></h2>
{post.data.excerpt && <p>{post.data.excerpt}</p>}
</article>
))}
</Base>
post.id는 Astro가 노출하는 라우트 식별자이며 보통 항목의 슬러그입니다. post.data.id는 데이터베이스 식별자입니다. 분류 체계나 댓글 헬퍼처럼 API가 저장된 콘텐츠 ID를 요구할 때는 data.id를 사용하세요.
다음 동적 라우트는 URL의 슬러그로 글을 조회하고 그 Portable Text 필드를 렌더링합니다.
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
import Base from "../../layouts/Base.astro";
const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.rewrite("/404");
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) return new Response("Could not load the post", { status: 500 });
if (!post) return Astro.rewrite("/404");
---
<Base title={post.data.title}>
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
</Base>
Astro 계속 배우기
EmDash 템플릿은 컴포넌트 스타일과 작은 브라우저 스크립트도 사용하지만, 이것들은 EmDash 개념이 아니라 일반적인 Astro 기능입니다. 범위 지정 스타일과 전역 스타일은 스타일과 CSS를, 컴포넌트에 브라우저 측 동작이 필요할 때는 스크립트와 이벤트 처리를 읽어 보세요.