00 한눈에 (개발자 · 비개발자 공통)
전문 용어 없이, 이게 무엇이고 왜 하는지부터.
30초 요약
| 질문 | 답 |
|---|---|
| 무엇을? | 실제 사용자 흐름을 자동으로 재현·검증 (현재 75개 시나리오 파일 · 183개 점검 케이스) |
| 언제 실행? | 배포될 때마다 자동(빠른 점검) + 매일 새벽·저녁 전체 점검 + 필요 시 수동 |
| 결과는 어디서? | ReportPortal 대시보드 + Slack 알림 (어느 코드에서 깨졌는지 추적) |
| 개발자가 할 일? | 새 기능 만들면 테스트도 같이 추가, 기능 고치면 테스트도 같이 수정 |
| 비개발자가 볼 곳? | ReportPortal에서 초록(통과)/빨강(실패) 현황 — 빨강이면 방금 배포가 뭔가 깨뜨린 것 |
01 개요
헥사고날(Ports & Adapters) + 도메인 분리 + 2026 web-first 패턴. e2e/ 는 독립 npm workspace.
디렉토리 구조
| 경로 | 역할 |
|---|---|
tests/auth.setup.ts | uiLogin → storageState 저장. 30분 TTL + JWT exp 캐시. 기본 WS "린다세일즈" 고정 + 신원 바인딩 |
tests/features/<도메인>/ | 도메인별 스펙 (auth·leads·sequences·email-replies·settings·platform·dashboard·ai·billing·content·admin·lead-discovery·onboarding) |
tests/db/ | drizzle 직접 접근, FK 무결성 검증 |
tests/dynamic/ | 변경 기반(worktree-pr 브랜치명) 동적 회귀 가드 |
tests/exploratory/ | axe 접근성 상세 스캔 |
tests/ui/ports.ts + adapters/playwright.ts | 헥사고날 인터페이스 + Playwright 구현 = 셀렉터 SSOT |
tests/fixtures/page-fixtures.ts | app 드라이버 + listeners 자동연결 + csrfRequest(CSRF 자동주입) |
scripts/ | bootstrap·sync·dev-up·port-alloc(slot 격리)·run-e2e(EC2 docker)·rp-* |
specs/ | Planner 에이전트 산출 Markdown 테스트 계획 |
02 playwright.config.ts
로컬/원격 자동 감지, 4개 project 분리, ReportPortal 조건부 연동.
| 항목 | 값 |
|---|---|
| baseURL | E2E_BASE_URL ?? https://alpha.rinda.ai |
| MODE | local(localhost) / alpha(원격) 자동감지, E2E_MODE override |
| workers | local=20, alpha=4 |
| retries | local=1, alpha=0 |
| timeout | test 90s · expect 20s · nav 20s · action 10s |
| reporters | list + html + json + ReportPortal(alpha 또는 E2E_FORCE_RP + RP_API_KEY 시) |
| trace/video/screenshot | failure-only, E2E_DEBUG_ARTIFACTS=1 시 전량 |
| viewport | 1440×900, headless, ignoreHTTPSErrors |
4개 project
| project | 대상 | storageState |
|---|---|---|
setup | auth.setup.ts (로그인) | 생성 |
chromium | noauth/db 제외 전체 | 로그인 상태 (setup 의존) |
chromium-noauth | *.noauth.spec.ts | 빈 상태 |
db | tests/db/** | 빈 상태 (setup 비의존) |
scripts/storage-path.ts — writer/reader 공유. 과거 ENOENT 근본원인을 차단.03 CI 자동 실행 기준 — 액션 → 워크플로우 매핑
9개 워크플로우. workflow_run 체인 2개(CI→리뷰, CD Alpha→E2E) 외 모두 독립 트리거.
| 액션 (이벤트) | 트리거되는 워크플로우 |
|---|---|
push → alpha | CI · CD Alpha · Release |
push → beta | CI · CD Beta (app.rinda.ai 프로덕션) |
push → main/master | CI |
| PR open/sync | CI |
| CI 완료 (PR + success) | Claude Code Review |
| CD Alpha 완료 (success) | E2E on Alpha (smoke) |
| cron 05:00 · 21:00 KST | E2E on Alpha (full) |
| 이슈 open | auto-assign · (본문 @claude 시) Claude Code |
| PR merge (close) | auto-close-issues |
코멘트/리뷰에 @claude | Claude Code |
| 수동 dispatch | CD Alpha · CD Beta · E2E on Alpha |
워크플로우 상세
3-tier 트리거:
- Tier 1 (smoke):
workflow_run← "CD Pipeline (Alpha)" completed. 배포 완료 시점 트리거(구버전 회피).@smoke만 ~2-3분 - Tier 2 (full):
schedulecron0 20 * * *(05:00 KST) ·0 12 * * *(21:00 KST). 전수 실행 - Tier 3 (선택):
workflow_dispatchinput mode=full/smoke. 릴리스·beta 동기화 게이트 온디맨드
3 job 체인: guard(배포 success 확인) → detect(git diff로 변경 분석·모드 결정·docs만 변경 시 skip) → dispatch(SSH+Infisical→EC2 전송→nohup run-e2e.sh 백그라운드)
GHA는 경량(~40s): detect + 시크릿 export + SSH 트리거만. 실제 실행은 EC2 docker가 비동기로. 결과는 RP + Slack 통지.
- 트리거: push → [main, master, alpha, beta], PR → 동일 브랜치 대상
- 동시성: PR은 cancel-in-progress, push는 SHA별 그룹(취소 안 함)
- 비용 게이트: 봇/내부 6명 login은 CI skip
- job: detect-changes(paths-filter) → admin(yarn ci:check + bundle) / server(bun ci:check: lint·type·check:routes 권한게이트·check:migrations·unit + bundle)
- 완료 시 Claude Code Review 트리거
- 트리거: push → alpha, workflow_dispatch. 동시성
queue: max(FIFO) - SSH + Infisical 시크릿 export(
.env.rp포함 + Copilot flag) → 변경 컴포넌트 감지 → rsync → DB 백업+S3 → zero-downtime-deploy → health 240s(실패 시 롤백) → Cloudflare purge → Slack - 완료 시 E2E on Alpha 트리거
- 트리거: push → beta, workflow_dispatch
- alpha와 구조 동일. 차이: beta 시크릿 ·
app.rinda.ai· ReportPortal export 없음 · Copilot flag 주입 없음 - E2E 미연결(E2E는 alpha 전용)
- code-review: CI 완료 + PR + success 시
/code-review자동 실행 - claude: 코멘트/리뷰/이슈에
@claude멘션 시 지시대로 실행
- release: alpha push 시 package.json 버전으로 GitHub Release + OpenAPI 첨부(동일 태그 있으면 skip)
- auto-assign: 새 이슈/카드를 기본담당(classygoody)+생성자에 assign
- auto-close: PR merge 시 본문
closes #N파싱→이슈 close
04 배포 → E2E 자동 체인
두 개의 workflow_run 체인이 자동화의 핵심.
05 테스트 케이스 전수 목록
도메인별 집계 (실측 · 정적 test() 기준)
| 도메인 / 영역 | spec | test() 케이스 | 비고 |
|---|---|---|---|
| sequences (캠페인) | 21 | 54 | 최대 도메인 · 회귀 가드 8 |
| auth (인증) | 6 | 29 | 전부 noauth |
| email-replies (수신함) | 5 | 17 | |
| settings (설정) | 7 | 16 | ws 전환·IAM |
| leads (바이어) | 6 | 14 | |
| admin (관리자) | 4 | 11 | |
| dashboard | 3 | 8 | |
| platform | 5 | 8 | + 데이터기반 12(i18n)·6·6 런타임 확장 |
| lead-discovery | 3 | 6 | |
| content | 4 | 4 | 전부 진입 smoke |
| ai | 2 | 3 | |
| billing | 2 | 3 | |
| onboarding | 2 | 3 | |
| dynamic (변경 기반) | 2 | 3 | worktree-pr 회귀 가드 |
| db (무결성) | 1 | 2 | 로컬 PG 미접근 시 skip |
| exploratory (a11y) | 1 | 1 | axe 상세 스캔 |
| 합계 | 75 | 183 |
test() 호출 기준 실측. public-pages.noauth(LANGS×PAGES=12), sidebar/main-menu(각 6) 등 데이터기반 파라미터화 테스트는 런타임에 더 많은 케이스로 확장되어 실제 실행 케이스는 이보다 큼.도메인별 spec 상세. 태그 범례: @smoke @critical a11y tree flow noauth
- signin-tree.noauth — signin 화면 트리: heading·email/password·"Rinda AI" 타이틀·Google/Login/Sign up/Forgot 버튼·약관/개인정보 링크·빈폼 validation·잘못된 자격증명 차단 (12 case)
- signin-flow.noauth — 입력 반영·잘못된 형식 제출 시 signin 유지·"Forgot password" 클릭→재설정 입력 노출 (3)
- login.noauth — 존재X 이메일 4xx·필드누락·이메일형식 거부·
/signin렌더·Google OAuth URL 발급 (5) - signup-otp.noauth — 가입 완주: Sign up→이메일→Redis OTP→이름·비번·약관→완료
- signup-tree.noauth —
/signup은 not-found(공개가입 미제공)·pageerror 0 (4) - routes-guard-tree.noauth — 전 라우트 비로그인 접근 가드
- list-tree —
/sequences·"Campaign Management"·All/In Progress/Completed 탭·New Campaign·검색 (8) - list-quality — 콘솔/5xx/pageerror 0·i18n 미번역 0·카드 발송진행률
\d+/\d+ 발송카운터·mobile 360 a11y (6) - list-smoke — 진입 안정(body·pageerror·5xx 0)
- create-tree / create-flow / create-a11y-tree —
/sequences/create트리·그룹 선택→step2·키보드/터치타겟 - campaign-create-entry — 목록→새 캠페인→모드 선택 모달
- proposal-tree / proposal-smoke — AI 제안서: 바이어선택·그룹선택지·계정선택·"Get AI draft"·history endpoint (5)
- personalized-preview — 바이어별 이메일 셀 프리뷰 클램프 + Enter/클릭→상세 전문
- reply-automation-tree — 답장 자동처리 Step3: 3개 토글 기본 ON·동작·매트릭스 비노출
- sendable-gate-flow — 발송가능 0명이면 다음단계 차단 (2)
- step-editor-a11y — Step2 터치타겟·SaveStatusIndicator·콘솔/pageerror/5xx 0·탭 키보드 (6)
- 회귀 가드 8 spec @critical — campaign-account-ux / batch1~4(#8747~8766) / create-parity(#42809) / multiaccount / remain(#8755·8760). 진입 깨짐·pageerror·5xx 0 가드
- save-roundtrip — 이름 변경→저장→reload 유지→원복
- rinda-mail-connect — Rinda Mail 연동: prefix→Connect→성공
- email-integration-tree — 내 이메일 탭·연동 시작→provider 다이얼로그
- workspace-switch-race — ws 전환 직후 옛 ws E403이 새 선택 wipe 안 함·admin/non-admin 분기 (4)
- workspace-switcher-tree — 현재 ws 이름·목록·선택 시 사이드바·localStorage 동기화 (3)
- page-smoke / iam-pages-smoke — 설정 진입 0 error / IAM 3페이지 렌더
- bucket-actions — Follow-up 클릭→view+URL 동기화·Sent↔Inbox 왕복 (2)
- email-tree —
/replied-emails·MAILBOX·Inbox·Follow-up·Compose·EmptyState (7) - inbox-buckets-tree — 버킷 카운트(Inbox·Follow-up·Positive·Negative·Today) (5)
- inbox-a11y-tree — 버킷 네비 role/키보드·MAILBOX 랜드마크·Compose Escape 취소 (2)
- page-smoke — URL 유지·main 렌더·pageerror 0
- leads-tree —
/leads·Buyer Groups·Lead Pipeline·검색·Create New Group·빈 WS 크래시없음 (8) - group-crud — 그룹 생성→상세→설정에서 삭제
- group-validation-tree — Escape 취소·다이얼로그 a11y (2)
- bulk-copy-combobox-layout — 긴 그룹명 truncate(다이얼로그 넘침 없음)
- confirm-modal-standard — 삭제 확인모달 표준구조(취소+파괴)
- list-smoke — URL유지·main·pageerror0·5xx0
- cors-origin — preflight wildcard ACAO/origin 반사 금지·정상 origin 통과 (2)
- public-pages.noauth — LANGS(ko/en/id/ja) × PUBLIC_PAGES(signin·terms·privacy) = 12 case, 미번역 0
- sidebar-nav-tree — MENUS 6개 클릭→이동+active
- main-menu — PAGES 6개 기본 렌더·인증리다이렉트X·pageerror0
- auto-public —
routes.json(114개)에서 public/root path param 없는 라우트 자동 smoke
- sales-autopilot-journey — 해외영업 자율주행: 셋업→영업정보→계획수립→가동→허브→Done
- sales-autopilot-access — flag ON 진입 렌더 / OFF
/dashboard리다이렉트 (2) - recordings-audio-progress — play 시 progress·range 변경으로 currentTime 이동
- console-page —
/adminbody·pageerror0·5xx0
- dashboard (3) — tree(WS 버튼·타이틀·Admin Menu·약관동의 후 인증유지) · a11y-tree(키보드·≥24px·새로고침 인증잔존) · page-smoke
- lead-discovery (3) — buyer-search-tree(질문 heading·AI 기회카드·Recent) · workspace-isolation(타 ws jobId 200 절대X) · page-smoke
- content (4) — campaign-calendar(#6045 5xx) · help-changelog(#6042 CSP/iframe) · knowledge-base · linkedin-sdr smoke
- onboarding (2) — tree(인증 컨텍스트 해소·약관동의) · page-smoke(셸 flash 회귀 #6121)
- ai (2) — copilot-sidebar(FAB·⌘J 토글·step2 자동숨김) · employee-smoke(AI Sales Team)
- billing (2) — checkout-smoke(invalid purchaseId graceful) · plans-render(구독·플랜 카드)
- dynamic/fix-workspace-fallback-gate — stale 캐시 있어도 비멤버 ws active 선택·발사 차단
- dynamic/fix-workspace-identity-binding — 타 계정 stale ws 미발사·false-positive wipe 없음 (2)
- db/fk-integrity — 모든 단순 FK 고아 row 0 (로컬 PG 미접근 시 skip)
- exploratory/a11y-detail — 6개 페이지 axe 상세 스캔, serious/critical을 JSON 산출(단언 없음)
06 실행 인프라 — EC2 오프로드
GHA는 트리거만, 무거운 실행은 EC2 docker로 오프로드(GHA 과금·다운로드 제거).
로컬 — bootstrap.sh 파이프라인
| 단계 | 내용 |
|---|---|
| ① sync-code | git pull + deps (자체 skip 판정으로 웜런 수초) |
| ② sync-db / db-per-slot | migrate + alpha 데이터. slot 0~9 격리: PG rinda_wt<slot> |
| ③ dev-up | FE(5173+slot) + BE(3001+slot) health, atomic lockfile lease |
| ④ E2E | mode=smoke/regression/record/dynamic/none |
원격 — run-e2e.sh (EC2)
- GHA가 배포 후 SSH로 EC2 docker(
playwright:v1.60.0-jammy)nohup백그라운드 실행 - flock 중복차단 + 15분 쿨다운(빈번 머지 합침)
- 산출물
~/e2e-artifacts(배포 트리 밖, rsync --delete 충돌 회피) - 결과 → ReportPortal(rp.rinda.ai) + Slack 알림
RP_MODE·RP_HEAD_SHA·GITHUB_PR_NUMBER를 env로 전달해 PR과 결과 연결.07 2026 레퍼런스 · 베스트프랙티스
최신 Playwright 권장 패턴과 우리 스위트의 정합성. (출처: playwright.dev 공식 문서 · 2026 best-practices 가이드)
왜 "최신 패턴"이 중요한가 (비개발자용)
핵심 권장 패턴 (개발자용)
| 2026 권장 | 내용 | 우리 적용 |
|---|---|---|
| 역할 우선 셀렉터 | getByRole 최우선 → getByLabel/getByPlaceholder → getByText → getByTestId → CSS/XPath 최후. role로 못 찾으면 접근성 결함이기도 함(동시 검증) | ✓ tests/ui/ ports/adapters에 role 우선 셀렉터 SSOT |
| web-first assertions | expect(locator).toBeVisible() 등이 조건 충족까지 자동 대기·재시도. 고정 waitForTimeout 금지(flaky 원인) | ✓ 전 spec web-first, 하드 wait 없음 |
| POM 대신 fixture | 소~중 규모는 Page Object Model 대신 조합형 fixture가 더 가볍고 격리 우수 | ✓ fixtures/page-fixtures.ts(app·listeners·csrfRequest) |
| ARIA 스냅샷 | v1.60+ expect(page).toMatchAriaSnapshot()(Page 지원)·pathTemplate·codegen aria 픽커. 접근성 트리 기반 구조 회귀 검증 | ◐ tree 패턴 사용 중, 표준 toMatchAriaSnapshot() 전환은 진행 노드(TEST-DESIGN-PLAN N1.3) |
| per-assertion 타임아웃 | 전역 타임아웃 0 대신 단언별 명시 타임아웃 → 간헐 실패를 재현 가능한 실패로 전환 | ✓ expect 20s·action 10s 명시 |
| 접근성 내장 | role 기반 로케이터가 곧 a11y 검증. 추가로 axe 스캔 병행 | ✓ a11y 헬퍼(axe wcag2a/2aa) + exploratory/ 상세 스캔 |
참고 링크
- Playwright 공식 Best Practices —
playwright.dev/docs/best-practices - ARIA Snapshot 문서 —
playwright.dev/docs/aria-snapshots(v1.60 Page 지원·boxes 옵션) - Release Notes —
playwright.dev/docs/release-notes(Trace Viewer JSON pretty-print·키 입력 표시 등) - 2026 패턴 가이드 — getautonoma.com / elionavarrete.com / qaskills.sh (role 우선·fixture·web-first 정리)
toMatchAriaSnapshot() 통일 ② MCP Playwright Agents 재연결로 self-healing 자동화 두 가지.
08 도입 프로세스 · 성과 · 커뮤니티 (2026)
대기업·유망 스타트업은 누가·어떻게 E2E를 운영하고 어떤 성과를 냈는가. (출처: State of Testing 2026 · State of JS 2025 · World Quality Report 2025-26 · 각 사 엔지니어링 블로그)
누가 / 어떻게 운영하나 — 2026 권장 "하이브리드 오너십"
| 모델 | 내용 | 평가 |
|---|---|---|
| 하이브리드 (권장) | 플랫폼팀이 실행 백본·공유 라이브러리·표준 소유, 제품 스쿼드 내 embedded QA/개발자가 자기 기능 영역을 책임 | 주인의식 + 일관성 양립 |
| 중앙 QA 단독 | 한 QA팀이 전부 소유 | 병목 · 제품팀 주인의식 부재 (가장 역기능적) |
| 개발자 100% | 개발자가 E2E도 전담 | 품질 하락 · 커버리지 공백 (전담 지원 없으면) |
우리 위치: 플랫폼(
e2e/ 인프라 + ui/ 셀렉터 SSOT) + 개발자(기능별 spec) = 하이브리드에 근접. EC2 오프로드 = "배포 환경 실행" 패턴의 self-hosted 버전. 다만 CI 품질 게이트(테스트 누락 차단)는 아직 없음 → 보강 필요.
실제 도입 사례 (대기업 · 스타트업)
| 조직 | 어떻게 |
|---|---|
| Microsoft | Playwright 자체 개발사. VS Code 등 자사 제품 E2E에 사용. Codegen·UI Mode·Trace Viewer·MCP 에이전트로 작성→실행→수리 전 과정 도구화 |
| Zalando (유럽 최대 패션 커머스) | 엔지니어링 블로그: Playwright "test probes"로 프로덕션 합성 모니터링(E2E를 배포 검증 + 상시 헬스체크로 확장) |
| Mission Lane (핀테크) | Percy + Playwright + Figma 조합으로 전 페이지 모바일/데스크톱 비주얼 스냅샷 자동 생성 |
| Fortune 500 프로덕션 | Amazon · Apple · NVIDIA · Walmart 등 — 12,000+ 기업이 프로덕션에서 Playwright 운영 |
| 스타트업 표준 패턴 | Vercel/Netlify preview 배포 + GitHub Actions에서 Playwright 자동 실행 → PR에 결과 코멘트 (우리 EC2 오프로드와 동형) |
성과 / 정량 지표
(State of Testing 2026, n=4,821)
(State of JS 2025, vs Cypress 72%)
E2E 도구 중 최고
(Cypress의 5배)
(잦은 릴리스·회귀 환경)
(95% 결함탐지·42% 빠른 실행)
09 Playwright Agents (self-healing 루프)
.mcp.json playwright-test MCP 서버 기반 3개 에이전트(sonnet). 1 cycle ≈ $0.3–0.6.
| 에이전트 | 역할 |
|---|---|
| planner | 라이브 앱 탐색 → specs/*.md 생성 (happy/edge/error 시나리오) |
| generator | plan 단계를 MCP로 실시간 실행 → tests/<domain>/*.spec.ts 생성 (1파일 1테스트) |
| healer | 실패 진단·셀렉터/타이밍 수정·재시도, 끝까지 안 되면 test.fixme() |