Use Cases

Claude Code로 블로그 CMS 구축하기

Next.js App Router 기반으로 블로그 CMS를 구축하는 과정을 데이터 모델, 글 관리 API, 마크다운 에디터, SEO 설정까지 정리했습니다.

Claude Code로 블로그 CMS 구축하기

자체 블로그 CMS를 가지고 있으면 디자인 자유도, SEO 제어, 성능 최적화를 온전히 내 손에 둘 수 있습니다. Claude Code를 활용하면 글 관리부터 미리보기, 게시 워크플로까지 갖춘 CMS를 짧은 시간 안에 구축할 수 있죠.

프롬프트 설계

> Next.js App Router로 블로그 CMS를 만들어줘.
> 마크다운 글 작성/수정/게시가 가능한 어드민 화면과
> SSG로 생성되는 프론트엔드까지 함께 만들어줘.
> 카테고리와 태그 필터링도 구현해줘.

글 데이터 모델 정의

// src/types/post.ts
export interface Post {
  id: string;
  title: string;
  slug: string;
  content: string;        // Markdown
  excerpt: string;
  coverImage?: string;
  category: string;
  tags: string[];
  status: 'draft' | 'published' | 'archived';
  publishedAt?: Date;
  createdAt: Date;
  updatedAt: Date;
  authorId: string;
}

export interface PostCreateInput {
  title: string;
  content: string;
  category: string;
  tags: string[];
  status?: 'draft' | 'published';
}

글 관리 API 구현

// src/app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { generateSlug } from '@/lib/utils';

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const category = searchParams.get('category');
  const tag = searchParams.get('tag');
  const status = searchParams.get('status') || 'published';

  const posts = await prisma.post.findMany({
    where: {
      status,
      ...(category && { category }),
      ...(tag && { tags: { has: tag } }),
    },
    orderBy: { publishedAt: 'desc' },
    select: {
      id: true,
      title: true,
      slug: true,
      excerpt: true,
      category: true,
      tags: true,
      publishedAt: true,
      coverImage: true,
    },
  });

  return NextResponse.json(posts);
}

export async function POST(request: NextRequest) {
  const body = await request.json();
  const slug = generateSlug(body.title);

  const post = await prisma.post.create({
    data: {
      ...body,
      slug,
      excerpt: body.content.substring(0, 160),
      publishedAt: body.status === 'published' ? new Date() : null,
    },
  });

  return NextResponse.json(post, { status: 201 });
}

마크다운 에디터 컴포넌트

// src/components/admin/MarkdownEditor.tsx
'use client';
import { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';

interface Props {
  initialContent?: string;
  onSave: (content: string) => void;
}

export function MarkdownEditor({ initialContent = '', onSave }: Props) {
  const [content, setContent] = useState(initialContent);
  const [isPreview, setIsPreview] = useState(false);

  return (
    <div className="border rounded-lg">
      <div className="flex border-b">
        <button
          onClick={() => setIsPreview(false)}
          className={`px-4 py-2 ${!isPreview ? 'bg-blue-50 font-bold' : ''}`}
        >
          편집
        </button>
        <button
          onClick={() => setIsPreview(true)}
          className={`px-4 py-2 ${isPreview ? 'bg-blue-50 font-bold' : ''}`}
        >
          미리보기
        </button>
      </div>
      {isPreview ? (
        <div className="prose p-4 max-w-none">
          <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
        </div>
      ) : (
        <textarea
          value={content}
          onChange={(e) => setContent(e.target.value)}
          className="w-full h-96 p-4 font-mono text-sm resize-none"
          placeholder="마크다운으로 글을 작성해 주세요..."
        />
      )}
      <div className="flex justify-end p-3 border-t">
        <button
          onClick={() => onSave(content)}
          className="bg-blue-600 text-white px-6 py-2 rounded"
        >
          저장
        </button>
      </div>
    </div>
  );
}

SEO 메타데이터 자동 생성

Claude Code에 부탁하면 글 내용에서 OGP 이미지를 자동 생성하거나 메타 태그를 구성하는 코드까지 함께 만들어 줍니다.

// src/lib/seo.ts
export function generatePostMeta(post: Post) {
  return {
    title: `${post.title} | MyBlog`,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.publishedAt?.toISOString(),
      tags: post.tags,
      images: post.coverImage ? [{ url: post.coverImage }] : [],
    },
  };
}

관련 글

CMS의 프론트엔드 부분은 SSR/SSG 비교 가이드가 도움이 됩니다. SEO 대책에 대해서는 SEO 최적화도 함께 확인해 보세요.

마크다운 처리 라이브러리를 고를 때는 플러그인 생태계가 풍부한 unified.js를 참고할 만합니다.

#Claude Code #CMS #blog #Next.js #TypeScript

Claude Code 워크플로우를 한 단계 업그레이드하세요

지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.

무료 제공

무료 PDF: 5분 완성 Claude Code 치트시트

주요 명령어, 단축키, 프롬프트 예시를 A4 한 장에 정리했습니다.

PDF 다운로드
M

이 글을 작성한 사람

Masa

Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.