← 블로그로 돌아가기

사이트의 숨겨진 JSON API: 내부 엔드포인트 찾기 및 크롤러 트래픽 줄이기

사이트는 JSON 형식으로 데이터를 스스로 반환하며, 이 응답은 HTML 페이지보다 수십 배 가볍습니다. DevTools에서 내부 엔드포인트를 찾는 방법, 복사한 cURL이 작동하는 이유, 귀하의 코드가 작동하지 않는 이유, 토큰과 페이지네이션을 처리하는 방법, 아이디어를 포기하는 것이 더 나은 경우에 대해 단계별로 설명합니다.

📅2026년 9월 22일
사이트의 숨겨진 JSON API: 내부 엔드포인트 찾기 및 크롤러 트래픽 줄이기

파서가 8KB의 JSON 응답을 위해 400KB의 HTML을 끌어옵니다. 50배의 차이는 "아름다운 코드"에 관한 것이 아니라, 기가바이트당 비용을 지불하는 레지던트 프록시 요금에 관한 것입니다. 내부 API를 찾는 방법, 2026년에 이를 반복하는 데 방해가 되는 것, 그리고 이 아이디어를 포기해야 할 때를 살펴봅니다.

HTML이 이미 파싱되고 있다면 숨겨진 API를 찾는 이유는?

거의 모든 현대 인터페이스 — React, Vue, Angular, Next.js — 는 먼저 페이지의 뼈대를 로드하고, 데이터를 별도의 요청으로 자신의 엔드포인트에 가져옵니다. 이러한 엔드포인트는 문서화되어 있지 않지만 존재하며, 순수 JSON으로 응답하고 headless 브라우저 없이도 접근 가능합니다.

이 엔드포인트로 전환하면 얻는 것:

  • 트래픽이 급격히 감소합니다. 전형적인 상품 결과를 분석할 때 HTML 페이지는 약 400KB의 크기를 가지며, 마크업, 스타일 및 트래커를 포함하고, 해당 JSON 엔드포인트는 약 8KB로, 내부 ID, 재고, 상품 옵션과 같은 더 많은 필드를 포함합니다.
  • 브라우저가 필요 없습니다. JavaScript 렌더링이 사라지고, 그에 따라 메모리, 프로세서 및 글꼴 및 분석을 위한 수십 개의 추가 요청이 필요하지 않습니다.
  • 데이터가 이미 구조화되어 있습니다. CSS 클래스 변경으로 인해 깨지는 선택자가 없습니다.
  • 요청 수가 줄어들면 차단될 이유도 줄어듭니다. 브라우저에서 카탈로그 페이지 하나를 렌더링하는 것은 사이트에 대한 수십 개의 요청을 의미합니다; 동일한 양의 데이터를 API를 통해 요청하면 하나의 요청으로 끝납니다.

레지던트 프록시 프로젝트의 경우 이는 직접적인 비용 절감입니다: 요금은 기가바이트 단위로 계산되며, 렌더링에서 JSON으로 전환하면 일반적으로 청구서가 더 많이 줄어듭니다. 관련 주제는 파서 트래픽을 5배 줄이는 방법입니다.

단계별: 엔드포인트 찾기

  1. 먼저 공식 API가 없는지 확인하세요. 목표 사이트의 /developers, /api, /docs를 확인하세요. 공개 문서화된 API는 버전 관리가 되며, 비공식 API는 조용히 변경됩니다.
  2. DevTools를 열고 (F12) Network 탭으로 이동하여 기록이 활성화되어 있는지 확인하세요.
  3. Fetch/XHR 필터를 활성화하세요. 이 필터는 이미지, 글꼴 및 분석 요청을 차단하고 데이터 요청만 남깁니다.
  4. 목록을 정리하세요 초기 로딩의 잡음을 제거합니다.
  5. 필요한 데이터를 유도하세요: 결과를 스크롤하고, "다음 페이지"를 클릭하고, 필터를 적용하고, 카드 정보를 엽니다. 관심 있는 요청이 동작하는 순간 나타납니다.
  6. 당신의 데이터가 포함된 응답을 찾으세요. 가장 빠른 방법은 Network 패널에서 Ctrl+F를 사용하여 화면에서 보이는 고유한 값을 검색하는 것입니다 (아티클, 정확한 가격, 이름의 일부) 그리고 어떤 요청이 그것을 생성했는지 확인하세요.
  7. 요청을 전체적으로 복사하세요: 행을 오른쪽 클릭 → 복사 → cURL로 복사. 이후 curlconverter를 통해 코드를 변환하세요 — 이렇게 하면 헤더를 잃지 않습니다.

먼저 살펴봐야 할 전형적인 경로: /api/, /v1/, /v2/, /search, /products, /listings, /graphql.

특별한 경우: Next.js 사이트

여기서 데이터는 종종 별도의 요청이 필요하지 않습니다 — HTML에 바로 포함되어 있습니다. 구형 Pages Router에서는 __NEXT_DATA__ 블록입니다. App Router(Next.js 13 이상)에서는 데이터가 여러 script 노드에서 self.__next_f.push() 호출로 분산되어 있습니다 — 이는 React Server Components의 직렬화된 페이로드입니다. 수동으로 분석하는 것은 불편합니다: 청크는 $ 접두사를 통해 서로를 참조하며 문자열 중간에 잘릴 수 있습니다. Python에는 HTML에서 Flight-payload와 원시 RSC 응답을 파싱하는 nextflight 라이브러리가 있으며, 키 이름으로 검색할 것을 제안합니다 — 이렇게 하면 파서가 사이트의 재배포를 견딜 수 있습니다.

파라미터 리버스: 페이지네이션 및 필터

발견된 엔드포인트는 거의 항상 파라미터화되어 있습니다. 세 가지 스킴이 있습니다:

  • 페이지별: ?page=3&per_page=20
  • 오프셋 및 제한: ?offset=40&limit=20
  • 커서: ?after=<token>&limit=20 — 다음 페이지의 토큰은 이전 응답 본문에 포함됩니다.

디버깅 시간을 절약하는 세 가지 규칙:

  • 미리 계산된 페이지 수가 아닌 빈 배치에서 멈추세요: 비공식 API의 total 카운터는 자주 잘못된 정보를 제공합니다.
  • 실제 배치 크기를 확인하세요. 100을 요청했는데 20이 왔다면, 엔드포인트에 자체 한도가 있으며 페이지 수에 대한 계산이 잘못되었습니다.
  • 500 페이지에 접근하지 마세요. 깊은 페이지네이션은 거의 모든 곳에서 서버에 의해 잘립니다; 대신 필터를 통해 선택을 잘라내세요 — 카테고리별, 가격 범위별, 날짜별.

브라우저에서 cURL이 작동하고 코드가 작동하지 않는 이유

가장 흔한 실패 지점이며, 이유는 거의 항상 하나입니다: 헤더가 손실되었습니다. 복사된 cURL은 요청의 모든 컨텍스트를 포함하고, 사용자 정의 클라이언트는 그렇지 않습니다.

일반적으로 필수적인 것:

  • X- 접두사가 있는 사용자 정의 헤더 — X-CSRF-Token, X-Requested-With: XMLHttpRequest 및 다양한 X-*-Token은 프론트엔드에서 자동으로 추가됩니다. 이들이 없으면 400–500 범위의 응답을 받게 됩니다.
  • Referer — 사용자 행동에 의해 생성되는 컨텍스트 헤더입니다. 많은 엔드포인트는 요청이 "자신의 페이지에서 왔는지" 확인합니다.
  • Authorization: Bearer <JWT> — 짧은 수명의 토큰으로, 일반적으로 15–60분 동안 유효합니다. 이를 하드코딩하는 것은 의미가 없습니다: 최신 토큰을 얻는 방법을 알아야 합니다.
  • 세션 쿠키 — 이를 세션 객체에 보관하고 수동으로 복사하지 마세요.
  • POST에 대한 올바른 Content-Type — application/json 및 application/x-www-form-urlencoded는 본문을 다르게 인코딩하며, 선언된 유형과 불일치하면 요청이 조용히 실패합니다.

쿠키에 없으면 토큰을 찾을 수 있는 곳: <script> 내부의 HTML 소스 (알려진 값을 통해 Ctrl+F로 검색), JavaScript 번들, localStorage 또는 IndexedDB — DevTools의 Application 탭에서 확인하세요.

늦게 알게 되는 함정

비공식 API는 예고 없이 변경됩니다. 버전 관리, 호환성 및 지원 약속이 없으며: 프론트엔드 팀이 목요일 저녁에 필드를 이름을 바꾸면, 당신의 파서는 빈 값을 수집합니다. 보호는 "신뢰할 수 있는 선택자"가 아니라 구조를 제어하는 것입니다: 필수 필드가 제자리에 있고 올바른 유형인지 확인하세요; 빈 값의 비율과 실행 중 레코드 수를 모니터링하세요; 손상된 레코드는 пропускайте, 그러나 결함이 10%를 초과하면 경고를 올리세요; 원시 응답을 저장하여 나중에 비교할 수 있도록 하세요.

API는 종종 페이지보다 더 강력하게 보호됩니다. 정기적으로 발생합니다: HTML은 문제없이 제공되지만, /api/에는 TLS 핑거프린트와 헤더 조합을 확인하는 안티봇이 있습니다. 그러면 트래픽 절약이 실패한 요청의 비율 증가로 이어지고, 이익이 사라집니다.

서명된 요청. 파라미터에서 sign, hash 또는 _s와 같은 것이 보이면, 프론트엔드는 JavaScript에서 서명을 계산합니다. 이를 재현하는 것은 별도의 프로젝트이며, 종종 HTML에 남아 있는 것이 더 저렴합니다.

빈도 제한. 비공식 엔드포인트는 흐름을 처리하도록 설계되지 않았습니다: 초당 1–2 요청을 유지하고, 연결 및 읽기에 대해 별도의 타임아웃을 설정하세요 (예: 5초 및 30초), 그리고 트랜지언트 오류 — 429, 500, 502, 503, 504 — 만 반복하고 401 및 404는 건드리지 마세요. 지터가 있는 지수 지연이 필수적이며, 그렇지 않으면 모든 워커가 동시에 두 번째 라운드로 돌진합니다. 자세한 내용은 프록시를 위한 타임아웃 및 재시도 로직 분석에서 확인하세요.

법적 프레임워크. 공개 비인증 엔드포인트는 한 상황이고, 계정에 로그인하는 것은 본질적으로 다른 상황입니다: 등록은 사용자 계약의 수락을 의미합니다. 개인 데이터는 얼마나 쉽게 접근할 수 있든지 GDPR의 적용을 받습니다. 사실 — 가격, 특성, 재고 — 는 저작권으로 보호되지 않지만, 텍스트와 이미지는 보호됩니다.

HTML에 남아 있어야 할 때

숨겨진 API는 항상 이득이 아닙니다. 페이지 분석을 유지하세요, 만약:

  • 사이트가 서버 기반이고 내부 API가 전혀 없다면;
  • 엔드포인트가 서명을 요구하거나 토큰 회전을 필요로 한다면 — 이를 유지하는 것이 페이지보다 비쌉니다;
  • API에 공개 페이지보다 더 강력한 보호가 있다면;
  • 프론트엔드가 여러 출처에서 수집한 최종 결과가 필요하다면;
  • 여러 사이트를 운영하고 있다면: 단일 HTML 파이프라인이 개별적인 특성을 가진 비공식 API의 동물원보다 더 잘 확장됩니다.

API 파싱을 위한 프록시 유형 선택하기

JSON으로 전환하면 계산이 변경됩니다. 왜냐하면 병목 현상이 이동하기 때문입니다: 트래픽은 줄어들고 IP 품질 및 세션 안정성에 대한 요구는 증가합니다.

  • 인증 없이 안티봇이 없는 공개 엔드포인트. 여기서는 데이터센터 프록시가 충분합니다: 데이터 양이 적고 레지던트에 대한 비용을 지불할 필요가 없습니다.
  • 안티봇 뒤에 있는 엔드포인트 또는 세션에 묶인 엔드포인트. 레지던트 프록시가 필요하며, 세션이 고정되어 있어야 합니다: 토큰, 쿠키 및 IP는 체인의 모든 부분에서 일치해야 하며, 그렇지 않으면 서버가 두 번째 요청에서 세션을 재설정합니다. 이 경우 청구서는 여전히 적당하게 유지됩니다 — JSON 모드에서 기가바이트는 천천히 소모됩니다.
  • 모바일 애플리케이션의 데이터. 웹 버전이 닫혀 있고 애플리케이션이 동일한 데이터를 더 쉽게 제공한다면, 엔드포인트는 트래픽 가로채기를 통해 찾습니다 — 이는 mitmproxy를 통한 모바일 애플리케이션의 숨겨진 API 검색에 대한 기사에서 설명된 별도의 절차입니다.

간단히 말해서

DevTools에서 20분은 종종 headless 브라우저와의 싸움에서 며칠을 대체합니다: Fetch/XHR 필터, 보이는 값으로 검색, cURL로 복사 — 그리고 당신은 작동하는 요청을 손에 쥐게 됩니다. 이후에는 세부 사항을 해결합니다: 모든 헤더를 전송하고, 페이지네이션 스킴을 분석하고, 응답 유효성을 검사하고, 엔드포인트가 페이지보다 더 강력하게 보호되고 있지 않은지 냉정하게 평가하세요. 비공식 API가 작동하는 곳에서는 트래픽 양과 요청 수를 줄여 — 즉, 프록시 비용과 차단 가능성을 동시에 줄입니다.