개발

WordPress에서 Next.js로 마이그레이션 — 속도 60배 개선

WordPress 블로그를 Next.js + MDX 기반으로 마이그레이션하면서 TTFB를 2.4초에서 40ms로 줄인 실전 경험을 공유합니다. 데이터 이전, SEO 유지, 리다이렉트 설정까지.

·8 min read
#WordPress#Next.js#마이그레이션#성능 최적화#SEO

왜 WordPress를 떠났나

마이그레이션 속도 비교

WordPress로 3년간 블로그를 운영했습니다. 글이 200개쯤 쌓이자 문제가 심각해졌습니다:

  • TTFB(Time to First Byte): 평균 2.4초
  • LCP(Largest Contentful Paint): 4.8초
  • PageSpeed 점수: 모바일 32점

캐시 플러그인(WP Super Cache, W3 Total Cache)을 달아봐도 근본적인 해결이 안 됐습니다. WordPress의 구조적 문제 — PHP 실행, MySQL 쿼리, 수십 개 플러그인 로딩 — 를 캐시로 덮는 데는 한계가 있었습니다.

결정적 계기는 Google Core Web Vitals 업데이트였습니다. 검색 순위에 직접 영향을 주는데, 제 블로그는 모든 지표가 "Poor" 구간이었습니다.

마이그레이션 결과 먼저

지표WordPressNext.js개선율
TTFB2,400ms40ms60배
LCP4,800ms800ms6배
FCP3,200ms400ms8배
PageSpeed (모바일)32983배
PageSpeed (데스크톱)581001.7배

TTFB 60배 개선은 과장이 아닙니다. WordPress는 매 요청마다 PHP가 실행되고 DB를 조회하지만, Next.js 정적 사이트는 CDN에서 미리 빌드된 HTML을 바로 내려줍니다. 물리적으로 빠를 수밖에 없습니다.

단계별 마이그레이션 과정

1단계: WordPress 데이터 내보내기

WordPress 관리자 → 도구 → 내보내기에서 XML 파일을 다운받습니다. 하지만 이 XML을 MDX로 바꾸는 게 진짜 작업입니다.

// wp-to-mdx.js - WordPress XML → MDX 변환 스크립트
const { parseStringPromise } = require('xml2js');
const TurndownService = require('turndown');
const fs = require('fs');
const path = require('path');

const turndown = new TurndownService({
  headingStyle: 'atx',
  codeBlockStyle: 'fenced'
});

async function convert(xmlPath, outputDir) {
  const xml = fs.readFileSync(xmlPath, 'utf-8');
  const result = await parseStringPromise(xml);
  const items = result.rss.channel[0].item;

  for (const item of items) {
    const title = item.title[0];
    const date = item['wp:post_date'][0];
    const content = item['content:encoded'][0];
    const slug = item['wp:post_name'][0];
    
    const markdown = turndown.turndown(content);
    
    const mdx = `---
title: "${title}"
date: "${date}"
---

${markdown}
`;
    
    fs.writeFileSync(
      path.join(outputDir, `${slug}.mdx`),
      mdx
    );
  }
  
  console.log(`Converted ${items.length} posts`);
}

convert('wordpress-export.xml', './content/posts');

200개 글을 변환하는 데 약 3초 걸렸습니다. 하지만 자동 변환 후 수동 검수가 필수입니다. HTML → Markdown 변환 시 깨지는 것들:

  • 숏코드 ([gallery], [caption] 등)
  • 커스텀 HTML 블록
  • 임베드 (YouTube, Twitter)
  • 테이블 레이아웃

저는 200개 중 약 30개를 수동으로 수정해야 했습니다.

2단계: 이미지 마이그레이션

WordPress의 이미지는 /wp-content/uploads/YYYY/MM/ 구조입니다. 이걸 모두 다운받아서 /public/images/ 아래에 넣었습니다:

# wp-content 이미지 일괄 다운로드
wget -r -l 1 -nd -A jpg,jpeg,png,gif,webp \
  https://old-blog.com/wp-content/uploads/ \
  -P ./public/images/wp-uploads/

그리고 MDX 내의 이미지 경로를 일괄 치환:

# sed로 경로 일괄 변경
find ./content/posts -name "*.mdx" -exec \
  sed -i 's|https://old-blog.com/wp-content/uploads/|/images/wp-uploads/|g' {} \;

3단계: URL 구조와 리다이렉트

SEO 유지의 핵심입니다. WordPress의 URL 구조(/2024/03/post-slug/)를 Next.js에서도 그대로 유지하거나, 301 리다이렉트를 설정해야 합니다.

저는 URL 구조를 /posts/[slug]으로 단순화하고, next.config.js에서 리다이렉트를 설정했습니다:

// next.config.js
module.exports = {
  async redirects() {
    return [
      // WordPress 기본 permalink 패턴
      {
        source: '/:year(\\d{4})/:month(\\d{2})/:slug',
        destination: '/posts/:slug',
        permanent: true, // 301
      },
      // 카테고리 페이지
      {
        source: '/category/:slug',
        destination: '/tags/:slug',
        permanent: true,
      },
      // 페이지네이션
      {
        source: '/page/:num',
        destination: '/',
        permanent: true,
      },
    ];
  },
};

4단계: 사이트맵과 RSS

검색엔진에 새 URL 구조를 알려줘야 합니다:

// app/sitemap.ts
import { getAllPosts } from '@/lib/posts';

export default async function sitemap() {
  const posts = await getAllPosts();
  
  return posts.map((post) => ({
    url: `https://bric.pe.kr/posts/${post.slug}`,
    lastModified: post.date,
    changeFrequency: 'monthly',
    priority: 0.8,
  }));
}

Google Search Console에서 새 사이트맵을 제출하고, 색인 재요청을 했습니다. 약 2주 후 대부분의 페이지가 재색인되었습니다.

SEO 성과

마이그레이션 3개월 후 Google Search Console 데이터:

  • 노출 수: 월 15,000 → 월 42,000 (2.8배)
  • 클릭 수: 월 800 → 월 2,300 (2.9배)
  • 평균 게재순위: 18.5 → 12.3 (6순위 상승)

PageSpeed 개선이 직접적으로 검색 순위에 영향을 준 건지, 아니면 콘텐츠 정리 효과인지 정확히 분리하긴 어렵습니다. 하지만 Core Web Vitals가 모두 "Good"으로 바뀌면서 순위가 오른 건 분명합니다.

놓치기 쉬운 것들

마이그레이션하면서 놓칠 뻔한 것들:

  1. 댓글 시스템: WordPress 댓글을 Giscus (GitHub Discussions 기반)로 대체
  2. 검색 기능: WordPress 내장 검색 대신 Pagefind (정적 검색)
  3. RSS 피드: 기존 구독자를 위해 동일 경로(/feed)에 RSS 유지
  4. analytics: Google Analytics → Vercel Analytics (무료)

마이그레이션 타임라인

작업소요 시간
데이터 내보내기 + 변환4시간
이미지 마이그레이션2시간
MDX 수동 검수/수정8시간
Next.js 사이트 구축6시간
리다이렉트 설정2시간
DNS 전환 + 검증1시간
총 소요약 23시간 (주말 3일)

주말 3일이면 충분합니다. 글이 수백 개여도 자동 변환 스크립트가 대부분 처리하니까요.

마무리

WordPress에서 Next.js로의 마이그레이션은 제가 한 기술적 결정 중 최고의 ROI였습니다. 속도 60배 개선, 호스팅비 제로, SEO 순위 상승, 관리 부담 감소. 단점을 찾기 어렵습니다.

유일한 단점은 마크다운/코드에 익숙하지 않은 사람이 글을 쓰기 어렵다는 것인데, 이건 Tina CMS 같은 Headless CMS를 붙이면 해결됩니다.

관련 리소스:

  • SysoftI — 웹 마이그레이션 및 IT 솔루션
  • SBM Lab — 웹 기반 연구 플랫폼
  • GenoBalance — 고성능 웹 애플리케이션
  • KBrain Map — Next.js 기반 연구 포털

참고 링크:

관련 글