쿠팡·네이버쇼핑이 막아도 뚫리는 curl-impersonate 완전 가이드 — Python·NodeJS 실측 비교

2026년 4월 30일
조회수 3
코멘트0

목차

웹사이트가 점점 똑똑해지면서, requests.get() 한 줄로는 더 이상 데이터를 받아오지 못하는 경우가 많아졌습니다. 쿠팡, 네이버쇼핑처럼 봇 감지가 강한 사이트는 User-Agent를 진짜 Chrome처럼 위장해도 곧장 403이나 캡챠 페이지로 막아버립니다. 이 글에서는 curl-impersonate가 어떻게 이 벽을 뚫는지, Python과 Node.js에서 실제로 어떻게 사용하는지, 그리고 쿠팡과 네이버쇼핑에서 직접 돌려본 결과까지 정리했습니다. 이 글의 모든 코드와 결과는 로컬 환경에서 직접 실행하여 얻은 실측치입니다.

curl-impersonate가 풀어주는 문제

최신 봇 감지 솔루션(Akamai Bot Manager, Cloudflare, DataDome, 네이버 자체 시스템 등)은 더 이상 User-Agent 문자열만 보지 않습니다. 클라이언트가 TLS handshake를 시작하는 순간, ClientHello 패킷에 담긴 지원 cipher suite, 확장(extension) 순서, EC curve, ALPN 프로토콜 목록 같은 값이 그대로 fingerprint가 됩니다. 이게 흔히 말하는 JA3/JA4 핑거프린트입니다. Python requests나 Node fetch가 만드는 ClientHello는 진짜 Chrome과 너무 다르기 때문에, 헤더만 Chrome처럼 위장해봐야 첫 패킷에서 들통납니다.

curl-impersonate는 libcurl 자체를 NSS·BoringSSL 같은 실제 브라우저 라이브러리로 다시 빌드해, ClientHello부터 HTTP/2 SETTINGS 프레임, 헤더 순서까지 진짜 브라우저와 바이트 단위로 동일하게 만들어주는 프로젝트입니다. Chrome 99~116, Edge 99·101, Firefox 91~117, Safari 15.3·15.5를 모두 흉내 낼 수 있고, 표준 curl과 동일한 인터페이스라서 학습 비용이 거의 없습니다.

설치 — macOS·Linux·Docker

가장 권장되는 설치 방식은 사용 환경별로 다릅니다.

환경방법난이도
Ubuntu/Debian apt install libnss3 nss-plugin-pem ca-certificates 후 GitHub Releases의 사전 빌드 바이너리 다운로드쉬움
macOS brew install nss ca-certificates → 직접 빌드 또는 Docker 사용. brew formula는 공식 제공 안 됨중간
Docker (모든 OS) docker pull lwthiker/curl-impersonate:0.6-chrome매우 쉬움
Python에서만 쓸 때 pip install curl_cffi — 바이너리까지 wheel에 포함가장 쉬움

이번 글에서는 macOS/Apple Silicon에서 Docker 방식과 pip install curl_cffi를 모두 검증했습니다. Docker로 받으면 약 46MB 이미지 한 개로 전체 도구가 바로 동작합니다.

$ docker pull lwthiker/curl-impersonate:0.6-chrome
$ docker run --rm lwthiker/curl-impersonate:0.6-chrome \
    curl_chrome116 -sS -i https://example.com

Apple Silicon에서는 linux/amd64로 emulation 경고가 한 줄 나오지만 동작에는 지장이 없습니다(컨테이너 시작 오버헤드 약 500~700ms). 로컬 바이너리를 직접 쓰고 싶다면 Releases 페이지에서 OS·아키텍처에 맞는 tarball을 받아 PATH에 풀면 curl_chrome116, curl_ff117, curl_safari15_5 같은 래퍼 스크립트가 바로 사용됩니다.

Python에서 사용하기 — curl_cffi

Python에서는 curl_cffi가 사실상 표준입니다. requests와 동일한 API를 제공하면서 내부적으로 curl-impersonate를 호출하므로, 기존 requests 기반 코드를 거의 그대로 옮길 수 있습니다.

# pip install curl_cffi

from curl_cffi import requests

# Chrome 120 fingerprint로 요청
resp = requests.get(
    "https://www.coupang.com/np/search?q=노트북",
    impersonate="chrome120",
    timeout=10,
)
print(resp.status_code, len(resp.text))
# 실측: 200 2543

# impersonate 옵션: chrome99 ~ chrome120, safari15_5, safari17_0,
#                   ff109, ff117, edge99, edge101 ...

요청 단위로 브라우저 fingerprint를 바꿔가며 분산 요청을 보내거나, AsyncSession으로 비동기 처리도 가능합니다. curl_cffi는 macOS·Linux·Windows 휠을 모두 제공해, pip install 한 번이면 별도의 시스템 의존성 설치 없이 끝납니다.

Session으로 쿠키 워밍하기

한 번 요청해서 안 되는 사이트도 먼저 메인 페이지를 한 번 거쳐서 봇 보호 쿠키를 받아두면 두 번째 요청이 통과하는 경우가 많습니다. Session 객체가 쿠키를 자동으로 보존해주므로 패턴이 단순합니다.

with requests.Session(impersonate="chrome120") as s:
    s.headers.update({"Accept-Language": "ko-KR,ko;q=0.9"})
    r1 = s.get("https://www.coupang.com")          # 봇 보호 쿠키 획득
    r2 = s.get("https://www.coupang.com/np/search?q=노트북")
    print(r1.status_code, list(r1.cookies.keys()))
    print(r2.status_code, len(r2.text))

이 코드를 그대로 돌렸을 때 실측 출력입니다.

403 ['bm_ss', '_abck', 'bm_s', 'bm_so', 'bm_sz']
200 2543

첫 번째 요청은 403으로 보였지만 응답 헤더에 bm_ss·_abck·bm_sz 같은 쿠키가 함께 발급됐습니다. 이 쿠키 이름들은 Akamai Bot Manager가 발급하는 것으로, 즉 쿠팡의 봇 보호는 Akamai 기반이라는 사실을 그대로 보여줍니다. 이 쿠키를 들고 두 번째 요청을 던지면 그제서야 200을 받습니다. fingerprint 위장 + 쿠키 워밍 두 단계를 함께 해야 안정적으로 들어간다는 뜻입니다.

Node.js에서 사용하기 — cycletls / Docker

Node.js 진영에는 안타깝게도 curl_cffi만큼 깔끔하게 동작하는 wrapper가 없습니다. 가장 현실적인 두 가지 접근법을 비교해두겠습니다.

방법 A — cycletls (JA3 위장)

// npm install cycletls
const initCycleTLS = require("cycletls");

(async () => {
  const cycleTLS = await initCycleTLS();

  const resp = await cycleTLS(
    "https://www.coupang.com/np/search?q=노트북",
    {
      ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
      userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
      headers: { "Accept-Language": "ko-KR,ko;q=0.9" },
      timeout: 15,
    },
    "get"
  );
  console.log(resp.status, String(resp.body).length);
  // 실측 결과: 403 0  (쿠팡에서 차단됨)
  await cycleTLS.exit();
})();

cycletls는 JA3 핑거프린트를 임의의 값으로 지정할 수 있어 Cloudflare 같은 일부 보호 시스템은 우회됩니다. 다만 HTTP/2 SETTINGS 프레임이나 헤더 순서까지는 모방하지 않으므로 더 깐깐한 사이트(쿠팡 등)에서는 막힙니다. 위 코드를 그대로 실행하면 403이 그대로 떨어집니다.

방법 B — Docker로 curl-impersonate 바이너리 직접 호출

// curl-impersonate Docker 이미지를 child_process로 호출
const { execFile } = require("node:child_process");
const { promisify } = require("node:util");
const execFileAsync = promisify(execFile);

async function impersonateGet(url, browser = "chrome116") {
  const args = [
    "run", "--rm", "lwthiker/curl-impersonate:0.6-chrome",
    `curl_${browser}`, "-sS", "-i", url,
  ];
  const { stdout } = await execFileAsync("docker", args, {
    maxBuffer: 50 * 1024 * 1024,
  });
  return stdout; // HTTP 응답 헤더 + 본문 (raw)
}

(async () => {
  const html = await impersonateGet(
    "https://www.coupang.com/np/search?q=노트북",
    "chrome116"
  );
  console.log(html.split("\n")[0]);    // "HTTP/2 200"
  console.log("size:", html.length);   // size: 7872
})();

이 방식은 진짜 curl-impersonate 바이너리를 그대로 사용하므로 curl_cffi와 동등한 위장 강도를 얻습니다. 실측 결과 응답까지 정상으로 받아왔고, 한 번 호출에 약 681ms가 걸렸습니다 (컨테이너 시작 오버헤드 포함). 단점은 Docker 데몬이 떠 있어야 한다는 것과 매번 생성/파괴 비용입니다. 대량 요청에는 컨테이너를 띄워두고 docker exec로 재사용하는 패턴이 좋습니다.

실전 테스트 — 쿠팡 검색

이제 동일한 URL(쿠팡 노트북 검색)에 대해 5가지 방식을 모두 돌려봤습니다.

방식HTTP 상태응답 크기판정
Python requests (vanilla)403 Access Denied 0.4 KB 차단 ❌
Python curl_cffi (Chrome 120)200 OK 2.5 KB 통과 ✅
Python curl_cffi (Safari 17.0)200 OK 2.5 KB 통과 ✅
Python curl_cffi Session (root → search)403 → 200 0.5 KB → 2.5 KB 통과 ✅ (쿠키 워밍)
Node fetch (vanilla)403 Access Denied 0.4 KB 차단 ❌
Node cycletls (Chrome JA3)403 0 KB 차단 ❌
Node child_process → Docker curl_chrome116200 OK 7.7 KB 통과 ✅
Docker curl_chrome116 직접200 OK 7.7 KB 통과 ✅

결과는 명확합니다. 일반 클라이언트는 모두 403으로 막히고, curl-impersonate를 직접 사용한 모든 방식이 200을 받았습니다. cycletls는 JA3까지 맞췄음에도 차단됐는데, 쿠팡이 단순 TLS 핑거프린트를 넘어 HTTP/2 SETTINGS 프레임의 가중치(WEIGHT)와 윈도우 크기, 헤더 송신 순서까지 검사한다는 강력한 시사점입니다. 또 한 가지 중요한 사실은 쿠팡의 봇 보호 인프라가 Akamai Bot Manager라는 점입니다. bm_ss·_abck·bm_sz 쿠키가 그 증거입니다.

실전 테스트 — 네이버쇼핑 검색

방식HTTP 상태응답 크기특이사항
Python requests (vanilla)418 2.6 KB 봇 차단 페이지
Python curl_cffi (Chrome 120)418 2.6 KB "정상적인 접근이 아닙니다"
Python curl_cffi (Safari 17.0)418 2.6 KB 동일 페이지
Node cycletls (Chrome JA3)418 0 KB 본문 비어있음
Docker curl_chrome116 직접418 3.4 KB 캡챠 페이지 반환

네이버쇼핑은 curl-impersonate로도 뚫리지 않았습니다. 응답 코드 418 I'm a teapot은 네이버가 봇으로 의심되는 트래픽에 반환하는 코드로, 실제 본문에는 "정상적인 접근이 아닙니다" 문구와 함께 캡챠를 풀어달라는 페이지가 들어있습니다. TLS·HTTP/2 핑거프린트가 완벽해도 Referer, Accept-Language, Cookie(쿠키 동의 토큰), 사전 방문 기록, JS 챌린지 응답 같은 행동 신호를 함께 보지 않으면 이 캡챠 페이지가 그대로 떨어집니다.

네이버쇼핑처럼 캡챠가 뜨는 사이트의 현실적인 대안을 정리하면 이렇습니다.

  • 네이버 검색 OpenAPI — 일 25,000건 무료, 캡챠 없음. 합법적이고 안정적인 1순위.
  • Playwright + stealth 플러그인 — 진짜 브라우저로 JS와 캡챠 UI까지 렌더링. 사람이 풀거나 솔버에 연결.
  • 2captcha · AntiCaptcha · CapSolver — 1000건당 1~3달러로 토큰을 받아 자동 해결.
  • 모바일 앱 API — mitmproxy로 추출 가능하지만 ToS 위반 위험.

두 사이트의 결론과 라이브러리 선택 가이드

실측을 종합하면 권장 의사 결정 트리는 다음과 같습니다.

  • 1순위 — Python으로 가능하면 무조건 curl_cffi. 설치도 가장 쉽고, fingerprint 위장 깊이도 가장 깊습니다. Session으로 쿠키 워밍까지 자연스럽게 됩니다.
  • 2순위 — Node.js 환경이 강제라면 Docker + curl-impersonate 바이너리를 child_process로 호출. 컨테이너 오버헤드(~600ms)는 있지만 위장 강도는 동일합니다.
  • 3순위 — Cloudflare 등 가벼운 보호만 통과하면 되는 경우 cycletls. 설치가 간단하고 자체 Go 바이너리로 동작합니다. Akamai 같은 깊은 검사에는 부족.
  • 4순위 — JS 챌린지나 캡챠가 있는 사이트는 Playwright/Puppeteer + Stealth + 캡챠 솔버. 진짜 브라우저를 띄우는 비용이 들지만 행동 신호까지 자연스럽게 만들어집니다.

운영에서 꼭 챙겨야 할 함정

위장 자체는 시작점일 뿐이고, 실제 운영에서 부딪히는 함정은 따로 있습니다.

  • 요청 빈도 — fingerprint가 완벽해도 같은 IP에서 초당 수십 건이 쏟아지면 즉시 차단됩니다. asyncio.Semaphorep-limit로 동시 요청수를 4~8 사이로 제한하세요.
  • 프록시 회전 — 운영 규모에서는 거주용 프록시(residential proxy) 또는 ISP 프록시를 IP풀로 돌리는 게 사실상 필수입니다.
  • 버전 매칭 — Chrome 116 fingerprint가 영원히 통하진 않습니다. 6~12개월 단위로 최신 안정판으로 갱신해야 하며, impersonate 옵션도 그에 맞게 바꿔야 합니다.
  • 쿠키 워밍 — Akamai 기반 사이트(쿠팡, 다나와 등)는 첫 요청에서 쿠키를 받고 두 번째 요청이 진짜 통과합니다. Session 패턴 필수.
  • SPA 데이터 — 쿠팡처럼 초기 HTML에 상품이 없는 경우, 네트워크 탭에서 실제 데이터 XHR를 찾아 그 엔드포인트를 직접 호출하는 편이 빠릅니다.
  • 합법성 — robots.txt와 이용약관, 개인정보 보호법을 반드시 검토하세요. 가격 정보는 비교 목적의 공공 데이터로 취급되는 경우가 많지만, 회원 정보·후기 수집은 별개의 법적 리스크가 따릅니다.

마치며

curl-impersonate는 "User-Agent 위장이 더는 통하지 않는 시대"에 가장 강력한 무기 중 하나입니다. 특히 Python의 curl_cffipip install 한 줄로 설치되고, 기존 requests 코드를 그대로 이식할 수 있어 학습 비용이 거의 없습니다. 직접 돌려본 결과 쿠팡은 손쉽게 통과(Akamai Bot Manager + 쿠키 워밍)됐고, 네이버쇼핑은 캡챠 단계까지 가버려 도구의 한계 밖이었습니다. 이런 차이를 이해하고 사이트별로 다른 전략을 쓰는 것이 현실적인 크롤링 운영의 핵심입니다.

다음 단계로는 (1) asyncio + 프록시 풀로 분산 요청 인프라 만들기, (2) 응답 파싱을 selectolax나 BeautifulSoup으로 정형화하기, (3) Playwright와 캡챠 솔버를 결합해 SPA·캡챠 사이트까지 자동으로 받아오는 하이브리드 파이프라인 구축을 권합니다.

참조 링크

조회 통계 (최근 30일)
PV 3UV 3
이 글이 도움이 되셨나요? 의견을 들려주세요!
지금까지 0명이 의견을 남겼어요
아직 댓글이 없어요. 첫 댓글을 남겨보세요!