택소노미

이 페이지

택소노미는 콘텐츠를 정리하기 위한 분류 시스템입니다. EmDash에는 카테고리와 태그가 기본 내장되어 있으며, 전문적인 분류 요구에 대한 사용자 정의 택소노미도 지원합니다.

기본 택소노미

택소노미유형설명
카테고리계층형부모-자식 관계를 가진 중첩 분류
태그평면형계층 없는 단순 라벨

둘 다 기본적으로 게시물 컬렉션에 사용 가능합니다.

용어 관리

용어 생성

관리자 대시보드

  1. 택소노미 페이지로 이동 (예: /_emdash/admin/taxonomies/category)
  2. 새로 추가 양식에 용어 이름 입력
  3. 선택적으로 설정: 슬러그, 상위 항목, 설명
  4. 추가 클릭

콘텐츠 에디터

  1. 에디터에서 콘텐츠 항목을 열기
  2. 사이드바에서 택소노미 패널 찾기
  3. 카테고리의 경우 체크박스를 선택하거나 + 새로 추가 클릭
  4. 태그의 경우 쉼표로 구분하여 태그 이름 입력
  5. 콘텐츠 저장

API

POST /_emdash/api/taxonomies/category/terms
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKEN

{
  "slug": "tutorials",
  "label": "튜토리얼",
  "parentId": "term_abc",
  "description": "실용 가이드 및 튜토리얼"
}

용어 편집

  1. 택소노미 용어 페이지로 이동 → 편집 → 업데이트 → 저장

용어 삭제

  1. 택소노미 용어 페이지로 이동 → 삭제 → 확인

택소노미 쿼리

import { getTaxonomyTerms } from "emdash";
const categories = await getTaxonomyTerms("category");
const tags = await getTaxonomyTerms("tag");
const tags = await getTaxonomyTerms("tag", { includeCounts: false });
import { getTerm } from "emdash";
const category = await getTerm("category", "news");
import { getEntryTerms } from "emdash";
const categories = await getEntryTerms("posts", "post-123", "category");
import { getEmDashCollection } from "emdash";
const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

택소노미 페이지 구축

카테고리 아카이브

---
import { getTaxonomyTerms, getEmDashCollection } from "emdash";
import Base from "../../layouts/Base.astro";

export async function getStaticPaths() {
  const categories = await getTaxonomyTerms("category");
  function flatten(terms) {
    return terms.flatMap((term) => [term, ...flatten(term.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>}
  <p>{category.count}개의 게시물</p>
  <ul>
    {posts.map((post) => (
      <li><a href={`/blog/${post.data.slug}`}>{post.data.title}</a></li>
    ))}
  </ul>
</Base>

태그 아카이브

---
import { getTaxonomyTerms, getEmDashCollection } from "emdash";
import Base from "../../layouts/Base.astro";

export async function getStaticPaths() {
  const tags = await getTaxonomyTerms("tag");
  return tags.map((tag) => ({ params: { slug: tag.slug }, props: { tag } }));
}

const { tag } = Astro.props;
const { entries: posts } = await getEmDashCollection("posts", {
  status: "published", where: { tag: tag.slug },
});
---

<Base title={`"${tag.label}" 태그 게시물`}>
  <h1>#{tag.label}</h1>
  <ul>
    {posts.map((post) => (
      <li><a href={`/blog/${post.data.slug}`}>{post.data.title}</a></li>
    ))}
  </ul>
</Base>

카테고리 목록 위젯

---
import { getTaxonomyTerms } from "emdash";
const categories = await getTaxonomyTerms("category");
---

<nav class="category-list">
  <h3>카테고리</h3>
  <ul>
    {categories.map((cat) => (
      <li>
        <a href={`/category/${cat.slug}`}>{cat.label} ({cat.count})</a>
        {cat.children.length > 0 && (
          <ul>
            {cat.children.map((child) => (
              <li><a href={`/category/${child.slug}`}>{child.label} ({child.count})</a></li>
            ))}
          </ul>
        )}
      </li>
    ))}
  </ul>
</nav>

태그 클라우드

---
import { getTaxonomyTerms } from "emdash";
const tags = await getTaxonomyTerms("tag");
const counts = tags.map((t) => t.count ?? 0);
const maxCount = Math.max(...counts, 1);
const minSize = 0.8;
const maxSize = 2;
function getSize(count: number) {
  return minSize + (count / maxCount) * (maxSize - minSize);
}
---

<div class="tag-cloud">
  {tags.map((tag) => (
    <a href={`/tag/${tag.slug}`} style={`font-size: ${getSize(tag.count ?? 0)}rem`}>{tag.label}</a>
  ))}
</div>

콘텐츠에 용어 표시

---
import { getEntryTerms } from "emdash";
interface Props { collection: string; entryId: string; }
const { collection, entryId } = Astro.props;
const categories = await getEntryTerms(collection, entryId, "category");
const tags = await getEntryTerms(collection, entryId, "tag");
---

<div class="post-terms">
  {categories.length > 0 && (
    <div class="categories">
      <span>카테고리:</span>
      {categories.map((cat, i) => (
        <>{i > 0 && ", "}<a href={`/category/${cat.slug}`}>{cat.label}</a></>
      ))}
    </div>
  )}
  {tags.length > 0 && (
    <div class="tags">
      {tags.map((tag) => (<a href={`/tag/${tag.slug}`} class="tag">#{tag.label}</a>))}
    </div>
  )}
</div>

사용자 정의 택소노미

POST /_emdash/api/taxonomies
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKEN

{
  "name": "genre",
  "label": "장르",
  "labelSingular": "장르",
  "hierarchical": true,
  "collections": ["books", "movies"]
}
const genres = await getTaxonomyTerms("genre");
const { entries: sciFiBooks } = await getEmDashCollection("books", {
	where: { genre: "science-fiction" },
});

택소노미 API 참조

엔드포인트메서드설명
/_emdash/api/taxonomiesGET택소노미 정의 목록
/_emdash/api/taxonomiesPOST택소노미 생성
/_emdash/api/taxonomies/:name/termsGET용어 목록
/_emdash/api/taxonomies/:name/termsPOST용어 생성
/_emdash/api/taxonomies/:name/terms/:slugGET용어 조회
/_emdash/api/taxonomies/:name/terms/:slugPUT용어 업데이트
/_emdash/api/taxonomies/:name/terms/:slugDELETE용어 삭제

다음 단계