Kontent.ai & Astro
Kontent.ai는 AI 기능이 지원되는 구조화되고 모듈화된 방식으로 콘텐츠를 관리할 수 있는 헤드리스 CMS입니다.
Astro와 통합
섹션 제목: Astro와 통합이 섹션에서는 Kontent.ai TypeScript SDK를 사용하여 Kontent.ai 프로젝트를 Astro 애플리케이션에 연결합니다.
전제조건
섹션 제목: 전제조건시작하려면 다음이 필요합니다.
-
Kontent.ai 프로젝트 - 아직 Kontent.ai 계정이 없다면 무료 회원가입 후 새 프로젝트를 생성하세요.
-
Delivery API keys - 게시된 콘텐츠에는 Environment ID가 필요하고 초안을 가져오려면 Preview API key가 필요합니다 (선택 사항). 두 키 모두 Kontent.ai의 Environment Settings -> API keys 탭에 있습니다.
자격 증명 설정
섹션 제목: 자격 증명 설정Astro에 Kontent.ai 자격 증명을 추가하려면 다음 변수를 사용하여 프로젝트 루트에 .env
파일을 만듭니다.
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_IDKONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEY
이제 이러한 환경 변수를 Astro 프로젝트에서 사용할 수 있습니다.
이러한 환경 변수를 위한 TypeScript IntelliSense를 얻으려면 src/
디렉터리에 새 env.d.ts
파일을 만들고 다음과 같이 ImportMetaEnv
를 구성할 수 있습니다.
interface ImportMetaEnv { readonly KONTENT_ENVIRONMENT_ID: string; readonly KONTENT_PREVIEW_API_KEY: string;}
이제 루트 디렉터리에 다음과 같은 새 파일이 포함되어야 합니다.
디렉터리src/
- env.d.ts
- .env
- astro.config.mjs
- package.json
종속성 설치
섹션 제목: 종속성 설치Astro를 Kontent.ai 프로젝트와 연결하려면 Kontent.ai TypeScript SDK를 설치하세요.
npm install @kontent-ai/delivery-sdk
pnpm add @kontent-ai/delivery-sdk
yarn add @kontent-ai/delivery-sdk
다음으로 Astro 프로젝트의 src/lib/
디렉터리에 kontent.ts
라는 새 파일을 만듭니다.
import { createDeliveryClient } from "@kontent-ai/delivery-sdk";
export const deliveryClient = createDeliveryClient({ environmentId: import.meta.env.KONTENT_ENVIRONMENT_ID, previewApiKey: import.meta.env.KONTENT_PREVIEW_API_KEY,});
Astro에서 환경 변수 가져오기에 대해 자세히 알아보세요.
이 구현은 .env
파일의 자격 증명을 사용하여 새로운 DeliveryClient
객체를 생성합니다.
previewApiKey
는 선택사항입니다. 사용하면 워크플로의 상태에 관계없이 콘텐츠 항목의 최신 버전을 반환하도록 Delivery API 엔드포인트에 대한 각 쿼리를 구성할 수 있습니다. 그렇지 않으면 게시된 항목만 반환됩니다.
마지막으로 Astro 프로젝트의 루트 디렉터리에는 이제 다음과 같은 새 파일이 포함되어야 합니다.
디렉터리src/
디렉터리lib/
- kontent.ts
- env.d.ts
- .env
- astro.config.mjs
- package.json
데이터 가져오기
섹션 제목: 데이터 가져오기이제 DeliveryClient
를 모든 컴포넌트에서 사용할 수 있습니다. 콘텐츠를 가져오려면 DeliveryClient
와 메서드 체이닝을 사용하여 원하는 항목을 정의하세요. 이 예시에서는 블로그 게시물의 기본 가져오기를 보여주고 제목을 목록으로 렌더링합니다.
---import { deliveryClient } from "../lib/kontent";
const blogPosts = await deliveryClient .items() .type("blogPost") .toPromise()---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Astro</title> </head> <body> <ul> {blogPosts.data.items.map(blogPost => ( <li>{blogPost.elements.title.value}</li> ))} </ul> </body></html>
Kontent.ai 문서에서 더 많은 쿼리 옵션을 찾을 수 있습니다.
Astro와 Kontent.ai로 블로그 만들기
섹션 제목: Astro와 Kontent.ai로 블로그 만들기위 설정을 통해 이제 Kontent.ai를 콘텐츠 소스로 사용하는 블로그를 만들 수 있습니다.
전제조건
섹션 제목: 전제조건-
Kontent.ai 프로젝트 - 이 튜토리얼에서는 빈 프로젝트를 사용하는 것이 좋습니다. 콘텐츠 모델에 이미 일부 콘텐츠 타입이 있는 경우 이를 사용할 수 있지만 콘텐츠 모델에 맞게 코드 조각을 수정해야 합니다.
-
Kontent.ai에서 콘텐츠를 가져오도록 구성된 Astro 프로젝트 - Kontent.ai를 사용하여 Astro 프로젝트를 설정하는 방법에 대한 자세한 내용은 위를 참조하세요.
콘텐츠 모델 설정
섹션 제목: 콘텐츠 모델 설정Kontent.ai에서 Content model로 이동하여 다음 필드와 값을 사용하여 새 콘텐츠 타입을 만듭니다.
- Name: Blog Post
- Elements:
- Text field
- Name: Title
- Element Required: yes
- Rich text field
- Name: Teaser
- Element Required: yes
- Allowed in this element: only check Text
- Rich text field
- Name: Content
- Element Required: yes
- Date & time field
- Name: Date
- URL slug field
- Name: URL slug
- Element Required: yes
- Auto-generate from: select “Title”
- Text field
그런 다음 Save Changes을 클릭하세요.
콘텐츠 생성
섹션 제목: 콘텐츠 생성이제 Content & assets 탭으로 이동하여 Blog Post 타입의 새 콘텐츠 항목을 만듭니다. 다음 값을 사용하여 필드를 채웁니다.
- Content item name: Astro
- Title: Astro is amazing
- Teaser: Astro is an all-in-one framework for building fast websites faster.
- Content: You can use JavaScript to implement the website functionality, but no client bundle is necessary.
- Date & time: select today
- URL slug: astro-is-amazing
완료되면 상단의 Publish 버튼을 사용하여 블로그 게시물을 게시하세요.
참고: 다음 단계로 넘어가기 전에 원하는 만큼 블로그 게시물을 작성해 보세요.
TypeScript에서 콘텐츠 모델 생성
섹션 제목: TypeScript에서 콘텐츠 모델 생성다음으로 콘텐츠 모델에서 TypeScript 타입을 생성합니다.
이 단계는 선택 사항이지만 훨씬 더 나은 개발자 경험을 제공하고 런타임이 아닌 빌드 시간에 잠재적인 문제를 발견할 수 있습니다.
먼저 Kontent.ai JS model generator, ts-node, dotenv를 설치합니다.
npm install @kontent-ai/model-generator ts-node dotenv
pnpm add @kontent-ai/model-generator ts-node dotenv
yarn add @kontent-ai/model-generator ts-node dotenv
그런 다음 package.json에 다음 스크립트를 추가합니다.
{ ... "scripts": { ... "regenerate:models": "ts-node --esm ./generate-models.ts" },}
타입에는 공개 API에서 사용할 수 없는 프로젝트에 대한 구조적 정보가 필요하므로 .env
파일에 콘텐츠 관리 API key도 추가해야 합니다. Environment settings -> API keys -> Management API에서 키를 생성할 수 있습니다.
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_IDKONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEYKONTENT_MANAGEMENT_API_KEY=YOUR_MANAGEMENT_API_KEY
마지막으로, 모델을 생성하도록 모델 생성기를 구성하는 generate-models.ts
스크립트를 추가합니다.
import { generateModelsAsync, textHelper } from '@kontent-ai/model-generator'import { rmSync, mkdirSync } from 'fs'
import * as dotenv from 'dotenv'dotenv.config()
const runAsync = async () => { rmSync('./src/models', { force: true, recursive: true }) mkdirSync('./src/models')
// 작업 디렉토리를 models로 변경 process.chdir('./src/models')
await generateModelsAsync({ sdkType: 'delivery', apiKey: process.env.KONTENT_MANAGEMENT_API_KEY ?? '', environmentId: process.env.KONTENT_ENVIRONMENT_ID ?? '', addTimestamp: false, isEnterpriseSubscription: false, })}
// 스스로 호출되는 비동기 함수;(async () => { await runAsync()})().catch(err => { console.error(err) throw err})
이제 실행해 보세요.
npm run regenerate:models
pnpm run regenerate:models
yarn run regenerate:models
블로그 게시물 목록 표시하기
섹션 제목: 블로그 게시물 목록 표시하기이제 일부 콘텐츠를 가져올 준비가 되었습니다. 모든 블로그 게시물 목록을 표시하려는 Astro 페이지로 이동합니다 (예: src/pages
의 홈페이지 index.astro
).
Astro 페이지의 프런트매터에 있는 모든 블로그 게시물을 가져옵니다:
---import { deliveryClient } from '../lib/kontent';import type { BlogPost } from '../models';import { contentTypes } from '../models/project/contentTypes';
const blogPosts = await deliveryClient .items<BlogPost> .type(contentTypes.blog_post.codename) .toPromise()---
모델 생성을 건너뛴 경우 타입이 지정되지 않은 객체와 문자열 리터럴을 사용하여 타입을 정의할 수도 있습니다.
const blogPosts = await deliveryClient .items() .type("blogPost") .toPromise()
fetch 호출은 data.items
의 모든 블로그 게시물 목록을 포함하는 response
객체를 반환합니다. Astro 페이지의 HTML 섹션에서 map()
함수를 사용하여 블로그 게시물을 나열할 수 있습니다.
---import { deliveryClient } from '../lib/kontent';import type { BlogPost } from '../models';import { contentTypes } from '../models/project/contentTypes';
const blogPosts = await deliveryClient .items<BlogPost> .type(contentTypes.blogPost.codename) .toPromise()---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Astro</title> </head> <body> <h1>Blog posts</h1> <ul> {blogPosts.data.items.map(blogPost => ( <li> <a href={`/blog/${blogPost.elements.url_slug.value}/`} title={blogPost.elements.title.value}> {blogPost.elements.title.value} </a> </li> ))} </ul> </body></html>
개별 블로그 게시물 생성
섹션 제목: 개별 블로그 게시물 생성튜토리얼의 마지막 단계는 자세한 블로그 게시물 페이지를 생성하는 것입니다.
정적 사이트 생성
섹션 제목: 정적 사이트 생성이 섹션에서는 Astro와 함께 정적 (SSG) 모드를 사용합니다.
먼저, /src/pages/blog/
에 [slug].astro
파일을 생성합니다. 이 파일은 CMS로부터 모든 데이터를 가져오는 getStaticPaths
함수를 내보내야 합니다.
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() { const blogPosts = await deliveryClient .items<BlogPost>() .type(contentTypes.blog_post.codename) .toPromise()---
지금까지 이 함수는 Kontent.ai에서 모든 블로그 게시물을 가져왔습니다. 코드 조각은 홈 페이지에서 사용한 것과 정확히 동일합니다.
다음으로 함수는 각 블로그 게시물의 경로와 데이터를 내보내야 합니다. 파일 이름을 [slug].astro
로 지정했으므로 URL 슬러그를 나타내는 매개변수를 slug
라고 합니다.
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() { const blogPosts = await deliveryClient .items<BlogPost>() .type(contentTypes.blog_post.codename) .toPromise()
return blogPosts.data.items.map(blogPost => ({ params: { slug: blogPost.elements.url_slug.value }, props: { blogPost } }))}---
마지막 부분은 HTML 템플릿을 제공하고 각 블로그 게시물을 표시하는 것입니다.
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() { const blogPosts = await deliveryClient .items<BlogPost>() .type(contentTypes.blog_post.codename) .toPromise()
return blogPosts.data.items.map(blogPost => ({ params: { slug: blogPost.elements.url_slug.value }, props: { blogPost } }))}
const blogPost: BlogPost = Astro.props.blogPost---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>{blogPost.elements.title.value}</title> </head> <body> <article> <h1>{blogPost.elements.title.value}</h1> <Fragment set:html={blogPost.elements.teaser.value} /> <Fragment set:html={blogPost.elements.content.value} /> <time>{new Date(blogPost.elements.date.value ?? "")}</time> </body></html>
Astro 미리보기 (기본적으로 http://localhost:4321/blog/astro-is-amazing/)로 이동하여 렌더링된 블로그 게시물을 확인하세요.
서버 측 렌더링
섹션 제목: 서버 측 렌더링SSR 모드를 선택한 경우 동적 경로를 사용하여 Kontent.ai에서 페이지 데이터를 가져옵니다.
/src/pages/blog/
에 [slug].astro
라는 새 파일을 만들고 다음 코드를 추가하세요. 데이터 가져오기는 이전 사용 사례와 매우 유사하지만 사용된 URL을 기반으로 올바른 블로그 게시물을 찾을 수 있는 equalsFilter
를 추가합니다.
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.paramslet blogPost: BlogPost;try { const data = await deliveryClient .items<BlogPost>() .equalsFilter(contentTypes.blog_post.elements.url_slug.codename, slug ?? '') .type(contentTypes.blog_post.codename) .limitParameter(1) .toPromise() blogPost = data.data.items[0]} catch (error) { return Astro.redirect('/404')}---
생성된 타입을 사용하지 않는 경우 대신 문자열 리터럴을 사용하여 콘텐츠 항목 타입과 필터링된 요소 코드명을 정의할 수 있습니다.
const data = await deliveryClient .items() .equalsFilter("url_slug", slug ?? '') .type("blog_post") .limitParameter(1) .toPromise()
마지막으로 HTML 코드를 추가하여 블로그 게시물을 렌더링합니다. 이 부분은 정적 생성과 동일합니다.
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.paramslet blogPost: BlogPost;try { const data = await deliveryClient .items<BlogPost>() .equalsFilter(contentTypes.blog_post.elements.url_slug.codename, slug ?? '') .type(contentTypes.blog_post.codename) .limitParameter(1) .toPromise() blogPost = data.data.items[0]} catch (error) { return Astro.redirect('/404')}---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>{blogPost.elements.title.value}</title> </head> <body> <article> <h1>{blogPost.elements.title.value}</h1> <Fragment set:html={blogPost.elements.teaser.value} /> <Fragment set:html={blogPost.elements.content.value} /> <time>{new Date(blogPost.elements.date.value ?? '')}</time> </body></html>
사이트 게시
섹션 제목: 사이트 게시웹사이트를 배포하려면 배포 가이드를 방문하여 선호하는 호스팅 제공업체의 지침을 따르세요.
Kontent.ai 변경에 따른 재빌드
섹션 제목: Kontent.ai 변경에 따른 재빌드프로젝트가 Astro의 기본 정적 모드를 사용하는 경우 콘텐츠가 변경될 때 새 빌드를 트리거하도록 웹후크를 설정해야 합니다. Netlify 또는 Vercel을 호스팅 공급자로 사용하는 경우 해당 웹후크 기능을 사용하여 Kontent.ai 이벤트에서 새 빌드를 트리거할 수 있습니다.
Netlify
섹션 제목: NetlifyNetlify에서 웹후크를 설정하려면:
- 사이트 대시보드로 이동하여 Build & deploy를 클릭합니다.
- Continuous Deployment 탭에서 Build hooks 섹션을 찾아 Add build hook를 클릭합니다.
- 웹후크의 이름을 제공하고 빌드를 트리거할 브랜치를 선택합니다. Save을 클릭하고 생성된 URL을 복사하세요.
Vercel
섹션 제목: VercelVercel에서 웹후크를 설정하려면 다음 안내를 따르세요.
- 프로젝트 대시보드로 이동하여 Settings을 클릭합니다.
- Git 탭에서 Deploy Hooks 섹션을 찾습니다.
- 빌드를 트리거할 웹후크와 브랜치의 이름을 제공합니다. Add를 클릭하고 생성된 URL을 복사합니다.
Kontent.ai에 웹후크 추가
Kontent.ai 앱에서 Environment settings -> Webhooks로 이동합니다. Create new webhook를 클릭하고 새 웹후크의 이름을 입력하세요. Netlify 또는 Vercel에서 복사한 URL을 붙여넣고 웹후크를 트리거할 이벤트를 선택하세요. 기본적으로 게시된 콘텐츠가 변경될 때 사이트를 다시 빌드하려면 Delivery API triggers 아래에 Publish 및 Unpublish 이벤트만 필요합니다. 완료되면 저장을 클릭하세요.
이제 Kontent.ai에 새 블로그 게시물을 게시할 때마다 새 빌드가 실행되고 블로그가 업데이트됩니다.