Skip to content
Published on

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

Authors

프롤로그 — "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-waitingclick, 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.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 GAComponent 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 APIdescribe/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 testingexpect(tree).toMatchSnapshot() — UI 트리를 직렬화해서 저장.
  • Module mockingjest.mock()으로 import 경로를 가짜로 치환.
  • Fake timersjest.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 — 한 번 캡처해서 여러 브라우저·디바이스 조합으로 검증.
  • 엔터프라이즈 가격. 큰 조직에서 압도적.

셋의 차이

항목ChromaticPercyApplitools
모태StorybookBrowserStack독립 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 어디서나 캡처를 던질 수 있다.

셋의 차이

항목LokiBackstopJSReg-Suit
모태Storybook 생태계독립일본 OSS
베이스라인 저장git로컬S3/GCS
캡처 엔진ChromiumPuppeteer외부 (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와 잘 어울린다.

강점

  • 앱 코드 무수정axiosjest.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 + ChromaticVitest + Playwright + Reg-Suit
중견 (50~500명)Storybook 9 + Chromatic + PlaywrightStorybook 9 + Reg-Suit + Playwright
대기업 (500+)자체 디자인 시스템 + Chromatic + Playwright자체 + Reg-Suit + Playwright
금융권자체 호스팅 Reg-Suit + Playwright자체 호스팅 + Selenium 5 잔존
글로벌 SaaSApplitools + PlaywrightApplitools + 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 149 ~ 649
Percy월 5,000 snapshot 무료~$199+
Applitools평가판enterprise quote
Cypress Cloud월 500 결과 무료~75 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