Skip to Content
WebReactReact 실무16. Playwright E2E: 로그인부터 책 등록까지

이번 편의 결과물: 실제 브라우저를 띄워 로그인 후 책을 등록하고 목록에서 확인하는 E2E 테스트 1개가 통과합니다. · 다루는 개념: Playwright 프로젝트 설정, 브라우저 기반 시나리오 작성, 로케이터·어서션

이 편에서 만드는 파일

bookshelf/ ├── playwright.config.js (+) 브라우저·webServer 설정 ├── e2e/ │ └── login-and-add-book.spec.js (+) E2E 테스트 1개 └── package.json (~) test:e2e 스크립트 추가

개념 정리

15편과 다른 점 — 무엇도 가짜로 만들지 않는다

15편의 통합 테스트는 MSW로 네트워크를 가로챘고, 컴포넌트도 JSDOM(가상 DOM)에서 렌더링했습니다. Playwright E2E는 셋 다 진짜입니다. npm run dev로 띄운 실제 개발 서버와 04편부터 써온 실제 json-server를, 실제로 설치된 Chromium 브라우저가 열어서 클릭하고 타이핑합니다. 08편의 라우트 가드, 13편의 언어 전환, 12편의 이미지 업로드까지 — 각 편에서 따로 확인했던 기능들이 한 사용자의 한 연속된 조작 안에서 실제로 이어지는지가 이 편의 관심사입니다.

계층브라우저백엔드
14편 단위없음(JSDOM도 아님)함수 목
15편 통합JSDOM(가상)MSW(네트워크 가로채기)
16편 E2E실제 Chromium실제 json-server

MSW는 15편의 단위·통합 테스트 전용입니다. E2E는 앱이 실제로 의존하는 백엔드(json-server)까지 함께 띄워, 화면부터 저장까지 전체 경로를 검증합니다. db.json에 실제로 쓰기가 일어나므로, 테스트가 추가한 책이 다음 실행에도 남아 있을 수 있다는 점만 유의합니다(직접 해보기에서 정리 방법을 다룹니다).

왜 E2E는 1개만 두는가

E2E는 느리고(실제 브라우저 실행), 깨지기 쉽고(타이밍 의존적), 실패 원인을 좁히기 어렵습니다. 그래서 “가장 값진 한 가지 흐름”만 검증합니다. 이 과목에서는 로그인부터 책 등록까지가 가장 많은 기능(인증, 폼, 서버 상태, 라우팅)을 가로지르는 흐름이라 이 하나를 선택합니다. 나머지 세부 로직은 이미 14~15편이 담당합니다.

실습

1. Playwright 설치

npm install @playwright/test@1.63.0 --save-dev npx playwright install chromium

e2e/ 폴더를 만들고 그 안에 테스트 파일을 둡니다.

2. Playwright 설정 작성

// bookshelf/playwright.config.js import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './e2e', fullyParallel: true, retries: 1, use: { baseURL: 'http://localhost:5173', trace: 'on-first-retry', }, projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }], webServer: [ { command: 'npx json-server db.json --port 3001', url: 'http://localhost:3001/books', reuseExistingServer: true, }, { command: 'npm run dev', url: 'http://localhost:5173', reuseExistingServer: true, }, ], });

webServer에 배열을 넘기면 Playwright가 두 명령을 함께 띄우고, 각 url이 응답할 때까지 기다린 뒤 테스트를 시작합니다. 이미 두 서버가 떠 있으면(개발 중이라면 흔한 상황) reuseExistingServer가 새로 띄우지 않고 그대로 씁니다.

3. E2E 테스트 작성

// bookshelf/e2e/login-and-add-book.spec.js import { test, expect } from '@playwright/test'; test('로그인 후 책을 등록하면 목록에 나타난다', async ({ page }) => { await page.goto('/login'); await page.getByLabel('이메일').fill('zeno@example.com'); await page.getByLabel('비밀번호').fill('password123'); await page.getByRole('button', { name: '로그인' }).click(); await expect(page.getByText('내 서재')).toBeVisible(); await page.getByRole('link', { name: '새 책 등록' }).click(); await page.getByLabel('제목').fill('이펙티브 자바 3판'); await page.getByLabel('저자').fill('조슈아 블로크'); await page.getByLabel('쪽수').fill('650'); await page.getByRole('button', { name: '등록' }).click(); await expect(page).toHaveURL('http://localhost:5173/'); await expect(page.getByText('이펙티브 자바 3판')).toBeVisible(); }); test('로그인하지 않고 목록에 접근하면 로그인 화면으로 이동한다', async ({ page }) => { await page.goto('/'); await expect(page).toHaveURL(/\/login$/); });

로케이터(getByLabel, getByRole)는 15편의 RTL 쿼리와 이름이 같습니다. 두 도구 모두 “사용자가 화면에서 찾는 방식”을 기준으로 요소를 찾는 철학을 공유합니다. 제목을 “이펙티브 자바 3판”처럼 db.json에 없는 값으로 지어, 10편에서 만든 중복 제목 검사에 걸리지 않게 합니다.

4. package.json에 스크립트 추가

{ "scripts": { "test:e2e": "playwright test" } }

5. 실행

npm run test:e2e

6. 확인

Running 2 tests using 1 worker ✓ e2e/login-and-add-book.spec.js:4:1 › 로그인 후 책을 등록하면 목록에 나타난다 (2.1s) ✓ e2e/login-and-add-book.spec.js:22:1 › 로그인하지 않고 목록에 접근하면 로그인 화면으로 이동한다 (0.6s) 2 passed (3.4s)
  • 실패 시 playwright-report/index.html에서 실패 지점의 스크린샷과 트레이스를 확인할 수 있습니다.
  • 테스트가 끝난 뒤 bookshelf/db.json을 열어보면 방금 등록한 책이 실제로 남아 있습니다. 반복 실행 전에 git checkout db.json으로 되돌리는 습관을 들입니다(직접 해보기 참고).

직접 해보기

테스트가 db.json에 남긴 변경을 매번 되돌리는 스크립트를 package.json에 추가해 보세요.

{ "scripts": { "pretest:e2e": "git checkout -- db.json", "test:e2e": "playwright test" } }

pretest:e2e는 npm이 test:e2e 실행 전 자동으로 실행하는 훅입니다. 실제 팀 프로젝트에서는 E2E 전용 데이터베이스 파일을 따로 두는 방식이 더 흔합니다.

자주 하는 실수

증상원인고치는 법
테스트가 로컬에서만 통과하고 CI에서 실패한다webServer 없이 수동으로 띄운 서버에 의존playwright.config.jswebServer로 두 서버 기동을 테스트에 포함
요소를 못 찾는다는 타임아웃 에러클릭 직후 페이지 전환을 기다리지 않음expect(page).toHaveURL()이나 toBeVisible()로 자동 대기 활용
두 번째 실행부터 등록 테스트가 실패같은 제목이 이미 db.json에 남아 10편의 중복 검사에 걸림매번 다른 제목을 쓰거나 db.json을 원상 복구
테스트가 가끔 실패한다(flaky)고정된 waitForTimeout 사용타임아웃 대신 로케이터·어서션의 자동 재시도 대기 사용

확인 문제

문제 14지선다
Playwright E2E와 15편 MSW 통합 테스트의 가장 큰 차이는 무엇입니까
문제 24지선다
이 과목이 E2E 테스트를 1개만 두는 이유로 옳은 것은 무엇입니까
문제 34지선다
playwright.config.js의 webServer에 배열을 넘기는 이유는 무엇입니까
문제 44지선다
E2E 테스트에서 고정된 waitForTimeout 대신 권장되는 방식은 무엇입니까
문제 54지선다
이 편의 등록 테스트가 db.json에 이미 있는 제목 대신 새 제목을 쓰는 이유는 무엇입니까

참고 자료

Last updated on