프론트엔드에서 버그가 가장 자주 발생하는 지점은 코드 한 줄이 아니라 기능이 서로 연결되는 구간이다.
예를 들면 로그인 → 토큰 저장 → 권한 체크 → API 호출 → 화면 렌더링처럼 여러 요소가 엮이는 흐름에서 문제가 터진다. E2E(End-to-End) 테스트는 사용자가 실제로 하는 행동(접속, 클릭, 입력)을 브라우저에서 그대로 재현해서, 서비스가 처음부터 끝까지 정상 동작하는지를 검증하는 테스트다.
단위 테스트(Unit Test)는 함수/컴포넌트 같은 작은 단위를 빠르게 검증한다.
하지만 아래 같은 문제는 단위 테스트만으로는 놓치기 쉽다.
E2E는 이런 통합 이슈를 잡기 위해 사용한다.
단위 테스트 vs E2E 테스트 (차이점)
구분 단위 테스트(Unit) E2E 테스트
테스트 대상 함수/컴포넌트 로직 실제 사용자 흐름
실행 환경 테스트 런타임(브라우저 X) 실제 브라우저(Chromium 등)
장점 빠르고 디버깅 쉬움 “서비스가 된다”를 보장
단점 통합 문제를 놓치기 쉬움 느리고 환경 세팅 필요
예시 formatDate()가 올바른지 로그인 → 글 작성 → 목록 확인
정리하면 단위 테스트는 코드가 맞는지, E2E는 “서비스가 되는지”를 확인한다.
프론트엔드 E2E 도구로는 Cypress도 있지만 최근에는 Playwright 채택이 빠르게 늘고 있다.
1) 설치
npm init playwright@latest
설치 후 playwright.config.ts와 tests/ 폴더가 생성된다.
2) 가장 중요한 원칙: “핵심 플로우만” 테스트한다
E2E는 느리기 때문에 모든 UI를 다 테스트하면 비용이 커진다.
그래서 서비스의 가치가 걸린 흐름만 최소 개수로 잡는 것이 핵심이다.
블로그 서비스라면 일반적으로 아래가 핵심 플로우다.
import { test, expect } from "@playwright/test";
test("로그인 후 글 작성 플로우", async ({ page }) => {
await page.goto("http://localhost:3000");
// 로그인
await page.fill('input[name="email"]', "test@test.com");
await page.fill('input[name="password"]', "123456");
await page.click('button[type="submit"]');
// 글쓰기 이동
await page.click("text=글쓰기");
// 글 작성
await page.fill('input[name="title"]', "E2E 테스트 글");
await page.fill('textarea[name="content"]', "내용입니다.");
await page.click("text=작성");
// 결과 확인
await expect(page.locator("text=E2E 테스트 글")).toBeVisible();
});
핵심은 코드 테스트가 아니라 사용자 행동을 그대로 적는 것이다.
1) 셀렉터는 텍스트보다 data-testid를 우선
UI 문구는 바뀌기 쉬워서 테스트가 자주 깨진다.
중요한 버튼/입력에는 data-testid를 붙이는 게 안정적이다.
<button data-testid="submit-post">작성</button>
await page.getByTestId("submit-post").click();
2) 테스트용 계정/데이터를 분리
E2E는 DB를 건드린다.그래서 테스트 계정(예: e2e@test.com)을 분리하고, 테스트가 생성한 데이터는 식별 가능하게 만드는 게 좋다.
3) CI에서 자동 실행되도록 연결
E2E는 한 번 해보기가 아니라 계속 돌려서 안전장치로 쓰는 것이 핵심이다.
.github/workflows/e2e.yml
name: E2E Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install
- run: npx playwright install --with-deps
- run: npm run build
- run: npx playwright test
이렇게 해두면 PR 단계에서 자동으로 실패/성공이 표시되고, merge 전에 문제를 막을 수 있다.