Articlestack.convex.dev·2026년 7월 5일·0

Resilient AI End-to-End Tests with Stagehand and Convex

Quick Summary

이 글은 Stagehand의 자연어 브라우저 제어와 Convex의 실행별 임시 백엔드를 결합해, 선택자 중심 E2E 테스트의 취약성을 줄이고 실제 사용자 의도에 가까운 테스트를 구성한 경험을 정리한다.

Resilient AI End-to-End Tests with Stagehand and Convex 관련 대표 이미지

🖼️ 인포그래픽

Resilient AI End-to-End Tests with Stagehand and Convex 내용을 설명하는 본문 이미지

🖼️ 4컷 인포그래픽

Resilient AI End-to-End Tests with Stagehand and Convex 내용을 설명하는 본문 이미지

💡 한 줄 요약

이 글은 Stagehand의 자연어 브라우저 제어와 Convex의 실행별 임시 백엔드를 결합해, 선택자 중심 E2E 테스트의 취약성을 줄이고 실제 사용자 의도에 가까운 테스트를 구성한 경험을 정리한다.

📌 핵심 요약

  • 저자는 버튼 이름이나 DOM 구조 변경 때문에 쉽게 깨지는 기존 Playwright 기반 E2E 테스트의 유지보수 문제를 출발점으로 삼아, CSS 선택자나 XPath 대신 자연어 의도로 브라우저를 조작하는 방식을 실험했다.
  • Stagehand는 Playwright 위에서 동작하며 act, extract, observe, agent 같은 비교적 명확한 API를 제공하고, Convex는 독립 실행형 바이너리로 매 테스트 실행마다 깨끗한 임시 백엔드를 만들 수 있게 해준다.
  • Vitest 안에서 단위 테스트와 E2E 테스트를 별도 프로젝트로 분리하고, E2E 설정 파일에서 Convex 바이너리 다운로드, 임시 백엔드 실행, 함수 배포, Stagehand 실행, 종료 정리까지 한곳에서 관리했다.
  • 테스트는 type-route 기반의 타입 안전한 라우팅, 테스트 전용 Convex mutation, IS_TEST 환경 변수 가드, Zod 스키마 기반 extract를 통해 실제 앱과 실제 백엔드를 대상으로 하면서도 재현 가능성과 안전성을 확보했다.
  • agent API는 여러 단계의 사용자 과업을 매우 짧은 코드로 표현할 수 있게 해주지만, 자율적으로 계획하고 실행하는 만큼 속도와 비결정성 문제가 남아 있으며, 저자는 이 부분을 향후 모델과 Stagehand API가 성숙하며 좁혀질 간극으로 본다.

🧩 주요 포인트

  1. 저자는 버튼 이름이나 DOM 구조 변경 때문에 쉽게 깨지는 기존 Playwright 기반 E2E 테스트의 유지보수 문제를 출발점으로 삼아, CSS 선택자나 XPath 대신 자연어 의도로 브라우저를 조작하는 방식을 실험했다.
  2. Stagehand는 Playwright 위에서 동작하며 act, extract, observe, agent 같은 비교적 명확한 API를 제공하고, Convex는 독립 실행형 바이너리로 매 테스트 실행마다 깨끗한 임시 백엔드를 만들 수 있게 해준다.
  3. Vitest 안에서 단위 테스트와 E2E 테스트를 별도 프로젝트로 분리하고, E2E 설정 파일에서 Convex 바이너리 다운로드, 임시 백엔드 실행, 함수 배포, Stagehand 실행, 종료 정리까지 한곳에서 관리했다.
  4. 테스트는 type-route 기반의 타입 안전한 라우팅, 테스트 전용 Convex mutation, IS_TEST 환경 변수 가드, Zod 스키마 기반 extract를 통해 실제 앱과 실제 백엔드를 대상으로 하면서도 재현 가능성과 안전성을 확보했다.
  5. agent API는 여러 단계의 사용자 과업을 매우 짧은 코드로 표현할 수 있게 해주지만, 자율적으로 계획하고 실행하는 만큼 속도와 비결정성 문제가 남아 있으며, 저자는 이 부분을 향후 모델과 Stagehand API가 성숙하며 좁혀질 간극으로 본다.

🧠 상세 정리

1. 선택자 기반 E2E 테스트의 유지보수 한계

글은 저자가 오랫동안 Playwright 테스트를 작성하며 겪은 반복적인 문제에서 시작한다. 월요일에는 통과하던 테스트가 수요일에는 누군가 버튼 이름을 바꿨다는 이유만으로 깨지는 상황이 흔했다. CSS 클래스, XPath, data-testid 같은 선택자에 의존하는 테스트는 기능이 실제 사용자에게 여전히 정상적으로 보이더라도 UI 구현 세부가 바뀌면 실패한다. 저자는 이것이 테스트가 사용자 행동을 검증하는 것이 아니라 특정 시점의 DOM 구현 스냅샷을 검증하기 때문에 생기는 문제라고 본다. 그래서 마감이 가까워질수록 팀들이 취약한 E2E 테스트를 주석 처리하고 그대로 배포하는 현실도 지적한다.

2. 사용자 의도 중심 테스트라는 목표

저자가 원하는 이상적인 테스트는 기능 설명을 읽고 앱을 열어 자연스러운 경로를 시도한 뒤 결과를 보고하는 주니어 QA 엔지니어에 가깝다. 이를 위해서는 선택자가 아니라 자연어로 의도를 표현할 수 있어야 하고, 실제 브라우저에서 앱을 조작해야 하며, 각 테스트가 필요한 상태를 깨끗하게 준비할 수 있어야 한다. 기존 테스트 피라미드에서 E2E 테스트가 꼭대기에 작게 놓이는 이유는 느리고 불안정하며 유지 비용이 높기 때문이다. 하지만 바로 그 E2E 테스트가 실제 사용자 행동에 가장 가까운 검증이라는 점에서, 저자는 이 영역이 구조적으로 과소 투자되고 있다고 본다.

3. Stagehand와 Convex를 조합한 이유

Stagehand는 Browserbase가 만든 Playwright 기반 라이브러리로, LLM이 해석하는 자연어 지시를 통해 브라우저를 조작할 수 있게 한다. 동시에 완전히 열린 프롬프트가 아니라 act, extract, observe, agent 같은 작고 명시적인 API 표면을 제공한다는 점이 중요하다. Convex는 독립 실행형 오픈소스 바이너리로 실행할 수 있기 때문에, 테스트마다 공유 개발 데이터와 분리된 깨끗한 백엔드를 만들 수 있다. 저자는 많은 AI 브라우저 도구가 백엔드를 리셋할 수 없는 블랙박스로 가정하지만, Convex를 쓰면 백엔드 자체가 테스트 픽스처의 일부가 된다고 설명한다.

4. Stagehand API의 역할과 결정성의 차이

Stagehand에서 act는 “구매 버튼을 클릭하라”처럼 자연어로 표현된 단일 행동을 수행한다. extract는 사용자가 제공한 스키마에 맞춰 페이지에서 구조화된 데이터를 뽑아오고, observe는 화면에 무엇이 있는지 또는 특정 조건이 성립하는지 모델에게 묻는다. 저자는 이 세 API가 한 번에 하나의 제한된 작업만 수행하기 때문에 어설션을 작성할 만큼 충분히 결정적이라고 평가한다. 반면 agent는 높은 수준의 목표를 넘기면 스스로 계획하고 실행하는 자율 루프라서 가능성은 크지만 결정성은 떨어진다. 이 차이는 글 후반부에서 agent API의 장점과 한계를 설명하는 핵심 축이 된다.

5. Convex의 반응형 백엔드와 실행별 임시 환경

Convex 쿼리는 반응형이기 때문에 테스트가 데이터를 변경하면 프론트엔드에 즉시 반영된다. 덕분에 모델이 오래된 UI를 상대로 기다리거나 재시도할 필요가 줄어들고, 선택자 기반 테스트에서 흔한 polling, sleep, hydration 대기 코드도 크게 줄어든다. 저자는 각 테스트 실행마다 Convex 백엔드를 자식 프로세스로 띄우고 임시 작업 디렉터리의 throwaway SQLite 파일을 사용하도록 구성했다. 이 방식은 개발용 배포 데이터를 오염시키지 않고, CI나 동료의 로컬 실행, watch loop처럼 여러 실행이 겹치는 상황에서도 서로 상태를 덮어쓰지 않게 한다. 시작 상태가 항상 비어 있으므로 테스트 재현성도 높아진다.

6. Vitest 구성과 E2E 실행 수명주기

저자는 단위 테스트와 E2E 테스트가 서로 다른 실행 특성을 갖기 때문에 Vitest 안에서 두 프로젝트로 분리했다. unit 프로젝트는 src//*.test.ts를 대상으로 하고, e2e 프로젝트는 e2e//*.test.ts와 E2E 설정 파일을 사용한다. 모든 테스트를 하나의 설정에 섞으면 평범한 Vitest 실행마다 E2E 준비 비용을 치르게 되어 개발 중 테스트를 덜 실행하게 되는 작은 마찰이 생긴다고 본다. e2e/setupE2E.ts는 Convex 바이너리를 필요하면 다운로드하고 캐시하며, 빈 포트를 찾아 백엔드를 띄우고, health endpoint 응답을 기다린 뒤, bunx convex deploy로 schema와 함수들을 임시 인스턴스에 배포한다. 이후 Stagehand가 Vite로 띄운 프론트엔드는 VITE_CONVEX_URL을 통해 같은 로컬 Convex 백엔드를 바라본다.

7. 타입 안전 라우팅과 자연어 기반 구매 흐름 테스트

첫 번째 테스트는 공개 티켓 구매 흐름에 대한 smoke test였다. 홈페이지를 열고 티켓 페이지로 이동한 뒤 구매 버튼을 누르고 성공 상태를 확인하는 과정이었다. 앱이 type-route를 사용하고 있었기 때문에 저자는 typed route 객체를 받아 Stagehand page를 해당 URL로 이동시키는 goTo helper를 만들었다. 이렇게 하면 라우트 파라미터가 바뀔 때 런타임의 404나 뒤늦은 flaky 실패가 아니라 TypeScript 컴파일 오류로 문제를 발견할 수 있다. 실제 구매 흐름에서는 act로 “티켓 구매 버튼을 클릭하라”고 지시하고 observe로 성공 확인 문구가 보이는지 묻는 식으로 작성했으며, 선택자나 명시적 wait 없이 테스트가 사용자 행동 설명처럼 읽히게 되었다.

8. 영상 기록과 테스트 전용 데이터 준비

Stagehand는 Playwright의 비디오 기록 기능을 물려받기 때문에, 저자는 launch 설정에서 이를 활성화하고 결과 영상을 test-results에 저장했다. 테스트가 실패했을 때 12초짜리 녹화 영상을 보면 에이전트가 무엇을 봤고 어디를 잘못 클릭했는지 바로 확인할 수 있어, 긴 로그를 뒤지는 것보다 훨씬 유용하다고 말한다. 또한 유의미한 E2E 테스트에는 구매할 투어, 이미 집계된 투표, 특정 역할의 사용자 같은 사전 조건이 필요하다. 이를 위해 convex/_testing.ts에 _testSeedTour 같은 테스트 전용 mutation을 만들고, 임시 백엔드에만 설정되는 IS_TEST 환경 변수가 없으면 실행되지 않도록 가드했다. _test 접두사는 코드 리뷰에서 위험한 helper를 눈에 띄게 하는 두 번째 방어선으로 쓰인다.

9. extract와 Zod를 이용한 구조화된 UI 검증

저자는 leaderboard 검증처럼 화면에 표시된 구조화 데이터를 확인해야 하는 테스트에서 Stagehand의 extract를 사용했다. 예를 들어 상위 다섯 개 항목을 추출하라는 지시와 함께 이름은 문자열, 투표 수는 숫자인 entries 배열 형태의 Zod 스키마를 제공한다. 그러면 반환값은 타입이 붙은 구조화 데이터가 되고, 테스트는 이 배열에 대해 일반적인 어설션을 수행할 수 있다. 이 방식은 DOM을 순회하거나 textContent를 취약하게 맞추거나 innerText에 정규식을 적용하는 방식과 다르다. 저자는 extract가 입력 구조가 아니라 원하는 출력 형태를 계약으로 삼는다는 점에서, 선택자 우선 접근과 정반대이며 테스트가 정말 필요로 하는 수준에 더 가깝다고 본다.

10. agent API의 가능성과 남은 한계

agent API는 Stagehand에 높은 수준의 목표 하나를 넘기고, 그 목표를 달성하기 위한 단계를 에이전트가 스스로 계획하고 실행하게 한다. 저자는 여러 단계의 투표 테스트를 기존의 40줄짜리 스크립트에서 단 3줄짜리 agent 호출로 바꿨고, 잘 동작했을 때는 인간 테스터처럼 자연스럽게 보였다고 설명한다. 특히 예상하지 못했던 중간 확인 대화상자를 에이전트가 직접 처리하고 계속 진행한 점은 scripted 테스트였다면 실패했을 부분이라고 본다. 하지만 흥미로운 실패 모드는 에이전트가 완전히 깨지는 것이 아니라 명백한 다음 단계를 찾는 데 너무 오래 걸리는 것이었다. 따라서 agent 계층에는 여전히 비결정성과 속도 문제가 남아 있지만, 저자는 이 부분이 모델과 Stagehand API가 성숙하면서 줄어들 여지가 있다고 본다.

🧾 핵심 주장 / 시사점

  • E2E 테스트의 핵심 취약점은 도구 자체보다 테스트가 DOM 구조를 사용자 의도보다 더 강하게 붙잡는 데 있으며, 자연어 기반 조작은 이 추상화 수준을 바꾸려는 시도다.
  • AI 브라우저 테스트가 실용적이려면 모델만 좋아서는 부족하고, 실행마다 깨끗하게 초기화되는 백엔드와 타입 안전 라우팅, 테스트 전용 데이터 주입 같은 전통적인 테스트 공학이 함께 필요하다.
  • Stagehand의 act, observe, extract는 제한된 작업에 적합하지만 agent는 더 큰 자동화 가능성과 비결정성을 동시에 갖기 때문에, 당장은 핵심 검증에는 bounded API를 쓰고 복합 탐색에는 agent를 신중히 쓰는 구성이 현실적이다.

✅ 액션 아이템

  • 선택자 중심 E2E 테스트의 DOM·버튼 변경 취약성을 줄이기 위해 Playwright 조작을 Stagehand의 자연어 기반 act/observe 흐름으로 재정의한다.
  • E2E와 단위 테스트를 Vitest에서 분리하고, Convex 바이너리 다운로드·임시 백엔드 실행·함수 배포·정리까지 설정 한 곳에서 일괄 관리한다.
  • type-route 라우팅, 테스트 전용 Convex mutation, IS_TEST 가드, Zod 기반 extract를 함께 적용해 실제 앱과 실제 백엔드 기반의 재현성·안전성 검증을 강화한다.

❓ 열린 질문

  • 자연어 기반 Stagehand 액션 도입 시 기존 selector 방식 대비 유지보수 비용 감소를 어떤 실패율/안정성 지표로 판단할 것인가?
  • Convex 임시 백엔드 방식을 매 실행마다 사용할 때 초기화 비용은 어떤 임계값 이하에서 채택 가능하다고 정의할 것인가?
  • agent API의 자율 실행이 동반하는 속도 편차와 비결정성은 반복 횟수와 허용 편차를 어떻게 잡아 정량화할 것인가?

관련 문서

공통 태그와 주제 흐름을 기준으로 같이 보면 좋은 문서를 이어서 제안합니다.