EmDash는 Astro 페이지와 컴포넌트에서 콘텐츠를 검색하기 위한 쿼리 함수를 제공합니다. 이러한 함수는 Astro의 라이브 콘텐츠 컬렉션 패턴을 따르며, 오류 처리가 포함된 구조화된 결과를 반환합니다.
쿼리 함수
EmDash는 두 가지 주요 쿼리 함수를 내보냅니다:
| 함수 | 목적 | 반환값 |
|---|---|---|
getEmDashCollection | 콘텐츠 타입의 모든 항목 검색 | { entries, error } |
getEmDashEntry | ID 또는 슬러그로 단일 항목 검색 | { entry, error, isPreview } |
emdash에서 가져옵니다:
import { getEmDashCollection, getEmDashEntry } from "emdash";
모든 항목 가져오기
getEmDashCollection을 사용하여 콘텐츠 타입의 모든 항목을 검색합니다:
---
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
console.error("게시물 로드 실패:", error);
}
---
<ul>
{posts.map((post) => (
<li>{post.data.title}</li>
))}
</ul>
로케일로 필터링
i18n이 활성화된 경우, 로케일로 필터링하여 특정 언어의 콘텐츠를 검색합니다:
// 프랑스어 게시물
const { entries: frenchPosts } = await getEmDashCollection("posts", {
locale: "fr",
status: "published",
});
// 현재 요청의 로케일 사용
const { entries: localizedPosts } = await getEmDashCollection("posts", {
locale: Astro.currentLocale,
status: "published",
});
단일 항목의 경우 locale을 세 번째 인수로 전달합니다:
const { entry: post } = await getEmDashEntry("posts", "my-post", {
locale: Astro.currentLocale,
});
locale이 생략되면 요청의 현재 로케일이 기본값으로 사용됩니다. 요청된 로케일에 대한 번역이 없으면 폴백 체인이 적용됩니다.
상태로 필터링
게시된 콘텐츠 또는 초안만 검색:
// 게시된 게시물만
const { entries: published } = await getEmDashCollection("posts", {
status: "published",
});
// 초안만
const { entries: drafts } = await getEmDashCollection("posts", {
status: "draft",
});
결과 제한
반환되는 항목 수를 제한:
// 최신 게시물 5개 가져오기
const { entries: recentPosts } = await getEmDashCollection("posts", {
status: "published",
limit: 5,
});
분류법으로 필터링
카테고리, 태그 또는 사용자 정의 분류법 용어로 항목 필터링:
// "news" 카테고리의 게시물
const { entries: newsPosts } = await getEmDashCollection("posts", {
status: "published",
where: { category: "news" },
});
// "javascript" 태그가 있는 게시물
const { entries: jsPosts } = await getEmDashCollection("posts", {
status: "published",
where: { tag: "javascript" },
});
// 여러 용어 중 하나와 일치하는 게시물
const { entries: featuredNews } = await getEmDashCollection("posts", {
status: "published",
where: { category: ["news", "featured"] },
});
where 필터는 단일 분류법에 대해 여러 값이 제공될 때 OR 논리를 사용합니다.
오류 처리
신뢰성이 중요한 경우 항상 오류를 확인하세요:
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
// 로그를 기록하고 우아하게 처리
console.error("게시물 로드 실패:", error);
return new Response("서버 오류", { status: 500 });
}
단일 항목 가져오기
getEmDashEntry를 사용하여 ID 또는 슬러그로 항목을 검색합니다:
---
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("서버 오류", { status: 500 });
}
if (!post) {
return Astro.redirect("/404");
}
---
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
항목 반환 타입
getEmDashEntry는 결과 객체를 반환합니다:
interface EntryResult<T> {
entry: ContentEntry<T> | null; // 찾을 수 없는 경우 null
error?: Error; // 실제 오류에 대해서만 설정됨("찾을 수 없음"이 아님)
isPreview: boolean; // 미리보기/초안 콘텐츠를 보는 경우 true
}
interface ContentEntry<T> {
id: string;
data: T;
edit: EditProxy; // 비주얼 편집 주석
}
entry 내의 data 객체에는 콘텐츠 타입에 대해 정의된 모든 필드가 포함됩니다. edit 프록시는 비주얼 편집 주석을 제공합니다(아래 참조).
미리보기 모드
EmDash는 미들웨어를 통해 미리보기를 자동으로 처리합니다. URL에 유효한 _preview 토큰이 포함되어 있으면 미들웨어가 이를 확인하고 요청 컨텍스트를 설정합니다. 그러면 쿼리 함수는 특별한 매개변수 없이 초안 콘텐츠를 제공합니다:
---
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
// 특별한 미리보기 처리가 필요 없음 — 미들웨어가 자동으로 수행
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("서버 오류", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
---
{isPreview && (
<div class="preview-banner">
미리보기 보기. 이 콘텐츠는 게시되지 않았습니다.
</div>
)}
<article>
<h1>{entry.data.title}</h1>
<PortableText value={entry.data.content} />
</article>
비주얼 편집
쿼리 함수에서 반환된 모든 항목에는 템플릿에 주석을 달기 위한 edit 프록시가 포함되어 있습니다. 요소에 스프레드하여 인증된 편집자에 대한 인라인 편집을 활성화합니다:
<article {...entry.edit}>
<h1 {...entry.edit.title}>{entry.data.title}</h1>
<div {...entry.edit.content}>
<PortableText value={entry.data.content} />
</div>
</article>
편집 모드에서 {...entry.edit.title}은 비주얼 편집 툴바가 인라인 편집을 활성화하는 데 사용하는 data-emdash-ref 속성을 생성합니다. 프로덕션에서는 프록시 스프레드가 출력을 생성하지 않습니다.
결과 정렬
getEmDashCollection은 정렬 순서를 보장하지 않습니다. 템플릿에서 결과를 정렬합니다:
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// 게시 날짜별로 정렬, 최신순
const sorted = posts.sort(
(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0),
);
일반적인 정렬 패턴
// 제목별 알파벳순
posts.sort((a, b) => a.data.title.localeCompare(b.data.title));
// 사용자 정의 순서 필드별
posts.sort((a, b) => (a.data.order ?? 0) - (b.data.order ?? 0));
// 무작위 순서
posts.sort(() => Math.random() - 0.5);
TypeScript 타입
컬렉션에 대한 TypeScript 타입 생성:
npx emdash types
이렇게 하면 각 컬렉션에 대한 인터페이스가 있는 .emdash/types.ts가 생성됩니다. 타입 안전성을 위해 사용하세요:
import { getEmDashCollection, getEmDashEntry } from "emdash";
import type { Post } from "../.emdash/types";
// 타입 안전 컬렉션 쿼리
const { entries: posts } = await getEmDashCollection<Post>("posts");
// posts는 ContentEntry<Post>[]
// 타입 안전 항목 쿼리
const { entry: post } = await getEmDashEntry<Post>("posts", "my-post");
// post는 ContentEntry<Post> | null
정적 vs. 서버 렌더링
EmDash 콘텐츠는 정적 페이지와 서버 렌더링 페이지 모두에서 작동합니다.
정적(사전 렌더링)
정적 페이지의 경우 getStaticPaths를 사용하여 빌드 시 경로를 생성합니다:
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
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);
---
서버 렌더링
서버 렌더링 페이지의 경우 콘텐츠를 직접 쿼리합니다:
---
export const prerender = false;
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("서버 오류", { status: 500 });
}
if (!post) {
return new Response(null, { status: 404 });
}
---
성능 고려사항
캐싱
EmDash는 캐싱을 자동으로 처리하는 Astro의 라이브 콘텐츠 컬렉션을 사용합니다. 서버 렌더링 페이지의 경우 HTTP 캐시 헤더 추가를 고려하세요:
---
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// 5분 동안 캐시
Astro.response.headers.set("Cache-Control", "public, max-age=300");
---
중복 쿼리 방지
한 번 쿼리하고 데이터를 컴포넌트에 전달합니다:
---
import { getEmDashCollection } from "emdash";
import PostList from "../components/PostList.astro";
import Sidebar from "../components/Sidebar.astro";
// 한 번 쿼리
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
const featured = posts.filter((p) => p.data.featured);
const recent = posts.slice(0, 5);
---
<PostList posts={featured} />
<Sidebar posts={recent} />