TechFeedTechFeed
Frontend

플레이라이트, Next.js, CI | E2E 테스트를 로컬에서 깔고 깃허브 액션에 붙이는 순서

2026년 E2E 테스트 선택 기준부터 Playwright 설치, playwright.config.ts webServer 설정, 첫 테스트 작성, 깃허브 액션 브라우저 캐시 연결까지 Next.js 기준으로 정리한다. 불안정한 테스트가 나오는 waitForTimeout 남용·전역 상태 공유·외부 API 의존 패턴도 포함.

by

E2E 테스트를 처음 붙이는 팀이 2026년에 고르는 도구는 대부분 Playwright입니다. Cypress가 한때 표준처럼 쓰였지만, 멀티 탭·iframe·네트워크 조건 설정에서 Playwright가 분명히 낫습니다. 저는 Next.js 사이트에 Playwright를 붙이고 깃허브 액션에 연결하는 과정에서 막히는 자리를 한 번씩 만났고, 그 케이스만 정리합니다.


Cypress를 이미 쓰고 있다면 교체 이유가 없습니다. 새로 E2E 테스트를 시작한다면 Playwright를 먼저 보라는 게 제 입장입니다. 테스트 러너 비교보다는 실제 설치와 CI 연결 흐름을 중심으로 씁니다.


Playwright와 Cypress, 어디서 차이가 나나

세세한 API 비교보다 실제 프로젝트에서 선택 기준이 되는 항목만 추립니다.


항목PlaywrightCypress
지원 브라우저Chrome, Firefox, WebKit (Safari)Chrome, Firefox, Edge (WebKit 실험적)
멀티 탭 / 멀티 윈도우기본 지원제한적 (우회 필요)
iframe직접 접근 가능동일 도메인만 쉬움
네트워크 가로채기route()로 요청/응답 수정cy.intercept()로 가능
병렬 실행기본 제공 (shard 옵션)유료 Cloud가 있어야 풀 병렬
실행 속도 (100테스트 기준)빠름 (병렬 기본)느림 (병렬 제한)
디버깅 UIVS Code 확장, Trace ViewerCypress App (직관적)
학습 곡선중간낮음 (API가 직관적)

처음 접하는 팀에는 Cypress가 학습 곡선이 낮습니다. 그런데 Safari 호환 테스트나 병렬 실행 비용이 문제가 되는 시점에 Playwright로 넘어오는 경우가 많습니다. 처음부터 Playwright를 쓰면 그 비용을 아낄 수 있습니다.


Playwright 테스트 실행 화면과 브라우저 테스트 결과
Playwright는 Chrome, Firefox, WebKit 세 브라우저를 한 번에 테스트한다

Next.js 프로젝트에 Playwright 설치하기

설치는 단순합니다. 세팅 명령 하나에 브라우저 바이너리 다운로드까지 포함됩니다.


Playwright 설치와 초기 설정
# npm npm init playwright@latest # 또는 pnpm pnpm create playwright # 선택 항목 # ✔ Where to put your end-to-end tests? › tests # ✔ Add a GitHub Actions workflow? › Yes # ✔ Install Playwright browsers? › Yes

init 명령이 playwright.config.ts와 예제 테스트 파일, 깃허브 액션 워크플로 파일을 만들어 줍니다. 브라우저 바이너리는 ~/.cache/ms-playwright에 저장됩니다. CI에서는 이 경로를 캐시에 올려두면 재설치 시간을 아낍니다.


playwright.config.ts — Next.js 로컬 서버 연동
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', use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, { name: 'webkit', use: { ...devices['Desktop Safari'] } }, ], // 테스트 실행 전 Next.js 개발 서버 자동 시작 webServer: { command: 'npm run dev', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, })

webServer 설정이 핵심입니다. 로컬에서는 이미 켜진 서버를 재사용하고(reuseExistingServer: true), CI에서는 새로 시작합니다. 빌드된 결과로 테스트하려면 command: 'npm run build && npm run start'로 바꿉니다.


첫 E2E 테스트 작성과 실행

Playwright의 로케이터 API는 ARIA 역할과 텍스트를 기준으로 요소를 찾습니다. CSS 셀렉터보다 더 사람 읽기 쉽고, 마크업이 바뀌어도 테스트가 덜 깨집니다.


기본 E2E 테스트 예시 (tests/home.spec.ts)
import { test, expect } from '@playwright/test' test('홈 페이지 제목 확인', async ({ page }) => { await page.goto('/') await expect(page).toHaveTitle(/tech.ambitstock/) }) test('검색 기능 동작 확인', async ({ page }) => { await page.goto('/') // 검색창 찾기 const searchInput = page.getByRole('searchbox') await searchInput.fill('Next.js') await searchInput.press('Enter') // 결과 페이지 확인 await expect(page).toHaveURL(/search/) await expect(page.getByText('Next.js')).toBeVisible() }) test('포스트 상세 페이지 OG 메타 확인', async ({ page }) => { await page.goto('/node-type-stripping-erasable-syntax-tsx-2026/') // OG title 메타 태그 확인 const ogTitle = page.locator('meta[property="og:title"]') await expect(ogTitle).toHaveAttribute('content', /타입 스트리핑/) })

실행은 npx playwright test입니다. 특정 파일만 돌리려면 npx playwright test home.spec.ts입니다. UI 모드로 보려면 npx playwright test --ui입니다. 실패한 테스트의 trace를 보려면 npx playwright show-report입니다.


Playwright UI 모드 화면에 테스트 목록과 결과가 표시된 화면
--ui 플래그로 열리는 UI 모드에서 테스트를 단계별로 따라갈 수 있다

깃허브 액션에 Playwright 연결하기

init이 만들어 준 워크플로 파일을 그대로 쓰면 됩니다. 브라우저 캐시만 추가하면 실행 시간을 줄일 수 있습니다.


.github/workflows/playwright.yml
name: Playwright E2E Tests on: push: branches: [main] pull_request: branches: [main] jobs: test: timeout-minutes: 20 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: lts/* cache: 'npm' - name: Install dependencies run: npm ci # 브라우저 바이너리 캐시 - name: Cache Playwright browsers uses: actions/cache@v4 with: path: ~/.cache/ms-playwright key: playwright-${{ hashFiles('package-lock.json') }} - name: Install Playwright Browsers run: npx playwright install --with-deps - name: Run Playwright tests run: npx playwright test # 실패 시 리포트 업로드 - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} with: name: playwright-report path: playwright-report/ retention-days: 7

캐시 키에 package-lock.json 해시를 넣으면 의존성이 바뀔 때만 브라우저를 다시 받습니다. 브라우저 바이너리가 250MB 정도라서 캐시 없이 매번 받으면 CI 시간이 1분 이상 늘어납니다.


병렬 실행이 필요하면 shards 옵션을 씁니다. 공식 문서: playwright.dev/docs/ci-intro


불안정한 테스트(flaky test)가 나오는 패턴

Playwright 테스트가 로컬에서는 통과하고 CI에서 간헐적으로 실패하는 경우가 있습니다. 대부분 세 가지 패턴입니다.


⚠️ page.waitForTimeout() 남용 | await page.waitForTimeout(1000)으로 기다리는 패턴은 CI 서버 속도에 따라 불안정합니다. 대신 await page.waitForSelector('...')await expect(element).toBeVisible()처럼 조건 기반으로 기다립니다. Playwright의 자동 재시도가 30초 타임아웃 안에서 알아서 기다립니다.


⚠️ 전역 상태 공유 | 테스트마다 독립적인 상태를 써야 합니다. 로그인 상태를 테스트 간에 공유하면 실행 순서에 따라 결과가 달라집니다. Playwright의 storageState를 쓰면 로그인 상태를 파일로 저장해서 각 테스트가 깔끔하게 씁니다.


⚠️ 외부 API 의존 | 실제 외부 API를 호출하는 테스트는 네트워크 상태에 따라 불안정합니다. page.route()로 API 응답을 가로채서 목업 데이터를 돌려주는 방식이 안정적입니다. E2E 테스트에서 외부 API를 실제로 호출해야 하는 건 일부 통합 테스트에서만입니다.


깃허브 액션에서 Playwright 테스트가 통과한 CI 화면
CI에서 통과한 Playwright 테스트 결과와 HTML 리포트

자주 묻는 것들

Playwright로 인증이 필요한 페이지를 테스트하려면?

globalSetup에서 로그인 후 storageState를 파일로 저장합니다. 각 테스트는 use: { storageState: 'auth.json' }으로 저장된 세션을 불러와서 씁니다. 테스트마다 로그인을 반복하지 않아도 됩니다.


Next.js App Router와 Playwright가 잘 맞나?

잘 맞습니다. webServernpm run devnpm run start를 연결하면 됩니다. Server Components, Server Actions 모두 브라우저가 렌더한 결과를 테스트하는 거라서 Playwright 입장에서는 다르지 않습니다.


테스트 파일을 어디 두는 게 좋나?

tests/ 폴더에 모아두는 게 일반적입니다. 컴포넌트 단위 테스트와 분리해야 관리가 쉽습니다. playwright.config.tstestDir으로 경로를 지정합니다.


Playwright 컴포넌트 테스트(CT)가 뭔가?

Playwright는 브라우저 E2E 테스트 외에 컴포넌트 단위 테스트도 지원합니다(@playwright/experimental-ct-react). 아직 실험적 단계이고, 컴포넌트 테스트는 Vitest + Testing Library 조합이 더 성숙합니다. E2E는 Playwright, 컴포넌트는 Vitest로 분리하는 게 현재 권장 패턴입니다.


Cypress에서 Playwright로 마이그레이션할 때 얼마나 걸리나?

API 구조가 달라서 테스트 파일을 그대로 옮길 수는 없습니다. 테스트 수와 커스텀 커맨드 복잡도에 따라 다르지만, 중간 규모 프로젝트(50~100개 테스트) 기준으로 2~5일 정도 예상합니다. 공식 마이그레이션 가이드가 있습니다: playwright.dev/docs/migrating-from-cypress


플레이라이트PlaywrightNext.jsE2E 테스트CI깃허브 액션자동화개발자품질QACypress테스트

함께 보면 좋은 문제 해결

EXPLORE / Frontend

이어서 읽어보기

전체 토픽 둘러보기