개발··6분 읽기

Next.js + MDX로 3개 국어 블로그 만들기

Next.js 14 App Router와 MDX, next-intl을 조합해서 한/영/일 3개 국어 개인 블로그를 만든 과정을 정리했다.

왜 직접 만들었나

기존 블로그 플랫폼을 다 써봤다. Medium, Velog, 티스토리. 항상 어딘가 하나씩 아쉬웠다. 다국어 지원이 안 되거나, 코드 하이라이팅이 맘에 안 들거나, 디자인 커스터마이징이 제한적이거나.

결국 내가 원하는 걸 다 넣을 수 있는 블로그를 직접 만들기로 했다.

블로그 플랫폼 비교
Medium
다국어 X, 코드 블록 약함
Velog
한국어 전용, 커스텀 제한
티스토리
디자인 자유도 낮음
직접 만들기
전부 가능

기술 스택

이 블로그를 구성하는 기술들
Next.js 14프레임워크

App Router + SSG로 정적 사이트 생성

Tailwind CSS스타일링

@tailwindcss/typography로 글 본문 렌더링

MDX콘텐츠

next-mdx-remote + gray-matter로 파싱

next-intl다국어

/ko, /ja, /en 라우팅 자동 처리

Vercel배포

GitHub push → 자동 빌드/배포

next-themes다크모드

시스템 설정 연동 + 수동 토글

핵심: MDX 파이프라인

MDX 파일을 읽어서 프론트매터를 파싱하고, 글 목록을 만드는 유틸리티를 작성했다.

글이 화면에 나오기까지
1
MDX 파일 읽기

content/[locale]/[category]/[slug].mdx 경로에서 파일을 읽는다

2
프론트매터 파싱

gray-matter로 title, date, tags 등 메타데이터 추출

3
MDX → React 변환

next-mdx-remote가 마크다운 + JSX를 React 컴포넌트로 변환

4
커스텀 컴포넌트 주입

ChatThread, BarChart 같은 MDX 컴포넌트를 매핑

5
정적 페이지 생성

빌드 타임에 모든 글을 HTML로 미리 생성 (SSG)

TypeScript
// src/lib/mdx.ts — 글 하나 읽기
export function getPostBySlug(locale: Locale, category: Category, slug: string) {
  const filePath = path.join(contentDir, locale, category, `${slug}.mdx`);
  const fileContent = fs.readFileSync(filePath, "utf-8");
  const { data, content } = matter(fileContent);
  return { ...data, content, readingTime: readingTime(content) };
}

포인트는 content/[locale]/[category]/[slug].mdx 구조다. 로케일과 카테고리 조합으로 파일 시스템 기반 라우팅을 구현했다. Next.js App Router의 동적 세그먼트와 1:1로 대응되니까 직관적이다.

디렉토리 구조

프로젝트 구조
src/app/[locale]/페이지 라우팅
├── page.tsx홈페이지
├── [category]/[slug]/page.tsx글 상세 페이지
content/MDX 콘텐츠
├── ko/dev/한국어 개발 글
├── ko/life/한국어 일상 글
src/components/재사용 컴포넌트
├── mdx/MDX 전용 커스텀 컴포넌트
messages/i18n 번역 파일 (ko, en, ja)

다국어 처리가 생각보다 까다로웠다

next-intl을 쓰면 라우팅 자체는 간단하다. /ko/dev/..., /en/dev/... 이렇게 URL에 로케일을 넣으면 된다.

문제는 콘텐츠다. 같은 글의 한국어 버전과 영어 버전이 별도 MDX 파일로 존재해야 하는데, 모든 글을 번역할 필요는 없다. 개발 글은 영어 번역 우선, 일본 여행 글은 일본어 번역 우선, 일상 에세이는 한국어 전용. 이 규칙을 파일 시스템 구조로 자연스럽게 표현할 수 있는 게 MDX 기반의 장점이다.

UI 텍스트 번역은 messages/ko.json, messages/en.json, messages/ja.json에 모아두고 useTranslations 훅으로 가져온다. 이건 크게 어렵지 않았다.

배포: Vercel + GitHub

배포는 놀라울 정도로 간단하다. GitHub 레포에 push하면 Vercel이 알아서 빌드하고 배포한다. 프리뷰 배포도 자동으로 만들어주니까 PR 단위로 확인할 수 있다.

SSG(정적 사이트 생성)로 빌드하니까 성능도 빠르다. 블로그 특성상 콘텐츠가 빌드 타임에 결정되니까 SSG가 딱 맞는다.

다음 단계

앞으로 할 일
giscus 댓글 시스템
GitHub Discussions 기반
RSS 피드 자동 생성
구독자용
Vercel Analytics
트래픽 분석
OG Image 자동 생성
SNS 공유용 썸네일

하나씩 진행하면서 다 기록할 예정이다.


다음 글: 내 API 키가 털렸다 — 이 블로그 만들면서 생긴 건 아니고, 다른 프로젝트에서 터진 사고인데... 꽤 충격적이었다.

관련 글

개발2026년 4월 2일

사고 도구에 AI가 꼭 필요한가

모든 프로덕트에 AI를 붙이는 시대. 하지만 Dimension은 AI 없이도 작동하는 사고 도구를 지향한다. 왜 그런 선택을 했는지, 그리고 이건 노트 앱과 어떻게 다른지.

개발2026년 4월 1일

생각에도 높이가 있다

같은 주제를 다뤄도 코드 레벨과 철학 레벨은 완전히 다른 높이다. Z축(추상도)이라는 개념으로 사고의 고도를 분석해본다.