Playwright BrowserContext: 무엇이며, 왜 중요한가, 그리고 어떻게 구성할까
Source: Dev.to
Playwright를 한동안 사용해 왔다면 BrowserContext를 확실히 사용해 본 적이 있을 겁니다—비록 그것을 완전히 인식하지 못했더라도 말이죠. 이는 테스트 환경을 조용히 형성하는 핵심 개념 중 하나입니다:
- 테스트 격리
- 속도 및 불안정성 감소
- 인증 상태 관리
- 병렬 실행
- 테스트 스위트 전체의 건전성
이 글은 실무에 바로 적용할 수 있는 심층 탐구를 제공합니다:
BrowserContext가 실제로 무엇이며 (왜 존재하는지)- Playwright가 컨텍스트를 어떻게 생성하고 관리하는지
playwright.config.ts에서 컨텍스트를 전역적으로 설정하는 방법- 상태를 공유하지 않으면서 테스트 간 설정을 공유하는 방법
- 여러 설정 파일 / 프로젝트를 언제, 어떻게 사용할지
- 흔히 저지르는 안티패턴과 실제 사용 사례
Audience: Playwright 기본을 이미 알고 있으며 테스트 아키텍처를 한 단계 끌어올리고 싶은 엔지니어들을 위한 내용입니다.
BrowserContext란 무엇인가?
A BrowserContext는 격리된 브라우저 프로필입니다.
One Browser → the actual Chrome / Firefox / WebKit instance
Multiple BrowserContexts → separate incognito‑like sessions
각 컨텍스트는 자체적으로 다음을 가집니다:
| 기능 | 설명 |
|---|---|
| Cookies | 컨텍스트에만 국한됨 |
| LocalStorage / SessionStorage | 컨텍스트별로 격리됨 |
| IndexedDB | 별도 저장소 |
| Cache | 공유되지 않음 |
| Permissions | 컨텍스트별 지정 |
| Auth state | 독립적임 |
모든 컨텍스트는 동일한 browser process를 공유하므로 빠르고 비용 효율적입니다.
두 개의 시크릿(incognito) 창을 나란히 열어본 적이 있다면, 본질적으로 두 개의 브라우저 컨텍스트를 사용한 것입니다.
Playwright의 기본 격리
test('example', async ({ page }) => {
// 이 페이지는 새 브라우저 컨텍스트에서 실행됩니다.
});
내부 구조
- Playwright가 브라우저를 실행합니다.
- 새로운 브라우저 컨텍스트를 생성합니다.
- 해당 컨텍스트 안에 페이지를 생성합니다.
- 테스트가 끝나면 컨텍스트를 파괴합니다.
장점
- 테스트 간 상태 누수가 없습니다
- 안전한 병렬 실행
- 예측 가능한 실패
남은 쿠키 때문에 Selenium 테스트가 불안정했던 경험이 있다면, Playwright가 훨씬 더 편리하게 느껴지는 이유가 바로 이것입니다.
참고: 컨텍스트를 직접 생성할 일은 거의 없지만, 방법을 알아두면 유용합니다:
import { chromium } from '@playwright/test';
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
Playwright의 테스트 러너는 별도로 지정하지 않는 한 모든 테스트마다 이 과정을 자동으로 수행합니다.
핵심 요점: 페이지는 BrowserContext 없이는 존재하지 않습니다.
Global Configuration – playwright.config.ts
Most context configuration happens via the use block.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: 'https://my-app.com',
headless: true,
viewport: { width: 1280, height: 720 },
locale: 'en-US',
timezoneId: 'America/New_York',
},
});
대부분의 컨텍스트 설정은 use 블록을 통해 이루어집니다.
Everything inside use becomes the default BrowserContext options.
use 안의 모든 항목은 기본 BrowserContext 옵션이 됩니다.
Thus every test gets the same viewport, locale, timezone… but still not the same state.
따라서 모든 테스트가 동일한 뷰포트, 로케일, 타임존을 갖게 되지만… 여전히 동일한 상태는 아닙니다.
킬러 기능: storageState
테스트마다 로그인하는 것은:
- 느림
- 깨지기 쉬움
- 중복됨
재사용 가능한 인증 스냅샷을 만드는 전역 설정
// global-setup.ts
import { chromium } from '@playwright/test';
export default async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://my-app.com/login');
await page.fill('#email', 'user@test.com');
await page.fill('#password', 'password');
await page.click('button[type=submit]');
// 인증된 상태를 파일에 저장
await context.storageState({ path: 'auth.json' });
await browser.close();
};
// playwright.config.ts (continued)
export default defineConfig({
globalSetup: require.resolve('./global-setup.ts'),
use: {
storageState: 'auth.json',
},
});
이제 모든 테스트:
- 로그인된 상태로 시작
- 여전히 새로운 브라우저 컨텍스트에서 실행
이것이 편리함과 격리를 동시에 제공합니다.
일반적인 안티패턴: 라이브 페이지 공유
let sharedPage;
beforeAll(async ({ browser }) => {
const context = await browser.newContext();
sharedPage = await context.newPage();
});
왜 나쁜가
- 테스트 격리를 깨뜨림
- 병렬 실행을 방해함
- 순서에 의존적인 실패를 초래함
권장 접근 방식 – 픽스처
// fixtures.ts
import { test as base } from '@playwright/test';
export const test = base.extend({
authenticatedPage: async ({ page }, use) => {
await page.goto('/dashboard');
await use(page);
},
});
각 테스트는 여전히 다음을 가짐:
- 자체 컨텍스트
- 자체 페이지
하지만 설정 로직은 깔끔하게 공유됩니다.
다중 사용자 / 역할
때때로 하나 이상의 인증된 사용자가 필요합니다(예: 채팅 앱, 관리자 흐름).
test('admin invites user', async ({ browser }) => {
const adminContext = await browser.newContext({ storageState: 'admin.json' });
const userContext = await browser.newContext({ storageState: 'user.json' });
const adminPage = await adminContext.newPage();
const userPage = await userContext.newPage();
await adminPage.goto('/admin');
await userPage.goto('/dashboard');
});
역할 기반 테스트를 위한 프로젝트 사용
// playwright.config.ts (continued)
export default defineConfig({
projects: [
{
name: 'guest',
use: { storageState: undefined },
},
{
name: 'user',
use: { storageState: 'auth.json' },
},
{
name: 'admin',
use: { storageState: 'admin.json' },
},
],
});
- 각 프로젝트는 동일한 테스트를 실행합니다.
- 서로 다른 브라우저 컨텍스트를 생성합니다.
- 병렬로 실행할 수 있습니다.
특정 프로젝트 실행:
npx playwright test --project=admin
공유해야 할 것 / 공유하면 안 되는 것
| ✅ 공유해도 되는 것 | ❌ 절대로 공유하면 안 되는 것 |
|---|---|
Config (use) | 실시간 페이지 |
Storage snapshots (storageState) | 실시간 컨텍스트 |
| Fixtures & helpers | 변경 가능한 전역 상태 |
Remember: 하나의 테스트 = 하나의 브라우저 컨텍스트 (명시적으로 더 만들지 않는 한). 이 규칙은 Playwright가 확장되는 이유, 병렬 실행이 작동하는 이유, 그리고 테스트가 상태를 누수하지 않는 이유를 설명합니다.
불안정한 테스트를 위한 빠른 체크리스트
| ❌ 나쁜 패턴 | ✅ 해결책 |
|---|---|
beforeAll가 페이지를 생성함 | Playwright가 테스트마다 새로운 컨텍스트를 생성하도록 함 |
| 페이지/컨텍스트를 전역 변수에 보관 | 픽스처 또는 storageState 사용 |
| 테스트가 이전 네비게이션에 의존 | 각 테스트를 독립적으로 유지 |
| UI 로그인 반복 | 전용 인증 스냅샷(storageState) 사용 |
TL;DR
- BrowserContext = 격리된 프로필 (쿠키, 스토리지, 인증 등)
- Playwright는 기본적으로 각 테스트마다 새로운 컨텍스트를 생성합니다.
- 기본값은
playwright.config.ts→use에서 설정합니다. - global setup +
storageState를 사용해 반복 로그인을 피합니다. - 라이브 페이지/컨텍스트를 절대 공유하지 말고, 불변 데이터(설정, 스냅샷, 픽스처)만 공유합니다.
- projects 를 활용해 역할 기반 또는 디바이스 기반 테스트를 수행합니다.
테스트 즐겁게 하세요! 🚀
Playwright에서 브라우저 컨텍스트 관리
beforeAll – 편리하지만 위험
// Example (do **not** use this pattern for shared state)
beforeAll(async () => {
// …
});
beforeAll을 사용하면 편리해 보이지만, 다음을 깨뜨립니다:
- 병렬 실행 – 동시에 실행되는 테스트가 서로 간섭할 수 있습니다.
- 격리 보장 – 공유된 상태가 테스트 사이에 새어나갑니다.
- 디버깅 가능성 – 실패 원인을 특정 테스트로 추적하기 어려워집니다.
해결책: 픽스처를 이용한 테스트당 설정을 선호하세요. 정말 한 번만 실행해야 하는 경우라면 공유 브라우저 상태를 만들지 않도록 해야 합니다.
안전하게 설정을 공유하는 방법
- Playwright 설정 옵션(
use,projects,fixtures)은 안전하게 공유할 수 있습니다. - 브라우저 컨텍스트, 페이지, 가변 전역 변수는 테스트 간에 공유하면 안전하지 않습니다.
테스트를 함께 실행했을 때만 실패가 나타난다면, 공유된 브라우저 상태가 원인인 경우가 대부분입니다.
다중 사용자 흐름: 흔한 함정
일부 팀은 복잡한 다중 사용자 시나리오를 단일 페이지에 강제로 넣으려 합니다. 이로 인해:
- 읽기 어려운 테스트
- 실제 동작 대신 가짜 목(mock) 사용
- 놓치는 버그
해결책: 각 사용자를 별도의 브라우저 컨텍스트(또는 별도 페이지)로 모델링하세요.
BrowserContext가 중요한 이유
BrowserContext는 Playwright의 가장 중요한 아키텍처 개념 중 하나입니다.
컨텍스트가 어떻게 동작하는지 이해하면 테스트 스위트가 자연스럽게 다음과 같이 됩니다:
- 더 빠름 – 컨텍스트를 병렬로 생성하고 효율적으로 재사용할 수 있습니다.
- 더 안정적 – 각 테스트가 격리된 환경에서 실행됩니다.
- 확장하기 쉬움 – 새로운 시나리오를 추가할 때 얽힌 상태를 관리할 필요가 없습니다.
- 이해하기 쉬움 – 각 사용자의 흐름이 명확히 드러납니다.
요약
Playwright 테스트가 깨지기 쉽거나 유지보수가 어려운 경우, 근본 원인은 브라우저 컨텍스트 관리 방식에 있습니다.
- 격리된 컨텍스트를 중심으로 설계하세요.
- 픽스처를 사용해 테스트당 설정을 수행하세요.
beforeAll은 정말 전역적이고 불변인 설정에만 제한적으로 사용하세요.
컨텍스트 처리를 올바르게 하면 나머지 Playwright는 수월하게 느껴집니다.
테스트 즐겁게 하세요!