Playwright와 MSW(Mock Service Worker)는 웹 개발과 테스트에서 많이 사용하는 도구 중 하나다.
Playwright와 MSW(Mock Service Worker)를 함께 사용하는 것은 프론트엔드 E2E 및 통합 테스트 환경에서 매우 강력한 조합이 될 수 있다.
Playwright
Playwright는 Microsoft에서 만든 오픈 소스 엔드투엔드(E2E) 테스트 프레임워크이다.
브라우저를 자동으로 제어하여 실제 사용자처럼 웹 애플리케이션을 테스트할 수 있다.
화면에 어떤 버튼, 입력값을 입력했을 때 오류 케이스, 알럿, 페이지 이동까지 어떻게 이루어져야 하는지 테스트할 수 있다.
단, 오래된 맥 (2024년 기준 2019년 맥북 프로) 에서는 실행되지 않았다.
- 멀티 브라우저 지원: Chromium(Chrome, Edge), Firefox, WebKit(Safari) 등 주요 브라우저를 지원
- 헤드리스/헤드풀 모드: GUI 없이 빠르게 실행하거나 실제 브라우저처럼 창을 띄워 실행 가능
- 크로스 플랫폼: Windows, macOS, Linux 모두 지원
- 자동 대기: DOM 요소가 준비될 때까지 자동으로 기다리므로 flakiness가 감소
- 병렬 실행 & 멀티 컨텍스트: 여러 브라우저 세션을 동시에 실행 가능
- 모바일 시뮬레이션: iPhone, Pixel 등의 디바이스 에뮬레이션을 제공
- 비디오/스크린샷 기록: 실패 시 디버깅에 도움을 주는 비디오, 스크린샷 저장 기능
import { expect, test } from '@playwright/test'
test('투자 성향 테스트 결과가 존재하는 경우', async ({ page }) => {
await withAuthContext(page, async () => {
const entryUrl = getUrl(['empty_user', 'intent', 'intent_exists'], '/intent', {
productId: '15',
})
await test.step('기존 결과 표시', async () => {
await page.goto(entryUrl)
await expect(page.getByText('공격형')).toBeVisible()
await expect(page.getByTestId('intent-graph')).toBeVisible()
const keepButton = page.getByText('현재 성향 유지')
await expect(keepButton).toBeVisible()
await expect(keepButton).toBeEnabled()
})
await test.step('재설문 시도', async () => {
const retryButton = page.getByText(/^재설문$/)
await expect(retryButton).toBeVisible()
await expect(retryButton).toBeEnabled()
await retryButton.click()
await page.waitForURL('/intent/purpose?*', {
waitUntil: 'networkidle',
})
})
})
})
MSW (Mock Service Worker)
MSW는 서비스 워커를 이용해 네트워크 요청을 가로채고, 가짜(mock) 응답으로 대체할 수 있는 도구이다.
프론트엔드 개발과 테스트에서 백엔드 API를 대체하거나 시뮬레이션할 때 주로 사용된다.
- 브라우저 기반 가상 API: 실제 네트워크 요청을 서비스 워커 레벨에서 가로챔
- Node.js 지원: 서버 사이드(Node, Jest 등)에서도 mock 가능
- 실제 네트워크와 유사한 환경: XHR/fetch 같은 요청을 가로채기 때문에 실제 API 호출과 거의 동일한 조건을 제공
- 테스트 신뢰성 강화: API 서버의 상태나 연결 문제에 영향을 받지 않음
- 백엔드 개발 지연 시 활용: API가 준비되기 전 프론트엔드 개발이 가능
Playwright + MSW 조합
- 다양한 API 응답 시나리오(성공/실패/지연)를 쉽게 시뮬레이션 가능
- 외부 API 의존성을 제거하고 안정적인 테스트 환경을 구축 가능
- CI/CD에서 flaky하지 않은 테스트를 유지 가능
Playwright + MSW 조합의 장점
1. 네트워크 안정성과 속도 확보
Playwright는 실제 브라우저 환경에서 테스트를 실행하지만, 외부 API나 백엔드 서버에 의존하면 테스트가 느리거나 불안정해질 수 있는데 이 때 MSW를 사용하면:
- 네트워크 요청을 가로채어 가짜 응답(mock)을 반환할 수 있음
- 외부 서비스가 다운되었거나 API가 변경되어도 테스트는 계속 안정적으로 작동
- 테스트 실행 속도가 훨씬 빨라짐
2. 더 쉬운 상태 시뮬레이션 (에러/엣지 케이스 등)
실제 API로는 시뮬레이션하기 어려운 상황을 MSW로 쉽게 구현할 수 있음
예:
- 로그인 API가 401 Unauthorized를 반환하는 상황
- 백엔드에서 특정 필드가 누락된 경우
- 서버 응답이 느려지는 상황(인위적으로 delay 추가)
이런 케이스들을 쉽게 mock 하여 UI와 사용자 흐름을 테스트할 수 있음
3. E2E와 Mocks의 통합
Playwright 내에서 runtime 중에 MSW 핸들러를 동적으로 바꾸는 것도 가능하다.
이를 통해 하나의 E2E 테스트 안에서도 다양한 mock 시나리오를 시도할 수 있다.
// Playwright 테스트 내에서 MSW 핸들러 동적으로 등록
await page.evaluate(() => {
window.msw.use(
rest.get('/api/user', (req, res, ctx) => {
return res(ctx.status(500));
})
);
});
// 프로젝트 내에서 사용한 방법
import { HttpResponse } from 'msw'
export function success<T>(data: T) {
return HttpResponse.json({
details: null,
errors: null,
result: data,
code: 0,
message: '정상 처리되었습니다.',
})
}
import type { ContractProgress } from '$src/lib/domain'
import { http } from 'msw'
import { success } from '../../../response'
export function createProgressHandlers(progress: ContractProgress) {
return [
http.get('https://api.test.com/contract/progress', async () => {
return success([progress])
}),
http.get(`https://api.test.com/contract/progress/${progress.productId}`, async () => {
return success(progress)
}),
]
}
4. 로컬 개발과 테스트에서의 일관성
MSW는 개발 모드에서도 사용할 수 있기 때문에:
- 개발 중에도 API를 기다리지 않고 UI 작업을 진행할 수 있고,
- 동일한 핸들러(mock 응답)를 테스트에서도 재사용하여 개발 ↔ 테스트 간 응답 불일치 문제를 줄일 수 있다.
정리
| 항목 | 장점 |
| 안정성 | API 의존도 제거, flaky 테스트 감소 |
| 속도 | API 대기 없이 즉시 응답, 테스트 시간 단축 |
| 유연성 | 에러/엣지 케이스 쉽게 시뮬레이션 가능 |
| 일관성 | 개발-테스트 동일한 mock 설정 공유 가능 |
| 유지보수 | API 변경에도 테스트 영향 최소화 |
이상 스텔라였습니다 ✍🏻

'TECH' 카테고리의 다른 글
| 🧠 Stack Overflow의 사용 통계 변화 (2008–2025) (0) | 2025.10.28 |
|---|---|
| DOM에 관하여 (0) | 2025.10.27 |
| rebase ↹ (0) | 2025.09.05 |
| Tailwind CSS 🪮 (1) | 2025.09.03 |
| 의존성 분류에 대해서 ➗ (0) | 2025.09.01 |