필사 모드: 프론트엔드 테스팅 2026 — Playwright / Cypress / Vitest / Jest / Storybook 9 / Chromatic 심층 비교
한국어프롤로그 — "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
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
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.visit` `cy.get` `cy.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
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
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
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
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 통합
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
test('homepage looks correct', async ({ page }) => {
await page.goto('/')
await percySnapshot(page, 'Homepage')
})
// Playwright + Applitools Eyes
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
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에서
const server = setupServer(...handlers)
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
// browser entrypoint (개발 환경)
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
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
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 무료 | ~$149 ~ $649 |
| Percy | 월 5,000 snapshot 무료 | ~$199+ |
| Applitools | 평가판 | enterprise quote |
| Cypress Cloud | 월 500 결과 무료 | ~$75 ~ $300+ |
| MSW | 무료 | — |
| Loki/BackstopJS/Reg-Suit | 무료 | — |
작은 팀의 시작점은 단순하다 — Vitest + Playwright + Chromatic 무료 tier. 여기서 시작해 팀이 커지면 Storybook 9를 디자인 시스템 중심으로, 시각 회귀를 유료 tier로, 그리고 Playwright MCP를 도입해서 신규 테스트 작성 속도를 높이는 게 표준 경로.
마치며 — "도구를 사는 게 아니라, 신뢰를 사는 거다"
2026년의 프론트엔드 테스팅 시장은 단일 도구의 토너먼트가 아니라 4축의 합주다. 그리고 다음 5가지 흐름이 시장을 흔들고 있다.
1. **Playwright의 표준화** — 신규 E2E의 70%+ 점유율. 시간이 갈수록 격차 확대.
2. **Vitest의 부상** — Vite 생태계와 함께 사실상 표준 유닛 러너.
3. **Storybook 9의 가벼워짐** — 디자인 시스템 + 컴포넌트 테스트 + 시각 회귀의 허브.
4. **AI 에이전트가 직접 테스트 작성** — Playwright MCP, Browser MCP가 일상화.
5. **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/](https://playwright.dev/)
- Playwright VS Code Extension — [https://playwright.dev/docs/getting-started-vscode](https://playwright.dev/docs/getting-started-vscode)
- Playwright Trace Viewer — [https://playwright.dev/docs/trace-viewer](https://playwright.dev/docs/trace-viewer)
- Playwright Component Testing — [https://playwright.dev/docs/test-components](https://playwright.dev/docs/test-components)
- Playwright MCP — [https://github.com/microsoft/playwright-mcp](https://github.com/microsoft/playwright-mcp)
- Cypress — [https://www.cypress.io/](https://www.cypress.io/)
- Cypress 14 release notes — [https://docs.cypress.io/guides/references/changelog](https://docs.cypress.io/guides/references/changelog)
- Cypress Cloud — [https://www.cypress.io/cloud](https://www.cypress.io/cloud)
- Vitest — [https://vitest.dev/](https://vitest.dev/)
- Vitest Browser Mode — [https://vitest.dev/guide/browser/](https://vitest.dev/guide/browser/)
- Jest — [https://jestjs.io/](https://jestjs.io/)
- Jest 30 announcement — [https://jestjs.io/blog/2025/06/04/jest-30](https://jestjs.io/blog/2025/06/04/jest-30)
- Testing Library — [https://testing-library.com/](https://testing-library.com/)
- React Testing Library — [https://testing-library.com/docs/react-testing-library/intro/](https://testing-library.com/docs/react-testing-library/intro/)
- Storybook — [https://storybook.js.org/](https://storybook.js.org/)
- Storybook 9 release — [https://storybook.js.org/blog/storybook-9/](https://storybook.js.org/blog/storybook-9/)
- Storybook Vitest addon — [https://storybook.js.org/docs/writing-tests/test-addon](https://storybook.js.org/docs/writing-tests/test-addon)
- Chromatic — [https://www.chromatic.com/](https://www.chromatic.com/)
- Percy — [https://percy.io/](https://percy.io/)
- Applitools — [https://applitools.com/](https://applitools.com/)
- Applitools Visual AI — [https://applitools.com/platform/visual-ai/](https://applitools.com/platform/visual-ai/)
- Loki (Storybook visual regression) — [https://loki.js.org/](https://loki.js.org/)
- BackstopJS — [https://github.com/garris/BackstopJS](https://github.com/garris/BackstopJS)
- Reg-Suit — [https://github.com/reg-viz/reg-suit](https://github.com/reg-viz/reg-suit)
- Selenium — [https://www.selenium.dev/](https://www.selenium.dev/)
- WebdriverIO — [https://webdriver.io/](https://webdriver.io/)
- MSW (Mock Service Worker) — [https://mswjs.io/](https://mswjs.io/)
- Model Context Protocol — [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/)
- State of JS 2024 — [https://stateofjs.com/](https://stateofjs.com/)
- Kent C. Dodds — Testing Trophy — [https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications](https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications)
- Mercari Engineering Blog — [https://engineering.mercari.com/en/blog/](https://engineering.mercari.com/en/blog/)
- 토스 기술 블로그 — [https://toss.tech/](https://toss.tech/)
- 카카오 기술 블로그 — [https://tech.kakao.com/](https://tech.kakao.com/)
현재 단락 (1/551)
2018년쯤에 "프론트엔드 테스팅 뭐 써?"라고 물으면 답은 간단했다. 유닛은 Jest, E2E는 Selenium 또는 Cypress, 시각 회귀는 한다는 사람만 BackstopJ...