JavaScript SEO: 구글이 내 콘텐츠를 보지 못하는 이유
페이지는 Chrome에서 완벽하게 동작하지만 구글은 빈 제목과 푸터 몇 줄만 색인합니다. 개발 팀은 최신 크롤러가 JavaScript를 실행한다고 하고, SEO 팀은 URL이 전혀 가시성을 얻지 못하는 것을 봅니다. 양쪽 모두 반만 맞습니다.
구글은 JavaScript를 렌더링할 수 있지만, 그렇다고 모든 JavaScript 애플리케이션이 빠르게 크롤링되고, 올바르게 이해되고, 완전히 색인되는 것은 아닙니다. 구글이 실제로 거치는 세 단계, 즉 HTML 크롤링, JavaScript 렌더링, 결과 색인을 통해 페이지를 진단하세요.
구글이 JavaScript 페이지를 처리하는 방식
1단계: 크롤링
Googlebot은 URL을 가져와 HTTP 상태, 헤더, robots 지시문, 초기 HTML을 읽습니다. 응답에 있는 <a> 링크, canonical, 제목, 콘텐츠는 이미 추출할 수 있습니다.
서버가 404, 500을 반환하거나, 크롤링을 차단하거나, noindex를 보내면 애플리케이션이 실행되기 전에 처리가 멈출 수 있습니다.
2단계: 렌더링
200 페이지는 렌더링 대기열에 들어갈 수 있습니다. 헤드리스 Chromium이 JavaScript를 실행하고, 허용된 리소스를 가져오고, 렌더링된 DOM을 만듭니다. 대기 시간은 크롤러의 자원에 따라 달라집니다.
3단계: 색인
구글은 렌더링된 HTML에서 콘텐츠와 링크를 읽은 다음 canonical, 품질, 중복, 색인 가능성을 평가합니다. 렌더링에 성공했다고 색인이 보장되지는 않습니다. 페이지는 가치와 일관된 신호를 제공해야 합니다.
가장 빠른 테스트: JavaScript 비활성화
페이지 소스 보기나 curl로 HTML 응답을 확인하세요. 스크립트가 실행된 뒤의 DOM을 보여 주는 DevTools Elements에만 의존하지 마세요.
다섯 가지를 확인하세요.
- 제목과 메타 설명이 있나요?
- H1과 핵심 문구가 보이나요?
- 내부 경로가 실제
<a href>링크인가요? - canonical과 robots 태그가 처음부터 올바른가요?
- 서버가 올바른 상태를 반환하나요?
응답에 <div id="app"></div>만 있다면 발견 가능성이 렌더링에 달려 있습니다. 이것이 자동으로 고장 난 것은 아니지만 색인과 디버깅이 필요 이상으로 느려집니다.
흔한 JavaScript SEO 실패 일곱 가지
1. 사용자 상호작용 후에만 콘텐츠가 나타남
Googlebot에게는 탭을 클릭하거나, 아코디언을 펼치거나, 무한 스크롤을 조작하거나, "더 불러오기"를 누를 의무가 없습니다. 동작 후에 가져오는 콘텐츠는 발견되지 않을 수 있습니다.
중요한 콘텐츠는 HTML에 렌더링하거나, 색인할 가치가 있는 각 상태마다 별도의 URL과 크롤링 가능한 링크를 제공하세요. 콘텐츠가 이미 DOM에 있는 아코디언이 클릭한 뒤에만 가져오는 아코디언보다 안전합니다.
2. 클라이언트 라우트가 항상 200을 반환
SPA는 "찾을 수 없음"을 표시하면서 서버는 200을 반환해 소프트 404가 생길 수 있습니다. 반대의 경우도 있습니다. 유효한 라우트가 프런트엔드 라우터가 시작되기 전에 서버 404를 받습니다.
색인 가능한 모든 URL에는 의미 있는 서버 상태가 필요합니다. 없는 페이지는 404, 이동한 콘텐츠는 리디렉션, 일시적인 애플리케이션 오류는 5xx를 반환합니다.
3. 크롤링할 수 없는 링크
다음은 사용자에게는 내비게이션처럼 보이지만 신뢰할 수 있는 크롤링 경로가 아닙니다.
<span onclick="goTo('/products')">Products</span>
대신 다음을 사용하세요.
<a href="/products">Products</a>
앵커에 실제 URL이 있기만 하면 클라이언트 라우터가 여전히 내비게이션을 가로챌 수 있습니다.
4. JavaScript가 canonical을 덮어씀
canonical은 HTML 소스에 있고 안정적으로 유지되어야 합니다. 소스는 URL A를 가리키는데 스크립트가 B로 바꾸면 크롤러는 서로 다른 단계에서 상충하는 신호를 받습니다. 마찬가지로 JavaScript가 noindex를 제거해 주기를 기대하지 마세요. 구글이 지시문을 보고 렌더링을 건너뛸 수 있습니다.
5. robots 규칙이 번들이나 API를 차단
애플리케이션이 콘텐츠를 구성하려고 JavaScript, CSS, 공개 API가 필요할 수 있습니다. Googlebot이 그 리소스를 로드하지 못하면 렌더링 결과가 비거나 불완전할 수 있습니다.
비공개 API를 보호하세요. 공개 렌더링과 리소스 접근은 브라우저 네트워크 도구 및 Search Console URL 검사로 확인합니다. Cloudflare 탐지만으로 비공개 WAF 정책을 검증할 수 없습니다.
6. 렌더링이 로컬 스토리지, 쿠키, 위치에 의존
Googlebot은 새 방문자처럼 도착합니다. localStorage 토큰, 특수한 동의 쿠키, 위치 결과가 있어야만 핵심 콘텐츠가 나타난다면 크롤러는 빈 상태를 받을 수 있습니다.
유용한 기본 버전을 제공하세요. 개인화는 이를 보강해야 하며 URL의 주제 전체를 대체해서는 안 됩니다.
7. 하이드레이션 실패
SSR이 완전한 HTML을 보내더라도 하이드레이션 오류가 콘텐츠를 지우거나 다른 DOM을 만들 수 있습니다. 검색은 여전히 응답을 읽을 수 있지만 사용자 경험과 렌더링 후 링크가 깨질 수 있습니다.
운영 환경의 콘솔 오류, 하이드레이션 불일치 경고, 실패한 요청을 모니터링하세요. 봇이나 느린 기기에만 영향을 주는 오류는 일반 QA에서는 놓치기 쉽습니다.
CSR, SSR, SSG, 하이브리드 렌더링
| 렌더링 모델 | SEO 이점 | 위험 |
|---|---|---|
| CSR | 유연한 프런트엔드 아키텍처 | 렌더링 의존성과 빈 앱 셸 |
| SSR | 콘텐츠가 HTML로 도착 | 서버 부하와 캐시 복잡도 |
| SSG | 빠르고 안정적이며 크롤러 친화적 | 재빌드 전략 필요 |
| 하이브리드 | 라우트마다 렌더링 방식을 맞춤 | 유지해야 할 규칙이 늘어남 |
블로그, 랜딩 페이지, 카테고리, 문서는 보통 SSR이나 SSG의 이점을 누립니다. 인증이 필요한 대시보드는 색인할 필요가 없으므로 CSR이 전적으로 적절합니다.
동적 렌더링은 봇에게만 특별한 HTML을 보내는 데 쓰였지만 두 버전을 유지하면 차이가 생기고 비용이 듭니다. 가능하면 사용자와 크롤러에게 미리 렌더링된 같은 핵심 콘텐츠를 제공하세요.
단계별 JavaScript SEO 감사
1단계: 세 가지 버전 비교
다음을 나란히 놓으세요.
- 소스 HTML;
- 브라우저에서 렌더링된 DOM;
- URL 검사의 렌더링된 HTML 또는 스크린샷.
내 브라우저에만 있고 URL 검사에는 없는 것은 강력한 단서입니다.
2단계: 요청과 리소스 검토
403, 404, CORS 오류, 타임아웃, robots 차단이 발생한 자산을 찾으세요. 메인 번들이 로드되더라도 API 하나가 실패하면 콘텐츠 영역 전체가 사라질 수 있습니다.
3단계: 메타데이터 감사
제목, 설명, canonical, robots 지시문, 구조화 데이터는 라우트별로 달라야 합니다. SPA는 이전 라우트의 메타데이터를 유지하거나 정규화되지 않은 URL에서 canonical을 만드는 경우가 많습니다.
메타 태그 검사기는 URL에 제공된 제목, canonical, Open Graph 태그, 헤딩을 보여 줍니다.
4단계: 링크와 사이트맵 점검
내부 링크에는 href가 있어야 하고, 사이트맵에는 canonical URL만 있어야 하며, 나열된 모든 URL은 의도한 상태를 반환해야 합니다. 사이트맵은 크롤링할 수 없는 내비게이션을 보완할 수 없습니다.
5단계: 성능 측정
큰 번들과 긴 작업은 사용자에게 불편을 주고 렌더링 비용을 높입니다. 페이지 속도 검사기를 실행해 LCP, INP, TBT, 사용하지 않는 JavaScript를 확인하세요.
6단계: 홈페이지만이 아니라 템플릿 테스트
글, 카테고리, 상품, 필터 결과, 페이지네이션, 없는 URL을 샘플링하세요. JavaScript SEO 결함의 대부분은 개발자가 수동으로 거의 열어 보지 않는 라우트에 있습니다.
전형적인 실패 사례
한 채용 사이트는 카테고리 컴포넌트가 마운트된 뒤 Vue로 채용 카드를 가져옵니다. 카테고리 H1은 소스에 있지만 모든 채용 카드는 JavaScript로 생성됩니다. 각 카드는 href 없이 <div>에 @click을 사용합니다.
방문자는 평소처럼 채용 공고를 둘러볼 수 있지만, 사이트맵에 채용 공고 URL이 나열되어 있어도 크롤링 가능한 경로와 내부 링크가 없어서 구글이 세부 정보를 찾는 데 어려움을 겪습니다. 발견 가능성을 높이려면 XML 사이트맵 검사기를 사용하세요.
해결책은 Vue를 없애는 것이 아닙니다. 팀은 카드를 <a href>로 바꾸고, 초기 목록을 서버 렌더링하고, 상호작용하는 필터는 클라이언트에 두고, 만료된 공고에는 진짜 404를 반환했습니다. 아키텍처는 여전히 최신이면서 발견 가능성이 더 이상 운에 달려 있지 않게 되었습니다.
릴리스 전 체크리스트
- 핵심 콘텐츠가 HTML에 있거나 안정적으로 렌더링된다.
- 유효한 라우트는
200, 제거된 라우트는404/410을 반환한다. - 중요한 내비게이션이
<a href>를 사용한다. - canonical과 robots 지시문이 소스에 있고 안정적으로 유지된다.
- 필요한 CSS, JavaScript, API를 크롤링할 수 있다.
- 새 방문자의 빈 상태가 핵심 콘텐츠를 대체하지 않는다.
- 내비게이션 시 메타데이터가 올바르게 업데이트된다.
- 구조화 데이터가 렌더링된 HTML에 나타난다.
- 모바일과 데스크톱이 동등한 핵심 콘텐츠를 보여 준다.
- URL 검사가 방문자가 보는 것을 본다.
릴리스 후 JavaScript SEO 모니터링
한 번의 점검은 명백한 결함을 잡아내지만 JavaScript 애플리케이션은 자주 바뀝니다. Search Console이 추세를 보고하기 전에 SEO 오류가 드러나도록 운영 환경에서 소규모 점검 세트를 추가하세요.
대표적인 공개 라우트를 HTML 수준의 테스트로 모니터링하세요.
- 예상한 제목과 canonical이 응답에 있다;
- 주요 제목과 알아볼 수 있는 콘텐츠 조각이 있다;
- 상태 코드가 라우트 상태와 일치한다;
- 중요한 내부 링크 하나 이상에 실제
href가 있다; - robots 지시문이 예기치 않게 바뀌지 않는다.
하이드레이션에 정당하게 의존하는 콘텐츠에는 렌더링된 브라우저 점검을 한 번 더 실행하세요. 콘솔 오류, 실패한 네트워크 요청, 최종 canonical, 구조화 데이터, 스크린샷을 수집하세요. HTML 테스트는 크롤러의 기본을 지키고 렌더링 테스트는 실제 애플리케이션을 지킵니다.
트래픽만이 아니라 템플릿 기준으로 URL을 고르세요. 홈페이지 모니터는 정상이어도 상품 상세, 페이지네이션, 현지화된 라우트는 깨질 수 있습니다. 색인 가능한 모든 템플릿마다 안정적인 고정 대상 하나와 404를 반환해야 하는 부정 대상 하나를 유지하세요.
릴리스 주석도 똑같이 유용합니다. 색인된 페이지나 노출이 떨어졌을 때 라우터, 동의 관리자, 렌더링 프레임워크, API가 정확히 언제 바뀌었는지 알면 진단이 빨라집니다. 그런 기록이 없으면 팀은 자체 배포로 생긴 퇴보를 알고리즘 업데이트 탓으로 돌리는 경우가 많습니다.
대형 사이트라면 소스 HTML과 렌더링된 DOM을 자동으로 비교하세요. 렌더링된 페이지가 텍스트나 링크의 상당 부분을 잃거나 콘텐츠가 지연된 사용자 동작 뒤에만 나타나면 알림을 보내도록 하세요. 이런 점검은 URL 검사를 대체하지 못하지만 예방할 수 있는 실수를 매일 잡아냅니다.
종합 점검: 대표 URL에 SEO 검사기를, 템플릿 전반에서 반복되는 문제를 찾으려면 무료 SEO 감사를 사용하세요.
결론
JavaScript가 SEO를 실패하게 만드는 것은 아닙니다. 클릭 뒤에만 존재하는 콘텐츠, 잘못된 상태 코드, href가 없는 링크, 늦게 바뀌는 메타데이터가 진짜 피해를 줍니다. 초기 응답이 의미를 갖게 하고, URL과 상태를 정직하게 유지한 다음, JavaScript는 핵심 콘텐츠를 숨기지 않고 경험을 개선하는 데 사용하세요.
자주 묻는 질문
구글은 JavaScript를 실행할 수 있나요?
Vue나 React 사이트는 모두 SSR로 완전히 옮겨야 하나요?
JavaScript로 noindex 태그를 제거하면 안 되는 이유는 무엇인가요?
웹사이트를 무료로 확인하세요
지금 SEO 감사를 실행하거나 트래픽 품질을 확인해 보세요. 가입이 필요 없습니다.