DevFinance
테크·5 min read

Next.js + MDX로 블로그 만들기 실전 가이드

Next.js App Router와 MDX를 활용해 개발자 블로그를 처음부터 만드는 방법을 단계별로 설명합니다. 정적 생성, 메타데이터, 코드 하이라이팅까지 다룹니다.

왜 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
SEOgenerateMetadata로 메타 태그
코드 하이라이팅rehype-pretty-code + shiki
배포Vercel 무료 배포

마크다운 파일 하나 추가하면 새 글이 발행되는 시스템. 개발자에게 이보다 편한 블로그 플랫폼은 없습니다.