본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 29. 13:16

Crawlberg v1.0.0 소개

요약

Rust 기반의 웹 크롤링 엔진인 Crawlberg가 v1.0.0 버전으로 업데이트되었습니다. 이번 업데이트는 새로운 기능 추가보다는 프로젝트 명칭 변경(kreuzcrawl에서 Crawlberg로)과 공개 API 동결을 통한 프로덕션 안정성 선언에 중점을 둡니다.

핵심 포인트

  • 프로젝트 명칭 변경 및 공개 API 동결을 통한 안정성 확보
  • SSRF 방어 기능 기본 활성화 및 환경 변수 이름 변경
  • Rust 기반의 고성능 엔진으로 14개 언어 런타임 지원
  • JavaScript 렌더링 및 구조화된 데이터 추출 기능 제공
  • 다양한 확장 지점(Trait)을 통한 커스텀 구성 가능

Crawlberg를 새로운 버전인 Crawlberg v1.0.0으로 업그레이드합니다. 이는 이전의 kreuzcrawl을 기반으로 구축되었습니다. 새로운 프로젝트 이름 아래 공개 API (public API)가 동결되었음을 선언합니다. 아래의 모든 기술적 기능은 v0.3.0 (2026-06-23)에서 출시되었습니다. v1.0.0은 새로운 기능 출시가 아닌, 안정성 선언 및 이름 변경입니다.

운영상의 조치가 필요할 가능성이 가장 높은 네 가지 프로덕션 관련 변경 사항은 다음과 같습니다:

  1. 패키지 및 환경 변수 (env var) 이름 변경 - 모든 아티팩트 식별자(artifact identifier)가 변경되었습니다. 마이그레이션 테이블을 참조하십시오.
  2. SSRF 방어 기능이 이제 기본적으로 활성화됩니다 - CRAWLBERG_ALLOW_PRIVATE_NETWORK=1 설정 없이는 내부 크롤링 대상 (localhost, RFC 1918, 클라우드 메타데이터) 접근이 실패합니다.
  3. CrawlError::WafBlocked가 이제 구조체 변형 (struct variant)입니다 - 업데이트 전까지는 철저한 매치 암 (exhaustive match arms)이 컴파일되지 않습니다.
  4. max_retries 의미론 (semantics) 변경 - 오프 바이 원 (off-by-one) 오류가 수정되었습니다. 이제 max_retries=3은 정확히 3번의 재시도를 수행합니다.

사전 컴파일된 바이너리 (Precompiled binaries)는 Linux (x86_64/aarch64), macOS (ARM64 및 x86_64), Windows x64를 지원합니다. Homebrew bottles 및 GHCR의 Docker 이미지도 사용 가능합니다.

Crawlberg란 무엇인가?

Crawlberg는 주로 Rust로 작성된 웹 크롤링 엔진으로, 14개의 언어 런타임 (language runtimes)에 걸쳐 단일하고 일관된 API를 제공합니다. 이는 HTTP 전송 (HTTP transport), JavaScript 렌더링 (JavaScript rendering), robots.txt 준수, 도메인별 속도 제한 (rate limiting), SSRF 안전성, 그리고 구조화된 추출 (structured extraction)을 처리합니다. 확장 지점 (Frontier, RateLimiter, CrawlStore, EventEmitter, ContentFilter, WafClassifier, ProxyProvider)은 주입 가능한 트레이트 (injectable traits)입니다. 엔진을 포크(fork)하지 않고도 자신만의 프론티어 (frontier), 스토리지 백엔드 (storage backend), 또는 프록시 풀 (proxy pool)을 연결할 수 있습니다.

단일 scrape() 호출을 통해 텍스트, 메타데이터, 링크, 이미지, 에셋 (assets), JSON-LD, Open Graph 태그, hreflang, 파비콘 (favicons), 헤딩 (headings), 응답 헤더 (response headers), 그리고 깨끗한 HTML→Markdown을 반환합니다. 사이트에 JavaScript가 필요한 경우, 선택 사항인 헤드리스 브라우저 (headless browser) 계층이 이를 투명하게 처리합니다.

v1.0.0은 v1.0.0-rc.2를 승격시키며, 새로운 프로젝트 이름 아래 공개 API (public API)를 동결합니다. 아래 섹션에서 설명하는 기능들은 1.0.0이 안정적(stable)이라고 선언하는 플랫폼을 나타내며, 이는 v0.3.0에서 이미 출시되었습니다.

v1.0.0이 안정적이라고 선언하는 기능들

이 기능들은 v0.3.0 (2026-06-23)에서 출시되었습니다. v1.0.0은 이들의 API를 동결하고 새로운 crawlberg 패키지 이름 아래 프로덕션 안정(production-stable) 상태로 선언합니다. 0.3.0을 실행 중인 엔지니어들은 이미 런타임 (runtime) 기능들을 보유하고 있습니다. 1.0.0으로 업그레이드한다는 것은 다음을 의미합니다: 패키지 이름 변경, 환경 변수 (env vars) 업데이트, 안정적인 API 계약 (API contract) 확보.

프로젝트 이름 변경: kreuzcrawlcrawlberg

운영 측면에서 가장 중요한 변화는 이름 변경입니다. 모든 아티팩트 (artifact) 식별자가 변경되었습니다:

아티팩트 (Artifact)이전 (Old)신규 (New)
크레이트 (Crate, crates.io)kreuzcrawlcrawlberg
...

동작 및 API 형태는 동일합니다. 이것은 재작성 (rewrite)이 아닌 이름 변경 (rename)입니다.

계층형 디스패치 엔진 (Tiered dispatch engine)

크롤 엔진은 정적인 설정 플래그 (configuration flag) 대신 시도당 신호 (per-attempt signals)에 의해 구동되며, HTTP → 우회 (bypass) → 헤드리스 브라우저 (headless browser)를 체인 형태로 연결합니다. 응답이 WAF 챌린지를 나타내면 엔진은 단계를 격상 (escalate)하며, 성공하면 도메인별 상태 (per-domain state)에 결과를 기록하고 이후 방문을 위한 시작 계층 (starting tier)을 조정합니다.

공개 타입 (Public types): Tier, EscalationStrategy, EscalationReason, AttemptOutcome, RetryDirective, RetryPolicy, WafSignal, DispatchProfile. 모든 디스패치 열거형 (dispatch enums)은 #[non_exhaustive]로 지정되어 있어, 향후 추가될 계층들은 하위 호환성을 깨뜨리지 않는 추가 사항이 됩니다.

핫 리로드 핑거프린트 (hot-reload fingerprints)를 이용한 WAF 탐지

TOML 핑거프린트 코퍼스 (rules/waf_fingerprints.toml, 34개 핑거프린트)가 Aho-Corasick 오토마톤 (automaton)에 입력됩니다. TomlClassifier::watch()는 디바운스 (debounced)된 와처 (watcher)를 통해 파일을 감시하며, ArcSwap을 통해 컴파일된 오토마톤을 원자적으로 (atomically) 교체합니다. 프로세스 재시작은 필요하지 않습니다. 이는 Kubernetes ConfigMap 업데이트에 안전합니다. TOML을 ConfigMap 볼륨으로 마운트하고 편집하면, 실행 중인 엔진이 몇 초 이내에 새로운 코퍼스를 반영합니다.

도메인별 차단율(block rates)은 EwmaDomainState를 통해 추적됩니다. 이는 지수 가중 이동 평균 (Exponentially Weighted Moving Average, EWMA)을 사용하여 최근 이력을 바탕으로 시작 티어(starting tier)를 자동으로 승격하거나 강등시킵니다.

SSRF 방어, 기본적으로 활성화됨

모든 fetch 경로는 네트워크 호출 전과 각 리다이렉트(redirect) 홉(hop) 이후에 URL 검증을 수행합니다. 차단된 주소 범위는 다음과 같습니다:

  • 127.0.0.0/8 (루프백 (loopback))
  • RFC 1918 사설 범위 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
  • 169.254.0.0/16 (링크 로컬 (link-local), 169.254.169.254와 같은 클라우드 메타데이터 엔드포인트 포함)
  • 0.0.0.0/8 (RFC 1122 §3.2.1.3에 따른 this-network/예약된 주소)
  • 224.0.0.0/4 (멀티캐스트 (multicast))
  • IPv6 ULA fc00::/7, 링크 로컬 fe80::/10, 멀티캐스트 ff00::/8
  • http(s) 이외의 모든 스킴 (scheme)

세 가지 보호 계층이 함께 작동합니다:

  • DNS 리바인딩 (DNS-rebinding) 완화: 호출 시점의 호스트네임뿐만 아니라, 해결된 모든 IP가 정책을 통과해야 합니다.
  • 리다이렉트 체인 재검증 (Redirect-chain re-validation): 각 홉마다 다시 해석(re-resolve)하고 재검증하며, 이는 ssrf.max_redirects (기본값 5)에 의해 제한됩니다.
  • 링크 인큐 (Link-enqueue) 검증: URL은 fetch 시점뿐만 아니라 크롤링 프런티어 (crawl frontier)에 추가되기 전에 SSRF 정책에 따라 검증됩니다.

HostMatcher (Exact/Suffix/Cidr 변형)를 통해 화이트리스트 (Allowlisting)를 사용할 수 있습니다. CRAWLBERG_ALLOW_PRIVATE_NETWORK=1을 설정하면 완전히 제외할 수 있습니다.

메모리 제한 스트리밍 크롤 (Memory-bounded streaming crawl)

기존의 crawl_stream()batch_crawl_stream()은 모든 CrawlEvent::Page를 메모리에 축적했습니다. 이제는 각 페이지를 생성(yield)하고 즉시 해제합니다. 변경 로그(changelog)에 기록된 내부 측정치에 따르면, 대규모 크롤링 시 피크 워킹 셋 (peak working-set)이 약 2.5 GB에서 약 20 MB로 감소합니다. 모든 페이지를 한 번에 반환하는 배치 crawl() API는 변경되지 않았습니다.

MCP 서버 및 완전한 CLI 기능 일치

CLI는 batch-scrape, batch-crawl, download, citations, version을 노출하며, 이는 코어 및 MCP 인터페이스와 1:1로 일치합니다. MCP 서버는 stdio/mcp 경로의 rmcp Streamable HTTP를 통해 도구(tools)를 제공합니다. HTTP 전송을 사용하려면 바이너리가 apimcp Cargo 기능을 포함하여 컴파일되어야 하며, 릴리스된 CLI 바이너리에는 두 기능이 모두 포함되어 있습니다. 각 도구는 도구를 호출하기 전에 부작용(side effects)을 추론해야 하는 에이전트 오케스트레이션 프레임워크를 위해 read_only/destructive/open_world 안전 주석(safety annotations)을 포함합니다.

공개 서브스트레이트 파서 (Public substrate parsers)

crawlberg::robotscrawlberg::sitemap은 이제 공개 모듈로, 전체 크롤링 엔진을 실행하지 않고도 사용할 수 있습니다. parse_robots_txt, is_path_allowed, RobotsRules, parse_sitemap_xml, parse_sitemap_index, is_sitemap_index는 모두 독립적으로 사용할 수 있으며, 자체적인 페치(fetch) 레이어를 관리하는 파이프라인에서 robots/sitemap 전처리에 유용합니다.

심층 기술 하이라이트 (Deep Technical Highlights)

에스컬레이션 예산 주입 (Escalation budget injection)

EscalationBudget은 사용자가 주입할 수 있는 트레이트(trait)입니다. 도메인별, 시간당 브라우저 예산 상한을 구현하거나, 에스컬레이션 정책을 실시간 프록시 비용 신호와 연결할 수 있습니다. 내장된 EwmaDomainState는 설정이 필요 없는(zero-configuration) 배포를 위해 설계되었으며, 트레이트 인터페이스는 사용자가 더 강력한 의견(stronger opinions)을 가지고 있을 때를 위해 설계되었습니다.

락 프리 코퍼스 핫 리로드 (Lock-free corpus hot-reload)

WAF 오토마톤(automaton)은 ArcSwap 하에 존재합니다. 읽기 작업자(Readers)는 단일 분류 호출 동안 가드(guard)를 취하며, 이는 마이크로초 단위로 이루어지고 쓰기 작업자(writer)와 절대 경합하지 않습니다. 쓰기 작업 측에서는 새로운 오토마톤을 컴파일하고(대규모 코퍼스의 경우 수십 밀리초 소요) 단일 원자적 저장(atomic store)으로 교체합니다. 진행 중인 요청은 이전 오토마톤을 기준으로 완료되며, 새로운 요청은 즉시 새 오토마톤을 사용합니다. 읽기 작업자는 코퍼스 업데이트 시 절대 차단(block)되지 않습니다.

모든 리다이렉트 홉에서의 SSRF

호출 지점(call site)에서의 URL 검증만으로는 불충분합니다. 호스트 이름이 초기 검사를 통과하더라도, 짧은 TTL이 만료된 후 DNS가 사설 주소로 해석될 수 있기 때문입니다 (DNS 리바인딩 (DNS rebinding)). Crawlberg는 모든 리다이렉트 시점에 재해석 및 재검증을 수행하며, 이는 ssrf.max_redirects(기본값 5)에 의해 제한됩니다. SsrfPolicy::from_env의 serde 기본값 설정은 JSON에서 역직렬화된 CrawlConfig가 환경 변수를 자동으로 준수함을 의미합니다. 이는 환경 변수가 주요 설정 채널인 컨테이너 배포 환경에서 매우 중요합니다.

브라우저 풀(Browser pool) 생명주기

BrowserPool은 공개(public)되어 있습니다. 시작 시 풀을 생성하고 warm(n)을 통해 예열한 뒤, CrawlEngineBuilder::with_browser_pool()을 통해 전달하십시오. 브라우저 인스턴스는 에스컬레이션(escalation) 이벤트마다 새로 생성되는 대신 크롤링 작업 전반에 걸쳐 재사용됩니다. CrawlEngineHandle::from_engine()은 복제 가능한 핸들을 생성하므로, 여러 비동기 태스크(async tasks)가 추가적인 조정 없이 단일 엔진과 풀을 공유할 수 있습니다.

SSRF 필터를 통한 에셋 다운로드

이번 릴리스 이전에는 download_documents가 단일 페이지 scrape()에 의해서만 준수되었습니다. 크롤 루프는 바이트를 가져오고, 플래그를 지정한 뒤 폐기했습니다. 이제 다운로드는 페이지 페치(fetch)와 동일한 전송 계층인 http_fetch를 통해 라우팅됩니다. 따라서 모든 파일 다운로드는 SSRF 정책 및 도메인별 속도 제한(rate limiting)의 적용을 받습니다.

성능 영향

가장 직접적으로 측정 가능한 변화는 스트리밍 메모리입니다. 대규모 크롤링 시 피크 워킹 셋(peak working-set)이 약 2.5 GB에서 약 20 MB로 감소했습니다 (이 수치는 변경 로그의 수치이며, 이번 릴리스에 대해 발표된 외부 벤치마크 제품군은 없습니다). 실질적인 영향은 crawl_stream을 사용할 때 크롤 코퍼스(corpus)의 크기가 더 이상 사용 가능한 RAM에 의해 제한되지 않는다는 점입니다.

처리량(Throughput): 계층형 디스패치(tiered dispatch) 모델은 에스컬레이션이 발생하는 요청에 대해 약간의 지연 시간(latency) 오버헤드를 추가합니다. 즉, 브라우저 구동 전 한 번의 추가적인 HTTP 프로브(probe)가 수행됩니다. 일반적인 HTTP에 정상적으로 응답하는 도메인은 이 비용을 전혀 지불하지 않습니다. 도메인별 EWMA(지수 가중 이동 평균) 상태 관리를 통해 모범적으로 동작하는 도메인은 HTTP 계층에서 시작하도록 유도하며, 깨끗한 도메인에 대한 불필요한 우회나 브라우저 에스컬레이션을 방지합니다.

ArcSwap 기반의 코퍼스(corpus) 재로드는 읽기 작업자(reader)의 관점에서 락 프리(lock-free)로 동작하므로, 핑거프린트(fingerprint) 코퍼스 업데이트가 운영 환경에서 지연 시간 스파이크(latency spikes)를 유발하지 않습니다.

이번 릴리스에는 처리량(throughput), 초당 요청 수(requests-per-second), 또는 지연 시간 백분위수(latency percentiles)에 대한 벤치마크 수치가 게시되지 않았습니다. 높은 처리량이 요구되는 워크로드에 대해 Crawlberg을 평가하려는 팀은 안정적인 1.0.0 인터페이스를 대상으로 자체 벤치마크를 실행해야 합니다. 저장소에 포함된 Criterion 벤치마크는 WAF 서브시스템을 다루고 있으며, 이는 테스트 범위를 확장하기 위한 시작점입니다.

언어 바인딩(Language Bindings) 하이라이트

14개의 모든 바인딩은 alef에 의해 동일한 Rust 코어로부터 생성되었으며, 언어별 추출 로직을 포함하지 않습니다. 아래의 코드 스니펫은 예시일 뿐입니다. 정확한 API 시그니처는 언어별 README를 확인하십시오.

Python

from crawlberg import CrawlConfig, scrape, crawl_stream

config = CrawlConfig(max_depth=3, max_pages=500, concurrency=20)
...
pip install crawlberg

JavaScript / Node.js

import { scrape, crawlStream, CrawlConfig } from "@xberg-io/crawlberg";

const config = new CrawlConfig({ maxDepth: 3, maxPages: 500, concurrency: 20 });
...
npm install @xberg-io/crawlberg

PHP

use XbergIo\Crawlberg\CrawlConfig;
use XbergIo\Crawlberg\Crawlberg;

...
composer require xberg-io/crawlberg

PHP 8.2, 8.3, 8.4를 지원합니다. 미리 컴파일된 NTS 확장(extensions)은 Linux (glibc, aarch64/x86_64), macOS (ARM64/x86_64), 그리고 Windows (VS16/VS17 x86_64)용으로 제공됩니다.

중대한 변경 사항 (Breaking Changes) / 호환성 참고 사항

v1.0.0은 프리릴리스(pre-release) kreuzcrawl / kreuzberg 네임스페이스 패키지 사용자에 대한 중대한 변경(breaking release)이 포함된 버전입니다. 0.x 프리릴리스부터 이미 crawlberg 이름을 사용 중인 사용자의 경우, 동작 측면에서의 중대한 변경 사항은 다음과 같습니다:

영역 (Area)기존 동작 (Old behavior)새로운 동작 (New behavior)필요한 조치 (Action required)
패키지 식별자 (Package identifiers)모든 곳에서 kreuzcrawl 사용crawlberg / @xberg-io/crawlberg 등 사용모든 매니페스트(manifests)의 의존성 선언을 업데이트하세요
...

업그레이드 가이드 (Upgrade Guide)

기존 kreuzcrawl 사용자를 위한 단계별 절차

1. 패키지 선언 업데이트 (Update package declarations)

# Python
pip install crawlberg        # kreuzcrawl을 대체합니다

...

2. 환경 변수 및 설정 업데이트 (Update environment variables and configuration)

# 기존 (Old)
KREUZBERG_ALLOW_PRIVATE_NETWORK=1
# 신규 (New)
...

3. 첫 실행 전 SSRF 설정 감사 (Audit SSRF settings before first run)

내부 네트워크(CI 테스트 대상, 내부 API, localhost)를 크롤링하는 경우, 업그레이드하기 전에 CRAWLBERG_ALLOW_PRIVATE_NETWORK=1을 설정하세요. 이 설정이 없으면 RFC 1918 및 루프백(loopback) 주소에 대한 요청이 CrawlError::SsrfPolicyViolation과 함께 실패합니다.

4. C FFI 호출 지점 업데이트 (Update C FFI call sites) (해당하는 경우)

모든 kcrawl_ 심볼(symbol) 참조를 cberg_로 교체하세요. cbindgen 헤더를 다시 생성하십시오.

5. CrawlError::WafBlocked 매치 암(match arms) 수정 (Fix CrawlError::WafBlocked match arms)

// 기존 (Old) - 유닛 변형 (unit variant)
CrawlError::WafBlocked => { /* ... */ }

...

6. #[non_exhaustive] 열거형(enums)에 대한 와일드카드 암 추가 (Add wildcard arms for #[non_exhaustive] enums)

CrawlError, NetworkErrorKind, 그리고 모든 디스패치 열거형(EscalationReason, EscalationStrategy 등)은 이제 #[non_exhaustive]입니다. 이러한 타입에 대해 완전한(exhaustive) match를 사용하면 와일드카드 _ => { ... } 암이 추가될 때까지 컴파일에 실패합니다.

검증 체크리스트 (Verification checklist)

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0