Use Cases

Contentful CMS Implementation: Claude Code 활용 가이드

contentful cms implementation: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.

Contentful CMS통합をClaude Code로 효율화하기

Contentfulはヘッドレス CMS の代表格で、APIファーストな콘텐츠관리を実現します。構造化された콘텐츠モデルとリッチな配信APIにより、あらゆる프론트엔드から콘텐츠を취득할 수 있습니다。Claude Code를 활용하면 콘텐츠モデルの설계から프론트엔드통합まで효율적으로進められます。

콘텐츠モデルの설계

> Contentfulでブ로그サイトの콘텐츠モデルを설계して。
> 글、카테고리、著者の3つの콘텐츠タイプで。
// scripts/setup-content-model.ts
import contentful from 'contentful-management';

const client = contentful.createClient({
  accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN!,
});

async function setupContentModel() {
  const space = await client.getSpace(process.env.CONTENTFUL_SPACE_ID!);
  const env = await space.getEnvironment('master');

  // 著者콘텐츠タイプ
  const author = await env.createContentTypeWithId('author', {
    name: 'Author',
    fields: [
      { id: 'name', name: 'Name', type: 'Symbol', required: true },
      { id: 'slug', name: 'Slug', type: 'Symbol', required: true },
      { id: 'bio', name: 'Bio', type: 'Text' },
      { id: 'avatar', name: 'Avatar', type: 'Link', linkType: 'Asset' },
    ],
  });
  await author.publish();

  // 카테고리
  const category = await env.createContentTypeWithId('category', {
    name: 'Category',
    fields: [
      { id: 'name', name: 'Name', type: 'Symbol', required: true },
      { id: 'slug', name: 'Slug', type: 'Symbol', required: true },
      { id: 'description', name: 'Description', type: 'Text' },
    ],
  });
  await category.publish();

  // ブ로그글
  const blogPost = await env.createContentTypeWithId('blogPost', {
    name: 'Blog Post',
    fields: [
      { id: 'title', name: 'Title', type: 'Symbol', required: true },
      { id: 'slug', name: 'Slug', type: 'Symbol', required: true },
      { id: 'excerpt', name: 'Excerpt', type: 'Text' },
      { id: 'body', name: 'Body', type: 'RichText' },
      { id: 'featuredImage', name: 'Featured Image', type: 'Link', linkType: 'Asset' },
      { id: 'author', name: 'Author', type: 'Link', linkType: 'Entry' },
      { id: 'category', name: 'Category', type: 'Link', linkType: 'Entry' },
      { id: 'tags', name: 'Tags', type: 'Array', items: { type: 'Symbol' } },
      { id: 'publishedAt', name: 'Published At', type: 'Date' },
    ],
  });
  await blogPost.publish();
}

콘텐츠の취득

> ContentfulのContent Delivery APIでブ로그글を
> 취득する클라이언트を作って。TypeScript타입も생성して。
// src/lib/contentful.ts
import { createClient, type Entry, type Asset } from 'contentful';

const client = createClient({
  space: process.env.CONTENTFUL_SPACE_ID!,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
});

// 미리보기用클라이언트
const previewClient = createClient({
  space: process.env.CONTENTFUL_SPACE_ID!,
  accessToken: process.env.CONTENTFUL_PREVIEW_TOKEN!,
  host: 'preview.contentful.com',
});

function getClient(preview = false) {
  return preview ? previewClient : client;
}

// 타입定義
interface BlogPostFields {
  title: string;
  slug: string;
  excerpt: string;
  body: any; // Rich Text Document
  featuredImage: Asset;
  author: Entry<AuthorFields>;
  category: Entry<CategoryFields>;
  tags: string[];
  publishedAt: string;
}

interface AuthorFields {
  name: string;
  slug: string;
  bio: string;
  avatar: Asset;
}

interface CategoryFields {
  name: string;
  slug: string;
  description: string;
}

// 글목록の취득
export async function getBlogPosts(options?: {
  limit?: number;
  skip?: number;
  category?: string;
  preview?: boolean;
}) {
  const { limit = 10, skip = 0, category, preview = false } = options || {};

  const query: any = {
    content_type: 'blogPost',
    order: ['-fields.publishedAt'],
    limit,
    skip,
    include: 2,
  };

  if (category) {
    query['fields.category.sys.contentType.sys.id'] = 'category';
    query['fields.category.fields.slug'] = category;
  }

  const entries = await getClient(preview).getEntries<BlogPostFields>(query);

  return {
    posts: entries.items,
    total: entries.total,
  };
}

// 個別글の취득
export async function getBlogPost(slug: string, preview = false) {
  const entries = await getClient(preview).getEntries<BlogPostFields>({
    content_type: 'blogPost',
    'fields.slug': slug,
    include: 2,
    limit: 1,
  });

  return entries.items[0] || null;
}

Next.jsでのISR구현

// app/blog/[slug]/page.tsx
import { getBlogPost, getBlogPosts } from '@/lib/contentful';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { notFound } from 'next/navigation';

export const revalidate = 60; // 60秒ごとに再검증

export async function generateStaticParams() {
  const { posts } = await getBlogPosts({ limit: 100 });
  return posts.map((post) => ({ slug: post.fields.slug }));
}

export default async function BlogPostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = await getBlogPost(params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-3xl mx-auto p-6">
      <h1 className="text-4xl font-bold">{post.fields.title}</h1>
      <p className="text-gray-500 mt-2">
        {new Date(post.fields.publishedAt).toLocaleDateString('en-US')}
      </p>
      <div className="prose mt-8">
        {documentToReactComponents(post.fields.body)}
      </div>
    </article>
  );
}

정리

ContentfulのAPIファーストなアプローチとClaude Codeを組み合わせれば、柔軟な콘텐츠관리システムを효율적으로구축할 수 있습니다。ブ로그CMS구축가이드SSR/SSG比較도 참고하세요.

Contentful의 상세 정보는Contentful공식 문서를 참고하세요.

#Claude Code #Contentful #CMS #ヘッドレスCMS #コンテンツ管理