Claude Code로 블로그 CMS 만들기: Astro MDX, 다국어 SEO, QA와 수익화 흐름
Claude Code와 Astro MDX로 스키마, 번역, SEO, QA, CTA까지 연결되는 블로그 CMS를 구축합니다.
블로그 CMS는 글쓰기 도구가 아니라 운영 시스템이다
기술 블로그가 자주 올라와도 교육 문의, 템플릿 판매, 컨설팅으로 이어지지 않는 경우가 많습니다. 원인은 글의 양보다 게시 전 운영에 있습니다. 메타데이터가 흔들리고, 번역이 동기화되지 않고, 코드가 실행되지 않으며, 마지막 CTA가 독자의 다음 행동과 맞지 않으면 좋은 글도 성과를 내기 어렵습니다.
CMS는 Content Management System, 즉 콘텐츠를 정해진 형식으로 저장하고 편집하고 검수하고 배포하는 시스템입니다. Astro의 Content Collections와 MDX를 쓰면 파일 기반으로도 충분히 엄격한 CMS를 만들 수 있습니다. Claude Code는 여기서 초안 작성자뿐 아니라 편집 운영자, 번역 검수자, QA 도구 작성자로 쓰는 것이 좋습니다.
관련 글로 Claude Code와 Contentful CMS, 콘텐츠 퍼널 감사, RSS 구현, 사이트맵 생성을 함께 보면 흐름이 분명해집니다. 공식 문서는 Astro Content Collections, MDX integration, Sitemap integration을 기준으로 삼으세요.
역할을 나누면 Claude Code가 더 정확해진다
“블로그 CMS를 만들어줘”라는 요청은 너무 넓습니다. 스키마, 에디터, API, 미리보기, 배포가 한꺼번에 섞일 수 있습니다. 파일 기반 Astro CMS에서는 책임을 작게 나누는 편이 안전합니다.
| 영역 | 역할 | Claude Code에 맡길 일 |
|---|---|---|
| content schema | frontmatter 필드와 타입 정의 | content.config.ts 작성, 타입 오류 수정 |
| MDX article | 본문, 표, 코드, CTA 관리 | 초안 개선, 예제 보강 |
| localization | 같은 slug를 여러 언어에 유지 | 누락 파일 탐지, 자연스러운 번역 |
| preview | 게시 전 화면 확인 | 개발 서버 실행, 링크와 레이아웃 점검 |
| SEO | title, description, OGP, sitemap 관리 | 메타데이터 감사 |
| QA gate | 문제 있는 글을 게시 전에 차단 | Node 검사 스크립트 작성 |
| monetization | 상품, 교육, 상담으로 연결 | CTA 위치와 문구 검토 |
실제 사용 사례 3가지
첫째, 개인 개발자는 Claude Code 튜토리얼을 유료 템플릿이나 프롬프트 라이브러리로 연결할 수 있습니다. 이때 CMS에는 ctaLabel, ctaUrl, relatedPosts, updatedDate, heroImage가 필요합니다.
둘째, 회사 기술 블로그는 잘못된 정보를 막아야 합니다. 오래된 API 이름, 실행되지 않는 코드, 일부 언어만 업데이트된 번역, 깨진 OGP 이미지는 신뢰를 떨어뜨립니다. Claude Code에는 작성보다 공개 전 검사를 맡기는 것이 효과적입니다.
셋째, 다국어 SEO에서는 같은 slug가 계약입니다. 제목은 현지 독자에게 맞게 바꾸되, 핵심 주장과 코드 예제, CTA 의도는 맞춰야 합니다.
site/src/content/blog/claude-code-blog-cms.mdx
site/src/content/blog-en/claude-code-blog-cms.mdx
site/src/content/blog-zh/claude-code-blog-cms.mdx
site/src/content/blog-ko/claude-code-blog-cms.mdx
site/src/content/blog-es/claude-code-blog-cms.mdx
site/src/content/blog-fr/claude-code-blog-cms.mdx
site/src/content/blog-de/claude-code-blog-cms.mdx
site/src/content/blog-pt/claude-code-blog-cms.mdx
site/src/content/blog-hi/claude-code-blog-cms.mdx
site/src/content/blog-id/claude-code-blog-cms.mdx
실행 가능한 Astro content schema
schema는 콘텐츠의 약속입니다. title은 문자열, description은 120자 이하, lang은 정해진 언어 코드여야 한다는 식으로 빌드 전에 확인합니다.
// src/content.config.ts
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";
const blogSchema = z.object({
title: z.string().min(20).max(80),
description: z.string().min(40).max(120),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date(),
category: z.enum(["getting-started", "tips-and-tricks", "use-cases", "comparison", "advanced"]),
tags: z.array(z.string()).min(2).max(8),
heroImage: z.string().startsWith("/images/"),
draft: z.boolean().default(false),
requireAllLocales: z.boolean().default(false),
lang: z.enum(["ja", "en", "zh", "ko", "es", "fr", "de", "pt", "hi", "id"]),
ctaLabel: z.string().max(40).optional(),
ctaUrl: z.string().url().optional(),
});
const makeBlogCollection = (base: string) =>
defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base }),
schema: blogSchema,
});
export const collections = {
blog: makeBlogCollection("./src/content/blog"),
"blog-en": makeBlogCollection("./src/content/blog-en"),
"blog-zh": makeBlogCollection("./src/content/blog-zh"),
"blog-ko": makeBlogCollection("./src/content/blog-ko"),
"blog-es": makeBlogCollection("./src/content/blog-es"),
"blog-fr": makeBlogCollection("./src/content/blog-fr"),
"blog-de": makeBlogCollection("./src/content/blog-de"),
"blog-pt": makeBlogCollection("./src/content/blog-pt"),
"blog-hi": makeBlogCollection("./src/content/blog-hi"),
"blog-id": makeBlogCollection("./src/content/blog-id"),
};
MDX frontmatter 예시
MDX는 컴포넌트를 쓸 수 있는 Markdown입니다. frontmatter는 목록, RSS, OGP, sitemap, CTA에 필요한 정보를 담는 머리말입니다.
---
title: "Claude Code로 블로그 CMS 만들기: Astro MDX, 다국어 SEO, QA와 수익화 흐름"
description: "Claude Code와 Astro MDX로 스키마, 번역, SEO, QA, CTA까지 연결되는 블로그 CMS를 구축합니다."
pubDate: "2025-12-22"
updatedDate: "2026-06-02"
category: "use-cases"
tags: ["Claude Code", "CMS", "블로그 운영", "Astro", "MDX"]
heroImage: "/images/hero/hero-036.png"
lang: "ko"
ctaLabel: "Claude Code 콘텐츠 운영 상담 예약"
ctaUrl: "https://example.com/consulting"
---
## 첫 섹션
이 글은 작성, 번역, SEO, QA, 수익화 CTA를 하나의 게시 흐름으로 연결합니다.
Node 검증 스크립트
scripts/validate-blog-cms.mjs로 저장하고 사이트 루트에서 node scripts/validate-blog-cms.mjs claude-code-blog-cms를 실행합니다.
// scripts/validate-blog-cms.mjs
import fs from "node:fs";
import path from "node:path";
const slug = process.argv[2];
if (!slug) {
console.error("Usage: node scripts/validate-blog-cms.mjs <slug>");
process.exit(1);
}
const locales = [["blog", "ja"], ["blog-en", "en"], ["blog-zh", "zh"], ["blog-ko", "ko"], ["blog-es", "es"], ["blog-fr", "fr"], ["blog-de", "de"], ["blog-pt", "pt"], ["blog-hi", "hi"], ["blog-id", "id"]];
const root = path.join(process.cwd(), "src", "content");
const failures = [];
function readFrontmatter(source) {
const match = source.match(/^---\n([\s\S]*?)\n---/);
if (!match) return {};
return Object.fromEntries(match[1].split("\n").flatMap((line) => {
const index = line.indexOf(":");
if (index === -1) return [];
return [[line.slice(0, index).trim(), line.slice(index + 1).trim().replace(/^"|"$/g, "")]];
}));
}
for (const [dir, lang] of locales) {
const file = path.join(root, dir, `${slug}.mdx`);
const source = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
const data = readFrontmatter(source);
if (!source) failures.push(`${dir}: missing file`);
if (data.lang !== lang) failures.push(`${dir}: wrong lang`);
if (!data.updatedDate) failures.push(`${dir}: missing updatedDate`);
if ((data.description || "").length > 120) failures.push(`${dir}: description too long`);
if (!/https:\/\/docs\.astro\.build/.test(source)) failures.push(`${dir}: missing official docs`);
if (!/\]\(\/blog\/claude-code-/.test(source)) failures.push(`${dir}: missing internal link`);
if (!/(CTA|상담|consult|training)/i.test(source)) failures.push(`${dir}: missing CTA`);
if ((source.match(/`{3}/g) || []).length < 6) failures.push(`${dir}: fewer than three code blocks`);
}
if (failures.length) {
console.error(failures.map((item) => `- ${item}`).join("\n"));
process.exit(1);
}
console.log(`OK: ${slug} passed localized CMS checks.`);
Claude Code에 줄 게시 전 프롬프트
Rewrite the article for slug <slug> as a production-ready Astro MDX blog post.
- Edit only the localized files for this slug.
- Keep heroImage and category.
- Add updatedDate: "2026-06-02".
- Keep description within 120 characters.
- Include 3 real use cases, concrete pitfalls, runnable code, official Astro docs, internal links, and a monetization CTA.
- Report changed files and focused check results.
실패 사례와 확인 결과
대표적인 실패는 한 언어만 갱신하는 것, Claude Code에 너무 넓은 쓰기 범위를 주는 것, 실행되지 않는 코드 블록을 올리는 것, CTA를 광고 문구처럼만 쓰는 것입니다. RSS, sitemap, OGP도 게시 흐름에 포함해야 합니다. 글 파일이 있어도 피드에 없고 검색엔진에 전달되지 않으면 독자에게 도달하지 않습니다.
직접 검증했을 때 schema는 날짜 누락을 잡았고, Node 스크립트는 긴 description, 공식 링크 누락, 내부 링크 누락, CTA 누락을 발견했습니다. 다만 한국어 번역의 자연스러움과 교육/컨설팅 CTA의 설득력은 자동화로 충분히 판단할 수 없어, 마지막에는 사람이 읽고 확인해야 합니다.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Obsidian 메모를 CLAUDE.md로 바꾸는 Claude Code 워크플로
Obsidian 작업 메모를 CLAUDE.md 운영 노트로 정리해 Claude Code 세션의 문맥 반복을 줄입니다.
Claude Code Revenue CTA Routing: 글에서 PDF, Gumroad, 상담으로 보내기
독자 의도에 따라 무료 PDF, Gumroad 상품, 상담으로 나누는 Claude Code CTA 설계입니다.
Claude Code 팀 인계 규칙: 리뷰 증거, 권한, 롤백, 수익 경로까지 넘기는 법
Claude Code 작업을 팀에 넘길 때 필요한 증거, 권한 규칙, 롤백, 무료 PDF, Gumroad, 상담 경로 체크리스트.