튜토리얼

Playwright E2E 테스트 환경 구축: CI/CD 연동 및 리포트 자동화 실전 가이드

강코의 코딩 일기 2026. 3. 31. 20:15

Playwright를 이용한 웹 애플리케이션 E2E 테스트 환경 구축부터 CI/CD 연동, 그리고 시각적인 테스트 리포트 자동화까지 실무 경험을 바탕으로 자세히 알아봅니다.

안녕하세요! 웹 애플리케이션 개발자라면 누구나 한 번쯤 고민해봤을 주제, 바로 테스트 자동화입니다. 특히 사용자 경험과 직결되는 E2E(End-to-End) 테스트는 개발 과정에서 빼놓을 수 없는 중요한 부분이죠. 하지만 막상 시작하려 하면 어떤 도구를 써야 할지, 어떻게 CI/CD에 연동하고 리포트는 어떻게 받아봐야 할지 막막할 때가 많습니다. 저 역시 수많은 시행착오 끝에 Playwright를 활용하여 만족스러운 E2E 테스트 환경을 구축할 수 있었습니다. 이 글에서는 제가 직접 경험하고 적용해 본 Playwright 기반의 E2E 테스트 환경 구축 노하우를 상세하게 공유하고자 합니다. 복잡한 설정부터 CI/CD 연동, 그리고 시각적인 리포트 자동화까지, 실전 가이드를 통해 여러분의 개발 생산성을 한 단계 끌어올리는 데 도움을 드리겠습니다.

왜 E2E 테스트가 필요한가? Playwright를 선택한 이유

웹 애플리케이션을 개발하고 배포하는 과정에서 기능의 정상 작동을 확인하는 것은 필수적입니다. 하지만 수동 테스트는 시간 소모가 크고, 반복적이며, 사람의 실수로 인한 오류 발생 가능성이 높습니다. 특히 기능이 많아지고 복잡해질수록 수동 테스트의 한계는 명확해지죠. 바로 이 지점에서 E2E 테스트 자동화의 필요성이 대두됩니다. E2E 테스트는 사용자의 관점에서 애플리케이션의 시작부터 끝까지 전체 흐름을 테스트하여, 실제 사용 환경에서의 문제점을 조기에 발견하고 안정적인 서비스를 제공하는 데 결정적인 역할을 합니다.

E2E 테스트 자동화의 중요성

  • 품질 보증: 실제 사용 시나리오를 시뮬레이션하여 잠재적인 버그를 파악하고 사용자 경험을 개선합니다.
  • 회귀 테스트 효율화: 새로운 기능 추가나 코드 변경 후 기존 기능이 손상되지 않았는지 빠르게 검증합니다.
  • 개발 생산성 향상: 반복적인 수동 테스트 부담을 줄여 개발자들이 핵심 기능 구현에 집중할 수 있도록 돕습니다.
  • 배포 안정성 확보: CI/CD 파이프라인에 연동하여 배포 전 최종 검증 단계에서 안정성을 높입니다.

Playwright, 왜 선택했는가?

수많은 E2E 테스트 도구 중에서 저는 Playwright를 선택했습니다. Cypress, Selenium 등 다른 훌륭한 도구들도 많지만, Playwright가 제공하는 몇 가지 독보적인 장점들이 저의 마음을 사로잡았습니다. 실제로 여러 도구를 비교 분석하고 직접 적용해 본 결과, Playwright가 저희 팀의 요구사항에 가장 잘 부합한다고 판단했습니다.

특징 Playwright Cypress (비교) Selenium (비교)
브라우저 지원 Chromium, Firefox, WebKit (모든 주요 브라우저) Chromium 기반, Firefox (제한적), Edge (베타) 모든 브라우저 (WebDriver 필요)
언어 지원 JavaScript/TypeScript, Python, Java, .NET JavaScript/TypeScript 다양한 언어 지원
실행 환경 Node.js 환경에서 브라우저 직접 제어 브라우저 내에서 직접 실행 (DOM 접근) WebDriver를 통해 브라우저 제어
병렬 실행 기본 지원 (워커 프로세스 기반) 플러그인 또는 유료 서비스 필요 Grid를 통해 병렬 실행 가능
자동 대기 (Auto-waiting) 기본 탑재 (요소 생성, 활성화 등을 자동으로 대기) 기본 탑재 수동으로 대기 로직 구현 필요
디버깅 및 트레이스 강력한 트레이스 뷰어, 코드젠, 테스트 인스펙터 타임 트래블링 디버거 로그 및 스크린샷 위주

특히 Playwright자동 대기(Auto-waiting) 기능은 테스트 코드 작성 시 불필요한 `waitFor` 구문을 줄여주어 코드를 훨씬 간결하고 안정적으로 만들어줍니다. 또한, 강력한 트레이스 뷰어(Trace Viewer)는 테스트 실패 시 문제의 원인을 시각적으로 파악하는 데 엄청난 도움을 줍니다. 제가 직접 써보니, 이 트레이스 뷰어 덕분에 디버깅 시간이 획기적으로 줄어드는 것을 체감했습니다. 이 외에도 다양한 언어 지원, 여러 브라우저 동시 지원, 그리고 CI/CD 환경에서의 높은 안정성 등은 Playwright를 선택하게 된 주요 요인들입니다.

Playwright 기본 환경 설정 및 첫 테스트 작성

이제 Playwright를 설치하고 기본적인 테스트를 작성해보는 과정을 살펴보겠습니다. Node.js가 설치되어 있다는 가정하에 진행하며, TypeScript를 사용하는 것을 기준으로 설명합니다.

Playwright 설치 및 브라우저 설정

먼저 프로젝트 폴더를 생성하고 Playwright를 초기화합니다. 이 과정에서 필요한 브라우저(Chromium, Firefox, WebKit)도 자동으로 설치됩니다.


mkdir my-playwright-project
cd my-playwright-project
npm init playwright@latest

위 명령어를 실행하면 몇 가지 질문이 나옵니다.

  • TypeScript or JavaScript? (TypeScript 선택)
  • Where to put your end-to-end tests? (tests 폴더로 기본값 유지)
  • Add a GitHub Actions workflow? (Yes 선택하여 CI/CD 파일 자동 생성)
  • Install Playwright browsers (chromium, firefox, webkit)? (Yes 선택)

설치가 완료되면 다음과 같은 기본 프로젝트 구조가 생성됩니다.


my-playwright-project/
├── tests/
│   └── example.spec.ts
├── playwright.config.ts
├── package.json
├── package-lock.json
└── .github/
    └── workflows/
        └── playwright.yml (GitHub Actions 워크플로우 파일)

playwright.config.ts 파일은 Playwright 테스트의 전반적인 설정을 담당합니다. 예를 들어, 테스트를 실행할 브라우저, 테스트 파일 경로, 타임아웃 등을 여기서 설정할 수 있습니다. 기본 설정도 훌륭하지만, 프로젝트 요구사항에 따라 이 파일을 수정하게 될 것입니다. 저의 경우, 기본적으로 모든 브라우저에서 테스트를 실행하되, 특정 환경에서는 Chromium만 사용하도록 프로파일을 추가하여 활용하기도 했습니다.

첫 테스트 작성하기

기본으로 생성된 example.spec.ts 파일을 열어보면 간단한 테스트 코드가 작성되어 있습니다. 이 코드를 조금 수정하여 실제 웹 페이지로 이동하고 요소를 확인하는 테스트를 작성해 보겠습니다. 가상의 로그인 페이지를 가정하고, 아이디와 비밀번호를 입력 후 로그인 버튼을 클릭하는 시나리오입니다.


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

test('사용자는 로그인 페이지에서 성공적으로 로그인할 수 있다', async ({ page }) => {
  // 1. 로그인 페이지로 이동
  await page.goto('https://www.example.com/login'); // 실제 로그인 페이지 URL로 변경하세요.
  
  // 2. 페이지 제목 확인 (선택 사항)
  await expect(page).toHaveTitle(/로그인/);

  // 3. 아이디 입력 필드에 값 입력
  await page.fill('input[name="username"]', 'testuser');

  // 4. 비밀번호 입력 필드에 값 입력
  await page.fill('input[name="password"]', 'testpassword');

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

  // 6. 로그인 성공 후 리다이렉션된 페이지 확인
  // 예를 들어, 대시보드 페이지로 이동했는지 확인
  await expect(page).toHaveURL(/dashboard/);
  await expect(page.locator('h1')).toContainText('환영합니다, testuser님!');
});

test('잘못된 자격 증명으로 로그인 시도 시 오류 메시지가 표시된다', async ({ page }) => {
  await page.goto('https://www.example.com/login');
  await page.fill('input[name="username"]', 'wronguser');
  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')).toContainText('아이디 또는 비밀번호가 올바르지 않습니다.');
});

위 코드는 page.goto()로 특정 URL에 접속하고, page.fill()로 입력 필드에 값을 채우며, page.click()으로 버튼을 클릭하는 등 기본적인 상호작용을 보여줍니다. expect() 함수를 통해 페이지의 제목, URL, 특정 요소의 가시성 및 텍스트 내용을 검증합니다. Playwright는 CSS 셀렉터뿐만 아니라 텍스트 기반, 역할 기반 등 다양한 로케이터 전략을 제공하여 요소를 쉽게 찾을 수 있도록 돕습니다.

테스트 실행하기

작성한 테스트는 다음 명령어로 실행할 수 있습니다.


npx playwright test

모든 브라우저에서 테스트를 실행하며, 기본적으로 headless 모드로 실행됩니다. 특정 브라우저에서만 실행하고 싶다면 --project 옵션을 사용합니다.


npx playwright test --project=chromium

UI 모드로 테스트를 실행하면 브라우저가 열리면서 테스트 과정을 눈으로 직접 확인할 수 있습니다. 디버깅에 매우 유용합니다.


npx playwright test --ui

이처럼 Playwright는 설치부터 테스트 작성, 실행까지 매우 직관적이고 사용자 친화적인 경험을 제공합니다. 제가 직접 사용해보면서 가장 인상 깊었던 점은 테스트 작성의 용이성과 강력한 디버깅 도구였습니다.

Page Object Model (POM) 패턴으로 테스트 코드 구조화

E2E 테스트 코드가 늘어나고 복잡해지면서 가장 먼저 직면하는 문제는 유지보수성입니다. 만약 웹 페이지의 요소 셀렉터가 변경되면, 해당 요소를 사용하는 모든 테스트 코드를 일일이 찾아 수정해야 하는 번거로움이 발생합니다. 이런 문제를 해결하기 위한 효과적인 방법 중 하나가 바로 Page Object Model (POM) 패턴입니다.

POM의 장점과 구현

Page Object Model은 웹 페이지의 요소(element)와 해당 요소에 대한 상호작용(action)을 추상화하여 별도의 클래스로 분리하는 디자인 패턴입니다. 각 페이지는 하나의 Page Object 클래스에 매핑되며, 이 클래스는 해당 페이지의 UI 요소에 접근하는 메서드와 페이지에서 수행할 수 있는 동작을 포함합니다.

POM을 적용하면 다음과 같은 장점을 얻을 수 있습니다.

  • 코드 재사용성: 동일한 페이지의 요소를 여러 테스트에서 재사용할 수 있습니다.
  • 유지보수성 향상: UI 변경이 발생해도 Page Object 클래스만 수정하면 되므로, 테스트 코드 전체를 수정할 필요가 없어 유지보수 비용이 크게 절감됩니다.
  • 가독성 향상: 테스트 코드가 실제 사용자 시나리오에 더 가깝게 작성되어 가독성이 좋아집니다.
  • 테스트 안정성: 셀렉터 변경으로 인한 Flaky test 발생 가능성을 줄여줍니다.

POM 구현 예시: 로그인 페이지

이전에 작성했던 로그인 테스트를 POM 패턴으로 리팩토링해보겠습니다. 먼저 pageObjects 폴더를 만들고 그 안에 LoginPage.ts 파일을 생성합니다.


// pageObjects/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('https://www.example.com/login');
  }

  async login(username: string, password: string) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }

  async getErrorMessageText() {
    return await this.errorMessage.textContent();
  }
}

이제 tests/login.spec.ts 파일을 다음과 같이 수정합니다.


// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pageObjects/LoginPage'; // LoginPage 임포트

test('사용자는 로그인 페이지에서 성공적으로 로그인할 수 있다 (POM)', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  
  await expect(page).toHaveTitle(/로그인/);

  await loginPage.login('testuser', 'testpassword');

  await expect(page).toHaveURL(/dashboard/);
  await expect(page.locator('h1')).toContainText('환영합니다, testuser님!');
});

test('잘못된 자격 증명으로 로그인 시도 시 오류 메시지가 표시된다 (POM)', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('wronguser', 'wrongpassword');

  await expect(loginPage.errorMessage).toBeVisible();
  await expect(loginPage.errorMessage).toContainText('아이디 또는 비밀번호가 올바르지 않습니다.');
});

보시는 바와 같이, 테스트 코드가 훨씬 간결해지고 비즈니스 로직에 집중하게 됩니다. 요소 셀렉터가 변경되더라도 LoginPage.ts 파일만 수정하면 되므로, 유지보수가 훨씬 쉬워집니다. 저의 실무 경험상, 프로젝트 규모가 커질수록 POM 패턴의 진가가 발휘됩니다. 초기에는 다소 번거롭게 느껴질 수 있지만, 장기적으로는 개발 시간과 비용을 크게 절감해주는 투자라고 확신합니다.

CI/CD 파이프라인에 Playwright 테스트 연동하기

E2E 테스트의 진정한 가치는 CI/CD(Continuous Integration/Continuous Deployment) 파이프라인에 통합될 때 빛을 발합니다. 개발자가 코드를 푸시할 때마다 자동으로 테스트가 실행되고, 문제가 발견되면 즉시 피드백을 받을 수 있다면, 버그가 프로덕션 환경에 배포되는 것을 효과적으로 방지할 수 있습니다. 여기서는 GitHub Actions를 사용하여 Playwright 테스트를 CI/CD 파이프라인에 연동하는 방법을 설명하겠습니다.

GitHub Actions 연동 예시

Playwright를 처음 설치할 때 "Add a GitHub Actions workflow?" 질문에 'Yes'를 선택했다면, 이미 .github/workflows/playwright.yml 파일이 생성되어 있을 것입니다. 이 파일은 GitHub Actions 워크플로우를 정의하는 YAML 파일입니다. 기본 생성된 파일을 바탕으로 좀 더 실용적인 구성을 살펴보겠습니다.


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

on:
  push:
    branches: [ main, master, develop ] # main, master, develop 브랜치에 푸시될 때 실행
  pull_request:
    branches: [ main, master, develop ] # main, master, develop 브랜치로 PR 생성/업데이트 시 실행

jobs:
  test:
    timeout-minutes: 60 # 최대 60분까지 실행
    runs-on: ubuntu-latest # 최신 Ubuntu 환경에서 실행

    steps:
    - uses: actions/checkout@v4 # 저장소 코드 체크아웃
    - uses: actions/setup-node@v4
      with:
        node-version: '20' # Node.js 버전 20 사용
    - name: Install dependencies
      run: npm ci # package-lock.json에 기반하여 의존성 설치
    - name: Install Playwright browsers
      run: npx playwright install --with-deps # Playwright 브라우저 설치 (의존성 포함)
    - name: Run Playwright tests
      run: npx playwright test # Playwright 테스트 실행
    - uses: actions/upload-artifact@v4 # 테스트 결과(스크린샷, 비디오, 트레이스) 아티팩트로 업로드
      if: always()
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30 # 30일간 아티팩트 보관

이 워크플로우는 main, master, develop 브랜지에 코드가 푸시되거나 해당 브랜치로 Pull Request가 생성/업데이트될 때 자동으로 실행됩니다. 각 단계의 역할은 다음과 같습니다:

  • Checkout: GitHub 저장소의 코드를 워크플로우 실행 환경으로 가져옵니다.
  • Setup Node.js: 테스트 실행에 필요한 Node.js 환경을 설정합니다.
  • Install dependencies: package.json에 명시된 프로젝트 의존성을 설치합니다. npm cinpm install보다 CI 환경에서 더 안정적인 설치를 보장합니다.
  • Install Playwright browsers: Playwright 테스트에 필요한 Chromium, Firefox, WebKit 브라우저를 설치합니다. --with-deps 옵션은 브라우저 실행에 필요한 시스템 의존성까지 설치해줍니다.
  • Run Playwright tests: npx playwright test 명령어를 실행하여 모든 E2E 테스트를 수행합니다.
  • Upload Playwright report: 테스트가 실패하더라도 항상 실행되어, Playwright가 생성하는 테스트 결과 보고서(HTML 리포트, 스크린샷, 비디오, 트레이스 등)를 GitHub Actions의 아티팩트로 업로드합니다. 이렇게 하면 나중에 실패한 테스트의 원인을 파악하기 위해 다운로드하여 확인할 수 있습니다. 저의 경험상 이 아티팩트 업로드 기능은 디버깅 시간을 획기적으로 줄여주었습니다.

이 워크플로우를 사용하면 코드 변경이 있을 때마다 자동으로 E2E 테스트가 실행되므로, 개발자는 코드가 배포되기 전에 잠재적인 문제를 파악하고 수정할 수 있습니다. 이는 지속적 통합(CI)의 핵심 원칙 중 하나이며, 개발 프로세스의 안정성과 효율성을 크게 높여줍니다.

테스트 리포트 자동화 및 시각화 전략

테스트가 성공적으로 실행되는 것도 중요하지만, 그 결과를 효과적으로 보고 이해하는 것 또한 매우 중요합니다. 특히 CI/CD 환경에서 수많은 테스트가 실행될 때, 어떤 테스트가 실패했는지, 왜 실패했는지, 그리고 어떤 부분이 문제가 되는지를 시각적으로 파악할 수 있는 테스트 리포트는 필수적입니다. Playwright는 기본적으로 몇 가지 리포트 기능을 제공하지만, 여기서는 더욱 강력하고 시각적인 Allure Report를 연동하는 방법을 소개하겠습니다.

Allure Report 연동

Allure Report는 다양한 테스트 프레임워크와 연동하여 풍부하고 인터랙티브한 테스트 리포트를 생성해주는 오픈 소스 프레임워크입니다. 테스트 결과의 요약, 상세 로그, 스크린샷, 스텝별 실행 시간 등 다양한 정보를 제공하여 테스트 분석 및 디버깅에 큰 도움을 줍니다.

1. Allure Playwright Reporter 설치

먼저 프로젝트에 Allure Playwright Reporter를 설치합니다.


npm install -D allure-playwright

2. Playwright 설정 파일에 Allure Reporter 추가

playwright.config.ts 파일을 열어 reporter 속성에 ['allure-playwright']를 추가합니다. 기존에 다른 리포터가 있다면 배열 안에 함께 추가할 수 있습니다.


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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html', // 기본 HTML 리포터
  // reporter: 'list', // 콘솔에 결과 출력
  // Allure Report를 추가합니다.
  reporter: [['html'], ['allure-playwright']], // 여러 리포터 사용 가능

  use: {
    trace: 'on-first-retry',
  },

  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

이렇게 설정하면 Playwright 테스트 실행 시 Allure 결과 파일이 allure-results 폴더에 생성됩니다.

3. Allure Report 생성 및 확인

테스트를 실행한 후, Allure Report를 생성하고 웹 브라우저로 확인합니다. Allure Commandline 도구가 필요합니다. Java가 설치되어 있어야 합니다.


# 테스트 실행 (allure-results 폴더에 결과 파일 생성)
npx playwright test

# Allure Report 생성 및 로컬 서버로 오픈
npx allure generate allure-results --clean
npx allure open

allure generate 명령은 allure-results 폴더의 데이터를 기반으로 HTML 리포트를 생성하고, allure open은 이 리포트를 웹 브라우저로 열어줍니다. 리포트에는 테스트 케이스별 통과/실패 여부, 실행 시간, 상세 로그, 스크린샷, 심지어 테스트 중 발생한 비디오까지 포함되어 있습니다. 이는 테스트 실패 원인 분석에 엄청난 도움을 줍니다. 저도 복잡한 플래키 테스트를 디버깅할 때 Allure Report의 스텝별 로그와 스크린샷, 비디오를 통해 문제의 핵심을 파악할 수 있었습니다.

4. CI/CD에 Allure Report 연동

GitHub Actions 워크플로우에 Allure Report 생성 단계를 추가하여, 테스트가 완료되면 자동으로 리포트가 생성되고 아티팩트로 업로드되도록 할 수 있습니다. 이를 위해서는 Allure Commandline 도구를 설치하는 단계가 필요합니다.


# .github/workflows/playwright-allure.yml (기존 파일에 아래 내용 추가 또는 새 파일 생성)
name: Playwright Tests with Allure Report

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

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: '20'
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright browsers
      run: npx playwright install --with-deps
    - name: Run Playwright tests
      run: npx playwright test # allure-results 폴더에 결과 파일이 생성됩니다.
    
    # Allure Report 관련 스텝 추가
    - name: Get Allure history
      uses: actions/checkout@v4
      if: always()
      continue-on-error: true
      with:
        ref: allure-history # allure-history 브랜치에서 이전 리포트 가져오기
        path: allure-history
    
    - name: Generate Allure Report
      uses: simple-elf/allure-report-action@v1.9 # Allure Report 생성 액션
      if: always()
      id: allure-report
      with:
        allure_results: allure-results
        gh_pages: gh-pages
        allure_history: allure-history
        
    - name: Deploy Allure Report to GitHub Pages
      if: always()
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: allure-history # 이전 리포트와 병합하여 배포
        force_orphan: true

위 예시는 Allure Report를 GitHub Pages에 배포하여 웹으로 접근 가능하게 하는 복잡한 설정입니다. 간단히 아티팩트로만 업로드하려면 actions/upload-artifact 액션을 사용하여 allure-results와 생성된 allure-report 폴더를 업로드하면 됩니다. 저의 경우, CI 환경에서 생성된 리포트를 S3 버킷에 업로드하여 팀원들이 언제든 접근할 수 있도록 구성했습니다. 이를 통해 테스트 결과의 투명성을 확보하고, 개발팀과 QA팀 간의 원활한 소통을 도모했습니다.

실제 프로젝트에 Playwright 적용하며 얻은 교훈

Playwright를 실제 프로젝트에 도입하고 운영하면서 다양한 경험을 했습니다. 그중에서도 특히 중요하다고 생각하는 몇 가지 교훈과 팁을 공유하고자 합니다.

Flaky Test와의 전쟁: 안정성 확보 전략

자동화 테스트의 가장 큰 적은 바로 Flaky test, 즉 '간헐적으로 실패하는 테스트'입니다. 코드 변경 없이도 어떤 때는 성공하고 어떤 때는 실패하는 테스트는 신뢰도를 떨어뜨리고 개발자의 시간을 낭비하게 만듭니다. Playwright는 기본적으로 Auto-waiting 기능을 제공하여 많은 Flaky test를 방지하지만, 여전히 발생할 수 있습니다.

  • 명시적인 대기: Playwright의 expect().toBeVisible(), expect().toBeEnabled() 등은 자동으로 요소를 기다려주지만, 때로는 특정 API 호출 완료나 애니메이션 종료 등 비동기적인 작업 완료를 명시적으로 기다려야 할 때가 있습니다. 이럴 때는 page.waitForResponse(), page.waitForTimeout() (최후의 수단), 또는 특정 UI 상태 변화를 기다리는 로직을 추가했습니다.
  • 테스트 재시도(Retries): playwright.config.ts에서 retries: 2와 같이 재시도 횟수를 설정할 수 있습니다. CI 환경에서는 2회 정도의 재시도를 설정하여 네트워크 불안정 등 일시적인 문제로 인한 실패를 방지했습니다.
  • 테스트 격리: 각 테스트는 독립적으로 실행되어야 합니다. 이전 테스트의 상태가 다음 테스트에 영향을 주지 않도록, 각 테스트 시작 전 데이터 초기화, 로그인/로그아웃 처리 등을 철저히 수행했습니다. test.beforeEach()를 활용하여 공통 전처리 로직을 구현하는 것이 효과적입니다.
  • 적절한 셀렉터 사용: ID, `data-testid`와 같이 변경될 가능성이 적은 고유한 셀렉터를 사용하는 것이 좋습니다. CSS 클래스나 XPath는 UI 변경에 취약할 수 있습니다.

테스트 데이터 관리

E2E 테스트는 실제 데이터를 기반으로 실행되는 경우가 많습니다. 테스트 데이터 관리는 테스트의 신뢰성과 재현성에 직결됩니다.

  • 테스트 데이터 생성/초기화 스크립트: 각 테스트 스위트가 실행되기 전에 필요한 데이터를 데이터베이스에 직접 생성하거나, Mock API를 호출하여 상태를 초기화하는 스크립트를 작성했습니다.
  • Seed 데이터: 미리 정의된 'seed' 데이터를 사용하여 테스트 환경을 일관되게 유지했습니다.
  • 임시 사용자 생성: 로그인 테스트를 위해 API를 통해 임시 사용자를 생성하고, 테스트 종료 후 해당 사용자를 삭제하는 방식을 사용했습니다. 이는 테스트 간의 의존성을 줄이고 병렬 실행을 용이하게 합니다.

디버깅과 트러블슈팅 팁

Playwright의 강력한 디버깅 도구는 실무에서 매우 유용합니다.

  • Playwright Trace Viewer: 테스트 실패 시 자동으로 생성되는 트레이스 파일을 통해 테스트 실행 과정의 스크린샷, 비디오, 액션 로그, 네트워크 요청 등을 상세하게 확인할 수 있습니다. npx playwright show-trace trace.zip 명령어로 확인할 수 있으며, 문제의 원인을 파악하는 데 가장 큰 도움이 됩니다.
  • Test Inspector & Codegen: npx playwright test --ui 모드에서 제공되는 Test Inspector를 통해 요소를 선택하고 어떤 로케이터가 사용되는지 실시간으로 확인할 수 있습니다. npx playwright codegen은 실제 브라우저에서 사용자가 수행하는 동작을 Playwright 코드로 자동 생성해주어 테스트 코드를 빠르게 작성하는 데 도움을 줍니다.
  • `page.pause()`: 테스트 코드 중간에 await page.pause();를 삽입하면 테스트 실행이 일시 중지되고 Playwright Inspector가 열립니다. 이 상태에서 요소를 검사하거나 콘솔에서 Playwright API를 직접 실행하며 디버깅할 수 있습니다.

이러한 실무 팁들을 적용하면서 Playwright 기반의 E2E 테스트 환경은 단순한 도구를 넘어, 저희 팀의 개발 생산성과 제품 품질을 향상시키는 핵심 요소가 되었습니다. 초기 도입에 드는 노력 이상의 가치를 충분히 제공한다고 확신합니다.

결론 및 추가 고려사항

지금까지 Playwright를 활용한 웹 애플리케이션 E2E 테스트 환경 구축의 전 과정을 살펴보았습니다. Playwright의 강력한 기능과 CI/CD 연동, 그리고 시각적인 리포트 자동화는 웹 애플리케이션의 품질을 향상시키고 개발 생산성을 극대화하는 데 필수적인 요소임을 직접 경험하며 깨달았습니다.

단순히 테스트 코드를 작성하는 것을 넘어, Page Object Model을 통해 테스트 코드를 체계적으로 관리하고, GitHub Actions와 같은 CI/CD 도구에 연동하여 자동화된 검증 프로세스를 구축하며, Allure Report로 테스트 결과를 명확하게 시각화하는 것은 프로젝트의 성공에 결정적인 역할을 합니다. 특히 실무에서 발생할 수 있는 Flaky test 문제를 해결하고 효율적인 디버깅 전략을 수립하는 것은 테스트 환경의 신뢰성을 확보하는 데 매우 중요합니다.

물론, E2E 테스트가 모든 것을 해결해 주지는 않습니다. 유닛 테스트, 통합 테스트와 함께 적절한 테스트 피라미드 전략을 수립하여 각 테스트 레벨이 담당하는 영역을 명확히 하는 것이 중요합니다. 하지만 사용자 경험과 가장 밀접하게 연결된 E2E 테스트는 배포 전 최종 방어선으로서 그 가치가 매우 높습니다.

여러분의 프로젝트에도 Playwright를 도입하여 더욱 안정적이고 효율적인 개발 환경을 구축해보시길 강력히 추천합니다. 처음에는 다소 어렵게 느껴질 수 있지만, 한번 구축하고 나면 얻게 될 이점은 상상 이상일 것입니다. 이 글이 여러분의 E2E 테스트 환경 구축 여정에 작은 도움이 되었기를 바랍니다.

혹시 Playwright를 사용하면서 겪었던 특별한 경험이나, 더 좋은 설정 방법, 또는 궁금한 점이 있다면 댓글로 남겨주세요. 함께 고민하고 발전시켜 나갈 수 있기를 기대합니다!

📌 함께 읽으면 좋은 글

  • [AI 머신러닝] 도메인 특화 LLM 구축: 공개 모델 미세 조정을 통한 성능 최적화 전략
  • [기술 리뷰] Vite와 Webpack: 현대 자바스크립트 번들러의 성능 및 개발 경험 비교 분석
  • [커리어 취업] 개발자 연봉 협상 필승 전략: 나만의 가치를 증명하고 원하는 보상 얻기

이 글이 도움이 되셨다면 공감(♥)댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.