taewoong.log
개발

Next.js로 개인 블로그 MVP 만들기

DB도 CMS도 없이 MDX 파일만으로 블로그를 만든 이유와, 일부러 빼버린 것들.

Next.jsMDXApp Router블로그

지난 글에서 나는 완벽하게 만들려다 늘 중간에 멈춘다고 고백했다. 이 블로그는 그 습관을 깨보려고 만든 거라, 만드는 방식부터 평소의 나와 반대로 갔다. 디자인을 다 잡고, 기능을 붙이고, 그러다 지쳐 멈추는 대신 — 홈, 글 목록, 글 상세 딱 3개만 만들고 일단 공개부터 했다.

이 글은 그 과정에서 내가 내린 선택들에 대한 기록이다. "이렇게 따라 만드세요"가 아니라, "나는 이렇게 정했고 그 이유는 이거였다"에 가깝다.

왜 블로그를 직접 만들었나

네이버, Tistory, Velog 등 여러 블로그들이 많다. 글만 쓸 거라면 거기가 훨씬 빠르다. 그런데도 직접 만든 이유는 세 가지다.

첫번째 이유는 공부다. 나는 프론트엔드 개발자고, 실제로 운영되는 무언가를 내 손으로 굴려봐야 배우는 게 보인다. 두번째 이유는 포트폴리오다. "블로그 만들 줄 안다"보다 "운영 중인 블로그가 있다"가 더 강하다. 세번째 이유는 소유권이다. 플랫폼 정책이 바뀌어도, 서비스가 문을 닫아도, 내 글과 도메인은 내 것으로 남는다.

물론 직접 만들면 귀찮다. 그 귀찮음을 감수할 만큼 위 세 가지가 나한테는 중요했다.

DB도 CMS도 쓰지 않았다

가장 먼저 정한 건 "안 만들 것"이었다. 데이터베이스도, 관리자 페이지도, 로그인도 만들지 않았다.

이유는 단순하다. 개인 블로그에 DB는 과하다. 글은 그냥 파일이면 된다. 글 하나가 .mdx 파일 하나이고, 그 파일들이 곧 콘텐츠다. 그러면

  • 운영 비용이 0에 수렴한다 (정적 사이트라 서버가 늘 떠 있을 필요가 없다)
  • 백업이 곧 git이다 (글 히스토리가 커밋 로그에 그대로 남는다)
  • 글쓰기는 그냥 에디터에서 파일을 하나 만드는 일이 된다

포기한 것도 있다. 실시간으로 글을 고치는 관리 화면이 없으니, 수정하려면 파일을 고치고 다시 배포해야 한다. 조회수 같은 동적인 데이터도 당장은 없다. 하지만 지금 단계에서 나한테 필요한 건 그런 기능이 아니라 글이 쌓이는 것이었다. 그래서 이 트레이드오프는 기꺼이 받아들였다.

글이 곧 파일이다

구조는 이렇게 잡았다. content/posts/ 안에 .mdx 파일을 두고, lib/posts.ts가 그 폴더를 읽어 frontmatter를 파싱하고 목록과 정렬을 만든다. 페이지는 Next.js App Router로 구성했다.

app/
  posts/[slug]/page.tsx   ← 글 상세
  posts/page.tsx          ← 글 목록
content/
  posts/*.mdx             ← 글 = 파일
lib/
  posts.ts                ← frontmatter 파싱 + 목록/정렬

각 글의 맨 위에는 frontmatter로 메타데이터를 적는다. 제목, 날짜, 카테고리, 태그, 공개 여부 같은 것들이다. 이게 사실상 "DB 한 줄" 역할을 한다.

---
title: "Next.js로 개인 블로그 MVP 만들기"
date: "2026-06-27"
category: "개발"
tags: ["Next.js", "MDX"]
published: true
---

lib/posts.ts는 이 파일들을 읽어서 published: true인 글만, 날짜 최신순으로 돌려준다. 목록 페이지도 상세 페이지도 결국 이 함수들 위에서 돈다. 데이터 소스가 폴더 하나라서 머릿속 모델이 단순하다.

의존성 하나를 고르는 데 들인 시간

MDX를 화면에 그리는 라이브러리를 고르는 단계에서 제일 오래 고민했다.

처음엔 많이들 쓰는 next-mdx-remote를 쓰려고 했다. 그런데 확인해보니 이 라이브러리는 저장소가 아카이브(유지보수 중단) 상태였다. 당장은 동작하더라도, 1년 넘게 굴릴 블로그의 핵심 의존성을 유지보수가 끊긴 패키지로 시작하는 건 내키지 않았다.

대안을 찾다가 그 포크이자 Next.js 공식 문서가 권장하는 next-mdx-remote-client로 정했다. API가 거의 동일해서 갈아타는 비용도 작았고, App Router의 서버 컴포넌트에서 쓰는 /rsc 경로도 그대로 있었다.

별것 아닌 결정처럼 보이지만, 나는 이 과정이 꽤 중요했다고 생각한다. "남들이 쓰니까"가 아니라 지금 그 패키지가 살아 있는지를 직접 확인하고 고른 것 — 의존성을 고를 때 마땅히 해야 하는 일을 한 거니까. 라이브러리 이름 하나에도 이유가 붙는 게 좋은 코드베이스라고 믿는다.

일부러 만들지 않은 것들

MVP에서 의식적으로 뺀 것들이다.

  • 다크모드
  • 검색
  • 댓글
  • 조회수
  • 태그 필터
  • shadcn/ui 같은 UI 라이브러리

전부 "있으면 좋은" 것들이다. 그리고 전부, 평소의 나였다면 여기에 매달리다 공개를 또 미뤘을 것들이다. 그래서 이번엔 규칙을 하나 세웠다. 공개를 막는 건 만들지 않는다. 위 기능들은 블로그가 세상에 나온 뒤에, 필요해질 때 하나씩 붙이면 된다.

완벽한 블로그와 공개된 블로그 중에 가치 있는 건 후자다. 아무도 안 보는 완벽한 코드보다, 누군가 읽을 수 있는 글 한 편이 낫다.

그래서, 일단 공개했다

그렇게 첫 두 편을 쓰고 Vercel에 올렸다.

배포 주소: https://taewoong.dev/

다음 단계는 이미 정해뒀다. SEO 기본기 세 가지 — sitemap, RSS, Open Graph 메타데이터 — 인데, 이건 App Router에서 각각 금방 붙는 것들이라 공개 직후 작업으로 미뤄뒀다. 역시, 이것 때문에 공개를 미루지는 않았다.

블로그를 키우는 일 자체가 내 장기 프로젝트다. 그러니 이 글도 완성본이 아니라 1차 버전이다. 일단 공개됐고, 그게 제일 중요하다.