시각 회귀 테스트란? 구현 방법 완벽 가이드

Ashley Goolam

Ashley Goolam

24 December 2025

시각 회귀 테스트란? 구현 방법 완벽 가이드

시각적 회귀 테스트는 기존 기능 테스트가 놓치는 버그를 잡아냅니다. 버튼이 여전히 클릭 가능하지만 화면 밖으로 벗어나 있을 수 있습니다. CSS 업데이트로 텍스트가 읽기 어려워질 수 있습니다. 레이아웃 변경으로 모바일 기기에서 반응형 디자인이 깨질 수 있습니다. 시각적 회귀 테스트는 애플리케이션의 스크린샷을 시간에 따라 비교하여 의도하지 않은 시각적 변경 사항이 사용자에게 도달하기 전에 자동으로 감지합니다.

이 가이드는 시각적 회귀 테스트 기법에 대한 실용적인 안내와 Playwright를 사용한 간소화된 구현 방법을 제공하여, 즉시 UI 버그를 찾아낼 수 있도록 돕습니다.

button

시각적 회귀 테스트란 무엇인가요?

시각적 회귀 테스트는 애플리케이션 UI의 스크린샷을 캡처하고 이를 기준 이미지와 비교하는 자동화된 기법입니다. 레이아웃 변경, 색상 변화, 글꼴 문제 또는 깨진 이미지 등으로 인해 새 스크린샷이 기준 이미지와 다르면 테스트는 실패하고 차이점을 강조 표시합니다.

기능 유효성을 검사하는 기존 테스트와 달리, 시각적 회귀 테스트는 모양과 레이아웃의 유효성을 검사합니다. 다음과 같은 질문에 답합니다.

시각적 회귀 테스트가 중요한 이유

다음 시나리오를 고려하면 시각적 회귀 테스트의 중요성이 명확해집니다.

  1. CSS 리팩토링: 중복 스타일을 통합했는데 갑자기 로그인 양식이 iPad 화면에서 푸터와 겹칩니다.
  2. 타사 위젯: 마케팅 팀에서 새로운 분석 스크립트를 추가하여 클릭 유도 버튼이 화면 밖으로 밀려납니다.
  3. 반응형 중단점: 겉보기에 무해한 패딩 변경으로 인해 모바일 내비게이션 메뉴가 깨집니다.
  4. 크로스 브라우저 렌더링: Firefox가 Chrome과 다르게 flexbox를 렌더링하여 레이아웃이 변경됩니다.
  5. API 기반 UI: 백엔드 API가 새 필드를 추가하여 실수로 테이블 열이 확장되어 레이아웃이 깨집니다.

시각적 회귀 테스트가 없으면 이러한 버그는 프로덕션에 도달하여 사용자를 불편하게 만듭니다. 이 테스트를 사용하면 개발 단계에서 버그를 발견하여 수정 비용이 저렴할 때 해결할 수 있습니다.

시각적 회귀 테스트 기법

1. 수동 스크린샷 비교

가장 간단한 접근 방식: 개발자가 변경 전후 스크린샷을 수동으로 캡처하고 육안으로 비교합니다.

장점: 설정 비용 없음
단점: 지루하고 오류 발생 가능성이 높으며 확장성이 떨어짐

2. DOM 비교

픽셀 단위의 완벽한 스크린샷 대신 DOM 구조를 비교하는 도구입니다.

장점: 사소한 픽셀 차이에 덜 민감함
단점: CSS 렌더링 문제를 놓침

3. 픽셀 단위 비교

전체 페이지 스크린샷을 캡처하고 모든 픽셀을 비교하는 도구입니다.

장점: 모든 시각적 변경 사항을 포착함
단점: 동적 콘텐츠(광고, 타임스탬프)로 인한 오탐 발생 가능성

4. 시각적 AI 비교

최신 도구는 AI를 사용하여 노이즈를 무시하면서 의미 있는 변경 사항을 식별합니다.

장점: 스마트 감지, 오탐 감소
단점: 구성 필요, 일부 비용 발생

기법 정확도 유지보수 최적 용도
수동 낮음 높음 일회성 검사
DOM 중간 중간 컴포넌트 테스트
픽셀 높음 중간 정적 페이지
AI 높음 낮음 동적 애플리케이션

실용 가이드: Playwright를 사용한 시각적 회귀 테스트

Playwright를 사용하여 시각적 회귀 테스트의 실제 작동 방식을 보여주는 간단한 프로젝트를 만들어 봅시다.

playwright

1단계 — 프로젝트 생성

Node.js 프로젝트를 초기화합니다:

mkdir visual-regression-demo && cd visual-regression-demo
npm init -y
npm install --save-dev @playwright/test
npx playwright install

다음 구조를 생성합니다:

project/
├── images/
│   ├── pic1.jpg
│   ├── pic2.jpg
│   ├── pic3.jpg
│   └── pic4.jpg
├── tests/
│   └── visual.spec.js
├── index.html
└── package.json

`package.json`에 다음을 추가합니다:

{
  "scripts": {
    "test": "playwright test",
    "test:update": "playwright test --update-snapshots"
  }
}

2단계 — 간단한 HTML 페이지 생성

`index.html`을 생성합니다:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Visual Regression Demo</title>
  <style>
    .gallery { display: flex; flex-wrap: wrap; gap: 10px; }
    .gallery img { width: 200px; height: 200px; object-fit: cover; }
  </style>
</head>
<body>
  <h1>Product Gallery</h1>
  <div class="gallery">
    <img src="images/pic1.jpg" alt="Product 1">
    <img src="images/pic2.jpg" alt="Product 2">
    <img src="images/pic3.jpg" alt="Product 3">
    <img src="images/pic4.jpg" alt="Product 4">
  </div>
</body>
</html>

페이지를 제공합니다: `python -m http.server 8000` 또는 Node.js 정적 서버를 사용합니다.

simple test html page

3단계 — Playwright 시각적 테스트 작성

`tests/visual.spec.js`를 생성합니다:

const { test, expect } = require('@playwright/test');

test.describe('Visual Regression Tests', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('http://localhost:8000');
  });

  test('homepage matches baseline', async ({ page }) => {
    // 전체 페이지 스크린샷 촬영
    await expect(page).toHaveScreenshot('homepage.png', {
      fullPage: true,
      threshold: 0.2, // 미미한 차이 허용
    });
  });

  test('gallery layout is responsive', async ({ page }) => {
    // 모바일 뷰포트 테스트
    await page.setViewportSize({ width: 375, height: 667 });
    await expect(page).toHaveScreenshot('mobile-homepage.png');
  });
});

4단계 — 기준 스냅샷 실행 및 생성

첫 실행 (기준선 생성):

npm run test:update

이렇게 하면 `__snapshots__` 폴더에 `homepage.png`와 `mobile-homepage.png`가 생성됩니다.

snapshots

5단계 — 테스트 성공적으로 실행

이제 테스트를 정상적으로 실행합니다:

npm test

`2 passed` ✅를 확인해야 합니다.

playwright tests

6단계 — 시각적 실패 강제 발생

`index.html`을 편집하고 이미지 하나를 중복시켜 레이아웃을 깨뜨립니다:

<img src="images/img3.webp" alt="Image 3">
<img src="images/img3.webp" alt="Image 4">

이제 테스트 HTML 페이지에는 중복 이미지가 있어야 합니다.

테스트를 다시 실행합니다:

npm test

레이아웃 변경을 보여주는 diff 이미지와 함께 `2 failed`를 보게 될 것입니다.

failed tests

"test-results" 폴더를 통해 변경 사항을 자세히 볼 수 있습니다.

view test results

빨간색으로 강조 표시된 영역은 변경된 영역을 나타냅니다. 테스트 결과는 다음과 같아야 합니다:

test results

완료 후, 변경 사항을 되돌리거나, 변경 사항이 의도적인 경우 아래 명령어를 사용하여 스냅샷을 업데이트할 수 있습니다:

npm run test:update

Apidog가 API 기반 시각적 테스트에 어떻게 도움이 되는가

시각적 회귀 테스트는 근본 원인이 API 문제일 때 종종 실패합니다. 백엔드가 잘못된 형식의 데이터나 올바르지 않은 이미지 URL을 반환하면 UI가 시각적으로 깨질 것입니다. Apidog는 API가 렌더링에 적합한 데이터를 전달하도록 보장합니다.

UI에 영향을 미치는 API 응답 테스트

# 제품 갤러리 API를 위한 Apidog 테스트
테스트: GET /api/products
조건: 카테고리 "featured"로 요청 전송
예측 1: 응답에 4개의 제품 포함
예측 2: 각 제품은 유효한 이미지 URL을 가짐 (200 상태)
예측 3: 이미지 URL은 CDN 도메인을 사용
예측 4: 응답에 깨진 이미지 링크 없음
automatically generate test cases in apidog
button

UI 컴포넌트를 위한 API 스키마 유효성 검사

// Apidog 스키마 유효성 검사를 통해 UI 손상을 방지합니다.
테스트: Product API Schema
예측: 응답이 ProductCard 스키마와 일치
  - id: 문자열 (필수)
  - name: 문자열 (최대 50자)
  - image_url: URL 형식
  - price: 숫자 (양수)

API가 `price`를 숫자가 아닌 문자열로 반환하면 UI가 가격을 올바르게 렌더링하지 못할 수 있습니다. Apidog는 시각적 레이아웃이 깨지기 전에 이를 감지합니다.

UI에 미치는 API 성능 영향 모니터링

테스트: GET /api/products - Performance
조건: 4개의 제품으로 요청
예측 1: 응답 시간 < 500ms
예측 2: 각 이미지 CDN 응답 시간 < 200ms

느린 API는 이미지 로딩을 지연시켜 시각적인 깜박임을 유발합니다. Apidog는 API가 UI를 빠릿하게 유지하는 성능 예산을 충족하도록 보장합니다.

API 계약 테스트는 시각적 회귀를 방지합니다.

API 계약이 변경되면(새 필드, 제거된 필드) Apidog가 이를 플래그합니다:

// Apidog 계약 테스트
테스트: Product API Version 2
예측: 새 필드 "badge"는 선택 사항이며 UI에 영향을 주지 않음

이는 API가 프론트엔드에서 아직 처리하지 않는 필드를 추가할 때 UI에 "undefined" 텍스트가 나타나는 것을 방지합니다.

API 계약 테스트 실용 가이드

시각적 회귀 테스트 모범 사례

  1. 안정적인 테스트 환경: 일관된 데이터를 사용하고 애니메이션을 비활성화합니다.
  2. 임계값 조정: 오탐을 피하기 위해 픽셀 차이 임계값(0.1-0.3)을 설정합니다.
  3. 대상 지정 테스트: 모든 것을 테스트하기 전에 중요한 페이지(홈페이지, 결제)를 테스트합니다.
  4. 반응형 커버리지: 모바일, 태블릿, 데스크톱 뷰포트를 테스트합니다.
  5. 동적 콘텐츠 마스킹: 타임스탬프, 광고 및 무작위 콘텐츠를 스크린샷에서 숨깁니다.
  6. 의도적인 기준선 업데이트: 스냅샷을 업데이트하기 전에 차이점을 검토합니다.
  7. CI/CD에서 실행: Apidog와 같은 도구를 사용하여 시각적 테스트를 파이프라인에 통합합니다.

자주 묻는 질문

Q1: 시각적 회귀 테스트가 기능 테스트를 대체할 수 있나요?

답변: 아니요. 시각적 회귀 테스트는 기능 테스트를 보완합니다. 레이아웃 문제를 잡아내지만, 기능 테스트는 동작과 API 정확성을 검증합니다. 두 가지를 모두 사용하세요.

Q2: 타임스탬프와 같은 동적 콘텐츠는 어떻게 처리하나요?

답변: Playwright의 `mask` 옵션을 사용하거나 스크린샷을 찍기 전에 동적 콘텐츠를 정적 값으로 대체하세요. Apidog는 API가 테스트를 위해 일관된 데이터를 반환하는지 검증할 수도 있습니다.

Q3: 시각적 테스트는 불안정한가요?

답변: 환경을 제어하지 않으면 불안정할 수 있습니다. 애니메이션을 비활성화하고, 일관된 데이터를 사용하며, 적절한 임계값을 설정하세요. Apidog는 API가 예측 가능한 데이터를 반환하도록 보장하여 도움을 줍니다.

Q4: 모든 페이지를 시각적으로 테스트해야 하나요?

답변: 먼저 중요한 사용자 경로에 집중하세요. 모든 것을 테스트하면 유지보수 오버헤드가 발생합니다. 트래픽이 많은 페이지와 컴포넌트에 우선순위를 두세요.

Q5: 시각적 버그가 CSS 관련인 경우 Apidog는 어떻게 도움이 되나요?

답변: 많은 시각적 버그는 API 데이터 변경(필드 누락, 잘못된 유형)에서 발생합니다. Apidog는 API 스키마와 응답을 검증하여 데이터 관련 시각적 오류가 UI에 나타나기 전에 방지합니다.

결론

시각적 회귀 테스트는 코드 변경으로 인한 의도치 않은 결과에 대비하는 안전망입니다. 기능 테스트가 버튼이 여전히 작동하는지 확인하는 동안, 시각적 테스트는 해당 버튼이 사용자가 예상하는 위치에 여전히 나타나는지 확인합니다. 이러한 차이는 사용자 경험과 브랜드 일관성에 매우 중요합니다.

Playwright 예제는 시각적 테스트가 얼마나 접근하기 쉬워졌는지를 보여줍니다. 몇 줄의 코드만으로 뷰포트 전체의 스크린샷을 캡처하고 비교하여 프로덕션 전에 레이아웃 손상을 감지할 수 있습니다. 핵심은 작게 시작하고, 테스트 환경을 안정화하며, 지속적으로 테스트를 실행하는 것입니다.

최신 애플리케이션은 점점 더 API 기반이 되어가고 있으며, 이는 시각적 버그가 종종 데이터 문제에서 시작된다는 의미입니다. Apidog는 API가 UI가 안정적으로 렌더링할 수 있는 일관되고 올바른 형식의 데이터를 제공하도록 보장하여 전체 그림을 완성합니다. API와 시각적 테스트가 함께 작동할 때, 기능성, 성능, 외관 등 모든 것을 자동으로 검증하는 완벽한 커버리지를 얻을 수 있습니다.

오늘 가장 중요한 사용자 흐름에 시각적 테스트를 추가하여 시작해 보세요. 테스트가 프로덕션에서 당황스러울 수 있었던 레이아웃 손상을 처음으로 잡아낼 때, 당신은 이것 없이 어떻게 소프트웨어를 출시했는지 의아해할 것입니다.

button

Apidog에서 API 설계-첫 번째 연습

API를 더 쉽게 구축하고 사용하는 방법을 발견하세요