# Astro

> Kanorio 콘텐츠로 빠른 Astro 사이트를 구축하세요. 정적 또는 서버 렌더링, 게시 시 재빌드, SEO 파일 및 에지 리다이렉트를 지원합니다.

> Markdown version of https://kanorio.com/ko/developers/guides/astro. Append `.md` to any developer docs URL for this format. Index: https://kanorio.com/llms.txt · MCP: https://app.kanorio.com/api/mcp
>
> Translated from the English original: https://kanorio.com/en/developers/guides/astro.md

Astro는 기본적으로 JavaScript를 포함하지 않으므로 브랜드 사이트나 블로그에 적합합니다. 컴포넌트의 프론트매터에서 `fetch`를 사용하여 Kanorio 콘텐츠를 불러옵니다. 정적 사이트의 경우 빌드 시점에, 서버 어댑터를 사용하는 경우 요청 시마다 불러옵니다.

## 1. 환경 변수

```bash
# .env
PUBLIC_KANORIO_SITE_URL="https://s-xxxxxxxxxxxx.kanorio.com"
```

[사이트 설정 › 개발자](https://app.kanorio.com/settings/developer)에서 영구 시스템 URL을 사용하세요.

## 2. 컬렉션 목록 가져오기

```astro
---
// src/pages/projects.astro
const SITE_URL = import.meta.env.PUBLIC_KANORIO_SITE_URL;
const res = await fetch(`${SITE_URL}/api/v1/collections/projects/items?sort=-publishedAt&limit=20`);
const { data, assets } = await res.json();
---

<section>
  <h1>Projects</h1>
  {data.map((item) => {
    const cover = assets[item.fieldData.cover];
    return (
      <article>
        {cover && <img src={cover.url} alt={cover.alt ?? ""} width={cover.width} height={cover.height} loading="lazy" />}
        <h2>{item.fieldData.title}</h2>
        <p>{item.fieldData.summary}</p>
      </article>
    );
  })}
</section>
```

## 3. 블로그 게시물

정적 빌드의 경우 `getStaticPaths`가 `nextCursor`를 사용하여 블로그의 모든 페이지를 순회합니다.

```astro
---
// src/pages/blog/[slug].astro
const SITE_URL = import.meta.env.PUBLIC_KANORIO_SITE_URL;

export async function getStaticPaths() {
  const siteUrl = import.meta.env.PUBLIC_KANORIO_SITE_URL;
  const slugs = [];
  let cursor = null;
  do {
    const url = new URL(`${siteUrl}/api/v1/collections/blog/items`);
    url.searchParams.set("fields", "title");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);
    const page = await (await fetch(url)).json();
    slugs.push(...page.data.map((post) => post.slug));
    cursor = page.hasMore ? page.nextCursor : null;
  } while (cursor);
  return slugs.map((slug) => ({ params: { slug } }));
}

const { slug } = Astro.params;
const res = await fetch(`${SITE_URL}/api/v1/collections/blog/items/${encodeURIComponent(slug)}?expand=authors,categories&formats=html`);
if (!res.ok) return Astro.redirect("/404");
const { item, assets } = await res.json();
const cover = item.fieldData.cover_image ? assets[item.fieldData.cover_image] : null;
---

<article>
  <h1>{item.fieldData.title}</h1>
  {cover && <img src={cover.url} alt={cover.alt ?? ""} />}
  {item.toc?.length > 0 && (
    <nav aria-label="Contents">
      <ul>{item.toc.map((h) => <li data-level={h.level}><a href={`#${h.id}`}>{h.label}</a></li>)}</ul>
    </nav>
  )}
  <div class="prose" set:html={item.contentHtml} />
</article>
```

## 4. 폼 및 업데이트

- **폼:** Elements의 `<kanorio-form>`을 사용하거나 [폼](https://kanorio.com/ko/developers/guides/forms)에 설명된 챌린지 및 제출 엔드포인트를 사용하세요. 허용된 출처에 도메인을 추가해야 합니다.
- **정적 빌드:** 호스트에서 배포 훅을 생성하고 [사이트 설정 › Webhooks](https://app.kanorio.com/settings/webhooks)에 Webhook으로 추가하여 컬렉션 및 페이지 이벤트를 구독하세요. 게시할 때마다 재빌드가 트리거됩니다.
- **서버 렌더링:** `@astrojs/cloudflare` 또는 `@astrojs/node`를 사용하는 경우, 요청 시마다 읽고 `X-Kanorio-Cache-Tags` 응답 헤더로 캐시합니다. `createRevalidateHandler`로 구축된 Webhook 핸들러는 `data.cacheTags`의 태그를 제거합니다(동일한 어휘 사용, [Webhooks](https://kanorio.com/ko/developers/webhooks#refresh-your-front-ends-cache) 참조).

## 5. SEO 파일

Astro에는 프록시 재작성 기능이 없으며, 리다이렉트를 사용하면 크롤러가 다른 도메인으로 전송됩니다. 대신 엔드포인트에서 파일을 제공하세요. `seoRewrites`는 각 경로의 소스 URL을 반환합니다.

```ts
// src/lib/kanorio-seo.ts
import { seoRewrites } from "@kanorio/sdk";

const targets = seoRewrites({ siteUrl: import.meta.env.PUBLIC_KANORIO_SITE_URL, framework: "astro" });

export async function proxySeoFile(path: string): Promise<Response> {
  const res = await fetch(targets[path]);
  return new Response(res.body, {
    status: res.status,
    headers: { "Content-Type": res.headers.get("Content-Type") ?? "text/plain; charset=utf-8" },
  });
}
```

```ts
// src/pages/robots.txt.ts, and the same for sitemap.xml, llms.txt, llms-full.txt and feed.xml
import { proxySeoFile } from "../lib/kanorio-seo";

export const GET = () => proxySeoFile("/robots.txt");
```

`fetchDeliveryJsonLd`를 사용하여 각 페이지에 JSON-LD를 추가하세요.

```astro
---
import { fetchDeliveryJsonLd } from "@kanorio/sdk";

const jsonLd = await fetchDeliveryJsonLd({ siteUrl: import.meta.env.PUBLIC_KANORIO_SITE_URL, path: Astro.url.pathname });
---
{jsonLd && <script type="application/ld+json" set:html={jsonLd.scriptJson} />}
```

## 6. 리다이렉트 및 크롤러 규칙

서버 어댑터를 사용하는 경우, 미들웨어에서 관리 화면의 리다이렉트 및 AI 크롤러 규칙을 적용하고 IndexNow 키 파일을 제공하세요.

```ts
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import { createKanorioMiddleware } from "@kanorio/sdk/edge";

const kanorioEdge = createKanorioMiddleware({ siteUrl: import.meta.env.PUBLIC_KANORIO_SITE_URL });

export const onRequest = defineMiddleware(async (context, next) => {
  const ctx = context.locals.runtime?.ctx; // Cloudflare adapter only
  const response = await kanorioEdge.handleRequest(context.request, {
    waitUntil: ctx ? (promise) => ctx.waitUntil(promise) : undefined,
  });
  return response ?? next();
});
```

정적 빌드에서는 미들웨어를 실행할 수 없습니다. 호스트에서 리다이렉트를 구성하고 `/{key}.txt`를 `https://{site}/api/v1/seo/indexnow-key.txt`로 전달하세요. 미들웨어는 전체 경로만 일치시키므로 와일드카드 리다이렉트는 `astro.config`의 `redirects`에 추가하세요.

## 7. 분석

레이아웃에서 Elements 스크립트를 한 번 로드하고 분석 요소를 추가하세요.

```html
<script type="module" src="https://cdn.kanorio.com/elements/v1/kanorio-elements.js"></script>
<kanorio-analytics site-url="https://s-xxxxxxxxxxxx.kanorio.com"></kanorio-analytics>
```

페이지 뷰, 리퍼러, 캠페인 매개변수 및 AI 어시스턴트로부터의 방문이 관리 화면의 분석에 표시됩니다.
