📑

로그 파일 분석과 크롤 예산

Googlebot이 사이트에서 실제로 무엇을 하는지 확인하고, 쓸모없는 URL에 크롤 예산을 낭비하는 일을 멈추세요.

📖 13 분 읽기 🕑 업데이트 2026-06-22

대부분의 SEO 도구는 사이트에 무슨 일이 일어날 수 있는지를 알려줍니다. 서버 로그는 실제로 무슨 일이 일어났는지를 알려줍니다. Googlebot이 URL을 가져올 때마다 서버는 로그 파일에 한 줄을 기록합니다 — 실제 URL, 실제 상태 코드, 실제 타임스탬프. 샘플링도, 추정도, Google인 척하는 서드파티 크롤러도 없습니다. 검색 엔진이 사이트에서 실제로 무엇을 하는지 알고 싶다면, 로그가 유일한 진실의 원천입니다.

이 가이드는 그 진실을 읽고 그에 따라 행동하는 방법을 다룹니다. 렌즈는 크롤 예산(crawl budget) 입니다. Google이 여러분의 사이트에 기꺼이 쓰려는 유한한 크롤링 양이죠. 작은 사이트에서는 예산이 사실상 무한하므로 다음 섹션까지만 읽고 그만두어도 됩니다. 큰 사이트에서는 — 이커머스 카탈로그, 마켓플레이스, 수십만 개의 URL을 가진 프로그래매틱 사이트 — 크롤 예산이 명백한 제약 조건이 되며, 로그는 어디서 예산이 새는지 찾아내는 방법입니다.

🧑‍💻 개발자 관점: 로그 파일은 (timestamp, ip, method, url, status, user_agent)를 키로 하는 추가 전용(append-only) 이벤트 스트림일 뿐입니다. 이 가이드의 모든 내용은 그 스트림에 대한 GROUP BY 또는 WHERE 절입니다. 액세스 로그로 서비스를 디버깅해 본 적이 있다면, 로그 기반 SEO를 다룰 근육은 이미 갖춘 셈입니다.

크롤 예산이란 무엇인가

“크롤 예산”은 Google이 공개하는 숫자가 아닙니다. Google이 모든 사이트에 대해 균형을 맞추는 두 가지 힘에서 발생하는 결과물입니다.

크롤 속도 제한(crawl rate limit) 은 Google이 서버를 얼마나 강하게 두드릴지에 대한 상한선입니다. Googlebot은 의도적으로 예의 바릅니다. 응답이 느려지거나 5xx 오류를 반환하기 시작하면, 사이트를 다운시키지 않기 위해 자동으로 물러납니다. 빠르고 건강한 서버는 상한선을 높이고, 느리거나 불안정한 서버는 낮춥니다. 이것은 공급 측면입니다 — 인프라가 얼마나 많은 크롤링을 감당할 수 있는지죠.

크롤 수요(crawl demand) 는 Google이 여러분을 얼마나 크롤링하고 싶어 하는지입니다. 인기도(링크와 트래픽이 많은 URL일수록 더 자주 크롤링됨)와 신선도(Google이 변경되었다고 판단하는 페이지를 다시 크롤링함)에 의해 결정됩니다. 끊임없이 업데이트되는 기사를 가진 뉴스 사이트는 높은 수요를 가지고, 정적인 브로슈어 사이트는 낮은 수요를 가집니다. 이것은 수요 측면입니다 — 콘텐츠가 실제로 얼마나 크롤링될 가치가 있는지죠.

실효 크롤 예산은 대략 min(rate limit, demand)입니다. 둘 다 끌어올려서 최적화합니다. 빠르게 서빙하고(제한을 높이고), 중요한 페이지에 Google의 주의를 집중시키세요(수요를 빚어내세요).

사이트 규모(색인 가능 URL)크롤 예산이 중요한가?
약 10k 미만거의 안 중요함 — Google이 손쉽게 전부 크롤링함
10k–100k때때로 — URL 파라미터나 패싯 내비게이션이 있다면 주시
100k 초과그렇다 — 핵심 레버임
크롤 트랩이 있는 모든 규모그렇다 — 캘린더나 필터 폭발이 작은 사이트도 침몰시킬 수 있음

⚠️ 주의: 가장 흔한 크롤 예산 재앙은 큰 사이트가 아니라, 무한한 URL을 생성하는 트랩을 가진 작은 사이트입니다(날짜 선택기, 무제한 필터 조합). Google은 200개의 실제 제품 페이지가 오래되는 동안 40만 개의 쓸모없는 필터 순열을 크롤링하느라 예산 전체를 태워버릴 수 있습니다.

서버 로그 기초

웹 서버 액세스 로그는 요청당 한 줄입니다. 고전적인 포맷은 Apache와 Nginx가 기본으로 내보내는 NCSA “combined log format”입니다.

66.249.66.1 - - [22/Jun/2026:08:14:32 +0000] "GET /products/widget-42?color=red HTTP/1.1" 200 5123 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

해독하면, SEO에서 신경 써야 할 필드는 다음과 같습니다.

필드예시왜 중요한가
클라이언트 IP66.249.66.1진짜 Googlebot 검증(역방향 DNS)
타임스탬프22/Jun/2026:08:14:32크롤 빈도, 신선도
메서드 + URLGET /products/widget-42?color=red무엇이 크롤링되었는지; 파라미터 낭비
상태 코드200404, 리다이렉트, 5xx 오류
바이트5123페이로드 크기, 응답 무게
사용자 에이전트...Googlebot/2.1...어떤 크롤러가 URL을 두드렸는지

로그를 확보하는 것이 진짜 난관입니다. 로그가 어디에 있는지는 스택에 따라 다릅니다.

  • 자체 호스팅 Nginx/Apache/var/log/nginx/access.log, logrotate로 매일 로테이션됩니다. 가장 쉬운 경우; 그냥 scp 하면 됩니다.
  • 클라우드 로드 밸런서 — AWS ALB는 액세스 로그를 S3에 기록하고, GCP 로드 밸런서는 Cloud Logging에 기록합니다. 리스너별로 활성화합니다.
  • CDN 뒤에 있을 때 — 이것이 함정입니다. Cloudflare, Fastly, CloudFront 뒤에 있다면 Googlebot은 오리진이 아니라 CDN 엣지를 두드립니다. 오리진 로그는 캐시 미스만 보므로 크롤링을 심하게 과소 집계합니다. CDN의 로그가 필요합니다.
  • 특히 CloudflareLogpush를 사용해 HTTP 요청 로그를 R2(또는 S3, 또는 외부 싱크)로 지속적으로 스트리밍하세요. 이것이 Cloudflare 호스팅 사이트의 정석 설정이며, 아래 Cloudflare 섹션에서 단계별로 살펴봅니다.

💡 팁: 결론을 내리기 전에 최소 30일치 로그를 수집하세요. 하루 동안의 Googlebot 크롤 패턴은 노이즈가 많지만, 한 달에 걸쳐 보면 낭비 패턴이 분명하게 드러납니다. 다시 쿼리할 수 있는 롤링 윈도우를 목표로 하세요.

Googlebot 분석하기

“Googlebot”으로 표시된 단 한 줄의 로그라도 신뢰하기 전에 검증하세요. 사용자 에이전트 문자열은 손쉽게 위조할 수 있습니다 — 스크레이퍼와 경쟁사는 속도 제한을 우회하려고 일상적으로 Googlebot을 사칭합니다. 가짜 Googlebot 히트를 크롤 예산으로 집계하면 유령을 쫓게 됩니다.

역방향 + 정방향 DNS로 검증하세요. 진짜 Googlebot IP는 역방향으로 googlebot.com 또는 google.com으로 해석되며, 그 호스트네임은 정방향으로 동일한 IP로 다시 해석되어야 합니다. 사칭자는 사용자 에이전트는 제어하지만 Google의 DNS는 제어하지 못합니다.

# Reverse lookup: does the IP claim a Google hostname?
host 66.249.66.1
# -> 1.66.249.66.in-addr.arpa domain name pointer crawl-66-249-66-1.googlebot.com.

# Forward lookup: does that hostname resolve back to the same IP?
host crawl-66-249-66-1.googlebot.com
# -> crawl-66-249-66-1.googlebot.com has address 66.249.66.1   ✓ verified

IP별 DNS 조회를 하고 싶지 않다면, Google은 크롤러 IP 범위를 https://developers.google.com/static/search/apis/ipranges/googlebot.json에서 JSON으로 공개합니다 — 클라이언트 IP를 그 CIDR과 오프라인으로 매칭하세요.

데이터를 신뢰하게 되면, 네 가지 질문을 던지세요.

무엇이 크롤링되고 있는가? 히트를 URL별로 그룹화하고 목록 상단을 보세요. 건강한 사이트에서 가장 많이 크롤링되는 URL은 가장 중요한 페이지입니다. 가장 많이 크롤링된 URL이 ?sessionid= 변형이나 필터 순열이라면, 그것이 첫 번째 누수입니다.

핵심 페이지는 얼마나 자주 크롤링되는가? 돈이 되는 페이지의 마지막 크롤 타임스탬프를 끌어내세요. 상위 카테고리 페이지가 18일 전에 마지막으로 가져와졌다면, Google은 그것이 오래되었거나 중요하지 않다고 생각하는 것입니다 — 신선도 문제죠.

예산은 어디서 낭비되는가? 이것이 로그 분석의 핵심입니다. 흔한 용의자들은 다음과 같습니다.

낭비 패턴로그에서 어떻게 보이는가전형적인 원인
파라미터 URL?sort=, ?utm_, ?sessionid=에 대한 많은 히트추적 파라미터, 정렬, 세션 ID
중복 콘텐츠/p/123/products/widget 아래의 동일 콘텐츠한 페이지로 가는 여러 경로
리다이렉트 체인301 -> 301 -> 200 시퀀스마이그레이션 위에 마이그레이션이 겹침
404 / 410 폭주봇에게 가는 대량의 404죽은 링크, 제거됐지만 여전히 링크된 제품
소프트 404실제로는 “찾을 수 없음”인 페이지의 200빈 검색 결과, 품절 상품
패싯 내비게이션조합형 ?color=&size=&brand= URL크롤 제어 없는 필터

원시 로그에 셸만으로 돌릴 수 있는 빠른 분류:

# Top 20 URLs crawled by anything claiming to be Googlebot
grep -i googlebot access.log \
  | awk '{print $7}' \
  | sort | uniq -c | sort -rn | head -20

# Status-code distribution for Googlebot — 4xx/5xx ratio is your waste signal
grep -i googlebot access.log \
  | awk '{print $9}' \
  | sort | uniq -c | sort -rn

Googlebot 요청의 30%가 404를 반환하거나 ?로 가득한 URL을 두드린다면, 크롤 예산의 대략 3분의 1이 쓰레기통으로 가고 있는 것입니다.

크롤 예산 최적화하기

로그가 누수 지점을 알려주면, 그것을 막습니다. 도구 모음은 작지만 각 도구에는 특정한 역할이 있습니다 — 잘못된 도구를 쓰는 것이 사람들이 실수로 사이트를 색인에서 제거하는 방법입니다.

robots.txt로 원천에서 차단하세요. 경로를 Disallow 하면 Google이 그것을 크롤링하는 것 자체를 멈추는데, 이는 내부 검색, 정렬 순서, 추적 파라미터 같은 크롤 예산 낭비에 정확히 원하는 바입니다. Disallow는 URL이 다른 곳에 링크되어 있다면 색인에서 제거하지는 않는다는 점에 유의하세요 — 가져오기만 멈춥니다.

User-agent: *
Disallow: /search
Disallow: /*?sort=
Disallow: /*?sessionid=
Disallow: /cart

rel=canonical로 중복을 통합하세요. 동일한 콘텐츠가 여러 URL에 존재할 때(파라미터화된 변형, 추적 태그가 붙은 링크), 모두 하나의 정규(canonical) URL을 가리키게 하세요. Google은 신호를 함께 묶고 정규 URL을 우선적으로 크롤링합니다.

<link rel="canonical" href="https://example.com/products/widget-42" />

noindex로 가치 낮은 페이지를 색인 밖으로 유지하세요. 사용자를 위해 크롤링은 가능해야 하지만 검색에서 경쟁해서는 안 되는 페이지 — 빈약한 태그 아카이브, 페이지네이션 꼬리 — 에는 noindex 메타 로봇 태그를 사용하세요. 중요: noindexrobots.txtDisallow와 결합하지 마세요. Google이 페이지를 크롤링할 수 없으면 noindex를 볼 수 없기 때문입니다.

가장 영향력이 큰 두 가지 구조적 수정:

크롤 트랩을 제거하세요. 패싯 내비게이션과 무한 캘린더는 고전적인 예산 소각로입니다. 각각 10개의 값을 가진 6개의 패싯이 있는 필터 UI는 백만 개의 URL 조합을 생성하며, 그중 색인할 가치가 있는 것은 없습니다. 봇이 따라가지 않을 필터 링크를 서빙하고(POST 폼, 또는 rel=nofollow 더하기 파라미터에 대한 Disallow), 무한 캘린더를 제한해 /events/2099/12가 합리적인 한계 너머에서는 404를 반환하도록 하여 고치세요.

리다이렉트 체인과 소프트 404를 복구하세요. 301 -> 301 -> 200 체인의 모든 홉은 낭비된 가져오기이자 링크 자산의 작은 손실입니다; 각 오래된 URL이 한 번의 홉으로 최종 목적지를 직접 가리키도록 체인을 축소하세요. 소프트 404 — 품절 상품이나 결과 없는 검색처럼 비어 보이지만 200을 반환하는 페이지 — 의 경우, 실제 404/410을 반환하여 Google이 그것을 살아 있는 콘텐츠처럼 다시 크롤링하는 것을 멈추도록 하세요.

💡 팁: 수정을 배포한 후에는 로그가 검증 도구이기도 합니다. 일주일치 Googlebot 히트를 다시 끌어내어 파라미터 URL과 404가 줄었는지, 그리고 돈이 되는 페이지의 크롤이 늘었는지 확인하세요. 쓰레기에서 해방된 크롤 예산은 좋은 페이지로 재분배됩니다 — 그 과정을 지켜볼 수 있습니다.

도구

grep, awk, sort만으로도 꽤 멀리 갈 수 있지만, 규모가 커지면 쿼리 가능한 무언가를 원하게 됩니다.

BigQuery는 큰 사이트의 일꾼입니다. 로그를 테이블에 로드하면 크롤 분석이 수십억 행에 대한 평범한 SQL이 됩니다.

SELECT
  REGEXP_EXTRACT(url, r'^[^?]+') AS path,   -- strip query string
  COUNT(*)                       AS hits,
  COUNTIF(status >= 400)         AS errors
FROM `logs.googlebot`
WHERE _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY path
ORDER BY hits DESC
LIMIT 50;

Screaming Frog Log File Analyser는 노코드 옵션입니다. 원시 로그 파일을 끌어다 놓고 크롤을 지정하면, 둘을 상호 참조해 크롤되었지만 크롤에는 없는(“고아”) URL과 중요하지만 거의 크롤되지 않는 페이지를 볼 수 있습니다. 일회성 감사에 훌륭합니다.

직접 만든 파서는 직접 소유하고 싶은 반복 분석에 가치가 있습니다. combined-log 한 줄은 하나의 패턴으로 파싱할 만큼 규칙적입니다.

import re
from collections import Counter

# Matches: ip ... "METHOD url HTTP/x" status bytes "ref" "ua"
LINE = re.compile(
    r'(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] '
    r'"(?P<method>\S+) (?P<url>\S+) [^"]+" '
    r'(?P<status>\d{3}) (?P<bytes>\S+) "[^"]*" "(?P<ua>[^"]*)"'
)

paths, statuses = Counter(), Counter()

with open("access.log", encoding="utf-8", errors="replace") as fh:
    for line in fh:
        m = LINE.match(line)
        if not m or "googlebot" not in m["ua"].lower():
            continue
        path = m["url"].split("?", 1)[0]   # group by path, ignore params
        paths[path] += 1
        statuses[m["status"]] += 1

print("Top crawled paths:")
for path, n in paths.most_common(20):
    print(f"{n:6d}  {path}")

print("\nStatus distribution:", dict(statuses))

이것을 한 달치 로그에 대해 실행하면, 몇 초 만에 값비싼 도구가 주는 것과 동일한 답 — 가장 많이 크롤링된 경로와 낭비를 정량화하는 오류 비율 — 을 얻습니다.

🧑‍💻 Cloudflare 관점

사이트가 Cloudflare에 있다면, 오리진 로그는 대부분의 크롤링에 눈이 멉니다 — Googlebot은 주로 엣지의 캐시된 응답을 두드립니다. 해결책은 Logpush입니다. Cloudflare 엣지에서 원하는 싱크로 HTTP 요청 로그를 스트리밍하는 관리형 파이프라인이죠. 가장 저렴하고 SEO 친화적인 싱크는 R2, 즉 이그레스 요금이 없는 Cloudflare의 S3 호환 객체 스토리지입니다.

설정의 형태:

  1. Cloudflare 대시보드에서 Analytics & Logs -> Logpush로 이동해 HTTP requests 데이터셋에 대한 작업을 생성합니다.
  2. 대상으로 R2를 선택하고 필요한 필드를 고릅니다: ClientIP, ClientRequestURI, EdgeResponseStatus, ClientRequestUserAgent, EdgeStartTimestamp, CacheCacheStatus.
  3. Logpush는 몇 분마다 gzip 처리된 JSON 배치를 R2 버킷에 떨어뜨립니다.

그런 다음 로그를 직접 쿼리합니다 — 예를 들어 내보낸 파일에 대한 R2 SQL / DuckDB 세션으로:

-- Top URIs fetched by Googlebot, last 7 days, with error ratio
SELECT
  ClientRequestURI                                     AS uri,
  COUNT(*)                                             AS hits,
  SUM(CASE WHEN EdgeResponseStatus >= 400 THEN 1 ELSE 0 END) AS errors
FROM read_json_auto('r2://my-logs/http/2026/06/*.json.gz')
WHERE lower(ClientRequestUserAgent) LIKE '%googlebot%'
GROUP BY uri
ORDER BY hits DESC
LIMIT 50;

Logpush가 CacheCacheStatus를 포함하기 때문에, 오리진이 절대 줄 수 없는 크롤 예산 초능력도 얻습니다: 어떤 크롤된 URL이 캐시에서 서빙되는지(hit) 대 오리진 가져오기를 강제하는지(miss/expired)를 볼 수 있습니다. 중요한 페이지에서 Googlebot 미스 비율이 높다는 것은 엣지 캐싱이 크롤러를 돕지 못하고 있다는 뜻이며 — 캐싱과 엣지 구성이 자리한 Build 레이어로 직접 이어지는 튜닝 기회입니다.

💡 팁: Bot Management 애드온이 있다면 검증된 봇 메타데이터도 캡처하도록 Logpush 작업을 설정하세요 — Cloudflare가 Googlebot을 대신 검증해주므로, 역방향 DNS 춤을 건너뛰고 신뢰할 수 있는 플래그로 필터링할 수 있습니다.

핵심 요약

  • ✅ 크롤 예산이 애초에 해당하는지 결정하세요: 깨끗한 URL이 약 10k 미만이면 건너뛰고, 100k 초과이거나 크롤 트랩이 하나라도 있으면 로그를 핵심 레버로 취급하세요.
  • 진짜 엣지 로그를 확보하세요 — CDN 뒤에 있다면, 캐시된 크롤을 놓치는 오리진 로그를 믿는 대신 Cloudflare Logpush를 R2로 보내세요.
  • ✅ 로그의 어떤 “Googlebot” 줄을 신뢰하기 전에 역방향 + 정방향 DNS(또는 IP 범위)로 Googlebot을 검증하세요.
  • ✅ 낭비를 정량화하세요: 파라미터 URL, 404, 리다이렉트 체인, 소프트 404로 가는 Googlebot 히트의 비중을 측정하세요.
  • ✅ 올바른 도구로 누수를 막으세요 — 크롤링을 멈추려면 robots.txt, 통합하려면 canonical, 색인을 제거하려면 noindex, 그리고 패싯 내비게이션과 캘린더 트랩에는 구조적 수정.
  • ✅ 루프를 닫으세요: 수정을 배포한 후 로그를 다시 끌어내어 낭비가 줄었고 돈이 되는 페이지의 크롤이 늘었는지 확인하세요.