E2E 테스트란? (프론트엔드에서 왜 필요한가)

김민석·2026년 3월 1일
post-thumbnail

E2E 테스트란?

프론트엔드에서 버그가 가장 자주 발생하는 지점은 코드 한 줄이 아니라 기능이 서로 연결되는 구간이다.
예를 들면 로그인 → 토큰 저장 → 권한 체크 → API 호출 → 화면 렌더링처럼 여러 요소가 엮이는 흐름에서 문제가 터진다. E2E(End-to-End) 테스트는 사용자가 실제로 하는 행동(접속, 클릭, 입력)을 브라우저에서 그대로 재현해서, 서비스가 처음부터 끝까지 정상 동작하는지를 검증하는 테스트다.

E2E 테스트를 하는 이유

1) 서비스가 작동한다를 보장

단위 테스트(Unit Test)는 함수/컴포넌트 같은 작은 단위를 빠르게 검증한다.
하지만 아래 같은 문제는 단위 테스트만으로는 놓치기 쉽다.

  • 로그인은 되는데 대시보드로 이동이 안 됨 (라우팅/권한)
  • API 응답 구조가 바뀌어 화면이 깨짐
  • 배포 환경에서만 CORS/환경변수 문제로 요청 실패
  • 권한이 없는 사용자가 수정 버튼을 볼 수 있음

E2E는 이런 통합 이슈를 잡기 위해 사용한다.

단위 테스트 vs E2E 테스트 (차이점)
구분 단위 테스트(Unit) E2E 테스트
테스트 대상 함수/컴포넌트 로직 실제 사용자 흐름
실행 환경 테스트 런타임(브라우저 X) 실제 브라우저(Chromium 등)
장점 빠르고 디버깅 쉬움 “서비스가 된다”를 보장
단점 통합 문제를 놓치기 쉬움 느리고 환경 세팅 필요
예시 formatDate()가 올바른지 로그인 → 글 작성 → 목록 확인

정리하면 단위 테스트는 코드가 맞는지, E2E는 “서비스가 되는지”를 확인한다.

Playwright를 사용하는 이유

프론트엔드 E2E 도구로는 Cypress도 있지만 최근에는 Playwright 채택이 빠르게 늘고 있다.

Playwright의 장점

  • 진정한 크로스 브라우징: 단순히 Chrome 계열만 지원하는 것이 아니라, Apple의 WebKit을 직접 포함하고 있어 Safari 환경까지 로컬에서 완벽하게 재현할 수 있습니다.
  • No More 'Sleep': Playwright의 Auto-waiting 기능은 요소가 화면에 나타나고, 클릭 가능해질 때까지 자동으로 기다려주기 때문에 "Flaky Test"(간헐적 실패)를 획기적으로 줄여줍니다.
  • 효율적인 리소스 사용: 브라우저 전체를 띄우는 대신 Browser Context를 활용해 세션을 격리하므로, 병렬 실행 시 메모리 소모가 적고 속도가 매우 빠릅니다.
  • Trace Viewer의 마법: CI에서 테스트가 실패했을 때, Playwright Trace Viewer를 사용하면 실패 시점의 스냅샷, 콘솔 로그, 네트워크 요청을 타임라인별로 다시 돌려볼 수 있어 디버깅 시간이 단축됩니다.

Playwright로 E2E 테스트 시작하기

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();
});

핵심은 코드 테스트가 아니라 사용자 행동을 그대로 적는 것이다.

E2E를 안정적으로 만들기 위한 팁 3가지

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는 한 번 해보기가 아니라 계속 돌려서 안전장치로 쓰는 것이 핵심이다.

CI에서 E2E 자동 실행 (GitHub Actions)

.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 전에 문제를 막을 수 있다.

profile
나만의 기록장

0개의 댓글