웹 자동화 글은 공개 사이트의 선택자를 복사해 “크롤링 성공”이라고 쓰기 쉽다. 그 예제는 사이트가 바뀌면 바로 깨지고, 약관·개인정보 문제도 남는다. 여기서는 이 저장소를 로컬로 빌드한 뒤 자체 도구 페이지를 검증하는 범위로 예제를 제한한다.

저장소에 이미 있는 브라우저 검사

이 저장소에는 Playwright 의존성이 없다. 대신 scripts/test_tools_cdp.mjs라는 88줄짜리 Chrome DevTools Protocol 검사기가 있다.

이 스크립트는 다음 순서로 동작한다.

  1. 127.0.0.1:9222의 Chrome 디버깅 대상을 찾는다.
  2. WebSocket으로 Runtime과 Page 도메인을 활성화한다.
  3. 기본 주소 127.0.0.1:8080/tools/로 이동한다.
  4. 활성 도구 카드를 하나씩 선택한다.
  5. 해당 패널만 보이는지, 입력 후 결과 텍스트가 바뀌는지 확인한다.
  6. 브라우저 예외를 JSON으로 출력한다.

핵심 호출은 실제 파일에서 다음처럼 확인된다.

await send("Page.navigate", { url: testUrl })
...
const result = await send("Runtime.evaluate", {
  expression,
  awaitPromise: true,
  returnByValue: true,
})

이것은 저장소에 존재하는 구현 설명이지, 이번 글 수정 과정에서 성공 결과를 새로 측정했다는 뜻은 아니다.

CDP 직접 사용과 Playwright 비교

항목현재 CDP 스크립트Playwright
브라우저 연결WebSocket 메시지를 직접 관리browser와 page API 사용
대기현재 1.5초 고정 대기locator 작업 전 actionability 자동 대기
선택자document.querySelectorrole, text, CSS locator
실패 메시지직접 수집·구성assertion과 trace 제공
의존성Node.js 내장 WebSocket 환경패키지와 브라우저 설치 필요
제어 범위필요한 CDP 명령을 세밀하게 사용일반 테스트 흐름을 짧게 작성

Playwright는 선택 가능한 상태, 안정성, 이벤트 수신 가능 여부 등을 확인한 뒤 작업을 실행한다. 정확한 조건은 Playwright 자동 대기 공식 문서에 정리되어 있다.

로컬 페이지를 검증하는 Python 예제

먼저 Python용 Playwright와 Chromium을 설치한다. 기존 전역 Python 환경을 오염시키지 않도록 가상환경 사용을 권장한다.

python -m venv .venv
.venv\Scripts\python -m pip install playwright
.venv\Scripts\python -m playwright install chromium

macOS와 Linux에서는 가상환경 실행 경로가 .venv/bin/python이다. 설치 기준은 Playwright Python 라이브러리 문서를 따른다.

로컬 서버가 실행 중이라는 전제에서 다음 스크립트는 페이지 제목, 활성 도구 카드, 브라우저 오류를 확인하고 실제 화면을 artifacts/tools-page.png에 저장한다.

from pathlib import Path
from playwright.sync_api import sync_playwright

BASE_URL = "http://127.0.0.1:8080/tools/"

Path("artifacts").mkdir(exist_ok=True)

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page(viewport={"width": 1440, "height": 1000})

    browser_errors = []
    page.on("pageerror", lambda error: browser_errors.append(str(error)))

    response = page.goto(BASE_URL, wait_until="networkidle")
    assert response is not None
    assert response.ok, f"HTTP {response.status}"

    heading = page.get_by_role(
        "heading",
        name="무료 웹 유틸리티와 실전 계산기",
    )
    heading.wait_for(state="visible")

    cards = page.locator(".tool-card:not(.disabled)")
    assert cards.count() > 0, "활성 도구 카드가 없습니다."

    first_card = cards.first
    target = first_card.get_attribute("data-target")
    assert target, "data-target이 없습니다."
    first_card.click()

    pane = page.locator(f"#tool-{target}")
    pane.wait_for(state="visible")
    page.screenshot(path="artifacts/tools-page.png", full_page=True)

    assert not browser_errors, browser_errors
    browser.close()

스크린샷은 장식이 아니라 테스트 증거로 다룬다. 파일과 함께 커밋, 뷰포트, URL, 실행 명령을 기록해야 나중에 같은 조건으로 비교할 수 있다.

로컬 실행 순서

이 프로젝트는 Node.js 22 이상이 필요하다.

.\.tools\node-v22.16.0-win-x64\node.exe .\quartz\bootstrap-cli.mjs build
python -m http.server 8080 --bind 127.0.0.1 --directory public
.venv\Scripts\python local_tools_test.py

첫 명령은 정적 사이트를 public에 만든다. 두 번째 명령은 로컬에서만 접근 가능한 서버를 연다. 마지막 명령이 위 테스트 파일을 실행한다.

테스트가 실패하면 다음을 함께 보관한다.

  • Playwright trace
  • 전체 페이지 스크린샷
  • 브라우저 console과 pageerror
  • 실패한 URL과 HTTP 상태
  • 테스트 당시의 Git 커밋

외부 사이트 자동화와 다른 점

자체 서비스 테스트는 운영자가 페이지와 테스트를 모두 통제한다. 외부 사이트를 수집하거나 조작할 때는 기술 가능성과 허용 범위가 다르다.

  • 서비스 약관과 robots.txt를 확인한다.
  • 로그인, 결제, CAPTCHA, 접근 통제를 우회하지 않는다.
  • 개인정보와 인증 정보가 trace·screenshot에 남지 않게 한다.
  • 요청 빈도와 병렬 수를 제한한다.
  • 공식 API가 있으면 브라우저 자동화보다 먼저 검토한다.

한계

  • 이번 저장소 감사 환경에는 Python Playwright가 설치되어 있지 않아 위 예제를 실행하지 않았다.
  • 따라서 성공 로그나 새 스크린샷을 결과처럼 첨부하지 않았다.
  • 현재 CDP 스크립트는 고정 1.5초 대기를 사용하므로 느린 환경에서 불안정할 수 있다.
  • networkidle은 장시간 연결을 유지하는 페이지에서 끝나지 않을 수 있다. 그 경우 특정 UI 상태를 기다리는 편이 낫다.
  • CSS 클래스 기반 선택자는 UI 리팩터링에 취약하다. 접근성 role이나 명시적 test id를 우선한다.
  • 헤드리스 브라우저 통과만으로 키보드 접근성, 실제 모바일 성능, 서버 부하까지 검증되지는 않는다.

이 예제의 목표는 데이터를 많이 가져오는 것이 아니라, 같은 로컬 화면을 같은 조건으로 다시 확인할 수 있게 만드는 것이다.

📦추천 상품🔒 개인정보 수집 없음

쿠팡 로켓배송 특가 & 가성비 추천 제품 확인

이 글에서 소개하거나 추천하는 IT 기기, 가공식품, 가성비 필수 꿀템을 쿠팡 로켓배송 최저가로 빠르게 만나보세요.

쿠팡 최저가 확인하기 →