블로그 만들기

이 페이지

이 가이드는 콘텐츠 유형 정의부터 카테고리와 태그를 사용한 게시물 표시까지, EmDash로 블로그를 만드는 방법을 다룹니다.

사전 요구 사항

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

Posts Collection 정의

EmDash는 설정 시 기본 “posts” collection을 생성합니다. 관리자 대시보드 또는 API에서 커스터마이즈할 수 있습니다.

기본 posts collection에는 다음이 포함됩니다:

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

첫 번째 게시물 작성

  1. /_emdash/admin에서 관리자 대시보드 열기

  2. 사이드바에서 Posts 클릭

    제목, 상태, 날짜를 보여주는 EmDash 게시물 목록
  3. New Post 클릭

    제목, 콘텐츠, 게시 옵션이 있는 EmDash 게시물 편집기
  4. 제목을 입력하고 리치 텍스트 편집기를 사용하여 콘텐츠 작성

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

  6. 상태를 게시됨으로 설정

  7. 저장 클릭

게시물이 즉시 라이브 상태가 되어 바로 표시됩니다.

사이트에 게시물 표시하기

모든 게시물 나열

다음 페이지는 모든 게시된 게시물을 표시합니다:

---
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="블로그">
  <h1>블로그</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>

단일 게시물 표시

다음 동적 라우트는 개별 게시물을 렌더링합니다:

---
import { getEmDashCollection, getEmDashEntry } from "emdash";
import { PortableText, Image } 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 && (
      <Image image={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에는 기본 제공 카테고리 및 태그 택소노미가 포함되어 있습니다. 용어 생성 및 관리에 대한 자세한 내용은 택소노미를 참조하세요.

카테고리별 게시물 필터링

다음 라우트는 단일 카테고리의 게시물을 나열합니다:

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

게시물 카테고리 표시

다음 컴포넌트는 게시물에 할당된 카테고리와 태그를 표시합니다:

---
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>카테고리:</span>
      {categories.map((cat) => (
        <a href={`/category/${cat.slug}`}>{cat.label}</a>
      ))}
    </div>
  )}

  {tags.length > 0 && (
    <div class="tags">
      <span>태그:</span>
      {tags.map((tag) => (
        <a href={`/tag/${tag.slug}`}>{tag.label}</a>
      ))}
    </div>
  )}
</div>

페이지네이션 추가

게시물이 많은 블로그의 경우, 다음 라우트로 게시물 목록을 페이지네이션합니다:

---
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={`블로그 - ${currentPage}페이지`}>
  <h1>블로그</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}`}>이전</a>
    )}
    <span>{currentPage} / {totalPages} 페이지</span>
    {currentPage < totalPages && (
      <a href={`/blog/page/${currentPage + 1}`}>다음</a>
    )}
  </nav>
</Base>

RSS 피드 추가

다음 엔드포인트는 블로그의 RSS 피드를 생성합니다:

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}`,
		})),
	});
}

피드는 @astrojs/rss 패키지에 의존합니다. 다음 명령으로 설치합니다:

npm install @astrojs/rss

다음 단계