깨작깨작 공부

Astro Framework Contents Collections 사용법 정리 본문

Web/Astro

Astro Framework Contents Collections 사용법 정리

CodezTree 2023. 12. 28. 01:27

Astro Contents Collections를 사용하여, 웹페이지의 컨텐츠들을 묶음으로 불러와 처리해줄 수 있는 자체 내장 기능 (API)이다.

웹페이지 상에 웹서버에 정적으로 저장되는 컨텐츠를 리스트로 나열시켜 줄 일이 있어서 찾아보게 되었다.

다음 튜토리얼 문서를 바탕으로 정리 및 추가 설명을 입맛대로 넣었다. 궁금하면 읽어보시길
https://docs.astro.build/en/guides/content-collections/

Contents Collections 이란 무엇인가?

  • content collection 이란, 프로젝트 디렉토리의 src/content 안에 있는 모든 상위 레벨의 디렉토리를 의미한다. 디렉토리라 함은, src/content/item, src/content/news 에서 item, news와 같은 content폴더 내의 최상위 디렉토리를 의미한다. 오로지 해당 디렉토리 안에는 content collections만 허용된다. (다른 용도로 사용 불가 - 프로그래밍 언어 키워드(Keyword) 같은 느낌)
  • collection entry는 content collection의 구성 요소로서, content collection 폴더에 저장되어 있는 모든 콘텐츠 요소를 의미한다. Entry는 Markdown(.md), MDX(.mdx) 혹은 YAML, JSON (.yaml, .json`) 포맷을 이용하여 표현할 수 있다.

Contents Collections 을 왜 사용하는가?

  • contents collections에 대비되는 요소로 직접 Astro에서는 페이지를 만들어 /src/pages 폴더에 저장하여 불러올 수는 있지만, 그렇게 불러온 해당 요소는 schema 가 없기 때문에, Typescript상에서 해당 Entry의 프로퍼티가 올바른 프로퍼티인지 검출 할 수가 없다!
    즉, contents collections 사용을 통해 추후에 생길 여러 문제들을 사전에 방지하는 역할도 있으며, 성능 측면에서 (Astro가 주장하길) 빠르다고 하다. (테스트는 안해봤다)

Contents Collections 은 어떻게 사용하는가?

우선, Astro의 최신 버전의 설치가 필요하다. 더불어, 다른 integration이 있다면 호환되는 버전으로 모두 업그레이드.
(2.0.0에서 추가된 기능이지만 아마 호환성 문제가 있을 수 있어 최신으로 업데이트 하라고 하는 듯)

# Upgrade to Astro v4.x
npm install astro@lastest

# If using Preact integration
npm install @astrojs/preact@latest

Content Collections 사용을 위해 Typescript를 설정해주자. (Typescript의 타입 검증의 혜택을 누리려고 쓰는거니까!)
기본적으로 프로젝트 폴더에 tsconfig.json에는 base세팅으로 맞추어져 있을텐데, 이를 strict 혹은 strictest로 바꿔주자.

# tsconfig.json

{
    // 만약 이미 extends에 "astro/tsconfigs/strict"나 "astro/tsconfigs/strictest"를 사용중이라면 넘어가기
    "extends": "astro/tsconfigs/base"
    "compilerOptions": {
        // 아래 두 줄 추가(수정)하기
        "strictNullChecks": true,        
        "allowJs": true,                
    }
}

다음으로, collection 폴더를 content폴더 아래에 추가한다.

src/content/product

이제부터 모든 컨텐츠(여기서는 Markdown을 사용한다)는 위에 추가한 폴더 안에 넣어준다.

---src
    ㄴcontent
      ㄴproduct
        ㄴ...md

collection 폴더 안에 config.ts 파일을 만들어주자. 해당 파일에서는 본 collection의 자료 schema를 정의해준다.

# src/content/config.ts

// Import util from 'astro:content'
import {z, defineCollection } from "astro:content";

// Define 'type' and 'schema' for collection
const productCollection = defineCollection({
    type: 'content',  // 그대로, 다른 종류도 있긴한데 여기선 content type으로 지정
    schema: z.object({
        title: z.string(),
        desc: z.string(),
        uploadDate: z.date(),
        image: z.object({
            url: z.string(),
            alt: z.string()
        })
    })
});
   // 더 추가하고 싶다면 정의해준다.

// Export collections objects to register collection(s)
export const collections = {
    product: productCollection,
    // 추가로 등록하고 싶으면 위에서 정의한 collections를 원하는 이름으로 등록해준다. ex) posts:postsCollection,
}

다음으로는 collection을 바탕으로 아이템을 띄워 줄 페이지를 생성해주자.

src/pages/product/[...slug].astro

기존 file-base 라우팅으로는 페이지가 생성되지 않으므로, 이를 페이지를 만들어서 해결해준다. 파일명 자체가 [...slug].astro이다.

다음 코드를 넣어주자

# src/pages/product/[...slug].astro

---
import { getCollection } from 'astro:content';
import MarkdownProductLayout from '@layouts/MarkdownProductLayout.astro'; // 미리 만들어진, 페이지 데이터를 띄어줄 레이아웃 파일

export async function getStaticPaths() {    // Astro가 본 함수를 통해 각 컨텐츠들을 바탕으로 정적 Path를 생성해준다
    const productEntries = await getCollection('product');    // 아까 config.ts에서 export한 그 collection 이름
    return productEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },        // slug를 빼내어 전달해주고, props (아이템 정보)는 entry 그대로 전달
    }));
}

const { entry } = Astro.props;
const { Content } = await entry.render();
----

<MarkdownProductLayout frontmatter={entry.data}>    // frontmatter에 아이템 정보 전달
    <Content />
</MarkdownPostLayout>

여기까지 되었으면 기존에 .md 파일에 설정한 layout 정보는 필요없다. 모든 컨텐츠에서 layout: ../../layouts/Markdown...astro 삭제!!

이제 받아온 collections를 해당하는 페이지에 쭉 띄워보자

# src/pages/shop.astro

---
import { getCollection } from "astro:content";
import BaseLayout from "../layouts/BaseLayout.astro";
import ShopProduct from "../components/ShopProduct.astro";     // 아이템 정보를 예쁘게 띄어놓을 수 있는, 꾸며놓은 아이템 컴포

const allProduct = await getCollection("product");    // src/content/product 폴더에서 모든 product 정보를 불러온다
---
<BaseLayout>
    <p>우리 회사 아이템 입니다 와!</p>
    <ul>
        {
            allProduct.map((product) => (
                <ShopProduct url={`/product/${product.slug}/`} title={product.data.title} imageUrl={product.data.image.url} />
            ))
        }
    </ul>
</BaseLayout>

끗.

(누가 볼지는 모르지만 syntax는 지멋대로인데 설정하기 귀찮다. 미안!)

Comments