MCP

Claude가 로그인까지 처리한다 — Playwright MCP 실전 2편: 자동화 패턴 완전 정복

cell-devlog 2026. 5. 27. 11:33
반응형

1편에서 설치를 마쳤다면 이제 진짜 써먹을 차례다. 자연어 한 줄로 데이터를 긁고, 폼을 채우고, 로그인 세션을 유지하는 실전 패턴을 전부 정리했다. 특히 "매번 로그인 다시 해야 하냐"는 가장 많이 받는 질문 — 이번에 완전히 끊어낸다.


핵심 요약

→ Playwright MCP는 자연어 프롬프트를 browser_navigate, browser_click 등 34개 도구 호출로 자동 변환
인증 세션 유지 — --user-data-dir 플래그로 한 번 로그인하면 이후 세션에서 재인증 불필요
storageState 패턴 — Playwright 공식 방식으로 쿠키+localStorage를 파일로 저장, 역할별 프로필 관리 가능
→ 동적 콘텐츠(무한 스크롤, AJAX 로딩) 스크래핑은 스크린샷 기반보다 접근성 트리 방식이 훨씬 안정적
→ MFA/OAuth/SSO도 처리 가능 — 헤드 모드(브라우저 창 표시)에서 사람이 직접 로그인 후 AI가 이어받는 방식
→ --vision auto 옵션으로 Canvas/WebGL 등 트리가 못 읽는 영역만 자동으로 스크린샷 전환
→ Shadow DOM이 많은 Web Components 앱은 공식 서버로 조작 불가 → playwriter 포크 사용 필요


1. 자연어 자동화 — Claude에게 말만 하면 끝

Playwright MCP의 핵심은 AI가 알아서 도구 호출 순서를 결정한다는 것이다. 셀렉터 없이 역할(role)과 레이블(label)로 요소를 찾는다.

웹 스크래핑 — 뉴스 헤드라인 수집

# Claude Desktop에 입력하는 프롬프트
"ycombinator.com/news 에 접속해서 현재 보이는 모든 게시글 제목과 링크를 표 형태로 정리해줘"
# Claude가 내부적으로 실행하는 흐름
browser_navigate("https://news.ycombinator.com/news")
browser_snapshot()    # 접근성 트리로 페이지 구조 파악
# → 제목/링크 목록 추출 후 마크다운 표로 반환

동적 콘텐츠도 문제없다. 스크린샷이 아닌 접근성 트리를 읽기 때문에 AJAX로 로드되는 요소도 실제 DOM 상태 그대로 가져온다.

# 무한 스크롤 페이지 처리
"linkedin.com/jobs 에서 'AI Engineer' 검색하고 스크롤 3번 내려서 나온 채용공고 제목 전부 모아줘"
# 내부 실행 흐름
browser_navigate("https://www.linkedin.com/jobs")
browser_snapshot()
browser_type(element="Search jobs", text="AI Engineer")
browser_click(element="Search button")
browser_scroll("down")   # ×3 반복
browser_snapshot()       # 최종 DOM 상태에서 데이터 추출

폼 자동화 — 복잡한 멀티 스텝 폼

# Claude Code에서 입력
"https://app.example.com/register 에서 회원가입 폼 채워줘
 이름: 홍길동, 이메일: hong@test.com, 비밀번호: Test1234!
 약관 체크박스 모두 동의하고 제출까지 해줘"
# 내부 실행 — 레이블 기반이라 셀렉터 불필요
browser_navigate("https://app.example.com/register")
browser_snapshot()                                      # 폼 구조 파악
browser_type(element="이름", text="홍길동")
browser_type(element="이메일", text="hong@test.com")
browser_type(element="비밀번호", text="Test1234!")
browser_click(element="서비스 이용약관 동의")           # 체크박스 레이블로 클릭
browser_click(element="개인정보 처리방침 동의")
browser_click(element="회원가입 버튼")
browser_snapshot()                                      # 결과 확인

핵심은 #submit-btn 같은 셀렉터가 아니라 "회원가입 버튼" 이라는 레이블로 찾는다는 점 — UI가 바뀌어도 레이블만 살아있으면 코드 수정 없이 동작한다.


2. 로그인 세션 유지 — 이게 핵심이다

Playwright MCP의 가장 큰 실전 장벽은 매 세션마다 재인증 문제다. 해결책은 두 가지.

방법 1 — --user-data-dir 으로 브라우저 프로필 영구 저장

// claude_desktop_config.json
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": [
        "@playwright/mcp@latest",
        "--user-data-dir", "/Users/cell/.playwright-profile"
        // Windows: "C:\\Users\\cell\\.playwright-profile"
      ]
    }
  }
}

처음 한 번만 브라우저 창에서 로그인하면 쿠키가 --user-data-dir 경로에 영구 저장된다. 이후 Claude Desktop을 재시작해도 로그인 상태가 유지된다.

# 이후 세션에서는 바로 로그인된 상태로 시작
"notion.so 열어서 내 워크스페이스의 최근 페이지 목록 알려줘"
# → 로그인 없이 바로 실행됨

방법 2 — storageState로 역할별 세션 저장 (Claude Code 환경)

테스트 자동화나 여러 계정을 돌려야 할 때 쓰는 방법이다. Playwright 공식 storageState API로 쿠키와 localStorage를 파일로 내보내 재사용한다.

// 1단계: 한 번만 실행 — 로그인 후 세션 저장
// save-auth.js

const { chromium } = require('@playwright/test');

(async () => {
  const browser = await chromium.launch({ headless: false }); // 창 표시
  const context = await browser.newContext();
  const page = await context.newPage();

  // 로그인 페이지 열기
  await page.goto('https://app.example.com/login');

  // ↓ 여기서 직접 로그인 (MFA, OAuth, SSO 모두 가능)
  console.log('로그인 완료 후 Enter 누르세요...');
  await new Promise(resolve => process.stdin.once('data', resolve));

  // 세션 저장
  await context.storageState({ path: './auth/user-session.json' });
  await browser.close();
  console.log('✅ 세션 저장 완료: ./auth/user-session.json');
})();
node save-auth.js
# 브라우저 창이 열리면 직접 로그인
# Enter 누르면 쿠키 + localStorage가 JSON으로 저장됨
// 2단계: 이후 자동화에서 저장된 세션 재사용
// playwright.config.js

module.exports = {
  projects: [
    {
      name: 'authenticated',
      use: {
        storageState: './auth/user-session.json', // 저장된 세션 로드
      },
    },
  ],
};
# Claude Code에서 이후 자동화 요청
"저장된 세션으로 app.example.com 대시보드 접속해서 이번 달 매출 데이터 스크린샷 찍어줘"
# → 재로그인 없이 바로 인증된 상태로 진입

MFA / OAuth / SSO 처리

// 헤드 모드(브라우저 창 표시) 설정 필수
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": [
        "@playwright/mcp@latest"
        // headless 옵션 없으면 기본값이 헤드 모드 (창 표시)
      ]
    }
  }
}
# 처리 순서
1. Claude에게 "https://accounts.google.com 로그인해줘" 지시
2. 브라우저 창에서 구글 로그인 페이지 뜸
3. MFA 코드 등 직접 입력 (AI가 대기 중)
4. 로그인 완료 후 "계속 진행해줘" 입력
5. Claude가 이어서 자동화 작업 수행 (쿠키 세션 유지됨)

3. 실전 사용 패턴 5가지

패턴 1 — 경쟁사 가격 모니터링

"coupang.com에서 '맥북 에어 M3' 검색해서
 상위 5개 상품의 이름, 가격, 평점을 표로 정리해줘.
 그 다음 gmarket.co.kr에서도 동일하게 검색해서 비교해줘"

패턴 2 — 반복 업무 자동화

"https://admin.mysite.com/posts 에 접속해서
 상태가 'draft'인 게시글 전부 찾아서 published로 변경해줘.
 변경된 항목 수 알려줘"

패턴 3 — E2E 테스트 자동 생성

"localhost:3000 에서 회원가입 → 로그인 → 프로필 편집 플로우를 직접 실행해보고
 각 단계별 실제 셀렉터를 확인해서 Playwright 테스트 코드 작성해줘"
// Claude가 실제 DOM을 보고 생성한 테스트 코드 예시
import { test, expect } from '@playwright/test';

test('회원가입 → 로그인 플로우', async ({ page }) => {
  // 실제 DOM에서 추출한 셀렉터 — 추측 아님
  await page.goto('http://localhost:3000/register');
  await page.getByRole('textbox', { name: '이메일' }).fill('test@example.com');
  await page.getByRole('textbox', { name: '비밀번호' }).fill('Test1234!');
  await page.getByRole('button', { name: '회원가입' }).click();
  await expect(page.getByText('가입이 완료됐습니다')).toBeVisible();
});

패턴 4 — 데이터 추출 + 파일 저장

# Claude Code 환경에서
"https://kr.investing.com/crypto/bitcoin 접속해서
 현재 BTC 가격, 24시간 변동률, 거래량 추출하고
 bitcoin_price.csv 파일로 저장해줘"

패턴 5 — 로컬 개발 QA 루프

"localhost:3000 열고 → 내가 방금 수정한 버튼 스타일 확인 →
 모바일 뷰포트(375px)로 전환해서 스크린샷 → 깨진 부분 있으면 알려줘"
// 뷰포트 동적 변경 설정
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": [
        "@playwright/mcp@latest",
        "--viewport-size", "375,812"  // iPhone 14 사이즈
      ]
    }
  }
}

4. Vision 모드 — Canvas/WebGL 처리

표준 접근성 트리 스냅샷은 Canvas나 WebGL 요소를 못 읽는다. --vision auto 옵션으로 해결한다.

{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": [
        "@playwright/mcp@latest",
        "--vision", "auto"
        // 접근성 트리로 90% 처리 → Canvas/WebGL 만날 때만 자동 스크린샷 전환
        // ⚠️ 비전 기능이 있는 모델만 지원 (Claude, GPT-5 등)
      ]
    }
  }
}

5. 알아두면 쓸모 있는 제약사항

# Shadow DOM 문제
→ Web Components, Lit, Shoelace 등 Shadow DOM 기반 UI는 접근성 트리에서 요소가 안 보임
→ 해결: @playwright/mcp 대신 playwriter 포크 사용 (shadow root 뚫는 raw JS 셀렉터 지원)

# 토큰 비용
→ MCP 방식: 태스크당 평균 114,000 토큰 소모
→ @playwright/cli 방식: 동일 태스크 약 27,000 토큰 (4배 절약)
→ 고처리량 에이전트는 3편(프로덕션 패턴)에서 다룰 CLI 전환 고려

# Cloudflare / 봇 감지
→ 공식 서버는 스텔스 기능 없음
→ Browserbase, Stagehand 등 클라우드 서버가 봇 감지 우회 기능 내장
→ 4편 비교글에서 상세 다룸

✅ 이번 편 핵심 정리

✅ 자연어 프롬프트만으로 스크래핑, 폼 자동화, QA 루프 전부 처리 가능
✅ --user-data-dir → 브라우저 프로필 영구 저장, 재로그인 불필요
✅ storageState → 역할별 세션 파일 저장, CI 환경에서도 인증 유지
✅ MFA/OAuth → 헤드 모드에서 사람이 직접 처리 후 AI가 이어받는 구조
✅ --vision auto → Canvas/WebGL 요소도 처리 가능

❌ Shadow DOM 앱은 공식 서버로 한계 있음
❌ 고처리량 환경에선 CLI 방식이 토큰 4배 절약

 

✅ 시리즈 전체 요약

 1편 — Playwright MCP 설치 + Claude Desktop/Cursor/VS Code 연결 https://cell-devlog.tistory.com/277
 2편 — 자연어 스크래핑·폼 자동화·로그인 세션 영구 유지 https://cell-devlog.tistory.com/278
 3편 — 에러 핸들링·샤딩·Docker·GitHub Actions CI 파이프라인 https://cell-devlog.tistory.com/279
 4편 — 클라우드 브라우저 MCP 생태계 비교 + 상황별 선택 기준 https://cell-devlog.tistory.com/280

 

반응형