콘텐츠로 이동

블로그 만들기

이 가이드는 콘텐츠 유형 정의부터 카테고리와 태그가 포함된 게시물 표시까지, EmDash로 블로그를 만드는 과정을 안내합니다.

  • 설정 및 실행 중인 EmDash 사이트 (시작하기 참조)
  • Astro 컴포넌트에 대한 기본적인 이해

EmDash는 설정 중에 기본 “posts” 컬렉션을 생성합니다. 사용자 정의가 필요한 경우 관리자 대시보드나 API를 사용하세요.

기본 posts 컬렉션에는 다음이 포함됩니다:

  • title - 게시물 제목
  • slug - URL 친화적인 식별자
  • content - 리치 텍스트 본문
  • excerpt - 짧은 설명
  • featured_image - 헤더 이미지 (선택 사항)
  • status - 초안, 게시됨, 예약됨
  • publishedAt - 게시 날짜 (시스템 필드)
  1. 관리자 대시보드 /_emdash/admin 열기

  2. 사이드바에서 Posts 클릭

    EmDash posts list showing titles, status, and dates
  3. New Post 클릭

    EmDash post editor with title, content, and publish options
  4. 제목을 입력하고 리치 텍스트 편집기를 사용하여 콘텐츠 작성

  5. 사이드바 패널에서 카테고리와 태그 추가

  6. 상태를 Published로 설정

  7. Save 클릭

게시물이 이제 라이브 상태입니다. 재빌드가 필요하지 않습니다.

게시된 모든 게시물을 표시하는 페이지 생성:

src/pages/blog/index.astro
---
import { getEmDashCollection } from "emdash";
import Base from "../../../layouts/Base.astro";
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// 게시 날짜 기준으로 정렬, 최신순
const sortedPosts = posts.sort(
(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0)
);
---
<Base title="Blog">
<h1>Blog</h1>
<ul>
{sortedPosts.map((post) => (
<li>
<a href={`/blog/${post.data.slug}`}>
<h2>{post.data.title}</h2>
<p>{post.data.excerpt}</p>
<time datetime={post.data.publishedAt?.toISOString()}>
{post.data.publishedAt?.toLocaleDateString()}
</time>
</a>
</li>
))}
</ul>
</Base>

개별 게시물을 위한 동적 경로 생성:

astro title="src/pages/blog/[slug].astro"
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
import Base from "../../../layouts/Base.astro";
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
return posts.map((post) => ({
params: { slug: post.data.slug },
}));
}
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
if (!post) {
return Astro.redirect("/404");
}
---
<Base title={post.data.title}>
<article>
{post.data.featured_image && (
<img src={post.data.featured_image} alt="" />
)}
<h1>{post.data.title}</h1>
<time datetime={post.data.publishedAt?.toISOString()}>
{post.data.publishedAt?.toLocaleDateString()}
</time>
<PortableText value={post.data.content} />
</article>
</Base>

EmDash에는 내장된 카테고리 및 태그 분류 체계가 포함되어 있습니다. 용어 생성 및 관리에 대한 자세한 내용은 분류 체계를 참조하세요.

astro title="src/pages/category/[slug].astro"
---
import { getEmDashCollection, getTerm, getTaxonomyTerms } from "emdash";
import Base from "../../../layouts/Base.astro";
export async function getStaticPaths() {
const categories = await getTaxonomyTerms("category");
// 계층적 카테고리 평면화
const flatten = (terms) => terms.flatMap((t) => [t, ...flatten(t.children)]);
return flatten(categories).map((cat) => ({
params: { slug: cat.slug },
props: { category: cat },
}));
}
const { category } = Astro.props;
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
where: { category: category.slug },
});
---
<Base title={category.label}>
<h1>{category.label}</h1>
{category.description && <p>{category.description}</p>
}
<ul>
{posts.map((post) => (
<li>
<a href={`/blog/${post.data.slug}`}>{post.data.title}</a>
</li>
))}
</ul>
</Base>

개별 게시물에 카테고리 표시:

astro title="src/components/PostMeta.astro"
---
import { getEntryTerms } from "emdash";
interface Props {
postId: string;
}
const { postId } = Astro.props;
const categories = await getEntryTerms("posts", postId, "category");
const tags = await getEntryTerms("posts", postId, "tag");
---
<div class="post-meta">
{categories.length > 0 && (
<div class="categories">
<span>Categories:</span>
{categories.map((cat) => (
<a href={`/category/${cat.slug}`}>{cat.label}</a>
))}
</div>
)}
{tags.length > 0 && (
<div class="tags">
<span>Tags:</span>
{tags.map((tag) => (
<a href={`/tag/${tag.slug}`}>{tag.label}</a>
))}
</div>
)}
</div>

많은 게시물이 있는 블로그의 경우 페이지네이션 추가:

astro title="src/pages/blog/page/[page].astro"
---
import { getEmDashCollection } from "emdash";
import Base from "../../../../layouts/Base.astro";
const POSTS_PER_PAGE = 10;
export async function getStaticPaths() {
const { entries: allPosts } = await getEmDashCollection("posts", {
status: "published",
});
const totalPages = Math.ceil(allPosts.length / POSTS_PER_PAGE);
return Array.from({ length: totalPages }, (_, i) => ({
params: { page: String(i + 1) },
props: { currentPage: i + 1, totalPages },
}));
}
const { currentPage, totalPages } = Astro.props;
const { entries: allPosts } = await getEmDashCollection("posts", {
status: "published",
});
const sortedPosts = allPosts.sort(
(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0)
);
const start = (currentPage - 1) * POSTS_PER_PAGE;
const posts = sortedPosts.slice(start, start + POSTS_PER_PAGE);
---
<Base title={`Blog - Page ${currentPage}`}>
<h1>Blog</h1>
<ul>
{posts.map((post) => (
<li>
<a href={`/blog/${post.data.slug}`}>{post.data.title}</a>
</li>
))}
</ul>
<nav>
{currentPage > 1 && (
<a href={`/blog/page/${currentPage - 1}`}>Previous</a>
)}
<span>Page {currentPage} of {totalPages}</span>
{currentPage < totalPages && (
<a href={`/blog/page/${currentPage + 1}`}>Next</a>
)}
</nav>
</Base>

블로그를 위한 RSS 피드 생성:

ts title="src/pages/rss.xml.ts"
import rss from "@astrojs/rss";
import { getEmDashCollection } from "emdash";
export async function GET(context) {
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
return rss({
title: "내 블로그",
description: "EmDash로 만든 블로그",
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.publishedAt,
description: post.data.excerpt,
link: `/blog/${post.data.slug}`,
})),
});
}

아직 설치하지 않았다면 RSS 패키지 설치:

Terminal window
npm install @astrojs/rss