8000 [feature] 올리브영 조회 복구 방안: Cloudflare 챌린지 우회 경로 결정과 릴레이 운영화 · Issue #192 · hmmhmmhm/daiso-mcp · GitHub
[go: up one dir, main page]

Skip to content

[feature] 올리브영 조회 복구 방안: Cloudflare 챌린지 우회 경로 결정과 릴레이 운영화 #192

Closed

Description

@chuuminggg

배경/문제

#189에서 올리브영 조회가 전면 실패하는 원인을 정리했습니다. 이 이슈는 실제로 동작하게 만들기 위해 무엇이 필요한지를 코드 기준으로 정리합니다.

차단 주체가 Cloudflare Managed Challenge로 확인됐습니다. 올리브영 엔드포인트에 직접 요청한 응답 헤더입니다.

HTTP/1.1 403 Forbidden
Cf-Mitigated: challenge
Server: cloudflare
Content-Type: text/html (49KB)
Critical-Ch: Sec-CH-UA-*

통과하려면 챌린지 JS를 실행해 cf_clearance 쿠키를 받아야 하고, 그 쿠키는 IP·UA·TLS 지문에 묶입니다. 따라서 다음은 모두 불가능합니다.

  • 헤더·UA 위장
  • 브라우저에서 쿠키만 뽑아 Worker에서 재사용 (Worker IP가 달라 무효)
  • headless 브라우저 (docs/zyte-free-access.md 실측에서 이미 차단)

scripts/relay/browser.ts가 통과하는 이유도 같은 맥락입니다. page.evaluate() 안에서 credentials: 'include'로 호출해 올리브영 페이지와 동일 출처가 되므로 브라우저가 보유한 cf_clearance가 자동으로 실립니다.

결국 선택지는 실제 브라우저 세션 경유(자체 릴레이) 또는 주거용 IP + JS 렌더링을 파는 상용 API 둘뿐입니다.

제안 내용

먼저 결정이 필요합니다. 유료 API(비용 재개) vs 자체 릴레이(Mac 상시 운영). 코드는 어느 쪽이든 src/services/oliveyoung/transport.ts 한 지점에서 갈립니다.

경로 A: 릴레이 운영화 (코드는 대부분 존재, 연결이 빠짐)

이미 있는 것: scripts/relay/ 993줄(서버·인증 핸들러·브라우저 수명 관리·프로세스 회수·macOS 설치)과 Worker 측 배선(transport.ts, handlers.ts, index.ts의 릴레이 분기 및 Access 헤더 주입).

막아야 할 구멍 7가지입니다.

  1. Secrets 미동기화 — .github/workflows/sync-worker-secrets.yml에 OY_RELAY_URL, OY_RELAY_TOKEN, OY_ACCESS_CLIENT_ID, OY_ACCESS_CLIENT_SECRET이 없습니다.
  2. 네트워크 경로 부재 (운영 작업) — Mac 127.0.0.1:4319 → Tunnel → Access(Service Auth) → Worker. 전용 계정, GUI 로그인, DNS, Access 정책은 사람이 구성해야 합니다.
  3. 오류 의미 붕괴 — transport.ts가 expectedStatus: 200, retries: 0으로 호출해, 릴레이의 429(쿼터)·503(busy/만료)·502(업스트림)가 모두 올리브영 브라우저 릴레이 요청 실패 한 문장으로 뭉개집니다. src/core/errors.ts도 설정 오류만 구분합니다. 재시도 판단·백오프·점검 신호 분리가 불가능합니다.
  4. 재고 조회가 릴레이 용량을 즉시 초과합니다 (최우선) — 릴레이는 브라우저 1개로 완전 직렬 처리하고(lifecycle.ts의 active 플래그 + tail 체인), 분당 30회·일 3,000회 상한이 있습니다. 반면 handleOliveyoungCheckInventory 1회는 상품검색 1 + 매장검색 1 + 상품당 goodsInfo 1 + stock-stores 1을 Promise.all로 날려 stockCheckLimit N일 때 2N+2 요청이 동시에 몰립니다. 8슬롯 초과 503과 15초 대기 만료가 다발합니다. MCP 쪽에 동시성 제한·큐·429 백오프가 없습니다. client.ts의 Promise.all을 동시성 1~2로 제한하고 stockCheckLimit 기본값을 낮춰야 합니다.
  5. 설정 여부가 점검에 안 보입니다 — src/api/configStatus.ts에 올리브영 릴레이 항목이 없어 /health가 ok를 주면서 미설정을 드러내지 못합니다. 릴레이 자체의 인증된 GET /health도 외부 점검에 연결돼 있지 않습니다.
  6. 헬스체크 요약 잘림 — .github/workflows/health-checks.yml의 lines.slice(0, 6) 때문에 실패 12개 중 6개만 보이고 올리브영이 가려집니다.
  7. 릴레이 중단 시 전면 실패 — 캐시가 인스턴스 메모리 30분(client.ts)과 엣지 캐시뿐입니다. wrangler.toml에 KV가 없습니다. 매장 목록처럼 거의 변하지 않는 데이터를 KV에 영속화하면 릴레이가 멈춰도 매장 검색은 유지할 수 있습니다.

최소 동작 경로

  1. 경로 결정 (유료 API vs 자체 릴레이)
  2. 릴레이라면: secrets 4개 동기화 → Tunnel/Access/DNS → Worker 주입 → /api/oliveyoung/stores 1건 실검증
  3. 코드 보강: 동시성 제한(4) → 오류 코드 보존(3) → 설정 노출(5) → 지속 캐시(7)
  4. 헬스체크 요약 잘림 해제(6)

대안/검토 사항

경로 B: transport를 상용 API로 교체

  • Zyte 복귀: 계정 정지 해제(결제)가 선행되어야 하고, src/utils/zyte.ts의 하드 차단 해제와 docs/zyte-free-access.md·릴리스 노트 수정이 필요합니다. #185를 되돌리는 결정입니다.
  • 다른 업체(Browserless, ScrapingBee, Bright Data 등): JS 렌더링 + 주거용 IP면 통과합니다. 비용 구조만 다르고 성격은 Zyte와 같습니다.
  • Cloudflare Browser Rendering: headless는 이미 차단으로 실측됐고 CF 데이터센터 IP라 기대치가 낮습니다. 검증 비용이 싸므로 시도해 볼 수는 있습니다.

경로 C: 축소해서 정직하게 실패시키기

  • 상품 페이지 HTML도 같은 챌린지 뒤에 있어 대체 소스가 없고, 매장 재고는 올리브영 외 출처가 없습니다.
  • 복구를 미룬다면 #157이 CU에 적용한 패턴(연결은 유지하고 available: false와 명시적 원인 반환)을 쓰고, 헬스체크에서 올리브영을 "설정 미완" 신호로 분리해 상시 실패부터 정리하는 선택지가 있습니다.

적용 범위 한계

  • 릴레이 설정은 바인딩 전용이라 MCP 도구 인자로 넘길 수 없습니다(의도된 설계). 이 복구는 mcp.aka.page 운영자만 수행할 수 있고, self-host 하는 쪽은 각자 릴레이를 세워야 합니다.

관련: #189, #157, #185, #186 / 확인일 2026-09-24

Activity

  1. hmmhmmhm commented on Oct 6, 2026

    @hmmhmmhm
    Owner

    릴레이 연결, 오류 진단, 재고 조회 동시성 제한과 헬스 점검을 반영했습니다. 현재는 브라우저 없이 HTTP 중계로 동작하며(#234, #235), KV 영속 캐시 대신 기존 캐시와 자동 경로 선택을 사용합니다. 복구 방향을 정리해 주셔서 감사합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      D 32F7 evelopment

      No branches or pull requests

      Issue actions

      0