본문으로 바로가기
리옵트 핸드북
리옵트 핸드북
엔터프라이즈 프로젝트 설계

모노레포 기반

Ch1. 모노레포 아키텍처 설계Ch2. Workspace 설계와 프로토콜Ch3. 공유 패키지 설계 패턴Ch4. Turborepo 파이프라인과 캐싱

앱 & 배포

Ch5. Next.js App Router 엔터프라이즈 패턴Ch6. Vercel 배포 전략Ch7. CI/CD 파이프라인 설계Ch8. 테스트 전략

에이전트 & 운영

Ch9. Agentic 개발 체계Ch10. skills.sh 생태계 활용Ch11. 보안과 코드 거버넌스Ch12. 모니터링과 장애 대응

부록

부록. 실무 템플릿Ch13. 레퍼런스와 아키텍처 결정 기록부록. 업데이트 이력검증 리포트
핸드북›엔터프라이즈 프로젝트 설계›Ch8. 테스트 전략
한국어English

Ch8. 테스트 전략

모노레포 환경 테스트 피라미드, Vitest 유닛, Playwright e2e

핵심 요약

  • 모노레포 테스트는 유닛(Vitest, 모든 PR)→통합(Vitest+MSW, 해당 앱 변경 시)→E2E(Playwright, main 머지 전) 피라미드로 CI 실행 조건을 분리합니다.
  • Vitest workspace로 apps/*·packages/* 설정을 묶어 한 명령으로 실행하고 --project로 특정 패키지만 돌립니다.
  • Playwright는 CI에서 retries: 2·workers: 4·github 리포터를 쓰고 webServer로 turbo dev 서버를 띄웁니다.
  • turbo.json test의 inputs를 정확히 지정하면 코드 미변경 시 캐시를 재사용해 테스트 실행 자체를 건너뜁니다.
  • E2E는 양보다 배포 차단 시나리오(로그인·결제·권한·rollback smoke)만 main gate로 올리고 나머지는 nightly·앱별 PR gate로 분리합니다.

모노레포 테스트 피라미드

레벨도구범위CI 실행 조건
유닛Vitest패키지/앱 내 함수·컴포넌트모든 PR
통합Vitest + MSW앱 내 API 연동·데이터 흐름해당 앱 변경 시
E2EPlaywright전체 사용자 시나리오main 머지 전

Vitest Workspace 설정

Vitest의 workspace 기능을 쓰면 모노레포 전체를 테스트 명령 하나로 실행합니다:

// vitest.workspace.ts (루트)
import { defineWorkspace } from 'vitest/config'

export default defineWorkspace(['apps/*/vitest.config.ts', 'packages/*/vitest.config.ts'])
// packages/utils/vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    name: '@acme/utils',
    include: ['src/**/*.test.ts'],
  },
})
// apps/web/vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    name: 'web',
    environment: 'jsdom',
    include: ['**/*.test.{ts,tsx}'],
    setupFiles: ['./tests/setup.ts'],
  },
})
# 전체 테스트
vitest run

# 특정 패키지만
vitest run --project=@acme/utils

# 워치 모드 (개발 중)
vitest --project=web

Playwright E2E 설정

// e2e/playwright.config.ts
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 4 : undefined,
  reporter: process.env.CI ? 'github' : 'html',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'mobile', use: { ...devices['Pixel 7'] } },
  ],
  webServer: {
    command: 'turbo run dev --filter=web',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
})
// e2e/tests/auth-flow.spec.ts
import { test, expect } from '@playwright/test'

test('로그인 → 대시보드 접근', async ({ page }) => {
  await page.goto('/login')
  await page.fill('[name="email"]', 'test@acme.com')
  await page.fill('[name="password"]', 'test1234')
  await page.click('button[type="submit"]')
  await expect(page).toHaveURL('/dashboard')
  await expect(page.locator('h1')).toContainText('대시보드')
})

컴포넌트 테스트 패턴

// packages/ui/src/button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './button'
import { expect, test, vi } from 'vitest'

test('클릭 이벤트 전달', () => {
  const onClick = vi.fn()
  render(<Button onClick={onClick}>확인</Button>)
  fireEvent.click(screen.getByText('확인'))
  expect(onClick).toHaveBeenCalledOnce()
})

test('variant별 스타일 적용', () => {
  const { rerender } = render(<Button variant="primary">버튼</Button>)
  expect(screen.getByRole('button')).toHaveClass(/primary/)

  rerender(<Button variant="ghost">버튼</Button>)
  expect(screen.getByRole('button')).toHaveClass(/ghost/)
})

CI에서 테스트 캐싱

// turbo.json
{
  "tasks": {
    "test": {
      "dependsOn": ["^build"],
      "inputs": ["src/**", "tests/**", "vitest.config.*", "playwright.config.*"],
      "outputs": ["coverage/**", "test-results/**", "playwright-report/**"],
    },
  },
}

inputs를 정확히 지정하면 테스트 대상 코드가 그대로일 때 캐시된 결과를 재사용합니다. 테스트 실행 자체를 건너뛰니 CI 시간이 크게 줄어듭니다.

2025~2026 테스트 도구 변경

Vitest 4.x

항목변경
Browser Mode 정식 라인Playwright/WebDriverIO 기반 실제 브라우저 테스트
인라인 Workspacevitest.workspace.ts 없이 vitest.config.ts에서 워크스페이스 정의 가능
성능 개선대규모 모노레포에서 워치 모드 속도 향상

Playwright 1.60+

항목변경
Aria snapshots접근성 트리 기반 스냅샷 테스트
향상된 병렬 실행테스트 샤딩 개선으로 CI 시간 단축
컴포넌트 테스트React/Vue/Svelte 컴포넌트 직접 테스트 안정화

엔터프라이즈 앱에서는 E2E 테스트를 "많이" 만들기보다 배포 차단 시나리오를 명확히 고릅니다. 로그인, 결제, 권한, 핵심 데이터 생성, rollback 후 smoke test처럼 장애 비용이 큰 경로만 main gate로 올리고 나머지는 nightly나 앱별 PR gate로 분리합니다.

참고 문서

  • 브라우저 러너를 넘어 gate, flaky, KPI, incident까지 다루는 테스트 환경 운영 심화는 에이전틱 테스트 환경 엔지니어링을 참고하세요.
  • Vitest: Workspace (영어)
  • Playwright: Getting Started (영어)
  • Testing Library: React (영어)
  • MSW: Getting Started (영어)

관련 문서

에이전틱 테스트 환경 엔지니어링

브라우저 러너를 넘어 agent·API·workflow·observability까지 묶어 테스트 환경을 설계하는 고급 핸드북

검증 리포트

에이전틱 테스트 환경 엔지니어링 · 에이전틱 테스트 환경 엔지니어링 핸드북의 구조·범위·검증 결과

테스팅 전략의 전환

개발자 언러닝 · 테스트를 AI 구현 위임의 작업 지시서로 삼는 전환과, 동작 명세 테스트·통합 테스트 비중 확대, AI가 테스트를 쓸 때의 함정을 다루는 장

Ch13. 레퍼런스와 아키텍처 결정 기록

ADR 템플릿, 기술 선택 근거, 전체 참고 문헌

접근성 내장

AI 시대의 디자인 시스템 · AI가 자동으로 접근성을 갖춘 컴포넌트를 생성하도록 설계하기

Ch7. CI/CD 파이프라인 설계

GitHub Actions + Turborepo, 병렬 빌드, 선택적 테스트, 릴리스 전략

Ch9. Agentic 개발 체계

Claude Code 중심 CLAUDE.md 설계, 멀티세션, 서브에이전트, Hooks 자동화

On this page

모노레포 테스트 피라미드Vitest Workspace 설정Playwright E2E 설정컴포넌트 테스트 패턴CI에서 테스트 캐싱2025~2026 테스트 도구 변경Vitest 4.xPlaywright 1.60+참고 문서