구글 서치 콘솔(GSC) '크롤링됨 - 미색인' 현상: 대규모 웹 트래픽 증발 사고 디버깅 리포트

지난 분기, 사내 레거시 커머스 플랫폼을 Next.js 기반의 모던 스택으로 마이그레이션한 직후, 마케팅 팀으로부터 긴급 호출을 받았습니다. "자연 유입 트래픽이 40% 급감했습니다." 서버 로그는 정상이었고, 5xx 에러도 없었습니다. 문제는 구글 서치 콘솔(Google Search Console)을 열어보고 나서야 명확해졌습니다. '색인 생성 범위' 리포트에서 유효 페이지 수는 바닥을 치고, 대신 '크롤링됨 - 현재 색인 생성되지 않음' 상태의 URL이 수만 건으로 치솟고 있었습니다. 이는 단순히 콘텐츠의 문제가 아니라, 기술적 구조가 검색 엔진 봇(Googlebot)의 크롤링 예산(Crawl Budget)을 낭비하게 만든 전형적인 엔지니어링 사고였습니다.

로그 분석: 구글봇이 멈춘 이유

우리는 흔히 SEO를 마케팅의 영역으로 치부하지만, 대규모 트래픽을 처리하는 web 서비스에서 SEO는 철저한 시스템 아키텍처의 문제입니다. 당시 우리 시스템은 AWS EC2 t3.xlarge 인스턴스 위에서 구동 중이었으며, 약 15만 개의 상품 페이지를 보유하고 있었습니다.

문제를 파헤치기 위해 Nginx 액세스 로그와 서치 콘솔의 '크롤링 통계'를 대조해 보았습니다. 놀랍게도 구글봇은 의미 있는 상품 페이지 대신, 필터링 파라미터가 무한히 붙은 URL(예: /product?color=red&size=xl&sort=desc...)을 수집하느라 리소스를 소진하고 있었습니다. 이를 '스파이더 트랩(Spider Trap)'이라고 부릅니다. 봇은 한정된 자원을 가지고 사이트를 방문하는데, 무의미한 URL 패턴에 갇혀 실제 중요한 페이지를 색인할 여력을 잃어버린 것입니다.

Critical Warning: '크롤링됨 - 현재 색인 생성되지 않음' 상태는 구글이 페이지를 찾았지만, 콘텐츠 가치가 낮거나 크롤링 예산 초과로 인해 색인 DB에 넣는 것을 '보류'했다는 의미입니다.

단순히 콘텐츠 품질이 낮아서가 아닙니다. 기술적으로 중복된 페이지(Duplicate Content)가 과도하게 생성되면서, 구글의 알고리즘이 해당 도메인 전체의 신뢰도를 낮게 평가하기 시작한 것이 근본 원인이었습니다.

실패한 접근: "색인 생성 요청" 버튼의 함정

초기에 주니어 개발자는 서치 콘솔 상단의 "URL 검사" 도구를 통해 수동으로 "색인 생성 요청" 버튼을 누르는 작업을 반복했습니다. 몇몇 페이지는 이 방법으로 해결되는 듯 보였습니다. 하지만 15만 개가 넘는 페이지를 수동으로 처리하는 것은 불가능할 뿐더러, 근본적인 수도꼭지를 잠그지 않고 바닥의 물만 닦는 격이었습니다. 하루 할당량(Quota) 제한에 걸려 작업은 곧 중단되었고, 근본적인 아키텍처 수정 없이는 해결될 수 없음을 깨달았습니다.

해결책: Sitemap Pruning 및 파라미터 제어 자동화

해결의 핵심은 구글봇에게 "여기는 보지 마"라고 명확히 지시하고, "이것만 봐"라고 정제된 경로를 제공하는 것입니다. 우리는 두 가지 전략을 병행했습니다. 첫째, robots.txt를 통해 무의미한 쿼리 스트링의 크롤링을 차단하고, 둘째, Python 스크립트를 사용하여 실제 유효한 페이지와 Sitemap에 선언된 페이지 간의 정합성을 검증했습니다.

다음은 우리가 Sitemap의 건전성을 진단하고, 서치 콘솔 데이터와 대조하기 위해 사용한 분석 로직의 일부입니다. 이 코드는 실제 XML 사이트맵을 파싱하여 응답 코드를 확인하고, 불필요한 리다이렉트 체인을 찾아냅니다.

import requests
import xml.etree.ElementTree as ET
from concurrent.futures import ThreadPoolExecutor

# 타겟 사이트맵 URL
SITEMAP_URL = "https://www.example.com/sitemap.xml"

def fetch_sitemap(url):
    try:
        response = requests.get(url)
        # XML 파싱: 네임스페이스 처리에 주의해야 합니다.
        root = ET.fromstring(response.content)
        # sitemap 표준 네임스페이스
        namespace = {'ns': 'http://www.sitemaps.org/schemas/sitemap/0.9'}
        urls = [url.text for url in root.findall('.//ns:loc', namespace)]
        return urls
    except Exception as e:
        print(f"Error fetching sitemap: {e}")
        return []

def check_url_status(url):
    try:
        # User-Agent를 Googlebot으로 위장하여 실제 봇의 응답을 시뮬레이션
        headers = {'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'}
        res = requests.head(url, headers=headers, timeout=5)
        
        # 200 OK가 아니거나 리다이렉트가 발생하면 경고
        if res.status_code != 200:
            return f"[FAIL] {res.status_code} : {url}"
        return None
    except requests.exceptions.RequestException:
        return f"[TIMEOUT] {url}"

# 실행 로직
if __name__ == "__main__":
    target_urls = fetch_sitemap(SITEMAP_URL)
    print(f"Total URLs found: {len(target_urls)}")
    
    with ThreadPoolExecutor(max_workers=10) as executor:
        results = executor.map(check_url_status, target_urls)
        
    for result in results:
        if result:
            print(result)

위 스크립트를 통해 우리는 사이트맵에 포함되어서는 안 될 404 페이지와 301 리다이렉트 페이지 약 12,000개를 식별하여 제거했습니다. check_url_status 함수에서 User-Agent를 구글봇으로 설정하는 것이 중요합니다(Line 24). 일부 서버 설정이나 방화벽은 일반 브라우저와 봇에게 다르게 반응할 수 있기 때문입니다.

지표 (Metric) 최적화 전 (Before) 최적화 후 (After)
일일 크롤링 요청 수 120,000회 (낭비 심함) 45,000회 (효율적)
평균 색인 지연 시간 14일 이상 24시간 이내
유효 색인 페이지 수 35,000개 142,000개

결과적으로 구글봇의 일일 크롤링 요청 수는 감소했지만, 유효 색인 페이지 수는 4배 가까이 증가했습니다. 이는 Google Search Console이 우리 사이트를 더 이상 '스팸성 파라미터 공장'이 아닌 '구조화된 정보의 원천'으로 인식하기 시작했음을 의미합니다. 낭비되던 크롤링 예산이 실제 중요한 콘텐츠로 집중된 것입니다.

구글 공식 사이트맵 가이드 확인하기

주의사항 및 엣지 케이스

이 최적화 작업을 수행할 때 주의해야 할 점이 있습니다. robots.txt를 수정하여 특정 파라미터(예: `/*?sort=`)를 차단(`Disallow`)할 때, 이미 색인되어 트래픽이 들어오고 있는 중요한 페이지까지 실수로 차단하지 않도록 극도로 주의해야 합니다. 차단 규칙을 적용하기 전, 서치 콘솔의 Robots.txt 테스터 기능을 사용하여 주요 URL이 차단되지 않는지 반드시 시뮬레이션해야 합니다.

Warning: noindex 태그와 robots.txt의 Disallow는 다릅니다. 이미 색인된 페이지를 검색 결과에서 제거하려면 noindex 메타 태그를 사용해야 하며, robots.txt로 막아버리면 구글봇이 noindex 태그를 읽을 수 없어 색인이 유지될 수 있습니다.

또한, SPA(Single Page Application) 환경에서는 자바스크립트 렌더링 이슈로 인해 구글봇이 빈 페이지를 크롤링하여 '소프트 404(Soft 404)'로 처리하는 경우가 빈번합니다. 이 경우 서버 사이드 렌더링(SSR)이나 동적 렌더링을 적용하여 봇에게 완전한 HTML을 제공하는 것이 필수적입니다.

결론

구글 서치 콘솔은 단순한 모니터링 툴이 아닙니다. 그것은 구글의 크롤러와 대화하는 프로토콜입니다. '크롤링됨 - 미색인' 문제는 콘텐츠의 질보다 기술적 SEO(Technical SEO) 설정의 미비로 발생하는 경우가 많습니다. 대규모 웹 사이트 운영자라면 자동화된 스크립트를 통해 사이트맵의 무결성을 주기적으로 검증하고, 크롤링 예산이 낭비되는 구멍을 적극적으로 메워야 합니다. 비즈니스의 성장은 훌륭한 콘텐츠와 견고한 엔지니어링이 만나는 지점에서 시작됩니다.

Post a Comment