masonfe-hyunsu
🎭 1. 개요 : E2E 테스트란 무엇인가.

사용자가 서비스를 이용하는 흐름을 처음부터 끝까지 검사하는 방식을 E2E(End-to-End) 테스트라고 한다.
함수 하나하나는 정상이어도, 그것들이 연결된 전체 흐름은 실제 브라우저에서 확인해야 알 수 있다.
단위 테스트가 아무리 많아도 실제 서비스에서는 다른 종류의 문제가 생긴다. 링크가 엉뚱한 곳으로 가거나, 페이지 이동이 중간에 막히는 문제다.
이 포스팅에서는 E2E 테스트가 무엇을 검사하는지, Playwright로 어떻게 작성하는지, 어떻게 안정적으로 유지하는지 정리해 본다.
E2E 테스트는 사용자가 실제로 이용하는 경로가 끝까지 동작하는지 확인한다.
🧪 2. 단위 테스트와 무엇이 다른가.
단위 테스트와 E2E 테스트는 확인하는 범위가 다르다.
단위 테스트
함수나 작은 로직을 따로 검사한다.
실행이 빠르고 문제의 원인을 찾기 쉽다.
예: 날짜 변환 함수가 원하는 문자열을 반환하는지 확인한다.
E2E 테스트
실제 브라우저에서 여러 기능이 연결된 흐름을 검사한다.
페이지, 라우팅, 버튼, 데이터 표시가 함께 동작하는지 알 수 있다.
예: 목록에서 항목을 누른 뒤 상세 페이지가 열리는지 확인한다.
둘 중 하나를 고르는 관계는 아니다. 작은 로직은 단위 테스트로 빠르게 확인하고, 중요한 사용자 흐름은 E2E 테스트로 한 번 더 확인한다.

🎯 3. 도구는 왜 Playwright인가.
E2E 도구로는 Cypress와 Playwright가 자주 비교된다. 둘 다 성숙한 도구지만, 새로 시작한다면 Playwright를 권하고 싶다.
멀티 브라우저: Chromium, Firefox, WebKit을 한 API로 다룬다.
자동 대기: 버튼이나 링크가 준비될 때까지 자동으로 기다린다.
트레이스 뷰어: 실패한 테스트의 실행 과정을 타임라인으로 되감아 볼 수 있다.
CI 친화: headless 실행과 병렬 실행이 기본에 가깝다.
처음부터 모든 브라우저를 검사할 필요는 없다. Chromium 하나로 시작해서 필요해지면 늘리는 편이 관리하기 쉽다.
📦 4. 설치와 설정.
테스트 패키지를 개발 의존성으로 설치하고, 브라우저를 별도로 받는다.
npm install -D @playwright/test
npx playwright install chromium설정과 테스트 파일은 한 폴더에 모아 두면 프로젝트 루트가 지저분해지지 않는다.
e2e/
├── playwright.config.ts
└── home.spec.ts설정 파일에서는 테스트 위치, 실행할 브라우저, 개발 서버 주소를 정한다.
// e2e/playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
const BASE_URL = 'http://localhost:3000';
export default defineConfig({
testDir: '.',
testMatch: '**/*.spec.ts',
use: {
baseURL: BASE_URL,
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: 'npm run dev',
url: BASE_URL,
reuseExistingServer: !process.env.CI,
},
});처음 보면 옵션이 많아 보이지만 역할은 단순하다.
baseURL: 테스트에서/login처럼 경로만 적을 수 있게 기준 주소를 정한다.webServer: 테스트 전에 개발 서버를 자동으로 실행한다. 서버를 따로 켜 둘 필요가 없다.reuseExistingServer: 이미 개발 서버가 실행 중이면 그대로 사용한다.trace: 재시도할 때 브라우저 동작 기록을 남긴다.
package.json에는 실행 명령만 추가한다.
{
"scripts": {
"test:e2e": "playwright test --config e2e/playwright.config.ts"
}
}✍️ 5. 첫 번째 사용자 흐름 작성하기.
첫 테스트는 서비스에서 가장 자주 지나는 길 하나를 고른다. 목록 페이지에서 상세 페이지로 이동하는 흐름이 좋은 예다.
// e2e/home.spec.ts
import { test, expect } from '@playwright/test';
test('목록에서 항목을 누르면 상세 페이지로 이동한다', async ({ page }) => {
await page.goto('/');
const firstItem = page.locator('a[href^="/detail/"]').first();
await expect(firstItem).toBeVisible();
await firstItem.click();
await expect(page).toHaveURL(/\/detail\//);
});코드를 순서대로 읽으면 실제 사용자의 행동과 비슷하다.
page.goto('/')로 홈을 연다.첫 번째 항목 링크를 찾는다.
링크가 화면에 보이는지 확인한다.
링크를 누른다.
주소가 상세 페이지 경로로 바뀌었는지 확인한다.
테스트는 다음 명령으로 실행한다.
npm run test:e2e
Playwright가 개발 서버를 준비한다.
Chromium이 페이지를 열고 지정한 동작을 수행한다.
예상한 결과와 같으면 테스트가 통과한다.
실패하면 어느 단계에서 문제가 생겼는지 결과와 트레이스로 확인한다.
🔁 6. 안정적인 테스트로 유지하기.
E2E 테스트는 화면과 실행 환경의 영향을 받는다. 따라서 테스트를 많이 만드는 것보다 중요한 경로를 안정적으로 검사하는 편이 좋다.
사용자가 이해할 수 있는 이름으로 요소를 찾는다.
getByRole('button', { name: '로그인' })getByLabel('이메일')
꼭 필요한 경우에만
data-testid를 사용한다.setTimeout이나 고정된 대기 시간을 넣지 않는다.외부 데이터가 매번 달라지는 테스트는 피한다.
Playwright의 expect는 조건이 만족될 때까지 일정 시간 기다린다.
await expect(page.getByText('저장 완료')).toBeVisible();따라서 화면이 뜰 때까지 임의로 몇 초를 기다리는 코드보다, 확인하려는 상태를 직접 적는 편이 명확하다.

테스트가 가끔 실패한다면 대기 방식, 요소를 찾는 방법, 테스트 데이터 순서로 확인한다.
🤖 7. CI에서는 어떻게 실행할까.
CI(자동 검사 환경)에서는 패키지를 설치한 뒤 Chromium과 E2E 테스트를 실행하면 된다.
- name: Install Playwright browser
run: npx playwright install --with-deps chromium
- name: Run E2E
run: npm run test:e2e--with-deps는 CI의 리눅스 환경에 필요한 시스템 패키지도 함께 설치한다.CI에서는
reuseExistingServer가 꺼지므로webServer가 매번 새 서버를 띄운다.테스트가 실패하면 리포트와 트레이스를 아티팩트로 남겨 원인을 확인할 수 있다.
🔺 8. 테스트 피라미드에서의 위치.
E2E 테스트는 강력하지만 느리고 깨지기 쉽다. 그래서 많이 두면 안 된다.
E2E (적게)
통합 / 컴포넌트
단위 (Vitest/Jest) ← 많이아래(단위)는 많고 빠르게, 위(E2E)로 갈수록 적고 굵게 가져간다.
유틸이나 순수 로직까지 E2E로 올리면 스위트가 무거워지고 자주 깨진다.
E2E는 로그인, 결제, 주요 페이지 이동처럼 실패했을 때 영향이 큰 흐름부터 추가한다.
Playwright UI에서는 테스트 목록, 실행 단계, 브라우저 화면을 한곳에서 확인할 수 있다. 아래는 npm run test:e2e:ui로 실행 결과를 확인한 화면이다.

📋 9. 정리.
E2E 테스트는 실제 브라우저에서 사용자 흐름이 끝까지 동작하는지 확인한다.
단위 테스트와 역할이 다르므로 대체 관계가 아니라 보완 관계다.
Playwright는 자동 대기, 트레이스, 멀티 브라우저 지원으로 시작하기 좋다.
설정과 테스트를 한 폴더에 모으면 프로젝트 구조를 단순하게 유지할 수 있다.
테스트 수를 늘리는 것보다 중요한 흐름을 안정적으로 유지하는 데 집중한다.
E2E 테스트를 별도의 거대한 작업으로 만들 필요는 없다. 사용자가 자주 지나는 경로 하나를 골라 실제 브라우저로 확인하는 것부터 시작하면 된다. 이후에는 실패했을 때 영향이 큰 흐름을 중심으로 조금씩 넓혀 가면 충분하다.
댓글