Playwright가 필요한 이유, 사이드 이펙트

웃음빵·2025년 10월 29일
post-thumbnail

왜 테스트는 통과했는데 서비스는 터질까?

테스트를 모두 완료했는데 실제 사용자 환경에서 에러가 나나면, 그건 사이드 이펙트(Side Effect) 때문일지도 모릅니다.


사이드 이펙트(Side Effect)란?

“겉보기에는 한 일만 한 것 같지만, 몰래 다른 일까지 벌이는 코드”
즉, 의도한 결과 외에 부수적인 영향(side effect) 을 미치는 행위로
함수가 입력값만 사용해야 하는데 외부에 영향을 주면, 그게 바로 사이드 이펙트입니다.


function add(a, b) {
  return a + b;
}

입력(a,b)이 같으면 항상 같은 결과를 주고, 외부에는 아무 영향도 안줍니다.
→ 순수 함수(pure function)


function addAndLog(a, b) {
  console.log(a + b); // 콘솔에 로그 출력
  return a + b;
}

단순히 더하기 결과를 반환하는게 아니라 외부 환경(콘솔, 로그 시스템)에 영향을 줍니다.
→ 사이드 이펙트 존재


실제 웹에서 흔한 사이드 이펙트 예시

// 1️⃣ 브라우저 전체 상태 변경
localStorage.setItem('user', ...)

// 2️⃣ 외부 서버 호출
fetch('/api/user')

// 3️⃣ DOM 직접 조작
document.querySelector('#id').click()

// 4️⃣ 다른 프레임과 통신
window.postMessage(...)

이런건 다른 시스템, 환경, 시간에 따라 결과가 달라질 수 있어서 테스트할 때 "예측 불가능성"이 생깁니다.

  • 예를 들어, localStorage에 이전 테스트의 값이 남아 있으면, 다음 테스트에서 결과가 달라질 수 있고
  • 로그 전송이 늦게 끝나면 버튼이 늦게 활성화되어 테스트가 실패할 수도 있습니다.

즉, 사이드 이펙트는 테스트의 불안정성을 만드는 주범 ☠️



E2E 테스트란?

End-to-End, 실제 사용자의 여정을 그대로 재현하는 테스트

1️⃣ 로그인 버튼 클릭 → 2️⃣ 서버 요청 → 3️⃣ 홈 이동 → 4️⃣ 화면 확인 ✅

E2E 테스트는 바로 이런 사이드 이펙트가 실제 서비스에서 문제를 일으키는지를 직접 검증해줍니다.

API 요청이 실제로 잘 가는지?
로그가 중복으로 전송되는지?
...

E2E 테스트 도구로는

  • Playwright
  • Cypress
  • Puppeteer
  • ...

E2E 테스트는 사이드 이펙트를 없애기보다, 그것을 통제하면서 신뢰할 수 있게 만드는 과정

이라고 생각하면 좋을 것 같습니다.



Playwright

Playwright는 브라우저를 자동으로 조작해 테스트를 수행하는 도구입니다.
즉, 사람이 실제로 클릭하고 입력하는 과정을 코드로 재현합니다.

쉽게 비유하자면, Playwright는 "자동 클릭 로봇" 🤖

우리가 직접 브라우저에서 하는 행동을 대신 반복해주는 로봇같은 존재


👩 로그인 버튼을 클릭
🤖 page.click('#login')

👩 입력창에 이름 입력
🤖 page.fill('#name', '소영')

👩 로그인 성공 확인
🤖 expect(page.getByText('환영합니다')).toBeVisible()


⚙️ Playwright가 하는 일 한눈에 보기

1️⃣ 브라우저를 자동으로 실행

  • Chrominum (Chrome), Firefox, WebKit(Safari) 지원
  • 실제 브라우저처럼 동작해서 환경 차이가 없음

2️⃣ 사용자 행동 재현

  • 클릭, 입력, 스크롤, 파일 업로드 등 모든 UI 상호작용 가능

3️⃣ 네트워크와 API 감시

  • 요청/응답을 intercept 하거나 mock 가능
  • 사이드 이펙트(예:로그 전송, 비동기 요청)를 제어

4️⃣ 멀티 브라우저·멀티 디바이스 테스트

  • "모바일 환경에서만 깨지네?" → 실제처럼 테스트 가능

5️⃣ 시각적·DOM 기반 검증

  • 화면 요소가 보이는지, 텍스트가 올바른지 자동 확인

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

test('로그인 성공 시 홈 화면으로 이동', async ({ page }) => {
  await page.goto('https://dankkumi.com/login');
  await page.fill('#id', 'jiyul1004');
  await page.fill('#pw', '1234');
  await page.click('button[type=submit]');

  await expect(page.getByText('오늘의 학습')).toBeVisible();
});

🎯 Playwright의 강점 요약

  • 모의(Mock)가 아닌 실제 렌더링 환경
  • 로그 전송, API 호출, 캐시 영향 등을 직접 확인하여 사이드 이펙트 감시 가능
  • 테스트 시나리오를 함수 단위로 정리 가능하여 테스트 구조화 용이
  • 병렬 실행, 자동 대기(auto-waiting)로 불안정성 최소화
  • 실패 시 시각적 기록(스크린샷/비디오)을 남겨 디버깅 용이


Playwright MCP x Cursor

“AI가 테스트를 직접 만든다면?”

상황

  • 개발자가 로그인 로직 변경
  • AI(Cursor)가 git diff 를감지하고 변경 내용을 분석
  • Playwright MCP가 자동으로 테스트 시나이로를 Markdown으로 생성
  • 생성된 테스트를 실제 브라우저에서 실행
  • 실패나 경고가 있으면 AI가 리포트를 정리해 슬랙/이메일로 보고

즉, "코드가 바뀌면 → AI가 테스트를 만들고 → 테스트를 돌리고 → 결과를 요약해주는" 완전 자동 테스트


실제 예시를 보겠습니다.

// 기존
export function login(id, pw) {
  return api.post('/login', { id, pw });
}

// 변경됨
export function login(id, pw) {
  const response = api.post('/login', { id, pw });
  localStorage.setItem('lastLogin', new Date().toISOString()); // 새 기능
  return response;
}

AI(Cursor + MCP) 분석 결과
🔍 감지된 사이드 이펙트:
localStorage에 lastLogin이 추가 저장됨
테스트 필요: “로그인 시 로컬스토리지에 lastLogin이 생성되는지 확인”

AI가 생성한 Markdown 테스트 시나리오

# Login Behavior Test (Auto-generated)

## Scenario: User login stores timestamp in localStorage
- Navigate to `/login`
- Fill ID input with `testuser`
- Fill PW input with `1234`
- Click on `로그인` button
- Wait for page navigation to `/home`
- Assert: `localStorage.getItem('lastLogin')` is not null
- Assert: Timestamp format matches ISO string

MCP가 Playwright 포맷으로 바로 실행 가능한 시나리오를 Markdown으로 구성

자동 생성된 Playwright 테스트 코드 (MCP 변환)

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

test('로그인 시 lastLogin이 저장되어야 한다', async ({ page }) => {
  await page.goto('/login');
  await page.fill('#id', 'testuser');
  await page.fill('#pw', '1234');
  await page.click('button#login');

  await expect(page).toHaveURL('/home');

  const lastLogin = await page.evaluate(() => localStorage.getItem('lastLogin'));
  expect(lastLogin).not.toBeNull();
  expect(lastLogin).toMatch(/^\\d{4}-\\d{2}-\\d{2}T/);
});

AI가 Markdown 기반 시나리오를 Playwright 코드로 변환하고 즉시 실행

테스트 실행 + AI 리포트 결과 (자동 요약)

✅ Test Passed: login page navigation OK
⚠️ Warning: localStorage timestamp value delayed by 400ms
🧠 Suggestion: Add `await page.waitForTimeout(500)` before checking storage

AI MCP가 Playwright 로그를 읽고
테스트 실패 원인을 “비동기 타이밍 이슈”로 분석 후
구체적 수정 제안까지 리포트 💬

Slack / PR 보고 자동 요약 예시

🧪 AI 테스트 결과 요약
• 총 12개 테스트 중 11개 성공, 1개 경고 ⚠️
• 원인: localStorage 비동기 반영 지연 (400ms)
• 추천 수정: await page.waitForTimeout(500) 추가
• 관련 커밋: #3f92d1a auth.ts
⏱ 테스트 실행 시간: 38초


사람이 테스트 코드를 쓰는 시대에서, AI가 테스트를 생성/실행/리포트하는 시대로 진화 중!

profile
good is good

0개의 댓글