고급Google

JavaScript SEO: CSR·SSR·ISR 인덱싱 차이와 대응법

핵심 요약 (TL;DR)

Googlebot의 JavaScript 렌더링 한계, CSR·SSR·SSG·ISR 방식별 인덱싱 차이, Next.js·Nuxt 기반 프로젝트의 JavaScript SEO 최적화 전략을 다룹니다.

읽기 22 2025-03-22

JavaScript SEO 문제의 본질

전통적인 HTML 정적 페이지와 달리, JavaScript로 렌더링되는 Single Page Application(SPA)은 구글이 처음 HTTP 응답을 받을 때 의미 있는 콘텐츠를 포함하지 않을 수 있습니다. 구글의 JavaScript 처리는 2단계 파이프라인으로 작동합니다.

단계시점설명
1단계: 크롤링 즉시 HTML 다운로드. JavaScript 실행 없음. 이 시점에 발견된 링크만 즉시 크롤 대기열 추가.
2단계: 렌더링 (WRS) 수 시간~수 주 후 Chromium 기반 렌더러(WRS)가 JS를 실행해 완성된 HTML 생성. 색인에 반영.

이 지연이 문제의 핵심입니다. SSR은 1단계에서 이미 완성된 HTML을 제공하므로 이 문제가 없습니다.

CSR1단계(크롤): 빈 HTML. 2단계(렌더): JS 실행 후 콘텐츠 생성 → 수 주 지연 가능
SSR1단계(크롤)에서 완성된 HTML 즉시 제공 → 지연 없음
SSG빌드 시 생성된 정적 HTML → 가장 빠른 TTFB + 지연 없음
ISRSSG + 주기적 재생성 → 최신성 확보 + 빠른 초기 응답

CSR·SSR·SSG·ISR SEO 비교표

렌더링 방식초기 HTML구글 인덱싱 속도TTBSEO 적합도사용 사례
CSR (Client-Side Rendering) 빈 HTML + JS 번들 느림 (2단계 대기) 느림 ❌ 최악 로그인 후 대시보드, 사내 툴
SSR (Server-Side Rendering) 완성된 HTML 빠름 중간 ✅ 우수 개인화 콘텐츠, 실시간 데이터
SSG (Static Site Generation) 완성된 정적 HTML 가장 빠름 가장 빠름 ✅ 최적 블로그, 문서, 마케팅 페이지
ISR (Incremental Static Regeneration) 완성된 HTML + 주기적 업데이트 빠름 빠름 ✅ 우수 쇼핑몰 제품, 뉴스, 자주 업데이트되는 콘텐츠

Next.js에서의 SEO 최적 렌더링 패턴

// 1. SSG — 빌드 시 정적 생성 (최고 SEO)
// app/blog/[slug]/page.tsx (기본값: SSG)
export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map(p => ({ slug: p.slug }))
}
export default async function Page({ params }) {
  const post = await getPost(params.slug)
  return <Article post={post} />
}

// 2. ISR — 주기적 재생성
export const revalidate = 3600 // 1시간마다 재생성

// 3. SSR — 요청마다 서버 렌더링
export const dynamic = 'force-dynamic'
// 또는 fetch에서 cache: 'no-store' 사용

// 4. 완전 CSR 피하기 — 검색 가능한 콘텐츠에는 사용 금지
// 'use client' + useEffect로 데이터 로드는 SEO에 위험

CSR 앱의 SEO 개선: Dynamic Rendering / Prerendering

기존 React SPA(Create React App 등)를 SSR로 전환하기 어려운 경우, Dynamic Rendering(크롤러에게만 SSR HTML 제공)이나 Prerendering(빌드 시 정적 HTML 생성)으로 SEO를 개선할 수 있습니다.

방법도구장점단점
Dynamic Rendering Rendertron, Prerender.io 기존 앱 변경 최소화 크롤러 User-Agent 감지 필요, 클oak 주의
Static Prerendering react-snap, Gatsby 정적 HTML 파일 생성, 빠른 TTFB 빌드 시간 증가, 동적 콘텐츠 제한
Next.js 마이그레이션 Next.js SSG/SSR/ISR 자유롭게 혼용 가능 코드 재작성 필요

Cloaking 주의: 크롤러에게만 다른 콘텐츠를 의도적으로 보여주는 것(Cloaking)은 구글 가이드라인 위반입니다. Dynamic Rendering은 기술적 한계에 의한 것이므로 구글이 허용하지만, 내용 자체가 달라서는 안 됩니다.

자주 묻는 질문 (FAQ)

Q. Google은 JavaScript를 완벽하게 렌더링할 수 있나요?
Google의 Web Rendering Service(WRS)는 최신 Chromium 기반으로 대부분의 JavaScript를 렌더링할 수 있습니다. 하지만 두 가지 한계가 있습니다: (1) 렌더링 지연 - 크롤링 후 렌더링까지 수 시간에서 수 주가 걸릴 수 있습니다. (2) 일부 API 미지원 - IndexedDB, WebSocket, 사용자 인터랙션 의존 콘텐츠는 렌더링되지 않습니다. 따라서 SEO가 중요한 콘텐츠는 SSR/SSG로 서버에서 렌더링하는 것이 안전합니다.
Q. React(CRA)로 만든 SPA를 SSR로 전환하지 않고 SEO를 개선할 수 있나요?
가능하지만 제한적입니다. (1) Dynamic Rendering: Rendertron 등으로 크롤러에게만 SSR HTML을 제공합니다. Google이 이 방법을 허용하지만, 클로킹 주의가 필요합니다. (2) Prerendering: react-snap 등으로 빌드 시 정적 HTML을 생성합니다. (3) 궁극적으로는 Next.js 마이그레이션이 가장 확실한 해결책입니다.
Q. Next.js App Router에서 'use client'를 쓰면 SEO에 문제가 되나요?
'use client' 자체가 문제는 아닙니다. 클라이언트 컴포넌트도 초기 HTML은 서버에서 렌더링됩니다(Server-Side Rendering). 문제는 useEffect에서 데이터를 fetch하여 콘텐츠를 렌더링하는 경우입니다. 이 경우 초기 HTML에 콘텐츠가 포함되지 않아 SEO에 불리합니다. 데이터 fetching은 서버 컴포넌트에서 처리하고, 인터랙션만 클라이언트 컴포넌트에서 처리하세요.
Q. SPA에서 라우팅 변경 시 Google은 각 페이지를 인식하나요?
Google의 WRS는 History API 기반 라우팅을 인식합니다. 하지만 모든 라우트가 별도의 HTML 응답으로 제공되는 것이 아니므로, 인덱싱이 지연되거나 누락될 수 있습니다. 각 라우트에 대한 고유 URL, 고유 title/description, 사이트맵 포함을 보장해야 합니다.
Q. Lazy Loading된 콘텐츠는 Google이 크롤링하나요?
스크롤 기반 Lazy Loading(Intersection Observer)은 Google WRS가 부분적으로 지원합니다. WRS는 페이지를 로드하고 스크롤을 시뮬레이션하지만, 무한 스크롤의 모든 콘텐츠를 로드하지는 않습니다. SEO가 중요한 콘텐츠는 초기 HTML에 포함시키고, 이미지만 Lazy Loading하는 것이 안전합니다.

지금 읽으신 SEO 지식, 바로 적용해보세요!

검색엔진 최적화는 실전입니다. SEO SOVISS의 무료 분석 도구로 내 웹사이트의 오디트 점수를 즉시 확인하고 기술적 문제점을 점검해보세요.

내 웹사이트 진단하기 →
주정만

AI 개발팀 팀장

주정만

LLM(대형 언어 모델)의 구동 원리를 리버스 엔지니어링하여, AI가 가장 선호하는 응답 구조(GEO)를 웹 기술로 구현합니다.

SEO SOVISS 전체 집필진 보기 →
JavaScript SEO: CSR·SSR·ISR 인덱싱 차이와 대응법