Nextra와 LLMs를 이용한 MCP 기반 블로그 포스트 생성기 구축하기
Updated on

이 튜토리얼에서는 대형 언어 모델(LLM)과 채팅하며 콘텐츠 생성을 요청할 수 있는 기술적 솔루션을 만들어보겠습니다. 생성된 콘텐츠는 MDX 형식으로 작성되며, 자동으로 GitHub 저장소의 블로그에 새 게시물로 커밋됩니다. 본문에서는 Model Context Protocol (MCP) 통합을 위한 두 가지 구현 방식을 탐구합니다:
- 서버리스 방식: GitHub Actions 이용 — GitHub Actions 워크플로우를 활용하여 LLM 호출과 게시물 커밋 과정을 전체 자동화
- 호스팅 방식: Next.js API 이용 —
jojocys/nextra-agent-blog
와 같은 블로그 플랫폼에 간단한 MCP 서버역할의 Next.js API 엔드포인트를 구축하여, 실시간으로 LLM 요청 후 GitHub에 게시
MCP는 AI 어시스턴트와 외부 도구 및 데이터 연결을 위한 개방 표준입니다. AI 모델이 클라우드 서비스, 데이터베이스, GitHub, Slack 등과 인터랙션할 수 있도록 하는 범용 인터페이스를 제공하며, 각 도구와의 커스텀 통합 없이도 AI가 다양한 외부 시스템에서 작업을 수행할 수 있게 합니다 (Model Context Protocol (MCP) 설명 (opens in a new tab)). 즉, MCP는 AI 에이전트가 “도구”를 활용하여 내부 지식을 확장하거나 행동하는 능력을 지원합니다. 예를 들어, 이메일 발송, 코드 배포, 또는 블로그 게시와 같은 작업을 MCP를 통해 수행할 수 있습니다 (Cloudflare에서 원격 MCP 서버 구축 및 배포하기 (opens in a new tab)). 본 예제에서는 새로운 MDX 파일을 생성하고 이를 Git 저장소(블로그)에 커밋하는 것이 바로 이 “도구” 역할이 됩니다.
이 가이드를 따라하면, 단일 사용자로서 AI 에이전트가 요청하는 블로그 글이 자동으로 사이트에 나타나는 시스템을 갖추게 됩니다. MCP 클라이언트↔︎LLM, LLM↔︎GitHub 연결 구조를 이해하고, 인증 없이 단일 사용자 환경을 전제로 설명합니다.
솔루션 개요
먼저 전체 아키텍처와 워크플로를 개략적으로 정리해봅니다:
-
블로그 플랫폼: Next.js와 Nextra 'blog theme'(
jojocys/nextra-agent-blog
활용)을 기반으로 콘텐츠를 호스팅합니다. 블로그 포스트는 repository 내pages/posts
폴더의 MDX 파일로 저장되며, Nextra는 해당 MDX 파일들을 읽어 블로그 목록에 보여줍니다. 제목, 날짜 등은 frontmatter로 포함. -
LLM (AI 어시스턴트): OpenAI의 GPT, Anthropic의 Claude 등과 같은 AI 모델을 사용하여 블로그 내용 생성. 사용자는 채팅 또는 API 호출로 특정 주제 또는 요청을 보내면, AI는 MDX 포맷의 글을 생성하여 반환.
-
MCP 클라이언트 & 서버: MCP는 AI가 외부 도구를 호출하는 구조체입니다.
- 서버리스 접근(GitHub Actions): 워크플로우가 MCP 클라이언트 역할을 하여 LLM 호출과 포스트 커밋을 담당.
- 호스팅 API 접근(Next.js API): Next.js 자체 API 엔드포인트를 MCP 클라이언트/서버로 활용하여, 요청을 받아 LLM 호출 → 게시글 생성 → GitHub에 커밋의 전체 과정을 처리하는 역할.
-
GitHub 연동: 두 방식 모두 GitHub API를 통해 새로운 포스트를 repo에 추가합니다. Git operations 또는 HTTP API를 사용하며, 이를 위해 예제도 준비했습니다. (Cloudflare MCP 서버에 GitHub 연동 포함 (opens in a new tab)). 본 예제에서는 직접 구현하는 방법을 보여줍니다.
먼저, 서버리스 GitHub Actions 방식을 살펴보겠습니다.
방식 1: 서버리스 블로그 포스트 생성 & 게시 — GitHub Actions 활용
이 방법은 GitHub Actions 워크플로우를 이용하여, 요청 시 자동으로 블로그 포스트를 생성하고 게시하는 방식입니다. 서버 운영 없이, GitHub의 러너를 통해 작업이 수행됩니다. 전체 작업은 다음과 같습니다:
- 트리거: 사용자(본인)가 워크플로우를 수동 또는 이벤트 트리거(예: 디스패치 이벤트)로 시작하며, 포스트 주제 또는 요청을 입력.
- LLM 호출: 워크플로우가 OpenAI 또는 Claude API를 호출하여, MDX 형식의 콘텐츠를 요청.
- 파일 생성: AI 응답을 받아 MDX 내용으로
.mdx
파일을 만들어posts/
폴더에 저장. - 커밋 및 푸시: 새 파일을 커밋 & 푸시하여 블로그 게시.
아래는 .github/workflows/llm-agent.yml
예제입니다:
name: LLM 블로그 포스트 생성기
on:
workflow_dispatch:
inputs:
prompt:
description: "블로그 포스트 주제 또는 요청"
required: true
type: string
permissions:
contents: write
jobs:
generate_post:
runs-on: ubuntu-latest
steps:
- name: 저장소 체크아웃
uses: actions/checkout@v3
- name: jq 설치 (JSON 파싱용)
run: sudo apt-get -y install jq
- name: OpenAI로 블로그 콘텐츠 생성
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
echo "요청 주제: ${{ github.event.inputs.prompt }}"
REQUEST_DATA=$(jq -n --arg prompt "${{ github.event.inputs.prompt }}" '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": "당신은 기술 블로그 글 작성 도우미입니다. YAML frontmatter (제목, 날짜, 설명)와 내용을 포함하는 MDX 포맷의 블로그 글을 작성하세요."},
{"role": "user", "content": $prompt}
]
}')
RESPONSE=$(curl -sS -H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "$REQUEST_DATA" \
https://api.openai.com/v1/chat/completions)
POST_CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content')
TIMESTAMP=$(date +"%Y-%m-%d-%H%M")
FILENAME="posts/llm-post-$TIMESTAMP.mdx"
echo "$POST_CONTENT" > $FILENAME
- name: 변경사항 커밋 및 푸시
run: |
git config user.name "github-actions"
git config user.email "github-actions@users.noreply.github.com"
git add posts/*.mdx
git commit -m "AI 생성 블로그 포스트 추가"
git push
이 워크플로우는 수동 실행이 가능하며, 제공한 주제로 LLM이 콘텐츠를 만들고 파일로 저장 후 push합니다. 사이트 배포 환경이 트리거될 경우, 새 글이 블로그에 바로 반영됩니다.
요약:
이 방식은 별도 서버 없이, GitHub 인프라만으로 쉽게 자동화된 포스팅이 가능하며, 비용 효율적입니다. 다만, 게시물 검토 단계가 없다면, 완전 자동 게시가 되기 때문에 콘텐츠 검증이 필요할 수 있습니다.
방식 2: Next.js API를 활용한 실시간 MCP 서버
이 방법은 블로그 애플리케이션 자체에 MCP 서버 역할을 하는 API 엔드포인트를 구현하는 방식입니다. 사용자 인터페이스에서 요청 시, 서버가 LLM에 요청하고 Content를 받아 GitHub에 커밋하는 과정을 수행합니다.
구성 개념
- 환경설정:
.env.local
등에 OpenAI API 키, GitHub Personal Access Token, 소유자명, 저장소명 등 기재
OPENAI_API_KEY=xxx
GITHUB_PAT=xxx
GITHUB_REPO_OWNER=yourusername
GITHUB_REPO_NAME=nextra-agent-blog
- API 엔드포인트:
pages/api/generate-post.js
또는app/api/generate-post/route.js
에 구현 - 작동 흐름: 클라이언트에서 요청 → 서버에서 LLM 호출 및 MDX 콘텐츠 생성 → GitHub에 커밋파일 생성 → 응답
예제: pages/api/generate-post.js
import { Octokit } from "@octokit/rest";
import { Configuration, OpenAIApi } from "openai";
export default async function handler(req, res) {
const { prompt } = req.body;
if (!prompt) return res.status(400).json({ error: "No prompt" });
try {
const openaiConfig = new Configuration({ apiKey: process.env.OPENAI_API_KEY });
const openai = new OpenAIApi(openaiConfig);
const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [
{ role: "system", content: "MDX 블로그 포스트 작성자입니다. 제목, 날짜, 설명, 블로그 유형 포함." },
{ role: "user", content: prompt }
],
});
const mdxText = completion.data.choices[0].message.content;
// 제목 parse
const titleMatch = mdxText.match(/^title:\s*\"?(.+)\"?/m);
const title = titleMatch ? titleMatch[1] : "untitled";
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
const filename = `posts/${slug || "untitled"}-${Date.now()}.mdx`;
// 필요시 frontmatter 수정
let finalContent = mdxText;
if (!/^date:\s*/m.test(finalContent)) {
finalContent = finalContent.replace(/^(title:.*)$/m, `$1\ndate: ${new Date().toISOString()}`);
}
if (!/^type:\s*/m.test(finalContent)) {
finalContent = finalContent.replace(/^(title:.*)$/m, `$1\ntype: posts`);
}
// GitHub에 커밋
const octokit = new Octokit({ auth: process.env.GITHUB_PAT });
await octokit.rest.repos.createOrUpdateFileContents({
owner: process.env.GITHUB_REPO_OWNER,
repo: process.env.GITHUB_REPO_NAME,
path: filename,
message: `자동 생성: "${title}" 포스트`,
content: Buffer.from(finalContent, "utf-8").toString("base64"),
committer: { name: "MCP Bot", email: "mcp-bot@example.com" },
author: { name: "MCP Bot", email: "mcp-bot@example.com" }
});
res.status(200).json({ message: "포스트 생성 완료", filename });
} catch (err) {
console.error(err);
res.status(500).json({ error: err.message });
}
}
사용 방법
- API 요청 (
/api/generate-post
)에 대해 POST 요청, 내용을 JSON으로 전달 ({"prompt": "어떻게 MCP를 활용한 블로그 구축할까?"}
) - 서버는 LLM 호출 → 콘텐츠 생성 → GitHub 커밋 수행
- 결과 URL 또는 메시지로 피드백 받을 수 있음
이 방법은 인터랙티브한 UI를 만들거나, 특정한 요청에 대해 즉시 게시 가능하도록 할 때 적합하며, MCP의 클라이언트-서버 역할을 모두 수행할 수 있습니다.
결론
이 튜토리얼에서는 AI와 블로그 콘텐츠 작업을 연결하는 두 가지 방식을 소개했습니다:
- 서버리스: GitHub Actions — 비용 효율적, 자동화 가능, 별도 서버 불필요
- 직접 호스팅: Next.js API — 실시간 요청 처리, 사용자 인터페이스 연동 용이
이 두 방법 모두, MCP 표준을 활용하여 AI가 외부 시스템에 액션을 취하도록 하는 아이디어의 실현 사례입니다. 앞으로 더 확장하여, 다양한 도구와 연동하거나, 다중 사용자 환경을 만들 수도 있습니다.
이와 같은 MCP 기반 연동을 통해, AI의 기능을 외부 도구와 손쉽게 통합하여, 자동화 및 지능적 업무 수행을 한 단계 높일 수 있습니다.
참고 자료:
- Anthropic, Introducing the Model Context Protocol (MCP)
- Cloudflare, MCP를 활용한 확장 시스템 구축 사례
- MCP 오픈소스 서버 모음, 특히 GitHub 연동 서버 예제
이제 여러분의 블로그와 외부 도구들을 MCP 표준으로 확장하여, AI와 함께하는 스마트 워크플로우를 만들어 보세요!