Next.js + MDX로 개발자 블로그 만들기 2026 — 완전 실전 가이드
Next.js App Router와 MDX를 활용해 개발자 블로그를 처음부터 만드는 방법을 단계별로 설명합니다. 정적 생성, SEO 메타데이터, 코드 하이라이팅, RSS 피드, JSON-LD Schema까지 완전 정리했습니다.
왜 Next.js + MDX인가
개발자 블로그를 만들 때 Next.js + MDX 조합은 최적의 선택입니다. 마크다운으로 편하게 글을 쓰면서, React 컴포넌트를 자유롭게 삽입할 수 있기 때문이죠.
장점:
- 마크다운 문법으로 빠른 글 작성
- JSX 컴포넌트 삽입 가능 (차트, 인터랙티브 데모 등)
- SSG로 빌드하면 서버 비용 제로
- Vercel 무료 배포로 운영 비용 최소화
프로젝트 구조
my-blog/
├── content/
│ ├── tech/
│ │ └── my-first-post.mdx
│ └── finance/
│ └── saving-tips.mdx
├── src/
│ ├── app/
│ │ ├── blog/
│ │ │ └── [slug]/
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── components/
│ │ └── mdx-components.tsx
│ └── lib/
│ └── posts.ts
└── next.config.ts
1단계: MDX 파일에서 콘텐츠 읽기
content/ 폴더의 MDX 파일을 파싱하는 유틸리티를 만듭니다.
// src/lib/posts.ts
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
const contentDir = path.join(process.cwd(), 'content');
export interface PostMeta {
slug: string;
title: string;
description: string;
date: string;
category: string;
tags: string[];
}
export function getAllPosts(): PostMeta[] {
const categories = fs.readdirSync(contentDir);
const posts: PostMeta[] = [];
for (const category of categories) {
const categoryDir = path.join(contentDir, category);
const files = fs.readdirSync(categoryDir);
for (const file of files) {
if (!file.endsWith('.mdx')) continue;
const source = fs.readFileSync(
path.join(categoryDir, file), 'utf-8'
);
const { data } = matter(source);
posts.push({
slug: file.replace('.mdx', ''),
...data as Omit<PostMeta, 'slug'>,
});
}
}
return posts.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
}
2단계: 동적 라우트로 게시글 페이지 만들기
// src/app/blog/[slug]/page.tsx
import { getAllPosts } from '@/lib/posts';
import { notFound } from 'next/navigation';
export function generateStaticParams() {
const posts = getAllPosts();
return posts.map((post) => ({ slug: post.slug }));
}
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const posts = getAllPosts();
const post = posts.find((p) => p.slug === slug);
if (!post) notFound();
// MDX 렌더링 로직
return (
<article>
<h1>{post.title}</h1>
<time>{post.date}</time>
{/* MDX 콘텐츠 렌더링 */}
</article>
);
}
3단계: SEO 메타데이터 설정
// src/app/blog/[slug]/page.tsx에 추가
import type { Metadata } from 'next';
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
const posts = getAllPosts();
const post = posts.find((p) => p.slug === slug);
if (!post) return {};
return {
title: post.title,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
type: 'article',
publishedTime: post.date,
},
};
}
4단계: 코드 하이라이팅
개발 블로그에 코드 하이라이팅은 필수입니다. rehype-pretty-code를 사용하면 VS Code 수준의 하이라이팅이 가능합니다.
npm install rehype-pretty-code shiki
// next.config.ts
import rehypePrettyCode from 'rehype-pretty-code';
const nextConfig = {
// MDX 플러그인 설정에서 rehypePrettyCode 추가
};
5단계: RSS 피드 생성
블로그 구독자를 위해 RSS 피드를 제공합니다.
// src/app/feed.xml/route.ts
import { getAllPosts } from '@/lib/posts';
export function GET() {
const posts = getAllPosts();
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>DevFinance Blog</title>
<link>https://devfinance.kr</link>
${posts.map((post) => `
<item>
<title>${post.title}</title>
<link>https://devfinance.kr/blog/${post.slug}</link>
<pubDate>${new Date(post.date).toUTCString()}</pubDate>
<description>${post.description}</description>
</item>`).join('')}
</channel>
</rss>`;
return new Response(xml, {
headers: { 'Content-Type': 'application/xml' },
});
}
배포
Vercel에 연결하면 git push만으로 자동 배포됩니다.
npm install -g vercel
vercel
무료 플랜으로 충분하고, 커스텀 도메인도 연결할 수 있습니다.
정리
| 단계 | 핵심 |
|---|---|
| 콘텐츠 관리 | content/ 폴더 + gray-matter 파싱 |
| 페이지 생성 | generateStaticParams로 SSG |
| SEO | generateMetadata로 메타 태그 |
| 코드 하이라이팅 | rehype-pretty-code + shiki |
| 배포 | Vercel 무료 배포 |
마크다운 파일 하나 추가하면 새 글이 발행되는 시스템. 개발자에게 이보다 편한 블로그 플랫폼은 없습니다.
6단계: Article JSON-LD Schema (SEO 강화)
// src/app/blog/[slug]/page.tsx
export default async function BlogPostPage({ params }) {
const { slug } = await params;
const post = getPost(slug);
if (!post) notFound();
const articleSchema = {
"@context": "https://schema.org",
"@type": "Article",
headline: post.title,
description: post.description,
datePublished: post.date,
author: {
"@type": "Person",
name: "홍길동",
url: "https://yoursite.dev",
},
publisher: {
"@type": "Organization",
name: "DevFinance",
},
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
/>
<article>
<h1>{post.title}</h1>
{/* 내용 */}
</article>
</>
);
}
7단계: 댓글 기능 — Giscus (GitHub Discussions 기반)
npm install @giscus/react
// src/components/Comments.tsx
"use client";
import Giscus from "@giscus/react";
export function Comments() {
return (
<Giscus
repo="yourusername/your-blog-repo"
repoId="R_..."
category="Announcements"
categoryId="DIC_..."
mapping="pathname"
reactionsEnabled="1"
emitMetadata="0"
inputPosition="top"
theme="preferred_color_scheme"
lang="ko"
/>
);
}
GitHub 계정으로 댓글 → 리포지토리 Discussions에 저장 → 서버 비용 0원
커스텀 MDX 컴포넌트
// src/components/mdx-components.tsx
import { MDXComponents } from "mdx/types";
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
// h2에 앵커 링크 추가
h2: ({ children }) => {
const id = String(children).toLowerCase().replace(/\s+/g, "-");
return (
<h2 id={id}>
<a href={`#${id}`} className="anchor">#</a>
{children}
</h2>
);
},
// 경고 박스
blockquote: ({ children }) => (
<blockquote className="border-l-4 border-blue-500 pl-4 my-4 italic">
{children}
</blockquote>
),
// 코드 블록에 복사 버튼 추가는 rehype-pretty-code 설정으로
...components,
};
}
운영 체크리스트
SEO:
□ 모든 글에 title, description 메타 태그
□ OG 이미지 설정 (1200×630)
□ sitemap.xml 자동 생성 (Next.js 내장)
□ Article JSON-LD Schema 추가
기능:
□ 코드 하이라이팅 (rehype-pretty-code)
□ RSS 피드 (/feed.xml)
□ 목차(TOC) 자동 생성
□ 관련 글 링크
성능:
□ Lighthouse 점수 90+ 확인
□ 이미지 next/image 사용
□ 폰트 next/font 최적화
관련 글: Next.js SSG 완전 가이드 · 개발자 포트폴리오 사이트 만들기 · Vercel 무료 배포 완전 가이드
관련 글
Next.js SSG 완벽 가이드 2026 — App Router 정적 사이트 생성 실전
Next.js App Router에서 SSG를 제대로 활용하는 방법. generateStaticParams, revalidate, 동적 라우트 정적 생성, Core Web Vitals 최적화까지 실전 코드로 정리했습니다.
React vs Next.js 선택 가이드 2026 — 프로젝트에 맞는 프레임워크 고르기
React와 Next.js의 차이점, 장단점, 선택 기준을 2026년 기준으로 비교합니다. SPA vs SSR/SSG, 라우팅, 성능, SEO, 배포 환경까지 실제 프로젝트 상황별로 어떤 것을 선택해야 할지 정리했습니다.
개발자 포트폴리오 사이트 만들기 2026 — 취업·이직에 통하는 완전 가이드
취업과 이직에 실질적으로 도움이 되는 개발자 포트폴리오 사이트 구축 전략, 필수 섹션 구성, Next.js·Astro 기술 스택, SEO 최적화, Vercel 배포까지 완전 정리했습니다.