본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 30. 14:28

쿠키와 프록시 컨텍스트를 잃어버리는 Playwright CDP 세션 디버깅

요약

Playwright를 사용하여 CDP(Chrome DevTools Protocol)로 브라우저에 연결할 때 발생하는 세션 및 프록시 컨텍스트 유실 문제를 다룹니다. 단순한 셀렉터 오류가 아닌 브라우저 정체성(identity) 불일치 관점에서의 디버깅 접근법을 제시합니다.

핵심 포인트

  • CDP 연결 시 쿠키 및 세션 상태 유실 여부를 먼저 확인해야 함
  • 셀렉터 수정 전 브라우저 정체성(identity) 일치 여부 검증 필요
  • AI 에이전트 사용 시 환경 변화로 인한 버그 은폐 가능성 주의
  • storageState 등 지속성 모델의 명확한 구분 필요

동일한 실패 패턴이 반복되는 것을 보고 저는 이를 별도의 버그 클래스로 취급하기 시작했습니다:

사용자가 **브라우저 프로필 (browser profile)**을 열고 로그인되어 있습니다.
Playwright 스크립트가 CDP를 통해 연결(attach)됩니다.
페이지가 열립니다.
그런 다음 워크플로우가 마치 **세션 (session)**이 비어 있거나, 계정이 로그아웃되었거나, 요청이 잘못된 **프록시 (proxy)**를 통해 들어오는 것처럼 동작합니다.

유혹적인 해결책은 대기 시간(wait)을 추가하거나 셀렉터(selector)를 다시 작성하는 것입니다.

하지만 그것은 대개 잘못된 첫 번째 조치입니다.

**CDP 연결 세션 (CDP-attached session)**이 **인증 상태 (auth state)**나 **프록시 컨텍스트 (proxy context)**를 잃어버렸을 때, 첫 번째 질문은 다음과 같아야 합니다:

어떤 셀렉터가 변경되었는가? 가 아닙니다.

질문은 다음과 같아야 합니다:

내 스크립트가 내가 수동으로 확인한 것과 동일한 브라우저 정체성(identity)에 연결되었는가?

이 노트는 해당 문제에 대한 실질적인 디버깅 체크리스트입니다.

문제: Playwright가 기존 Chromium 브라우저에 연결되지만, 쿠키 (cookies), 세션 상태 (session state) 또는 **프록시 컨텍스트 (proxy context)**가 예상과 일치하지 않습니다.

범위: Chromium, connectOverCDP, 브라우저 프로필 (browser profiles), 지속적 세션 (persistent sessions), 헤드리스 체크 (headless checks), 그리고 AI 에이전트 디버깅 (AI agent debugging).

포함되지 않는 내용: 플랫폼 보호 우회, CAPTCHA 처리, 또는 관련 없는 계정 간의 인증 데이터 이동.

실패 패턴

실행 상태는 겉보기에 보통 정상적으로 보입니다.

브라우저가 시작됩니다. **CDP 엔드포인트 (CDP endpoint)**가 응답합니다. Playwright가 연결됩니다. 페이지가 로드됩니다. 스크립트는 클릭할 수 있고 DOM을 읽을 수 있습니다.

하지만 환경이 잘못되었습니다.

다음 증상 중 하나 이상이 나타날 수 있습니다:

  • **쿠키 (Cookies)**가 비어 있거나 예상보다 적습니다.
  • 새로고침 후 앱이 로그인 페이지로 리다이렉트됩니다.
  • localStorage는 존재하지만, 앱은 여전히 사용자를 로그아웃된 상태로 취급합니다.
  • 계정이 수동으로는 작동하지만 CDP를 통해 연결하면 실패합니다.
  • 공인 IP가 예상된 **프록시 경로 (proxy route)**와 일치하지 않습니다.
  • 작업이 가시 모드(visible mode)에서는 작동하지만 **헤드리스 모드 (headless mode)**에서는 실패합니다.
  • **AI 브라우저 에이전트 (AI browser agent)**가 작업을 재시도하고 다른 컨텍스트에서 성공으로 표시합니다.

마지막 케이스는 특히 놓치기 쉽습니다.

일반적인 스크립트는 환경이 변하면 종종 중단됩니다. 하지만 **AI 에이전트 (AI agent)**는 계속 진행하고, 적응하며, 실제 버그를 숨길 수 있습니다.

따라서 셀렉터 (selectors)를 디버깅하기 전에, 저는 한 가지를 증명하려고 시도합니다:

스크립트가 예상된 브라우저 식별 정보 (browser identity)로부터 실행되고 있는가.

먼저 세 가지 지속성 모델 (persistence models)을 구분하십시오

디버깅을 시작하기 전에, 당신이 실제로 사용하고 있는 모델의 이름을 명명하십시오.

많은 **Playwright 세션 버그 (Playwright session bugs)**는 다음 세 가지 모델이 서로 뒤섞여 있기 때문에 혼란을 야기합니다:

  • storageState
  • launchPersistentContext
  • connectOverCDP

이들은 서로 연관되어 있지만, 서로 교체 가능한 것은 아닙니다.

1. storageState

storageState는 스냅샷 (snapshot)입니다.

인증 (auth)이 쿠키 (cookies), localStorage, 그리고 때로는 IndexedDB로 표현될 때 유용합니다.

이것은 전체 **브라우저 프로필 (browser profile)**과 동일한 것이 아닙니다.

적합한 경우:

  • 테스트 계정
  • 반복 가능한 로그인 설정
  • CI 워크플로우 (CI workflows)
  • 깨끗한 브라우저 컨텍스트 (clean browser contexts)

위험한 가정:

내 실제 Chrome 프로필이 로그인되어 있으므로, storageState가 모든 것을 포함하고 있을 것이다.

그렇지 않을 수도 있습니다.

일부 앱은 sessionStorage, 서비스 워커 (service workers), 확장 프로그램 (extensions), 디바이스 바운드 상태 (device-bound state), 또는 앱 전용 저장소 (app-specific storage)를 포함하기도 합니다.

2. launchPersistentContext

launchPersistentContext는 특정 **사용자 데이터 디렉토리 (user data directory)**와 함께 브라우저를 시작합니다.

해당 디렉토리는 쿠키 (cookies)localStorage와 같은 브라우저 세션 데이터를 저장합니다.

적합한 경우:

  • 장기 유지되는 자동화 프로필
  • 로컬 디버깅
  • 프로필 디렉토리가 신뢰할 수 있는 유일한 원천 (source of truth)인 워크플로우

위험한 가정:

여러 브라우저 인스턴스를 동일한 프로필 디렉토리에 지정할 수 있다.

그렇게 하지 마십시오.

**사용자 데이터 디렉토리 (user data directory)**를 잠금 가능한 데이터베이스처럼 취급하십시오. 실행 중에는 하나의 브라우저만이 이를 소유합니다.

3. connectOverCDP

connectOverCDP는 **Chrome DevTools Protocol (CDP)**을 통해 Playwright를 기존의 Chromium 브라우저에 연결합니다.

적합한 경우:

  • 다른 도구에 의해 시작된 브라우저에 연결하기
  • 이미 열려 있는 프로필 디버깅하기
  • 수동으로 검사 중인 세션에 자동화를 연결하기

위험한 가정:

CDP 연결이 Playwright로 실행한 브라우저와 정확히 동일한 동작을 보장한다.

종종 잘 작동하기도 하지만, 연결 모델 자체가 동일한 것은 아닙니다. 버그가 쿠키 (cookies), 프로필 (profiles), 또는 **프록시 컨텍스트 (proxy context)**와 관련되어 있다면, 작업 로직을 건드리기 전에 연결된 브라우저를 먼저 확인하십시오.

1단계: 전용 브라우저 프로필 시작하기

평소 사용하는 Chrome 프로필로 디버깅하지 마십시오.

전용 자동화 디렉토리를 사용하십시오:

google-chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/pw-cdp-profile \
...

그 다음, 테스트에 인증된 상태가 필요하다면 해당 브라우저에서 수동으로 로그인하십시오.

체크리스트:

  • userDataDir이 이 테스트를 위해 전용으로 설정되었는가.
  • 다른 Chrome 프로세스가 동일한 디렉토리를 사용하고 있지 않은가.
  • 브라우저가 예상된 **원격 디버깅 포트 (remote debugging port)**와 함께 시작되었는가.
  • 프로필이 개인용 기본 Chrome 프로필이 아닌가.
  • 프로필을 수동으로 다시 열었을 때 여전히 예상되는 **로그인 상태 (login state)**를 유지하는가.

검증 표준:

자동화 도구가 프로필을 신뢰하기 전에, 수동으로 먼저 프로필의 신뢰성을 확인해야 합니다.

만약 수동 브라우저에서 이미 로그아웃된 상태라면, 아직 Playwright의 문제는 아닙니다.

2단계: CDP를 통해 연결하고 실제 컨텍스트 검사하기

실제 워크플로우를 실행하기 전에 가능한 가장 작은 규모의 스크립트를 실행하십시오.

import { chromium } from "playwright";

const browser = await chromium.connectOverCDP("http://127.0.0.1:9222");
...

이 단계를 건너뛰지 마십시오.

체크리스트:

  • contextCount가 예상한 값인가.
  • pageCount가 예상한 값인가.
  • 스크립트가 예상된 탭을 사용하거나, 예상된 컨텍스트 내에서 새 탭을 생성하는가.
  • cookieCount가 예상치 못하게 0이 아닌가.
  • localStorageKeys가 로그인한 앱과 일치하는가.
  • sessionStorageKeys가 인증 정보가 존재하는 유일한 장소는 아닌가.
  • 페이지를 새로고침했을 때 즉시 로그인 페이지로 리다이렉트되지 않는가.

이 최소한의 스크립트가 실패한다면, 실제 워크플로우는 고려할 가치도 없습니다.

문제는 연결된 **브라우저 컨텍스트 (browser context)**에 있습니다.

Step 3: 쿠키가 실제로 인증 소스인지 확인하기

흔히 하는 디버깅 실수 중 하나는 **쿠키 (cookies)**를 세션의 전부로 취급하는 것입니다.

많은 애플리케이션의 경우, 이는 사실이 아닙니다.

각 레이어를 확인하십시오:

  • 쿠키 (Cookies)
  • localStorage
  • IndexedDB
  • sessionStorage
  • 서비스 워커 (service worker) 상태
  • 확장 프로그램 (extension) 상태
  • 앱 전용 디바이스 또는 세션 바인딩 (session binding)

유용한 질문은 다음과 같습니다:

Playwright가 볼 수 있는 데이터만으로 로그인 상태를 설명할 수 있는가?

이 스냅샷을 단순한 복사-붙여넣기 메커니즘이 아닌, 검사를 위한 용도로 사용하십시오:

const state = await context.storageState({ indexedDB: true });

console.log({
...

이 로그는 비밀 값(secret values)이 아닌 메타데이터를 기록한다는 점에 유의하십시오.

체크리스트:

  • 필요한 **쿠키 도메인 (cookie domains)**이 현재 URL과 일치하는지 확인합니다.
  • 필요한 **쿠키 경로 (cookie paths)**가 너무 좁게 설정되어 있지 않은지 확인합니다.
  • 필요한 쿠키가 만료되지 않았는지 확인합니다.
  • Secure 쿠키가 HTTPS를 통해 사용되는지 확인합니다.
  • SameSite 동작이 리다이렉트 흐름에 적합한지 확인합니다.
  • 앱이 인증 정보를 그곳에 저장하는 경우 IndexedDB를 확인합니다.
  • 새 탭이나 새로고침 시 로그인이 사라진다면 sessionStorage를 고려합니다.
  • 인증 값이 CI 로그에 출력되지 않도록 합니다.

검증 기준:

앱이 인증 정보를 어디에 저장하는지 알기 전까지는 이를 쿠키 버그라고 부르지 마십시오.

Step 4: 연결된 세션 내부에서 프록시 확인하기

페이지가 로드되는 것만으로는 충분하지 않습니다.

페이지는 반드시 예상된 **네트워크 정체성 (network identity)**을 통해 로드되어야 합니다.

연결된 동일한 페이지에서 IP 확인을 실행하십시오:

await page.goto("https://api.ipify.org?format=json", {
  waitUntil: "domcontentloaded",
});
...

운영 환경(production) 시스템의 경우, 공개 엔드포인트를 자체 내부 IP 에코(echo) 서비스로 교체하십시오.

체크리스트:

  • 관찰된 **공용 IP (public IP)**가 예상되는 프록시 그룹과 일치하는지 확인합니다.
  • 관찰된 **IP 지역 (IP region)**이 프로필 플랜과 일치하는지 확인합니다.
  • 시간대 (Timezone), 로캘 (locale), 그리고 **언어 (language)**가 해당 지역과 일치하는지 확인합니다.
  • 프록시가 의도한 계층(layer)에서 설정되었는지 확인합니다.
  • **CDP가 연결된 브라우저 (CDP-attached browser)**와 **헤드리스 워커 (headless worker)**가 서로 다른 프록시 경로를 사용하지 않는지 확인합니다.
  • 재시도 로직(Retry logic)이 사용자 모르게 다른 프록시로 전환되지 않는지 확인합니다.

검증 표준:

다음 항목들이 일치할 때만 실행이 유효합니다:

  • 프로필 ID (profile ID)
  • 브라우저 프로필 디렉토리 (browser profile directory)
  • CDP 엔드포인트 (CDP endpoint)
  • 쿠키 상태 (cookie state)
  • 스토리지 상태 (storage state)
  • 프록시 경로 (proxy route)
  • IP 지역 (IP region)
  • 실행 모드 (execution mode)

IP가 잘못되었다면, 셀렉터(selector) 디버깅을 중단하십시오.

단계 5: 헤디드(headed), CDP 연결, 헤드리스(headless) 동작 비교

워크플로우가 수동으로는 작동하지만 자동화에서는 실패할 경우, 환경을 나란히 놓고 비교하십시오.

다음 매트릭스를 사용하십시오:

확인 항목헤디드 수동 프로필CDP 연결 Playwright헤드리스 실행
동일한 프로필 경로
...

목표는 모든 수치를 동일하게 만드는 것이 아닙니다.

목표는 모든 차이점을 설명하는 것입니다.

위험 신호 (Red flags):

  • CDP 연결 Playwright의 쿠키가 수동 모드보다 적습니다.
  • 헤드리스 (Headless) 모드가 헤디드 모드와 다른 프록시를 사용합니다.
  • 동일한 URL이 각 모드에서 다르게 리다이렉트됩니다.
  • 프로필 경로가 다르지만 실행 로그에는 나타나지 않습니다.
  • 에이전트 재시도 시 연결된 컨텍스트(attached context) 대신 새로운 컨텍스트(fresh context)를 엽니다.

검증 표준:

헤디드 모드의 성공이 헤드리스 모드의 준비 완료를 증명하지 않습니다. CDP 연결이 프로필의 정확성을 증명하지 않습니다.

단계 6: AI 에이전트가 계속 진행하기 전에 중단 조건 추가

이 지점이 **AI 에이전트 디버깅 (AI agent debugging)**이 스크립트 디버깅과 다른 부분입니다.

결정론적(deterministic)인 스크립트는 종종 명확하게 실패를 알립니다.

하지만 에이전트는 적응하여 계속 진행할 수 있습니다.

이는 UI 변동에는 유용하지만, 신원 불일치 (identity mismatch) 측면에서는 위험합니다.

에이전트에게 로그인된 브라우저를 제공하기 전에, 환경 검사를 요구하십시오.

다음의 경우 에이전트를 중단하십시오:

  • **쿠키 개수 (cookie count)**가 예상치 못하게 감소합니다.
  • 페이지가 로그인 페이지로 리다이렉트(redirect)됩니다.
  • **공용 IP (public IP)**가 예상된 프록시와 일치하지 않습니다.
  • 재시도(retry) 중에 **IP 지역 (IP region)**이 변경됩니다.
  • **시간대 (timezone)**와 프록시 지역이 일치하지 않습니다.
  • localStorage 또는 IndexedDB가 비어 있어야 하지 않음에도 비어 있습니다.
  • 에이전트가 예상된 **CDP 연결 컨텍스트 (CDP-attached context)**에서 작동하지 않습니다.
  • 페이지가 지갑, 결제, 계정 설정, 삭제, 권한 또는 파괴적인 흐름(destructive flows)으로 진입합니다.
  • 에이전트가 결과와 프로필 (profile), 프록시 (proxy), 시간 (time), **모드 (mode)**를 연결하는 실행 로그(run log)를 생성할 수 없습니다.

훌륭한 에이전트 실행은 다음 질문에 답할 수 있어야 합니다:

  • 어떤 프로필을 사용했는가?
  • 어떤 브라우저 컨텍스트 (browser context)에 연결했는가?
  • 어떤 프록시 경로 (proxy route)를 사용했는가?
  • 페이지가 관찰한 IP는 무엇인가?
  • 행동하기 전에 어떤 세션 상태 (session state)를 확인했는가?
  • 왜 계속 진행할 수 있었는가?
  • 무엇이 나를 중단하게 만들었을 것인가?

에이전트가 이 질문들에 답할 수 없다면, 로그인된 세션에서 동작해서는 안 됩니다.

7단계: 모든 디버깅 시도에 대해 작은 실행 로그 (run log) 사용하기

로그가 화려할 필요는 없습니다.

그저 실행의 정체성(identity)을 캡처하기만 하면 됩니다.

예시 필드:

run_id: 2026-05-30-cdp-auth-debug-001
mode: cdp-attached
cdp_endpoint: http://127.0.0.1:9222
...

이는 다음과 같이 모호한 디버깅 메모를 방지합니다:

로컬에서는 작동했지만 에이전트에서는 실패했습니다.

이 문장만으로는 충분하지 않습니다.

더 나은 메모는 다음과 같습니다:

CDP 연결 (CDP-attached) 실행에서 예상된 프로필과 쿠키 개수를 사용했으나, 관찰된 IP가 예상된 **프록시 지역 (proxy region)**과 일치하지 않아 계정 작업 전에 에이전트가 중단되었습니다.

이것이 디버깅 가능한 형태입니다.

일반적인 원인 및 첫 번째 점검 사항

원인 1: 잘못된 브라우저가 연결됨

증상:

  • context.cookies()가 예상보다 적은 쿠키를 반환합니다.
  • 다른 Chrome 창은 로그인되어 있음에도 페이지가 로그아웃되어 있습니다.
  • CDP 포트가 이전 브라우저 프로세스에 속해 있습니다.

첫 번째 점검 사항:

  • 모든 테스트용 Chrome 프로세스를 종료합니다.
  • 하나의 userDataDir을 사용하여 브라우저를 하나만 실행합니다.
  • CDP endpoint를 다시 엽니다.
  • browser.contexts().length를 출력합니다.
  • 작업을 실행하기 전에 현재 URL과 쿠키 개수를 출력합니다.

원인 2: 잘못된 프로필 디렉토리 (wrong profile directory)

증상:

  • 한 브라우저에서는 로그인이 작동하지만, CDP는 깨끗한 세션(clean session)을 확인합니다.
  • 앱이 초기 설정(first-time setup)을 요구합니다.
  • 쿠키 저장소(cookie jar)가 비어 있습니다.

첫 번째 점검 사항:

  • 실행 시 사용된 정확한 userDataDir을 확인합니다.
  • chrome://version을 수동으로 확인합니다.
  • 매일 사용하는 기본 Chrome 프로필을 사용하지 마세요.
  • 두 개의 브라우저 인스턴스가 동일한 디렉토리를 공유하게 하지 마세요.

원인 3: 인증 정보가 쿠키에만 저장되지 않음 (auth is not only stored in cookies)

증상:

  • 쿠키가 존재하지만 앱에서 여전히 로그아웃됩니다.
  • 새로고침 시에는 로그인이 유지되지만, 새 탭에서는 유지되지 않습니다.
  • OAuth 콜백(callback)은 성공하지만 대시보드가 다시 로그인 페이지로 리다이렉트(redirect)됩니다.

첫 번째 점검 사항:

  • localStorage를 검사합니다.
  • IndexedDB를 검사합니다.
  • sessionStorage를 확인합니다.
  • 서비스 워커(service worker) 및 확장 프로그램(extension) 의존성을 확인합니다.
  • 쿠키의 도메인(domain), 경로(path), Secure, 그리고 SameSite 설정을 확인합니다.

원인 4: 프록시 컨텍스트 이탈 (proxy context drifted)

증상:

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0