React SPA의 치명적 한계: SEO와 검색 엔진 크롤러\n\n최근 Z-Labs 팀의 가장 큰 골칫거리는 "구글 애드센스 승인 보류"와 "저조한 자연 검색 트래픽"이었습니다. Z-Labs의 랜딩 페이지와 하위 서비스들은 최신 프론트엔드 빌드 툴인 Vite를 기반으로 한 순수 React SPA(Single Page Application)로 개발되어 있었습니다.\n\n빠른 화면 전환과 훌륭한 사용자 경험(UX)을 제공했지만, 클라이언트 사이드 렌더링(CSR)이라는 태생적 한계 때문에 구글 크롤러(Googlebot)가 방문했을 때는 자바스크립트가 실행되기 전의 "텅 빈 HTML" 뼈대만 보여준다는 치명적인 문제가 있었습니다.\n\n### 마이그레이션 결단: Next.js App Router 도입\n\n임시방편으로 `react-helmet-async` 등을 도입해 보았지만, 결국 근본적인 구조 개선이 필요하다는 결론에 도달했습니다. Node.js 생태계에서 가장 강력한 서버사이드 렌더링(SSR) 및 정적 사이트 생성(SSG) 프레임워크인 **Next.js 15 (App Router)**로의 전면 마이그레이션을 결정했습니다.\n\n**핵심 마이그레이션 작업:**\n\n1. **라우팅 시스템 전면 개편**: 기존 `react-router-dom`의 `<BrowserRouter>`와 `<Routes>` 구조를 갈아엎고, Next.js의 파일 시스템 기반 라우팅(`src/app/...`)으로 모든 페이지를 이동시켰습니다.\n2. **서버 컴포넌트와 클라이언트 컴포넌트의 분리**: `framer-motion` 애니메이션이 들어가거나 상태(State) 관리가 필요한 페이지 최상단에 `"use client"` 지시어를 추가하여 에러를 해결했습니다.\n3. **정적 빌드(SSG)를 위한 `generateStaticParams` 적용**: 동적 라우팅을 사용하는 블로그 포스트(`[slug]`)들이 빌드 시점에 완벽한 HTML로 구워지도록 레이아웃 레이어에 사전 렌더링 함수를 적용했습니다.\n\n### "use client"와 "SSR"의 아이러니 해결\n\n마이그레이션 중 가장 애를 먹었던 부분은 이메일 발송 기능을 담당하던 `@emailjs/browser` 라이브러리였습니다. Node.js 런타임에서 브라우저 전용 전역 객체(window, document)를 참조하려다 빌드 타임에 에러(`auth of a is undefined`)가 발생했습니다.\n\n이를 해결하기 위해 해당 라이브러리를 최상단에서 정적 Import 하지 않고, 폼이 제출되는 이벤트 핸들러 내부에서 **Dynamic Import(동적 임포트)**를 하도록 코드를 리팩토링하여 서버 렌더링 충돌을 우회했습니다.\n\n### 결과: 완벽한 정적 HTML 렌더링과 SEO 점수 폭발\n\n`npm run build`를 실행했을 때 나오는 `Route (app) ... (SSG) prerendered as static HTML` 로그를 보는 순간의 쾌감은 이루 말할 수 없었습니다.\n\n이제 Z-Labs의 모든 블로그 글과 서비스 페이지는 서버에서 완성된 형태의 풍부한 텍스트를 담은 HTML로 Vercel을 통해 서빙됩니다. 구글 애드센스 봇이 사이트를 스캔할 때 꽉 찬 콘텐츠를 즉시 읽어들일 수 있게 되었습니다. 이번 Node.js / Next.js 생태계로의 전환은 Z-Labs가 진정한 웹 플랫폼으로 도약하는 중요한 퀀텀 점프가 될 것입니다.