웹 애플리케이션의 복잡성이 나날이 증가함에 따라, 사용자에게 완벽한 경험을 제공하기 위한 품질 보증의 중요성 또한 커지고 있다. 개발팀은 끊임없이 새로운 기능을 추가하고 기존 기능을 개선하지만, 이 과정에서 예상치 못한 버그가 발생하거나 기존 기능이 오작동하는 경우가 빈번하다. 이러한 문제를 수동 테스트만으로 해결하는 것은 시간, 비용, 인력 측면에서 비효율적이며, 궁극적으로는 사용자 이탈과 비즈니스 손실로 이어질 수 있다.

이러한 배경 속에서 자동화된 테스트, 특히 E2E(End-to-End) 테스트는 애플리케이션의 전반적인 기능을 실제 사용자 관점에서 검증하는 핵심적인 방법론으로 자리 잡았다. E2E 테스트는 사용자가 애플리케이션과 상호작용하는 모든 시나리오를 시뮬레이션하여, 시스템의 안정성과 신뢰성을 확보하는 데 결정적인 역할을 수행한다. 본 가이드에서는 강력하고 효율적인 E2E 테스트 프레임워크인 Playwright를 활용하여 웹 애플리케이션의 테스트 환경을 구축하고, 실제 서비스에 적용할 수 있는 실전 전략을 상세히 제시하고자 한다.

Playwright를 활용한 웹 애플리케이션 E2E 테스트 환경 구축 및 실전 가이드 - covid, testing, corona test, covid-19, corona, coronavirus, sars-cov-2, concept, quick test, pcr, pcr-test, covid test, covid, covid, covid, covid, covid, corona, corona, covid test, covid test, covid test

Image by analogicus on Pixabay

웹 애플리케이션 E2E 테스트의 중요성

웹 애플리케이션 개발 과정에서 테스트는 필수적인 단계이다. 그중에서도 E2E 테스트는 시스템의 모든 구성 요소가 통합된 상태에서 최종 사용자의 관점으로 애플리케이션의 흐름을 검증하는 방식이다. 이는 단순히 개별 컴포넌트나 모듈의 기능을 확인하는 것을 넘어, 전체 시스템의 비즈니스 로직사용자 경험이 정상적으로 작동하는지 확인하는 데 중점을 둔다.

E2E 테스트가 제공하는 가치

  • 사용자 경험 검증: E2E 테스트는 실제 사용자가 겪을 수 있는 시나리오를 재현하여, 로그인부터 결제, 정보 조회 등 핵심적인 사용자 여정이 원활하게 이루어지는지 확인한다. 이는 곧 사용자 만족도와 직결되는 부분이다.
  • 비즈니스 로직 확인: 애플리케이션의 핵심 비즈니스 로직이 프론트엔드부터 백엔드, 데이터베이스까지 일관되게 작동하는지 검증한다. 예를 들어, 특정 상품을 장바구니에 담고 결제하는 일련의 과정이 올바르게 처리되는지 확인할 수 있다.
  • 회귀 방지: 새로운 기능 추가나 기존 코드 변경으로 인해 이전에 정상 작동하던 기능이 오작동하는 회귀 버그를 사전에 방지한다. 자동화된 E2E 테스트 스위트를 통해 회귀를 지속적으로 모니터링할 수 있다.
  • 개발 비용 절감: 개발 초기 단계에서 버그를 발견할수록 수정 비용은 기하급수적으로 감소한다. 예를 들어, 개발 단계에서 버그를 수정하는 비용이 1이라면, 테스트 단계에서는 10, 운영 환경에서는 100에 달할 수 있다는 '1:10:100 법칙'은 E2E 테스트의 경제적 가치를 명확히 보여준다.

다양한 테스트 자동화 도구 비교

E2E 테스트 자동화를 위한 도구는 다양하게 존재하며, 각 도구마다 특징과 장단점이 명확하다. 대표적인 도구로는 Selenium, Cypress, 그리고 Playwright가 있다. 이들을 비교하여 Playwright의 강점을 파악할 수 있다.

특징 Selenium Cypress Playwright
브라우저 지원 모든 주요 브라우저 (WebDriver 필요) Chromium, Firefox, Edge (Electron 기반) Chromium, Firefox, WebKit (Safari)
언어 지원 다양한 언어 (Java, Python, C#, JS 등) JavaScript, TypeScript JavaScript, TypeScript, Python, Java, C#
아키텍처 WebDriver 프로토콜 브라우저 내에서 직접 실행 브라우저 외부에서 WS 프로토콜 통신
병렬 처리 Grid를 통해 지원 유료 서비스를 통해 지원 기본적으로 지원 (워커 프로세스)
자동 대기 (Auto-wait) 명시적/암묵적 대기 필요 내장 지원 내장 지원 (더 강력함)
네트워크 컨트롤 제한적 강력하게 지원 강력하게 지원 (API Mocking 용이)
디버깅 브라우저 개발자 도구 활용 GUI 테스트 러너 Test Inspector, Trace Viewer 등 강력한 도구

위 비교에서 알 수 있듯이, Playwright는 멀티 브라우저 지원, 강력한 자동 대기 기능, 병렬 처리, 네트워크 컨트롤 및 우수한 디버깅 도구를 제공하여 현대 웹 애플리케이션 E2E 테스트에 매우 적합한 솔루션으로 평가된다. 특히 웹킷(Safari) 브라우저를 기본 지원하는 점은 다양한 사용자 환경을 고려할 때 큰 장점으로 작용한다.

Playwright란 무엇인가? 핵심 특징 분석

Playwright는 Microsoft에서 개발한 오픈소스 웹 테스트 자동화 라이브러리이다. Chromium, Firefox, WebKit (Safari) 등 주요 브라우저를 단일 API로 제어할 수 있으며, Node.js 기반으로 동작한다. Playwright는 단순히 브라우저를 제어하는 것을 넘어, 안정적이고 신뢰할 수 있는 E2E 테스트를 작성하는 데 필요한 다양한 기능을 제공한다.

  • 멀티 브라우저, 멀티 플랫폼 지원: Playwright는 단일 API로 Chromium(Chrome, Edge), Firefox, WebKit(Safari)을 지원한다. 또한 Windows, Linux, macOS 등 다양한 운영체제에서 실행될 수 있어, 실제 사용자 환경과 유사한 테스트를 수행할 수 있다. 이는 특히 크로스 브라우징 호환성을 검증하는 데 필수적인 요소이다.
  • 자동 대기 (Auto-wait) 기능: 웹 애플리케이션은 동적으로 콘텐츠를 로드하거나 애니메이션을 사용하는 경우가 많다. Playwright는 요소가 나타나거나, 활성화되거나, 특정 상태가 될 때까지 자동으로 대기하는 기능을 내장하고 있어, "flaky test" (간헐적으로 실패하는 테스트)를 줄이고 테스트의 안정성을 크게 향상시킨다. 개발자는 명시적인 대기 시간을 설정하는 데 시간을 낭비할 필요가 없다.
  • 강력한 디버깅 도구: Playwright는 테스트 실패의 원인을 신속하게 파악할 수 있도록 다양한 디버깅 도구를 제공한다.
    • Playwright Inspector: GUI 기반의 도구로, 테스트 스크립트 실행을 단계별로 추적하고, 요소 셀렉터를 자동으로 생성하며, DOM 스냅샷을 확인할 수 있다.
    • Trace Viewer: 테스트 실행의 전체 기록을 시각적으로 보여주는 도구이다. 각 액션 전후의 스크린샷, DOM 스냅샷, 네트워크 로그, 콘솔 로그 등을 상세하게 제공하여 실패 지점을 정확하게 분석할 수 있다.
    • 비디오 녹화 및 스크린샷: 테스트 실행 과정을 비디오로 녹화하거나, 특정 시점에 스크린샷을 찍어 시각적인 증거를 확보할 수 있다.
  • 병렬 실행 지원: Playwright는 여러 테스트를 동시에 실행할 수 있는 병렬 처리 기능을 기본적으로 제공한다. 이는 수많은 테스트 케이스를 가진 대규모 애플리케이션의 테스트 시간을 획기적으로 단축시키는 데 기여한다. 기본적으로 CPU 코어 수에 따라 워커 프로세스를 생성하여 테스트를 분산 실행한다.
  • 네트워크 컨트롤 (API Mocking): Playwright는 네트워크 요청을 가로채고 수정할 수 있는 강력한 기능을 제공한다. 이를 통해 백엔드 API의 응답을 모의(mock)하거나, 특정 네트워크 상황(예: 느린 네트워크)을 시뮬레이션하여 프론트엔드 컴포넌트의 동작을 독립적으로 테스트할 수 있다. 이는 테스트의 신뢰성실행 속도를 높이는 데 매우 유용하다.

아래는 Playwright를 이용한 간단한 웹 페이지 방문 및 스크린샷 테스트 코드 예시이다.

import { test, expect } from '@playwright/test';

test('메인 페이지를 방문하고 스크린샷을 찍는다', async ({ page }) => {
  await page.goto('https://www.example.com');
  await expect(page).toHaveTitle(/Example Domain/);
  await page.screenshot({ path: 'example_screenshot.png' });
});

이 코드는 Playwright가 얼마나 직관적이고 강력한지 보여준다. page.goto()로 페이지를 방문하고, expect(page).toHaveTitle()로 페이지 제목을 검증하며, page.screenshot()으로 스크린샷을 저장한다. 이 모든 과정이 최소한의 코드로 안정적으로 동작한다.

Playwright E2E 테스트 환경 구축 단계

Playwright를 이용한 E2E 테스트 환경 구축은 비교적 간단하며, 몇 가지 단계를 거쳐 쉽게 설정할 수 있다. Node.js 환경에서 진행되며, TypeScript 또는 JavaScript로 테스트 스크립트를 작성할 수 있다.

초기 설정 및 프로젝트 생성

Playwright를 사용하기 위해서는 Node.jsnpm(Node Package Manager)이 시스템에 설치되어 있어야 한다. Node.js 공식 웹사이트에서 최신 LTS 버전을 설치하는 것을 권장한다.

새로운 프로젝트를 생성하거나 기존 프로젝트에 Playwright를 추가하는 과정은 다음과 같다.

    1. Playwright 설치: 프로젝트 루트 디렉토리에서 다음 명령어를 실행하여 Playwright를 설치하고 초기 설정을 진행한다.
npm init playwright@latest
  1. 설정 옵션 선택: 명령어 실행 후, 몇 가지 질문에 답하게 된다.
    • Do you want to use TypeScript or JavaScript? (TypeScript 또는 JavaScript 중 선택) - TypeScript를 권장한다.
    • Where to put your end-to-end tests? (테스트 파일 저장 경로) - 기본값인 tests 폴더를 사용할 수 있다.
    • Add a GitHub Actions workflow? (CI/CD 연동 여부) - 나중에 수동으로 추가할 수 있으므로 No를 선택해도 무방하다.
    • Install Playwright browsers (Chromium, Firefox, WebKit)? (브라우저 설치 여부) - Yes를 선택하여 필요한 브라우저를 설치한다.

이 과정을 거치면 프로젝트 루트에 playwright.config.ts(또는 .js) 파일과 tests 폴더가 생성된다. playwright.config.ts 파일은 테스트 실행에 필요한 설정(브라우저, 테스트 타임아웃, 리포터 등)을 정의하는 핵심 파일이다.

기본 테스트 스크립트 작성 및 실행

설정이 완료되면 tests 폴더 안에 테스트 파일을 생성하여 스크립트를 작성할 수 있다. 파일 이름은 .spec.ts 또는 .test.ts (JavaScript의 경우 .spec.js 또는 .test.js)로 끝나는 것이 일반적이다.

다음은 간단한 로그인 페이지 테스트 시나리오를 가정한 코드 예시이다.

// tests/login.spec.ts
import { test, expect } from '@playwright/test';

test.describe('로그인 페이지 테스트', () => { // 테스트 그룹 정의
  test('유효한 자격 증명으로 로그인에 성공한다', async ({ page }) => {
    // 1. 로그인 페이지로 이동
    await page.goto('http://localhost:3000/login'); // 테스트할 웹 애플리케이션의 로그인 URL

    // 2. 사용자 이름 입력 필드 찾기 및 입력
    await page.fill('input[name="username"]', 'testuser');
    await expect(page.locator('input[name="username"]')).toHaveValue('testuser'); // 입력값 검증

    // 3. 비밀번호 입력 필드 찾기 및 입력
    await page.fill('input[name="password"]', 'password123');
    await expect(page.locator('input[name="password"]')).toHaveValue('password123'); // 입력값 검증

    // 4. 로그인 버튼 클릭
    await page.click('button[type="submit"]');

    // 5. 로그인 성공 후 리다이렉트된 페이지 검증
    await page.waitForURL('http://localhost:3000/dashboard'); // 대시보드 페이지로 이동했는지 확인
    await expect(page.locator('h1')).toHaveText('환영합니다, testuser님!'); // 환영 메시지 검증
  });

  test('유효하지 않은 자격 증명으로 로그인에 실패하고 에러 메시지를 표시한다', async ({ page }) => {
    await page.goto('http://localhost:3000/login');

    await page.fill('input[name="username"]', 'invaliduser');
    await page.fill('input[name="password"]', 'wrongpassword');
    await page.click('button[type="submit"]');

    // 에러 메시지가 표시되는지 확인
    await expect(page.locator('.error-message')).toBeVisible();
    await expect(page.locator('.error-message')).toHaveText('로그인 정보가 올바르지 않습니다.');
  });
});

이 스크립트는 test.describe를 사용하여 관련 테스트 케이스를 그룹화하고, test 함수 내에서 실제 사용자 행동을 모방한다. page.goto(), page.fill(), page.click() 등의 메서드를 사용하여 페이지 이동, 입력, 클릭 액션을 수행하고, expect() 함수와 다양한 매처(Matcher)를 사용하여 예상되는 결과를 검증한다. 예를 들어, toHaveValue()는 입력 필드의 값을, waitForURL()은 특정 URL로의 이동을, toHaveText()는 요소의 텍스트 내용을 검증한다.

작성된 테스트를 실행하려면 다음 명령어를 사용한다.

npx playwright test

특정 브라우저에서만 실행하거나, 헤드리스 모드를 비활성화하려면 playwright.config.ts 파일을 수정하거나 명령줄 인수를 사용할 수 있다. 예를 들어, Chromium 브라우저에서만 실행하려면 npx playwright test --project=chromium 명령어를 사용한다.

Playwright를 활용한 웹 애플리케이션 E2E 테스트 환경 구축 및 실전 가이드 - test, virus, coronavirus, self-test, covid-19, infection, lock down, hygiene, transmission, shutdown, pandemic, test, test, test, test, test, virus, virus, virus, virus, coronavirus, coronavirus, coronavirus, coronavirus, covid-19, covid-19, covid-19, covid-19, pandemic, pandemic

Image by Alexandra_Koch on Pixabay

Playwright 실전 가이드: 고급 기능 활용

기본적인 테스트 환경 구축을 넘어, Playwright의 고급 기능을 활용하면 더욱 견고하고 효율적인 E2E 테스트 스위트를 구축할 수 있다.

Page Object Model (POM) 패턴 적용

대규모 애플리케이션의 E2E 테스트는 수많은 페이지와 상호작용하게 된다. 이때 각 테스트 케이스마다 동일한 요소 셀렉터와 상호작용 로직을 반복적으로 작성하게 되면, 코드의 중복이 심해지고 유지보수가 어려워진다. Page Object Model (POM)은 이러한 문제를 해결하기 위한 디자인 패턴이다.

POM은 웹 페이지의 각 영역 또는 페이지를 객체로 추상화하여, 페이지 내의 요소(Elements)와 해당 요소에 대한 상호작용 메서드(Actions)를 하나의 클래스 안에 캡슐화한다. 이 패턴을 적용하면 다음과 같은 장점을 얻을 수 있다.

  • 유지보수성 향상: UI 변경이 발생하더라도 해당 페이지 객체만 수정하면 되므로, 여러 테스트 파일에 흩어져 있는 셀렉터를 일일이 수정할 필요가 없다.
  • 코드 재사용성: 동일한 페이지에 대한 액션은 페이지 객체 내 메서드를 통해 재사용된다.
  • 가독성 증진: 테스트 스크립트가 비즈니스 로직에 집중하게 되어, 무엇을 테스트하는지 명확하게 이해할 수 있다.

다음은 로그인 페이지에 POM을 적용한 예시이다.

// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly usernameInput: Locator;
  readonly passwordInput: Locator;
  readonly loginButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.usernameInput = page.locator('input[name="username"]');
    this.passwordInput = page.locator('input[name="password"]');
    this.loginButton = page.locator('button[type="submit"]');
    this.errorMessage = page.locator('.error-message');
  }

  async goto() {
    await this.page.goto('http://localhost:3000/login');
  }

  async login(username: string, password: string) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }
}
// tests/login.spec.ts (POM 적용)
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage'; // LoginPage 클래스 임포트

test.describe('로그인 페이지 테스트 (POM)', () => {
  test('유효한 자격 증명으로 로그인에 성공한다', async ({ page }) => {
    const loginPage = new LoginPage(page);
    await loginPage.goto();
    await loginPage.login('testuser', 'password123');

    await page.waitForURL('http://localhost:3000/dashboard');
    await expect(page.locator('h1')).toHaveText('환영합니다, testuser님!');
  });

  test('유효하지 않은 자격 증명으로 로그인에 실패하고 에러 메시지를 표시한다', async ({ page }) => {
    const loginPage = new LoginPage(page);
    await loginPage.goto();
    await loginPage.login('invaliduser', 'wrongpassword');

    await expect(loginPage.errorMessage).toBeVisible();
    await expect(loginPage.errorMessage).toHaveText('로그인 정보가 올바르지 않습니다.');
  });
});

POM을 적용함으로써 테스트 코드가 훨씬 간결해지고, UI 변경에 대한 대응이 용이해졌음을 확인할 수 있다.

API Mocking 및 네트워크 인터셉션

실제 백엔드 API에 의존하지 않고 프론트엔드 테스트를 독립적으로 수행해야 하는 경우가 많다. 백엔드 API가 아직 개발 중이거나, 특정 응답을 시뮬레이션하여 프론트엔드의 에러 처리 로직 등을 테스트하고 싶을 때 Playwright의 네트워크 인터셉션 기능을 활용할 수 있다. page.route() 메서드를 사용하면 특정 URL로 가는 요청을 가로채서 원하는 응답을 반환하도록 설정할 수 있다.

예를 들어, 사용자 목록을 불러오는 API 요청을 가로채서 고정된 데이터를 반환하도록 설정할 수 있다.

// tests/user-list.spec.ts
import { test, expect } from '@playwright/test';

test('사용자 목록을 성공적으로 불러온다', async ({ page }) => {
  // 특정 API 요청을 가로채서 모의 응답 반환
  await page.route('http://localhost:3000/api/users', async route => {
    const json = [
      { id: 1, name: '김철수', email: 'chulsoo@example.com' },
      { id: 2, name: '박영희', email: 'younghee@example.com' },
    ];
    await route.fulfill({ json }); // JSON 응답으로 대체
  });

  await page.goto('http://localhost:3000/users'); // 사용자 목록 페이지로 이동

  // 모의 응답으로 인해 페이지에 사용자 이름이 표시되는지 확인
  await expect(page.locator('text=김철수')).toBeVisible();
  await expect(page.locator('text=박영희')).toBeVisible();
});

test('사용자 목록 불러오기 실패 시 에러 메시지를 표시한다', async ({ page }) => {
  // API 요청을 가로채서 에러 상태 코드 반환
  await page.route('http://localhost:3000/api/users', async route => {
    await route.fulfill({
      status: 500, // 500 Internal Server Error 반환
      contentType: 'application/json',
      body: JSON.stringify({ message: '서버 오류가 발생했습니다.' }),
    });
  });

  await page.goto('http://localhost:3000/users');

  // 에러 메시지가 표시되는지 확인
  await expect(page.locator('.error-message')).toBeVisible();
  await expect(page.locator('.error-message')).toHaveText('데이터 로딩에 실패했습니다.');
});

이 기능은 백엔드 개발에 대한 의존성을 줄이고, 프론트엔드 컴포넌트의 다양한 상태(로딩, 성공, 실패, 빈 데이터 등)를 효율적으로 테스트할 수 있게 해준다. 또한, 네트워크 지연을 시뮬레이션하여 애플리케이션의 로딩 성능을 테스트하는 데도 활용될 수 있다.

인증 및 세션 관리

로그인이 필요한 애플리케이션의 경우, 매 테스트 케이스마다 로그인 과정을 반복하는 것은 비효율적이다. Playwright는 세션 상태를 저장하고 재사용하는 기능을 제공하여 이러한 문제를 해결한다. storageState 옵션을 활용하면 로그인 후의 쿠키, 로컬 스토리지, 세션 스토리지 등을 파일로 저장하고, 다른 테스트에서 해당 상태를 불러와서 사용할 수 있다.

이는 특히 수십, 수백 개의 테스트 케이스가 존재하는 대규모 애플리케이션에서 테스트 실행 시간을 크게 단축시키는 데 기여한다.

// global-setup.ts (글로벌 셋업 파일 생성)
import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // 1. 로그인 페이지로 이동
  await page.goto('http://localhost:3000/login');

  // 2. 로그인 수행
  await page.fill('input[name="username"]', 'admin');
  await page.fill('input[name="password"]', 'adminpassword');
  await page.click('button[type="submit"]');

  // 3. 로그인 성공 후 대시보드로 이동했는지 확인 (선택 사항)
  await page.waitForURL('http://localhost:3000/dashboard');

  // 4. 인증 상태 저장
  await page.context().storageState({ path: 'playwright-auth.json' });

  await browser.close();
}

export default globalSetup;

playwright.config.ts 파일에 globalSetup 옵션을 추가하여 위 파일을 실행하도록 설정한다.

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  // ... 기타 설정
  globalSetup: require.resolve('./global-setup'), // 글로벌 셋업 파일 경로 지정

  projects: [
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright-auth.json', // 저장된 인증 상태 로드
      },
    },
    // ... 다른 브라우저 설정
  ],
});

이제 playwright-auth.json 파일에 저장된 인증 상태를 사용하여 모든 테스트가 로그인된 상태에서 시작될 수 있다. 개별 테스트에서는 로그인 과정 없이 바로 특정 페이지로 이동하여 테스트를 시작할 수 있게 된다.

// tests/dashboard.spec.ts (로그인 상태에서 테스트 시작)
import { test, expect } from '@playwright/test';

test('대시보드 페이지에 접근하여 사용자 정보를 확인한다', async ({ page }) => {
  // 글로벌 셋업에서 저장된 인증 상태 덕분에 이미 로그인된 상태이다.
  await page.goto('http://localhost:3000/dashboard');

  await expect(page.locator('h1')).toHaveText('환영합니다, admin님!');
  await expect(page.locator('text=관리자 권한')).toBeVisible(); // 관리자 권한 텍스트 확인
});

이러한 세션 관리는 테스트 환경을 더욱 효율적으로 만들고, 테스트 실행 시간을 최적화하는 데 필수적인 전략이다.

Playwright를 활용한 웹 애플리케이션 E2E 테스트 환경 구축 및 실전 가이드 - coronavirus, mass testing, covid-19, slovakia, swab test, swab testing, pandemic, new normal, antigen test, man, testing, coronavirus test, medical worker, healthcare professional, healthcare worker, face masks, žilina, slovakia, slovakia, slovakia, swab test, swab test, swab test, swab test, swab test, pandemic, antigen test, antigen test, antigen test, testing, testing, coronavirus test, coronavirus test, medical worker, healthcare professional, healthcare worker, healthcare worker

Image by lukasmilan on Pixabay

CI/CD 파이프라인에 Playwright 통합

자동화된 E2E 테스트의 진정한 가치는 CI/CD(Continuous Integration/Continuous Deployment) 파이프라인에 통합될 때 발휘된다. 코드가 변경될 때마다 자동으로 테스트가 실행되어, 버그를 조기에 발견하고 안정적인 배포를 보장할 수 있다.

지속적 통합 환경 설정

Playwright 테스트를 CI/CD 환경에 통합하는 것은 비교적 간단하다. 대부분의 CI/CD 서비스(예: GitHub Actions, Jenkins, GitLab CI, CircleCI)는 Node.js 환경을 지원하며, 명령줄에서 Playwright 테스트를 실행할 수 있다.

GitHub Actions를 예시로 들면, 다음과 같은 워크플로우를 구성할 수 있다.

# .github/workflows/playwright.yml
name: Playwright Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest # 리눅스 환경에서 실행

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 18

    - name: Install dependencies
      run: npm ci

    - name: Install Playwright browsers
      # CI 환경에서는 브라우저 종속성을 직접 설치해야 한다.
      run: npx playwright install --with-deps

    - name: Run Playwright tests
      run: npx playwright test

    - uses: actions/upload-artifact@v4
      if: always() # 테스트 실패 여부와 상관없이 항상 아티팩트 업로드
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30

위 워크플로우는 코드가 main 또는 develop 브랜치에 푸시되거나 Pull Request가 생성될 때마다 실행된다. 주요 단계는 다음과 같다.

  • 종속성 설치: npm ci 명령으로 프로젝트의 Node.js 패키지를 설치한다.
  • Playwright 브라우저 설치: CI 환경에서는 브라우저가 기본적으로 설치되어 있지 않으므로, npx playwright install --with-deps 명령을 사용하여 Playwright가 지원하는 브라우저와 그에 필요한 시스템 종속성을 설치한다. 특히 --with-deps는 리눅스 환경에서 필요한 추가 패키지들을 설치해준다.
  • 테스트 실행: npx playwright test 명령으로 모든 Playwright 테스트를 실행한다.
  • 리포트 업로드: 테스트 결과 리포트(예: HTML Reporter)를 GitHub Actions의 아티팩트로 업로드하여, 테스트 실행 후 결과를 시각적으로 확인할 수 있도록 한다.

테스트 결과 보고서 및 모니터링

CI/CD 파이프라인에서 테스트가 실행된 후, 그 결과를 효과적으로 분석하고 공유하는 것은 매우 중요하다. Playwright는 다양한 형태의 리포터를 제공하여 테스트 결과를 시각화하고 문제 해결을 돕는다.

  • HTML Reporter: 가장 기본적인 리포터로, 웹 페이지 형태로 테스트 결과를 보여준다. 어떤 테스트가 성공했고 실패했는지, 각 테스트 케이스의 실행 시간은 얼마인지 등을 상세하게 확인할 수 있다. 실패한 테스트의 경우, Test Trace Viewer로 연결되는 링크를 제공하기도 한다.
  • JUnit Reporter: XML 형식의 리포트를 생성하여 Jenkins와 같은 CI 도구에서 테스트 결과를 통합하고 시각화하는 데 사용될 수 있다.
  • List Reporter, Dot Reporter: 명령줄에서 간결하게 테스트 진행 상황을 보여준다.

playwright.config.ts 파일에서 reporter 옵션을 설정하여 원하는 리포터를 사용할 수 있다. 예를 들어, HTML 리포터와 List 리포터를 동시에 사용하려면 다음과 같이 설정한다.

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  // ... 기타 설정
  reporter: [['html'], ['list']], // HTML 리포터와 List 리포터 사용
  // ...
});

테스트 실패 시 생성되는 Test Trace Viewer 파일은 실패 원인 분석에 결정적인 역할을 한다. 이 파일은 각 액션별 스크린샷, DOM 스냅샷, 네트워크 로그, 콘솔 로그, 소스 코드 등을 시간 순서대로 제공하여, 어떤 단계에서 문제가 발생했는지 정확하게 파악할 수 있도록 돕는다. CI 환경에서 Trace Viewer 파일을 아티팩트로 저장하고 다운로드하여 로컬에서 분석하는 것이 일반적인 워크플로우이다.

이러한 통합과 모니터링 체계는 개발팀이 지속적인 품질 관리를 수행하고, 안정적인 웹 애플리케이션을 사용자에게 제공하는 데 필수적인 요소로 기능한다.

결론

웹 애플리케이션의 복잡성과 사용자 기대치가 높아짐에 따라, E2E 테스트는 더 이상 선택 사항이 아닌 필수적인 개발 프로세스의 한 부분으로 자리매김했다. 본 가이드에서 살펴본 Playwright는 강력한 기능과 높은 안정성을 바탕으로 현대 웹 애플리케이션의 E2E 테스트를 위한 최적의 도구 중 하나로 평가된다.

Playwright는 멀티 브라우저 지원, 자동 대기 기능, 강력한 디버깅 도구, 그리고 효율적인 CI/CD 통합 능력을 통해 개발팀이 빠르고 신뢰할 수 있는 테스트 스위트를 구축할 수 있도록 돕는다. Page Object Model 패턴의 적용, API Mocking, 세션 관리와 같은 고급 기법들을 활용한다면, 더욱 견고하고 유지보수성이 높은 테스트 환경을 조성할 수 있다.

궁극적으로 Playwright를 활용한 E2E 테스트 자동화는 버그를 조기에 발견하고, 회귀를 방지하며, 개발 비용을 절감하는 동시에 사용자에게 최상의 경험을 제공하는 데 기여한다. 이는 애플리케이션의 품질 보증을 넘어, 비즈니스 성공을 위한 핵심 전략으로 작용할 것이다.

Playwright를 활용한 E2E 테스트 환경 구축에 대한 여러분의 경험이나 궁금한 점이 있다면 언제든지 댓글로 남겨주세요. 함께 더 나은 웹 애플리케이션 개발 문화를 만들어 나갈 수 있기를 바랍니다.