- Published on
프론트엔드 테스팅 2026 — Playwright / Cypress / Vitest / Jest / Storybook 9 / Chromatic 심층 비교
- Authors

- Name
- Youngju Kim
- @fjvbn20031
프롤로그 — "Selenium 한 개로 모든 걸 막던 시대는 끝났다"
2018년쯤에 "프론트엔드 테스팅 뭐 써?"라고 물으면 답은 간단했다. 유닛은 Jest, E2E는 Selenium 또는 Cypress, 시각 회귀는 한다는 사람만 BackstopJS. 그게 전부였다. 컴포넌트 단위 테스트라는 말은 아직 어색했고, "스토리북이 테스트 인프라"라는 발상도 없었다.
2026년 5월 현재, 그 그림은 산산조각 났다. 한 회사의 프론트엔드 테스트 파이프라인을 그려보면 보통 이렇게 생긴다.
- 유닛 테스트 — Vitest 3 또는 Jest 30, Testing Library와 함께.
- 컴포넌트 테스트 — Storybook 9 + Vitest, 혹은 Playwright Component Testing.
- E2E 테스트 — Playwright 1.50+ (절반 이상), Cypress 14, WebdriverIO 9.
- 시각 회귀 — Chromatic, Percy, Applitools (SaaS) 또는 Loki, BackstopJS, Reg-Suit (OSS).
- 네트워크 모킹 — MSW (Mock Service Worker) 2.x.
- AI 보조 — Playwright MCP, Browser MCP, Claude/Cursor 에이전트가 직접 테스트 작성.
- CI 통합 — GitHub Actions + Playwright trace + Chromatic publish.
이 글은 2026년 시점에서 위 도구들이 각각 어디에 서 있는지, 무엇을 잘하고 무엇을 못하는지, 그리고 "우리 팀은 무엇을 골라야 하나"를 정리한다. 단순한 리스트가 아니라 — Playwright의 표준화, Vitest의 부상, Storybook 9의 가벼워짐, AI 에이전트가 브라우저를 직접 모는 새로운 패러다임까지 — 2024~2026 사이에 이 시장을 흔든 4가지 흐름을 함께 본다.
1장 · 2026년 프론트엔드 테스팅 지도 — Unit / Component / E2E / Visual 4축
먼저 큰 그림. 프론트엔드 테스트는 4개의 축으로 나뉜다.
| 축 | 무엇을 검증하나 | 대표 도구 |
|---|---|---|
| Unit | 함수, 훅, 유틸 — 가장 빠른 피드백 | Vitest 3, Jest 30, Mocha |
| Component | 컴포넌트 단위 렌더링·상호작용 | Testing Library, Storybook 9 + Vitest, Playwright CT |
| E2E (End-to-End) | 사용자 플로우, 다중 페이지 | Playwright, Cypress 14, WebdriverIO 9, Selenium 5 |
| Visual Regression | 픽셀 단위 UI 변화 감지 | Chromatic, Percy, Applitools, Loki, BackstopJS, Reg-Suit |
여기에 2024~2026 사이에 새로 끼어든 카테고리가 둘 있다.
- Network mocking — API 호출을 가로채는 계층. MSW가 사실상 표준.
- AI agent testing — LLM이 직접 브라우저를 운전. Playwright MCP, Browser MCP.
그리고 2026년의 트렌드는 명확하다. "한 도구로 여러 축을 묶는다". Playwright는 E2E + Component + Visual을 한 묶음으로 팔고, Vitest는 Unit + Component를 (Storybook과 함께) 한 묶음으로 묶었다. Cypress도 비슷한 통합을 시도하지만 속도에서 밀린다. 단일 점 도구로 시작해서 플랫폼이 되거나, 처음부터 플랫폼으로 들어오거나 둘 중 하나다.
테스팅 피라미드(Unit 많고, E2E 적은)는 여전히 유효하지만 — 2026년에는 그 모양이 변형됐다. Mike Cohn의 클래식 피라미드는 컴포넌트 단위가 빠지면서 "다이아몬드" 또는 "트로피" 모양이 됐다. Component 테스트가 폭발적으로 증가하면서, 유닛 테스트는 도메인 로직 위주로 가벼워지고, 컴포넌트 테스트가 가운데를 채운다.
2장 · Playwright — 사실상 표준 (VSCode 통합, Trace Viewer)
Playwright는 Microsoft가 만든 E2E 테스트 도구로, 2020년 1.0 출시 이후 빠르게 시장을 장악했다. 2026년 5월 기준 1.50+ 버전이며, State of JS 2024 조사에서 사용 의향이 가장 높은 E2E 도구로 꼽혔다.
핵심 개념
- Browser context — 각 테스트마다 독립된 브라우저 컨텍스트. 쿠키·스토리지가 격리.
- Auto-waiting —
click,fill같은 액션이 자동으로 요소를 기다린다. 명시적 sleep 불필요. - Locator — DOM 검색이 매번 새로 평가되는 lazy 객체. stale element 문제 없음.
- Trace Viewer — 실패한 테스트의 스냅샷·네트워크·콘솔을 GUI로 재생.
- Component Testing — React/Vue/Svelte 컴포넌트를 격리 환경에서 마운트.
강점
- 속도 — Cypress보다 2~3배 빠른 경우가 흔하다. Chromium·Firefox·WebKit을 모두 지원.
- VSCode Extension — 테스트를 GUI에서 작성·실행·디버그. Codegen으로 자동 생성.
- Trace Viewer — 실패 분석이 30초 안에 끝난다. 다른 도구가 흉내 내기 어렵다.
- 병렬 실행 — 기본 워커 분산. CI에서 60% 시간 절감 흔함.
- 다중 페이지·도메인 — 한 테스트에서 여러 origin을 자유롭게 오갈 수 있다.
약점
- 러닝 커브 — Cypress보다 추상화가 깊다. 처음 며칠은 헤맨다.
- 컴포넌트 테스트는 베타에서 GA로 — 1.40대에 GA 됐지만 Vitest+Testing Library만큼 가볍지는 않다.
- 이미지 비교는 SaaS가 강하다 —
toHaveScreenshot이 있지만, Chromatic·Percy의 베이스라인 관리에 비하면 약하다.
언제 고르나
- 새 프로젝트 시작 — 거의 무조건 Playwright.
- 다중 브라우저 검증이 필요 (특히 Safari/WebKit).
- 큰 monorepo에서 병렬 E2E가 필요.
- Trace 기반 디버깅이 중요한 팀.
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test'
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
reporter: [['html'], ['github']],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
],
})
// e2e/checkout.spec.ts
import { test, expect } from '@playwright/test'
test('user can complete checkout', async ({ page }) => {
await page.goto('/products/coffee-beans')
await page.getByRole('button', { name: 'Add to cart' }).click()
await page.getByRole('link', { name: 'Cart' }).click()
await expect(page.getByText('Coffee Beans')).toBeVisible()
await page.getByRole('button', { name: 'Checkout' }).click()
await page.getByLabel('Email').fill('test@example.com')
await page.getByRole('button', { name: 'Place order' }).click()
await expect(page).toHaveURL(/thank-you/)
})
Playwright의 진짜 가치는 Trace Viewer다. CI에서 실패한 테스트의 trace.zip을 받아서 로컬에서 열면, 각 액션의 DOM 스냅샷·네트워크 호출·콘솔 로그가 GUI 타임라인으로 재생된다. "왜 실패했지?"가 캡처에서 끝난다. Cypress의 비디오 녹화보다 한 단계 위.
3장 · Cypress 14 — 여전한 강자
Cypress는 2017년경 등장해 한동안 E2E의 표준이었다. 2026년 5월 기준 14 버전이며, 점유율은 Playwright에 밀렸지만 여전히 큰 사용자 기반을 가지고 있다.
핵심 개념
- In-browser test runner — 테스트가 실제 브라우저 안에서 실행된다. DOM에 직접 접근.
- Time-travel debugger — 각 명령의 스냅샷을 좌측 패널에서 시간순으로 본다.
- Real-time reload — 코드 저장하면 즉시 테스트가 재실행.
- Cypress Cloud — 클라우드 대시보드, 평행 실행 분산, 플레이키 감지.
- Component Testing — React/Vue/Angular 컴포넌트를 격리 환경에서.
강점
- DX가 압도적 — 처음 시작이 쉽다.
cy.visitcy.getcy.click이 직관적. - Time-travel debugging — 각 명령의 DOM 스냅샷이 자동 캡처. 디버깅이 즐겁다.
- 실시간 hot reload — TDD 사이클이 빠르다.
- 풍부한 문서·커뮤니티 — Stack Overflow에 답이 거의 다 있다.
약점
- 단일 도메인 제약 —
cy.origin이 추가됐지만 여전히 다중 도메인은 어색. - Safari/WebKit 지원이 약함 — 실험적 단계, 안정성 부족.
- 속도 — Playwright 대비 30~50% 느리다.
- iframe 처리가 까다롭다 — 광고나 외부 위젯이 들어간 페이지가 아프다.
- Cypress Cloud 가격 — 평행 실행과 결과 보관이 무료 tier에서 빠르게 제한.
언제 고르나
- 기존 Cypress 코드베이스가 큰 경우 — 마이그레이션 비용이 크다.
- 단일 도메인 SPA, 작은 팀.
- 디버깅 경험을 최우선으로 하는 경우.
// cypress/e2e/login.cy.js
describe('Login flow', () => {
beforeEach(() => {
cy.intercept('POST', '/api/login', { fixture: 'login-success.json' }).as('login')
cy.visit('/login')
})
it('logs in with valid credentials', () => {
cy.get('[data-cy=email]').type('user@example.com')
cy.get('[data-cy=password]').type('s3cret!')
cy.get('[data-cy=submit]').click()
cy.wait('@login')
cy.url().should('include', '/dashboard')
cy.contains('Welcome back').should('be.visible')
})
})
Cypress 14에서 추가된 가장 큰 기능은 WebKit GA와 Component Testing UX 개선이다. 하지만 Playwright가 이미 한 발 앞서 있어서, 신규 프로젝트가 Cypress를 고르는 비중은 2024년부터 빠르게 떨어지고 있다.
4장 · Vitest 3 — Vite와 함께 오는 최고 속도
Vitest는 2021년 Anthony Fu가 만든 유닛 테스트 러너로, Vite의 트랜스포머·HMR을 그대로 활용한다. 2026년 기준 3.x 버전이며, JS/TS 신규 프로젝트의 유닛 테스트 표준이 됐다.
핵심 개념
- Vite-native — Vite config을 그대로 재사용. esbuild + Rollup이 트랜스파일.
- HMR for tests — 변경된 모듈만 다시 평가, 밀리초 단위 사이클.
- Jest-compatible API —
describe/it/expect가 Jest와 거의 동일. - Workspaces — monorepo에서 여러 패키지를 한 vitest 명령으로.
- Browser mode — 실제 브라우저에서 컴포넌트 테스트 (Playwright/WebdriverIO 백엔드).
강점
- 속도가 압도적 — Jest 대비 2~10배 빠르다. 콜드 스타트도 빠르다.
- Vite 생태계와 한 몸 — Vite 프로젝트라면 별도 설정 없이 즉시 동작.
- TypeScript first-class — esbuild가 트랜스파일하므로 ts-jest 같은 우회 불필요.
- Worker pool 최적화 — Node worker_threads로 격리 + 빠른 실행.
- Storybook 9와 통합 —
@storybook/addon-vitest로 스토리가 곧 테스트.
약점
- CRA/Next.js 레거시 — Webpack 기반 프로젝트는 마이그레이션 비용.
- JSDOM/happy-dom 호환성 — 일부 DOM API에서 사소한 차이.
- Jest의 거대한 플러그인 생태계 —
jest-extended등 일부 미러링이 부족.
언제 고르나
- Vite를 쓰는 모든 프로젝트.
- 새 프로젝트 시작 — 거의 무조건 Vitest.
- 유닛 테스트 속도가 중요한 경우 (CI 분 단위 절감).
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'happy-dom',
setupFiles: ['./test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: ['node_modules/', 'test/'],
},
},
})
// src/utils/format.test.ts
import { describe, it, expect } from 'vitest'
import { formatCurrency } from './format'
describe('formatCurrency', () => {
it('formats KRW without decimals', () => {
expect(formatCurrency(12345, 'KRW')).toBe('₩12,345')
})
it('formats USD with two decimals', () => {
expect(formatCurrency(12.5, 'USD')).toBe('$12.50')
})
it('handles negative numbers', () => {
expect(formatCurrency(-100, 'USD')).toBe('-$100.00')
})
})
Vitest 3의 가장 큰 변화는 Browser mode GA다. JSDOM이 아니라 실제 Chromium에서 컴포넌트를 마운트해서 테스트한다. Playwright Component Testing과 비슷한 영역인데, Vite 기반 프로젝트에서 더 자연스럽다.
5장 · Jest 30 — 레거시 + Next 호환
Jest는 Facebook이 2014년 만든 유닛 테스트 러너로, 한동안 JS 테스트의 사실상 표준이었다. 2025년 9월에 30이 출시됐고, 2026년 5월에는 30.x 마이너 릴리스가 안정화 단계다.
핵심 개념
- Snapshot testing —
expect(tree).toMatchSnapshot()— UI 트리를 직렬화해서 저장. - Module mocking —
jest.mock()으로 import 경로를 가짜로 치환. - Fake timers —
jest.useFakeTimers()로 setTimeout/setInterval 제어. - Coverage — Istanbul 기반 커버리지가 내장.
강점
- 거대한 생태계 —
jest-extended,jest-fetch-mock등 수만 개 플러그인. - CRA/Next.js 기본 통합 — Next.js의
next/jest가 표준 설정을 제공. - 안정성 — 10년의 검증. 큰 코드베이스에서 신뢰성이 높다.
- VSCode 통합 — Jest Runner 확장이 잘 작동.
약점
- 속도 — Vitest 대비 2~5배 느리다. 큰 코드베이스에서 CI 시간이 부담.
- ESM 지원이 어색 — 여전히 일부 ESM 패키지에서 hack이 필요.
- TypeScript는 ts-jest 또는 babel — Vitest의 esbuild 대비 느리다.
언제 고르나
- 기존 Jest 코드베이스가 큰 경우.
- Next.js 12 미만 또는 Webpack 기반 CRA.
- Jest 전용 플러그인에 의존하는 경우.
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({ dir: './' })
const customConfig = {
setupFilesAfterEach: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jsdom',
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.stories.tsx',
],
}
module.exports = createJestConfig(customConfig)
Jest 30의 가장 큰 변화는 첫번째 native ESM 지원 안정화와 메모리 사용량 30% 감소다. 마이그레이션 비용 때문에 여전히 큰 조직에서는 Jest를 쓰지만, 신규 프로젝트가 Jest를 고르는 일은 점점 줄어든다.
6장 · Testing Library 철학 — Implementation detail 피하기
Testing Library는 2018년 Kent C. Dodds가 만든 라이브러리 가족이다. DOM, React, Vue, Svelte, Solid 각각의 어댑터가 있다. 2026년 기준 모든 컴포넌트 테스트의 기본기다.
핵심 철학
- "The more your tests resemble the way your software is used, the more confidence they can give you."
- 컴포넌트의 내부 state를 보지 말고, 사용자가 보고 클릭하는 것을 본다.
getByRole,getByLabelText,getByText— 접근성 기준 쿼리.getByTestId는 마지막 수단.
강점
- 리팩토링 친화적 — 컴포넌트 내부 구조가 바뀌어도 테스트는 그대로.
- 접근성과 함께 간다 — 쿼리가 ARIA role 기반이라 자연스럽게 a11y를 검증.
- 프레임워크 독립 — React/Vue/Svelte 어디서나 같은 철학.
- TanStack/Mantine/Radix 같은 라이브러리도 Testing Library 기준으로 빌드.
약점
- 순수 단위 테스트보다 느리다 — DOM 렌더링이 필요.
- userEvent vs fireEvent — 두 API의 차이가 초보자에게 혼란.
- 비동기 처리 —
findBy*,waitFor의 사용을 헷갈리는 사람이 많다.
예시 — React Testing Library + Vitest
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginForm } from './LoginForm'
describe('LoginForm', () => {
it('shows error when password is too short', async () => {
const user = userEvent.setup()
render(<LoginForm onSubmit={() => {}} />)
await user.type(screen.getByLabelText(/email/i), 'user@example.com')
await user.type(screen.getByLabelText(/password/i), 'abc')
await user.click(screen.getByRole('button', { name: /sign in/i }))
expect(
await screen.findByText(/password must be at least 8 characters/i)
).toBeInTheDocument()
})
it('calls onSubmit with valid input', async () => {
const onSubmit = vi.fn()
const user = userEvent.setup()
render(<LoginForm onSubmit={onSubmit} />)
await user.type(screen.getByLabelText(/email/i), 'user@example.com')
await user.type(screen.getByLabelText(/password/i), 'longerpassword')
await user.click(screen.getByRole('button', { name: /sign in/i }))
expect(onSubmit).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'longerpassword',
})
})
})
흔히 빠지는 함정 — screen.getByText("Loading...") 같은 텍스트 정확 매칭은 i18n 시 깨진다. getByRole("status") 또는 정규식이 안전. 그리고 data-testid="submit-button"은 정말 최후의 수단으로만. 사용자는 testid를 보지 않는다.
7장 · Storybook 9 (2025.6) — 가벼워지고 Vitest와 통합
Storybook은 2016년 등장한 컴포넌트 워크숍이다. 2025년 6월에 출시된 9 버전은 큰 전환점이었다. 무게가 가벼워지고(번들 사이즈 50% 감소), Vitest와 깊게 통합됐다.
핵심 변화 (Storybook 9)
- 무게 감소 — 9.0 발표에서 "최대 75% 적은 의존성, 48% 가벼운 설치"라는 수치.
- Vitest 기반 테스트 —
@storybook/addon-vitest가 스토리를 곧 테스트로. - Component testing — Play function이 사실상 상호작용 테스트 표준.
- Visual testing 내장 — Chromatic 연동이 더 매끄러워짐.
- A11y 검증 내장 —
@storybook/addon-a11y가 기본 옵션화.
강점
- 단일 작성, 다중 활용 — 한 스토리가 카탈로그·상호작용 테스트·시각 회귀·a11y 검증 모두 커버.
- 디자이너와의 협업 — Figma 통합. 디자인 토큰을 실시간 확인.
- Vitest 통합 — 스토리 실행이 곧 단위 테스트 실행과 동일한 인프라.
- Plugins/Addons 생태계 —
addon-controls,addon-actions,addon-docs.
약점
- 빌드 시간 — 큰 디자인 시스템에서 Storybook 빌드가 분 단위.
- CSF (Component Story Format) 3 — 학습 필요. 처음 작성하는 팀은 어색.
- 번들 충돌 — Vite 기반인지 Webpack 기반인지에 따라 설정이 갈린다.
예시 — CSF 3 + Play function
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { expect, userEvent, within } from '@storybook/test'
import { Button } from './Button'
const meta: Meta<typeof Button> = {
title: 'UI/Button',
component: Button,
tags: ['autodocs'],
}
export default meta
type Story = StoryObj<typeof Button>
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Click me',
},
}
export const Clicked: Story = {
args: { variant: 'primary', children: 'Click me' },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const btn = canvas.getByRole('button', { name: /click me/i })
await userEvent.click(btn)
await expect(btn).toHaveAttribute('aria-pressed', 'true')
},
}
// vitest.workspace.ts — Storybook 9 + Vitest 통합
import { defineWorkspace } from 'vitest/config'
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'
export default defineWorkspace([
'./vitest.config.ts',
{
extends: './vitest.config.ts',
plugins: [storybookTest({ configDir: '.storybook' })],
test: {
name: 'storybook',
browser: {
enabled: true,
headless: true,
name: 'chromium',
provider: 'playwright',
},
},
},
])
Storybook 9의 진짜 가치는 "스토리를 한 번 작성하면 카탈로그·테스트·시각 회귀·접근성이 다 따라온다"는 점이다. 디자인 시스템 팀이라면 사실상 필수.
8장 · Chromatic / Percy / Applitools — 시각적 회귀
시각적 회귀(Visual Regression)는 UI 변경이 의도된 것인지 아닌지 픽셀 단위로 검사한다. SaaS 3강은 Chromatic, Percy(BrowserStack), Applitools다.
Chromatic
- Storybook을 만든 회사가 직접 운영. Storybook과 한 몸.
- 스토리 단위 시각 회귀가 강점.
- Cross-browser 베이스라인을 클라우드에 저장.
- 검토 워크플로(diff → approve → merge)가 깔끔.
- 가격은 snapshot 기준 — 작은 팀은 무료 tier로 충분.
Percy (BrowserStack)
- 2017년 BrowserStack이 인수. Percy CLI로 Playwright/Cypress/WebdriverIO 통합.
- 다중 viewport, 다중 브라우저 베이스라인.
- BrowserStack의 실 디바이스 클라우드와 연동.
Applitools
- AI 기반 시각 비교가 강점 — "의미 있는 변화만" 잡는다.
- Visual AI 알고리즘이 dynamic content(타임스탬프, 광고)를 자동 무시.
- Ultrafast Grid — 한 번 캡처해서 여러 브라우저·디바이스 조합으로 검증.
- 엔터프라이즈 가격. 큰 조직에서 압도적.
셋의 차이
| 항목 | Chromatic | Percy | Applitools |
|---|---|---|---|
| 모태 | Storybook | BrowserStack | 독립 SaaS |
| 강점 | Storybook 통합 | 다중 viewport, BS 디바이스 | AI Visual diff |
| 가격 모델 | snapshot 기준 | snapshot + DOM 기준 | enterprise seat |
| Free tier | 후함 (월 5,000 snapshot) | 작음 | 평가판만 |
| AI 처리 | 부분적 | 부분적 | 핵심 기능 |
// Playwright + Percy
import { test } from '@playwright/test'
import percySnapshot from '@percy/playwright'
test('homepage looks correct', async ({ page }) => {
await page.goto('/')
await percySnapshot(page, 'Homepage')
})
// Playwright + Applitools Eyes
import { test } from '@playwright/test'
import { Eyes, BatchInfo, Configuration } from '@applitools/eyes-playwright'
test('checkout flow visual', async ({ page }) => {
const eyes = new Eyes()
const cfg = new Configuration()
cfg.setBatch(new BatchInfo('Smoke 2026-05-16'))
eyes.setConfiguration(cfg)
await eyes.open(page, 'Shop', 'Checkout')
await page.goto('/checkout')
await eyes.check('Checkout page', undefined)
await eyes.close()
})
시각 회귀의 가장 큰 함정은 flaky snapshot이다. 폰트 로딩, 애니메이션, 캐러셀, 광고 — 이런 동적 요소를 마스킹하지 않으면 95%의 diff가 false positive다. Applitools가 강한 이유가 이걸 자동으로 처리하기 때문.
9장 · Loki / BackstopJS / Reg-Suit — 오픈소스 시각 회귀
SaaS가 부담스럽거나, 베이스라인을 자기 저장소에 두고 싶다면 OSS 옵션이 있다.
Loki
- Storybook 전용.
loki test로 모든 스토리를 캡처·비교. - Chromium/Firefox/WebKit 지원. Docker 기반 일관 렌더링.
- 베이스라인은 git에 같이 커밋 — review가 PR에서.
BackstopJS
- 가장 오래된 (2014년) 오픈소스 시각 회귀 도구.
- Puppeteer 기반. URL 리스트와 셀렉터로 정의.
- HTML 리포트가 깔끔. 작은 팀에서 여전히 인기.
Reg-Suit
- 일본 발 OSS. 메루카리 등이 채택.
- S3 또는 GCS에 스냅샷 저장. PR diff를 GitHub 코멘트로.
- Playwright/Cypress/Storybook 어디서나 캡처를 던질 수 있다.
셋의 차이
| 항목 | Loki | BackstopJS | Reg-Suit |
|---|---|---|---|
| 모태 | Storybook 생태계 | 독립 | 일본 OSS |
| 베이스라인 저장 | git | 로컬 | S3/GCS |
| 캡처 엔진 | Chromium | Puppeteer | 외부 (Playwright 등) |
| PR 통합 | 직접 | 약함 | 강함 (GitHub bot) |
// backstop.json — BackstopJS 기본 설정
{
"id": "my-project",
"viewports": [
{ "label": "mobile", "width": 375, "height": 667 },
{ "label": "desktop", "width": 1920, "height": 1080 }
],
"scenarios": [
{
"label": "Homepage",
"url": "http://localhost:3000/",
"delay": 500,
"misMatchThreshold": 0.1
}
],
"engine": "puppeteer",
"report": ["browser"]
}
OSS의 강점은 베이스라인을 자기 인프라에 두는 것. 보안이 엄격한 금융권·헬스케어에서는 SaaS를 못 쓰는 경우가 많아 Reg-Suit 또는 자체 호스팅이 선호된다.
10장 · Browser MCP + Playwright MCP — AI 에이전트가 브라우저 운전
2024년 후반부터 큰 변화가 있었다. MCP(Model Context Protocol)가 표준화되면서, AI 에이전트가 직접 브라우저를 운전하는 패턴이 일반화됐다. 2026년 5월 기준 Playwright MCP와 Browser MCP 두 진영이 있다.
Playwright MCP (Microsoft)
- Microsoft가 직접 만든 공식 MCP 서버. Playwright의 모든 API를 MCP 도구로 노출.
- Claude·Cursor·VSCode Copilot이 이걸 통해 브라우저 액션을 호출.
- Accessibility tree 기반 — 스크린샷이 아니라 의미적 구조로 페이지를 본다.
- Trace 캡처와 함께 작동. 에이전트가 만든 테스트도 Trace Viewer로 재생.
Browser MCP
- 오픈소스 진영의 MCP 서버. 다양한 백엔드(Playwright, Puppeteer, Selenium).
- Local Chrome extension을 통해 현재 사용자의 세션에서 작동.
- 로그인된 상태로 작업해야 하는 시나리오에 강하다.
사용 패턴
// .cursor/mcp.json — Playwright MCP 등록
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
이 설정 후 AI 에이전트에게 "체크아웃 플로우 테스트를 작성하고 실행해줘"라고 시키면, 에이전트가 직접 브라우저를 열고 클릭·입력하면서 *.spec.ts를 생성한다. Codegen의 다음 세대.
한계
- flaky — 에이전트가 비결정적이라 테스트 자체가 안정적이지 않을 수 있다.
- 비용 — LLM 토큰 비용이 매 실행마다.
- 보안 — 프로덕션 자격증명을 에이전트에 넘기는 건 위험.
그래서 2026년의 패턴은 **"에이전트가 초안을 작성 → 사람이 리뷰 + 안정화 → CI에 등록"**이다. 에이전트가 매 CI에서 도는 건 아직 아니다.
11장 · MSW (Mock Service Worker) + 네트워크 모킹 전략
MSW는 2019년 Artem Zakharchenko가 만든 네트워크 모킹 라이브러리다. 2026년 기준 2.x이며, 프론트엔드 테스트의 사실상 표준 네트워크 모킹 도구가 됐다.
핵심 개념
- Service Worker 기반 — 브라우저의 네트워크 계층에서 가로챈다.
fetch,axios,XHR무관. - Node 환경에서도 동작 —
setupServer로 Vitest/Jest에서 사용. - REST + GraphQL — 두 프로토콜 모두 지원.
- 타입 안전 — TypeScript와 잘 어울린다.
강점
- 앱 코드 무수정 —
axios를jest.mock()하는 게 아니라, 실제 네트워크 호출을 가로챈다. - 개발·테스트·스토리북 공통 — 같은 핸들러를 세 환경에서 재사용.
- GraphQL 지원 — Apollo Client/urql 통합이 매끄럽다.
- Devtools 통합 — 브라우저 devtools의 Network 탭에 그대로 보인다.
예시 — MSW 핸들러
// mocks/handlers.ts
import { http, HttpResponse } from 'msw'
export const handlers = [
http.get('/api/products', () => {
return HttpResponse.json([
{ id: 1, name: 'Coffee Beans', price: 25000 },
{ id: 2, name: 'Tea Set', price: 35000 },
])
}),
http.post('/api/cart', async ({ request }) => {
const body = await request.json()
return HttpResponse.json({ ok: true, cartId: 'abc-123' }, { status: 201 })
}),
http.get('/api/user/me', () => {
return new HttpResponse(null, { status: 401 })
}),
]
// test/setup.ts — Vitest에서
import { setupServer } from 'msw/node'
import { handlers } from '../mocks/handlers'
import { afterAll, afterEach, beforeAll } from 'vitest'
const server = setupServer(...handlers)
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
// browser entrypoint (개발 환경)
import { setupWorker } from 'msw/browser'
import { handlers } from './mocks/handlers'
if (process.env.NODE_ENV === 'development') {
const worker = setupWorker(...handlers)
worker.start()
}
MSW의 진짜 가치는 **"테스트·개발·스토리북에서 같은 모킹 코드를 쓴다"**는 점. 백엔드가 아직 없을 때도 프론트엔드가 진행 가능, 테스트와 실 개발이 같은 fixture를 공유.
대안으로 Playwright의 page.route(), Cypress의 cy.intercept()도 있지만 — 각 도구 전용이라 재사용성이 떨어진다. 그래서 MSW를 한 층 두고, E2E에서는 그 위에 Playwright route를 얹는 하이브리드가 흔하다.
12장 · Page Object / Component Testing 패턴
E2E 테스트가 늘어나면 코드 중복이 문제다. 그래서 2026년의 표준 패턴은 둘이다.
Page Object Model (POM)
- 한 페이지 = 한 클래스. 셀렉터와 액션을 클래스에 캡슐화.
- Selenium 시대부터 내려온 패턴. 2026년에도 Playwright·Cypress에서 유효.
// e2e/pages/CheckoutPage.ts
import { Page, Locator, expect } from '@playwright/test'
export class CheckoutPage {
readonly page: Page
readonly emailInput: Locator
readonly submitButton: Locator
constructor(page: Page) {
this.page = page
this.emailInput = page.getByLabel('Email')
this.submitButton = page.getByRole('button', { name: 'Place order' })
}
async goto() {
await this.page.goto('/checkout')
}
async fillEmail(email: string) {
await this.emailInput.fill(email)
}
async submit() {
await this.submitButton.click()
}
async expectSuccess() {
await expect(this.page).toHaveURL(/thank-you/)
}
}
// e2e/checkout.spec.ts
import { test } from '@playwright/test'
import { CheckoutPage } from './pages/CheckoutPage'
test('user completes checkout', async ({ page }) => {
const checkout = new CheckoutPage(page)
await checkout.goto()
await checkout.fillEmail('user@example.com')
await checkout.submit()
await checkout.expectSuccess()
})
Component Testing
- 컴포넌트를 격리 환경에서 마운트해서 검증.
- Playwright CT, Cypress CT, Vitest browser mode, Storybook play function.
- "유닛 테스트만큼 빠르고 E2E만큼 진짜 같다"는 위치.
어떤 패턴을 언제
- 유닛/순수 함수 — Vitest, JSDOM.
- 컴포넌트 단독 — Storybook + Vitest 또는 Playwright CT.
- 2~3개 컴포넌트 조합 — Testing Library + Vitest.
- 전체 페이지 — Page Object + Playwright/Cypress.
- 사용자 여정 — Page Object + Playwright, 데이터 격리.
POM의 함정 — 너무 추상화하면 테스트가 뭘 하는지 안 보인다. "Login → Cart → Checkout"이 한 줄로 끝나면 read하기는 좋지만, 실패 분석이 어렵다. 적당한 추상화 + Playwright Trace Viewer의 조합이 최적.
13장 · 한국 / 일본 사례 — 토스, 카카오, Mercari
한국 — 토스의 UI 테스트
토스는 100명 이상의 프론트엔드 팀을 가진 큰 조직이고, 자체 디자인 시스템(Toss DS)을 운영한다. 공개된 블로그·발표 자료에서 드러나는 패턴은 다음과 같다.
- Storybook + Chromatic — 디자인 시스템의 모든 컴포넌트가 스토리북에 등록.
- Playwright — E2E의 표준. 멀티 브라우저 검증.
- Vitest + React Testing Library — 컴포넌트 단위 테스트.
- 자체 MSW 핸들러 라이브러리 — 토스 API 패턴에 맞춘 공통 fixture.
- CI에서 시각 회귀가 차단 — Chromatic 승인 없이는 머지 불가.
토스 블로그의 "프론트엔드에서 테스트가 정말 필요한가요?" 같은 글에서 보이는 철학은 — "테스트는 디자인 시스템과 한 몸". 컴포넌트 단위가 강하면 페이지 단위는 적게.
한국 — 카카오의 frontend
카카오 진영(카카오, 카카오뱅크, 카카오엔터프라이즈)도 비슷한 스택이다.
- Jest 또는 Vitest — 레거시는 Jest, 신규는 Vitest.
- Cypress가 많이 남아 있음 — 2020~2023 사이에 채택한 코드베이스가 많다.
- Playwright로 이전 중 — 신규 프로젝트와 큰 마이그레이션.
- 카카오뱅크는 더 엄격 — 금융 규제로 자체 인프라 검증, OSS 시각 회귀(Reg-Suit 또는 자체).
일본 — Mercari의 컴포넌트 테스팅
Mercari는 Storybook + Vitest + Reg-Suit의 조합을 공개적으로 발표한 적이 있다. 패턴은 다음과 같다.
- Storybook — 디자인 시스템과 모든 컴포넌트.
- Reg-Suit — 시각 회귀, S3에 베이스라인, GitHub PR에 자동 코멘트.
- Vitest 또는 Jest — 유닛.
- Playwright — E2E.
- MSW — 네트워크 모킹.
Mercari Engineering Blog의 시각 회귀 글들이 Reg-Suit의 실 사용 사례로 자주 인용된다. 일본에서 Reg-Suit가 강한 이유 중 하나가 메르카리 채택.
결론 — 지역별 권장
| 시나리오 | 한국 권장 | 일본 권장 |
|---|---|---|
| 스타트업 (10~50명) | Vitest + Playwright + Chromatic | Vitest + Playwright + Reg-Suit |
| 중견 (50~500명) | Storybook 9 + Chromatic + Playwright | Storybook 9 + Reg-Suit + Playwright |
| 대기업 (500+) | 자체 디자인 시스템 + Chromatic + Playwright | 자체 + Reg-Suit + Playwright |
| 금융권 | 자체 호스팅 Reg-Suit + Playwright | 자체 호스팅 + Selenium 5 잔존 |
| 글로벌 SaaS | Applitools + Playwright | Applitools + Playwright |
14장 · 어떤 스택을 골라야 하나 — 시나리오별 가이드
시나리오 A — 큰 SaaS (수십 개 페이지, 다국어, 다중 브라우저)
- Unit: Vitest 3 + React Testing Library
- Component: Storybook 9 + Vitest browser mode
- E2E: Playwright 1.50+ (Chromium, Firefox, WebKit, Mobile Safari)
- Visual: Chromatic 또는 Applitools
- Network: MSW 2.x
- CI: GitHub Actions + Playwright trace + Chromatic publish + 실패 시 Slack
- AI: Playwright MCP로 신규 테스트 초안 생성
시나리오 B — 디자인 시스템 / 컴포넌트 라이브러리
- Unit: Vitest 3
- Component: Storybook 9 (CSF 3 + play function)
- Visual: Chromatic (Storybook과 한 몸)
- A11y:
@storybook/addon-a11y+ axe-core - Docs: Storybook autodocs
- E2E는 최소화 — 라이브러리는 컴포넌트 단위가 핵심
시나리오 C — E-commerce (장바구니, 결제, 다중 페이지)
- Unit: Vitest 3
- Component: Storybook 9 또는 Testing Library
- E2E: Playwright (Page Object 패턴)
- Visual: Percy 또는 Chromatic (모바일 viewport 핵심)
- Network: MSW + Playwright route 하이브리드
- 결제 플로우는 격리된 테스트 계정 + Stripe test mode
시나리오 D — 미디어 / 콘텐츠 사이트
- Unit: Vitest 3 (SEO 메타 검증 위주)
- E2E: Playwright (Lighthouse CI 함께)
- Visual: Chromatic 또는 BackstopJS
- Network: MSW로 CMS 응답 모킹
- 성능 회귀가 중요 — Playwright + Lighthouse 통합
시나리오 E — 레거시 마이그레이션 (Jest + Cypress → Vitest + Playwright)
- 한 번에 다 옮기지 말고 새 코드는 새 도구로.
- Jest와 Vitest 공존 —
vitest run --project new로 점진. - Cypress E2E는 그대로 두고, 신규 E2E만 Playwright.
- 12~18개월 마이그레이션이 현실적.
비용 추산 (2026년 5월 기준, 50명 프론트엔드 팀)
| 도구 | 무료 tier | 유료 (월간) |
|---|---|---|
| Playwright | 무료 | — |
| Vitest | 무료 | — |
| Storybook | 무료 | — |
| Chromatic | 월 5,000 snapshot 무료 | ~649 |
| Percy | 월 5,000 snapshot 무료 | ~$199+ |
| Applitools | 평가판 | enterprise quote |
| Cypress Cloud | 월 500 결과 무료 | ~300+ |
| MSW | 무료 | — |
| Loki/BackstopJS/Reg-Suit | 무료 | — |
작은 팀의 시작점은 단순하다 — Vitest + Playwright + Chromatic 무료 tier. 여기서 시작해 팀이 커지면 Storybook 9를 디자인 시스템 중심으로, 시각 회귀를 유료 tier로, 그리고 Playwright MCP를 도입해서 신규 테스트 작성 속도를 높이는 게 표준 경로.
마치며 — "도구를 사는 게 아니라, 신뢰를 사는 거다"
2026년의 프론트엔드 테스팅 시장은 단일 도구의 토너먼트가 아니라 4축의 합주다. 그리고 다음 5가지 흐름이 시장을 흔들고 있다.
- Playwright의 표준화 — 신규 E2E의 70%+ 점유율. 시간이 갈수록 격차 확대.
- Vitest의 부상 — Vite 생태계와 함께 사실상 표준 유닛 러너.
- Storybook 9의 가벼워짐 — 디자인 시스템 + 컴포넌트 테스트 + 시각 회귀의 허브.
- AI 에이전트가 직접 테스트 작성 — Playwright MCP, Browser MCP가 일상화.
- MSW의 표준화 — 네트워크 모킹 = MSW. 도구 독립적 fixture.
작은 팀의 첫 줄 — npm i -D vitest @testing-library/react @playwright/test msw. 이 네 개로 95%의 케이스가 커버된다. 그 다음 단계가 Storybook 9 + Chromatic, 그 다음이 Playwright MCP 통합.
하나의 진리 — "도구를 사는 게 아니라, 신뢰(테스트가 깨졌을 때 진짜로 버그를 잡는다는 신뢰)를 사는 거다". 어떤 도구를 고르더라도 그 신뢰가 90% 미만이면, 그 테스트는 PR을 차단하는 잡음에 불과하다. 도구 평가의 첫 질문은 항상 "이 테스트가 빨간 줄을 띄울 때, 실제로 버그를 잡았던 비율은?"이다.
참고 / References
- Playwright — https://playwright.dev/
- Playwright VS Code Extension — https://playwright.dev/docs/getting-started-vscode
- Playwright Trace Viewer — https://playwright.dev/docs/trace-viewer
- Playwright Component Testing — https://playwright.dev/docs/test-components
- Playwright MCP — https://github.com/microsoft/playwright-mcp
- Cypress — https://www.cypress.io/
- Cypress 14 release notes — https://docs.cypress.io/guides/references/changelog
- Cypress Cloud — https://www.cypress.io/cloud
- Vitest — https://vitest.dev/
- Vitest Browser Mode — https://vitest.dev/guide/browser/
- Jest — https://jestjs.io/
- Jest 30 announcement — https://jestjs.io/blog/2025/06/04/jest-30
- Testing Library — https://testing-library.com/
- React Testing Library — https://testing-library.com/docs/react-testing-library/intro/
- Storybook — https://storybook.js.org/
- Storybook 9 release — https://storybook.js.org/blog/storybook-9/
- Storybook Vitest addon — https://storybook.js.org/docs/writing-tests/test-addon
- Chromatic — https://www.chromatic.com/
- Percy — https://percy.io/
- Applitools — https://applitools.com/
- Applitools Visual AI — https://applitools.com/platform/visual-ai/
- Loki (Storybook visual regression) — https://loki.js.org/
- BackstopJS — https://github.com/garris/BackstopJS
- Reg-Suit — https://github.com/reg-viz/reg-suit
- Selenium — https://www.selenium.dev/
- WebdriverIO — https://webdriver.io/
- MSW (Mock Service Worker) — https://mswjs.io/
- Model Context Protocol — https://modelcontextprotocol.io/
- State of JS 2024 — https://stateofjs.com/
- Kent C. Dodds — Testing Trophy — https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications
- Mercari Engineering Blog — https://engineering.mercari.com/en/blog/
- 토스 기술 블로그 — https://toss.tech/
- 카카오 기술 블로그 — https://tech.kakao.com/