- Published on
BabyAGI에 Playwright 붙여 로그인 자동화하기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서두에서 분명히 짚고 넘어가야 합니다. CAPTCHA “우회”는 많은 서비스의 약관 위반이거나 불법 소지가 있을 수 있고, 계정 정지·법적 리스크로 이어질 수 있습니다. 이 글은 BabyAGI + Playwright로 로그인 자동화를 구성하되, CAPTCHA는 우회가 아니라 합법적인 처리 방식(사람 확인, 공급자 공식 API, 세션 재사용, 차단 회피가 아닌 안정화) 중심으로 설명합니다.
왜 BabyAGI에 Playwright를 붙이나
BabyAGI는 “목표를 달성하기 위해 할 일을 쪼개고 실행하는” 에이전트 패턴의 대표 예시입니다. 하지만 웹 업무(로그인, 설정 변경, 데이터 다운로드)는 대부분 브라우저 상호작용이 필요합니다. 이때 Playwright를 붙이면 다음이 가능해집니다.
- 실제 브라우저 컨텍스트에서 로그인 UI를 클릭·입력·제출
- 쿠키/스토리지 보존으로 세션 재사용
- 네트워크 이벤트/콘솔 로그로 실패 원인 추적
- 2FA, CAPTCHA 등 “자동화가 어려운 구간”을 사람 승인 단계로 분리
에이전트가 모든 걸 “완전 자동”으로 처리하려고 들수록 실패율이 높아집니다. 현실적인 설계는 자동화 가능한 구간은 자동화하고, 사람 확인이 필요한 구간은 워크플로에 포함하는 것입니다.
전체 아키텍처: 계획(LLM)과 실행(브라우저)을 분리
핵심은 BabyAGI가 “웹 로그인”을 직접 하지 않고, 브라우저 실행 도구(tool) 를 호출하도록 만드는 것입니다.
- BabyAGI: 목표를 작업으로 쪼개고(예:
로그인 페이지 열기,아이디 입력,비밀번호 입력,2FA 처리,로그인 성공 검증) 순서를 결정 - Playwright Tool: 각 작업을 브라우저에서 실행하고 결과를 구조화해 반환
- 상태 저장소:
storageState.json같은 파일로 세션을 유지 - 사람 승인(선택): CAPTCHA/2FA가 뜨면 “수동 승인”을 요청하고 이어서 진행
이 패턴은 LangChain의 도구 호출 루프에서도 자주 문제가 되는 “무한 재시도”를 줄이는 데 유리합니다. 관련해서는 LangChain 도구호출 무한루프 차단 7가지에서 말하는 재시도 제한, 타임아웃, 실패 분류를 그대로 적용할 수 있습니다.
준비물: Playwright 프로젝트 세팅
Node.js 기준 예시입니다.
npm init -y
npm i playwright
npx playwright install
로그인 자동화는 “헤드리스”로도 가능하지만, 초기 디버깅은 headed 모드가 훨씬 안정적입니다.
Playwright 로그인 도구 만들기
아래는 BabyAGI(혹은 어떤 에이전트든)가 호출할 수 있는 “로그인 수행 함수” 예시입니다. 중요한 포인트는 다음입니다.
- 선택자(selector)는 최대한 안정적인 것을 사용:
data-testid우선 - 성공 조건을 명확히 정의: URL 변화, 특정 요소 존재 등
- 실패 시 스크린샷/HTML 덤프를 남겨 원인 분석
// tools/loginWithPlaywright.js
import { chromium } from 'playwright';
import fs from 'fs/promises';
export async function loginWithPlaywright({
loginUrl,
username,
password,
storageStatePath = './storageState.json',
headed = true,
}) {
const browser = await chromium.launch({ headless: !headed });
const context = await browser.newContext();
const page = await context.newPage();
try {
await page.goto(loginUrl, { waitUntil: 'domcontentloaded' });
// 예시: 안정적인 selector를 쓰는 게 핵심
await page.fill('[data-testid="username"]', username);
await page.fill('[data-testid="password"]', password);
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.click('[data-testid="submit"]'),
]);
// 로그인 성공 검증(예: 프로필 버튼이 보인다)
await page.waitForSelector('[data-testid="profile-menu"]', { timeout: 15000 });
// 세션 저장
const state = await context.storageState();
await fs.writeFile(storageStatePath, JSON.stringify(state, null, 2));
return {
ok: true,
message: 'login success',
storageStatePath,
url: page.url(),
};
} catch (e) {
await page.screenshot({ path: './login-failed.png', fullPage: true });
const html = await page.content();
await fs.writeFile('./login-failed.html', html);
return {
ok: false,
message: `login failed: ${String(e)}`,
url: page.url(),
artifacts: ['login-failed.png', 'login-failed.html'],
};
} finally {
await browser.close();
}
}
세션 재사용: CAPTCHA를 “줄이는” 가장 현실적인 방법
CAPTCHA가 매번 뜨는 환경에서 “정면 돌파”를 시도하면 실패 확률이 급격히 올라갑니다. 대신 세션을 한 번만 사람 손으로 만들고, 이후 자동화는 세션을 재사용하는 전략이 실무에서 가장 안전합니다.
// tools/openWithSession.js
import { chromium } from 'playwright';
export async function openWithSession({
url,
storageStatePath = './storageState.json',
headed = false,
}) {
const browser = await chromium.launch({ headless: !headed });
const context = await browser.newContext({ storageState: storageStatePath });
const page = await context.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
return {
ok: true,
url: page.url(),
};
}
이 방식은 “CAPTCHA 우회”가 아니라, 정상 로그인으로 획득한 세션을 보존하는 것입니다. 서비스 정책상 허용되는 범위에서만 사용해야 하며, 세션 만료/기기 변경 시 다시 사람 승인이 필요할 수 있습니다.
CAPTCHA/2FA가 뜨면: 자동화가 아니라 워크플로 설계로 푼다
CAPTCHA나 2FA는 의도적으로 자동화를 어렵게 만든 장치입니다. 기술적으로 뚫는 접근은 위험합니다. 대신 다음처럼 설계합니다.
1) “사람 승인 단계”를 에이전트 플로우에 포함
Playwright가 CAPTCHA/2FA 화면을 감지하면 에이전트는 다음 작업을 생성합니다.
- “사용자에게 브라우저를 열어 인증을 완료해달라”
- 인증 완료 후
storageState를 저장하고 다음 단계로 진행
감지 예시는 서비스마다 다르지만, 보통 iframe 또는 특정 텍스트로 판단합니다.
// tools/detectHumanStep.js
export async function needsHumanVerification(page) {
// 예: reCAPTCHA iframe, 또는 "Verify you are human" 문구
const recaptchaFrame = page.locator('iframe[src*="recaptcha"]');
if (await recaptchaFrame.count()) return true;
const humanText = page.getByText('Verify you are human');
if (await humanText.count()) return true;
return false;
}
2) 공식 채널 사용
- 기업 서비스라면 SSO(SAML/OIDC) 로 전환해 자동화 대신 토큰 기반 접근
- CAPTCHA 공급자의 공식 Enterprise/검증 API를 서비스 제공자가 지원하는 경우 그 경로 사용
3) 실패 분류와 재시도 정책
CAPTCHA가 뜬 상황을 “재시도”로 해결하려고 하면, 오히려 위험 신호로 분류되어 더 자주 뜰 수 있습니다. 다음을 권장합니다.
- CAPTCHA 감지 시 즉시 중단하고 사람 승인 태스크로 전환
- 무작정 새 브라우저/새 IP로 반복하지 않기
- 재시도 횟수 제한, 백오프 적용
이 부분은 에이전트 운영 안정성과 직결됩니다. 네트워크/외부 의존성이 섞인 자동화는 “원인 분석”이 중요하므로, 장애 대응 관점은 OpenAI Responses API 502 Bad Gateway 원인과 해결처럼 실패를 관측하고 분류하는 방식을 참고할 만합니다.
BabyAGI에 도구로 연결하기(간단 예시)
BabyAGI 구현체는 여러 변형이 있지만, 핵심은 “에이전트가 특정 함수(tool)를 호출할 수 있게” 만드는 것입니다. 아래는 매우 단순화한 형태의 예시로, 에이전트가 loginWithPlaywright를 호출한다고 가정합니다.
주의: MDX에서 꺾쇠는 빌드 에러를 유발할 수 있으니, 제네릭 표기 같은 건 피하고 문자열은 백틱으로 감싸는 습관을 권장합니다.
// agent/run.js
import { loginWithPlaywright } from '../tools/loginWithPlaywright.js';
async function runGoal() {
const goal = '서비스 A에 로그인한 뒤 대시보드에서 보고서를 다운로드한다';
// 실제 BabyAGI라면 여기서 task 생성/우선순위화/실행 루프가 존재
// 데모에서는 "로그인"만 수행
const result = await loginWithPlaywright({
loginUrl: 'https://example.com/login',
username: process.env.USERNAME,
password: process.env.PASSWORD,
storageStatePath: './storageState.json',
headed: true,
});
if (!result.ok) {
// CAPTCHA/2FA 가능성이 있으면 여기서 사람 승인 플로우로 전환
console.error(result);
process.exit(1);
}
console.log('Logged in, session saved:', result.storageStatePath);
}
runGoal().catch((e) => {
console.error(e);
process.exit(1);
});
운영에서 자주 터지는 문제와 해결 체크리스트
로그인 자동화는 “로컬에서 한 번 성공”이 끝이 아니라, 운영에서 다음 이슈들이 반복됩니다.
선택자 불안정
- 클래스 기반 selector는 UI 변경에 취약
- 가능하면
data-testid를 제품팀에 요청 - 불가하면
getByRole기반으로 의미 있는 요소를 찾기
await page.getByRole('button', { name: 'Sign in' }).click();
타임아웃과 레이스 컨디션
- 클릭 후 네비게이션이 없는 SPA도 많음
waitForNavigation만 믿지 말고, “성공 요소 등장”을 기다리기
await page.click('[data-testid="submit"]');
await page.waitForSelector('[data-testid="profile-menu"]', { timeout: 15000 });
봇 탐지 강화
- 헤드리스 탐지, 비정상적인 이벤트 패턴 등으로 CAPTCHA 빈도 상승
- 해결책은 “우회 트릭”이 아니라 정상 사용 패턴에 가깝게
- 과도한 병렬 실행 금지
- 사람과 유사한 속도로 입력(필요 시
type사용) - 세션 재사용
실패 관측(스크린샷/HTML/트레이스)
Playwright 트레이스는 디버깅에 매우 강력합니다.
await context.tracing.start({ screenshots: true, snapshots: true });
// ... steps
await context.tracing.stop({ path: 'trace.zip' });
이런 “관측 가능성”을 갖추면, 단순 자동화가 아니라 운영 가능한 자동화가 됩니다. 프론트 성능/사용자 경험을 개선할 때 관측이 중요한 것처럼(Chrome INP 급락 원인 찾기 - Long Task 추적), 자동화도 증거(artifact) 를 남겨야 재현과 수정이 가능합니다.
결론: CAPTCHA는 우회가 아니라 설계로 다루자
BabyAGI에 Playwright를 붙이면 로그인 같은 “브라우저 작업”을 에이전트 플로우로 끌어올 수 있습니다. 하지만 CAPTCHA/2FA는 자동화의 적이 아니라 사람 확인을 요구하는 정책 장치입니다. 실무적으로는 다음 조합이 가장 안전하고 성공률이 높습니다.
- Playwright로 로그인 플로우를 자동화하되
- CAPTCHA/2FA는 사람 승인 단계로 분리하고
storageState로 세션을 재사용하며- 실패를 분류하고(무한 재시도 금지) 관측 가능한 형태로 남긴다
이 원칙만 지켜도 “한 번 성공하는 데모”를 넘어, 운영 가능한 브라우저 에이전트에 훨씬 가까워집니다.