게임 UI 반응형 자동테스트: 해상도별 스크린샷 수집과 픽셀 비교 구축법

Unity 게임 UI를 다양한 해상도와 화면 비율에서 자동 실행해 스크린샷을 수집하고 기준 이미지와 픽셀 비교하는 테스트 시스템을 구축하는 방법을 정리합니다. 안전영역·글자 잘림·레이아웃 회귀를 배포 전에 찾을 수 있습니다.

✓ 복사 완료!
챕터일반 가이드
난이도중급
상태● 검토 완료
대상 엔진
Engine Agnostic
최종 업데이트
게임 UI 반응형 자동테스트: 해상도별 스크린샷 수집과 픽셀 비교 구축법

게임 UI는 기능 테스트만 통과해도 안전하지 않다. 16:9 개발 화면에서는 정상인 버튼이 19.5:9 모바일에서 안전영역에 가려지거나 번역된 텍스트가 4:3 화면에서 잘릴 수 있다. 해상도별 자동 실행·스크린샷 수집·기준 이미지 비교를 하나의 CI 단계로 묶으면 이런 시각적 회귀를 배포 전에 검출할 수 있다.

왜 게임 UI 반응형 테스트는 별도 자동화가 필요한가?

게임 UI의 실패는 대개 클릭 가능 여부만으로 드러나지 않는다. 자동 UI 테스트가 설정 버튼을 찾아 클릭했다고 해도 버튼의 우측 절반이 노치 영역에 가려져 있거나 글자가 버튼 밖으로 넘쳤다면 플레이어 경험은 이미 손상된 상태다.

특히 다음 변경은 시각적 회귀를 만들기 쉽다.

  • Unity Canvas Scaler의 Match Width Or Height 값 변경
  • RectTransform 앵커 또는 피벗 변경
  • 현지화 문자열 증가와 Dynamic Font 교체
  • iOS Safe Area, Android 펀치홀, 태블릿 비율 대응 누락
  • 주소 지정 가능 에셋 또는 원격 이미지의 로딩 타이밍 변화
  • UI Toolkit USS, Unity UGUI Layout Group, TextMeshPro 자동 크기 조정 변경

반응형 검증의 목표는 모든 픽셀이 항상 동일한지 확인하는 데 있지 않다. 의도한 화면 상태에서 허용 범위를 넘는 시각적 차이가 생겼는지 일관되게 판정하는 데 있다.

해상도 테스트 매트릭스는 어떻게 정할까?

실제 기기 전체를 테스트 대상으로 삼으면 실행 시간이 급격히 늘어난다. 먼저 화면 비율, 최소 해상도, 노치·안전영역 여부를 대표하는 조합을 선정한다.

분류예시 해상도화면 비율주로 찾는 문제
표준 모바일1920×108016:9기본 앵커와 HUD 배치
세로형 모바일1080×240020:9상하 여백, 세로 메뉴, 긴 화면
노치 기기1170×2532약 19.5:9Safe Area 침범, 상단 HUD 겹침
태블릿2048×15364:3좌우 확장, 팝업 폭, 텍스트 줄바꿈
울트라와이드 PC3440×1440약 21.5:9중앙 고정 UI, 배경 확장, 미니맵 위치

UI의 기준 좌표계가 1920×1080일 때 화면 비율은 다음처럼 계산할 수 있다.

aspectRatio=screenWidthscreenHeightaspectRatio = \frac{screenWidth}{screenHeight}

단, 이 비율만으로 충분하지 않다. 같은 19.5:9이라도 안전영역 inset이 다를 수 있으므로 모바일 빌드에서는 Screen.safeArea 값을 별도 테스트 입력으로 취급해야 한다.

여러 화면 비율의 게임 HUD와 안전영역을 비교하는 반응형 UI 테스트 화면

스크린샷 기반 UI 자동테스트 시스템 구성

권장 구조는 게임이 테스트 가능한 화면 상태를 제공하고 외부 러너가 해상도별 캡처와 비교를 담당하는 방식이다.

flowchart LR
    A[CI 파이프라인] --> B[테스트 빌드 실행]
    B --> C[테스트 시나리오 진입]
    C --> D[해상도·안전영역 적용]
    D --> E[스크린샷 수집]
    E --> F[기준 이미지와 비교]
    F -->|허용 범위 이내| G[테스트 통과]
    F -->|차이 초과| H[Diff 이미지·리포트 저장]
    H --> I[PR 또는 빌드 결과에 첨부]

각 계층의 책임을 분리하면 유지보수가 쉬워진다.

계층책임구현 예시
게임 테스트 훅특정 화면을 재현 가능한 상태로 전환Unity -executeMethod, 테스트 전용 딥링크, ITestDriver
실행 제어해상도와 시나리오를 순차 실행PowerShell, Bash, GitHub Actions, Jenkins
캡처화면을 PNG 또는 WebP로 저장Unity ScreenCapture.CaptureScreenshotAsTexture followed by PNG encoding
비교기준 이미지와 후보 이미지의 차이 판정Pixelmatch, Resemble.js, OpenCV
결과물실패 원인 확인에 필요한 이미지 제공actual, expected, diff, 로그, 빌드 번호

Unity에서 해상도별 스크린샷을 어떻게 수집할까?

가장 단순한 시작점은 테스트 전용 씬 또는 개발 빌드에서 화면 해상도와 UI 상태를 명시적으로 설정하는 것이다. 아래 예시는 UGUI 화면을 준비한 뒤 프레임 렌더링을 기다리고 스크린샷을 저장한다.

using System.Collections;
using System.IO;
using UnityEngine;

public sealed class UiScreenshotRunner : MonoBehaviour
{
    [SerializeField] private string scenarioName = "settings";

    private IEnumerator Start()
    {
        Screen.SetResolution(1920, 1080, FullScreenMode.Windowed);

        // 테스트 대상 화면을 결정론적으로 연다.
        yield return UiTestRouter.OpenScenario(scenarioName);

        // 레이아웃 재계산과 비동기 UI 갱신을 기다린다.
        Canvas.ForceUpdateCanvases();
        yield return new WaitForEndOfFrame();
        yield return new WaitForEndOfFrame();

        var fileName = $"{scenarioName}-1920x1080.png";
        var path = Path.Combine(Application.persistentDataPath, "ui-snapshots", fileName);
        Directory.CreateDirectory(Path.GetDirectoryName(path));
        Texture2D screenshot = ScreenCapture.CaptureScreenshotAsTexture();
        File.WriteAllBytes(path, screenshot.EncodeToPNG());
        Destroy(screenshot);

        Debug.Log($"UI_SCREENSHOT={path}");
        Application.Quit(0);
    }
}

Screen.SetResolution은 에디터와 데스크톱 빌드에서 유용하지만 모바일 실기기의 물리 해상도를 임의로 바꾸지는 못한다. 모바일은 Android Emulator 또는 iOS Simulator의 기기 프로필을 사용하고 테스트 훅을 통해 동일한 시나리오를 실행하는 편이 적절하다.

Step 1. 화면 상태를 결정론적으로 만든다

스크린샷 테스트에서 가장 중요한 조건은 재현성이다. 현재 시간, 랜덤 보상, 네트워크 응답, 광고, 원격 공지처럼 매 실행마다 달라지는 요소는 고정하거나 가짜 데이터로 교체한다.

  • 서버 시간 대신 IClock 테스트 구현으로 고정 시각 주입
  • 난수 시드는 Random.InitState(12345)처럼 고정
  • API 응답은 로컬 fixture JSON으로 대체
  • 애니메이션은 첫 프레임 또는 지정한 진행률에서 정지
  • 로딩 스피너와 커서처럼 비교 의미가 낮은 영역은 마스크 처리

Step 2. 기준 이미지와 후보 이미지를 비교한다

픽셀 비교는 각 픽셀의 RGB 차이를 누적해 임계값을 넘는지 판단하는 방식이 일반적이다. 안티앨리어싱, GPU 드라이버, 폰트 래스터라이징 차이 때문에 완전 일치만 요구하면 오탐이 많아진다.

차이 비율은 다음처럼 관리할 수 있다.

diffRate=differentPixelstotalPixels×100diffRate = \frac{differentPixels}{totalPixels} \times 100

예를 들어 1920×1080 이미지에서 차이가 난 픽셀이 2,000개라면 차이 비율은 약 0.096%다. 0.1%나 0.2%는 특정 환경에서 시험해 볼 초기 임계값일 뿐 보편적인 기준은 아니다. 프로젝트는 대표 화면의 의도된 변경과 재현 오차를 살펴 허용값과 마스크 영역을 정해야 한다.

Node.js의 Pixelmatch를 쓰는 비교 스크립트는 다음과 같이 간결하게 작성할 수 있다.

import fs from "node:fs";
import { PNG } from "pngjs";
import pixelmatch from "pixelmatch";

const expected = PNG.sync.read(fs.readFileSync(process.argv[2]));
const actual = PNG.sync.read(fs.readFileSync(process.argv[3]));

if (expected.width !== actual.width || expected.height !== actual.height) {
  throw new Error("스크린샷 크기가 기준 이미지와 다릅니다.");
}

const diff = new PNG({ width: expected.width, height: expected.height });
const differentPixels = pixelmatch(
  expected.data,
  actual.data,
  diff.data,
  expected.width,
  expected.height,
  { threshold: 0.1 }
);

fs.writeFileSync(process.argv[4], PNG.sync.write(diff));
const diffRate = differentPixels / (expected.width * expected.height) * 100;

if (diffRate > 0.2) {
  throw new Error(`UI 시각 회귀 감지: ${diffRate.toFixed(3)}%`);
}

Step 3. CI에서 실패 증거를 반드시 남긴다

실패했을 때 콘솔에 diffRate만 남기면 원인 파악이 느리다. CI 아티팩트에 최소 네 파일을 저장한다.

  1. 기준 이미지 expected.png
  2. 새 캡처 이미지 actual.png
  3. 차이 강조 이미지 diff.png
  4. 해상도, 기기 프로필, 커밋 SHA, 테스트 시나리오가 담긴 metadata.json

PR 검토자는 이미지를 나란히 보며 변경이 의도된 것인지 즉시 판단할 수 있다. 의도된 UI 변경이면 검토 승인 후 기준 이미지를 갱신하고 의도되지 않은 변화면 레이아웃 또는 에셋 변경을 수정한다.

기준 이미지 갱신은 누가 승인해야 하는가?

기준 이미지는 단순한 테스트 산출물이 아니라 UI 계약이다. 따라서 테스트 실패를 없애기 위해 자동으로 덮어쓰면 회귀 탐지 기능이 사라진다.

권장 운영 정책은 다음과 같다.

  • 일반 CI: 기준 이미지 갱신을 금지하고 비교 결과만 생성한다.
  • UI 변경 PR: 작성자가 update-baseline 작업을 명시적으로 실행한다.
  • 코드 리뷰: UI 담당자 또는 QA 담당자가 diff 이미지를 확인한다.
  • 병합 후: 승인된 기준 이미지만 저장소 또는 버전 관리되는 아티팩트 저장소에 반영한다.

이미지 용량이 커지는 프로젝트는 Git LFS를 사용할 수 있다. 다만 기준 이미지의 이력, 접근 권한, CI checkout 시간을 먼저 측정한 뒤 도입 여부를 결정하는 편이 좋다.

자주 묻는 질문 (FAQ)

픽셀 비교만으로 UI 테스트를 끝낼 수 있나?

아니다. 픽셀 비교는 잘림, 겹침, 색상 변경 같은 시각적 회귀에 강하지만 버튼 클릭, 입력 검증, 접근성 같은 동작 검증은 별도의 UI 자동테스트가 필요하다.

폰트 렌더링 차이로 테스트가 자주 실패하면 어떻게 하나?

가능하면 OS, Unity 버전, 폰트 파일, GPU 또는 소프트웨어 렌더러를 CI와 기준 이미지 생성 환경에서 고정한다. 그래도 변동하는 영역은 마스크 처리하고 전체 임계값을 무작정 높이는 방식은 피한다.

모든 화면과 언어를 매 커밋마다 캡처해야 하나?

아니다. 매 PR에는 핵심 화면과 대표 해상도만 실행하고 전체 해상도·언어 매트릭스는 야간 빌드나 릴리스 후보 빌드에서 실행하면 비용과 검출력을 균형 있게 유지할 수 있다.

정리

반응형 UI 자동테스트의 핵심은 해상도 수를 무작정 늘리는 것이 아니라 대표 기기 매트릭스, 결정론적인 화면 상태, 검토 가능한 diff 아티팩트를 갖추는 것이다. 먼저 로그인·로비·상점·인게임 HUD처럼 플레이 빈도가 높은 화면부터 기준 이미지를 만들고 CI에서 해상도별 스크린샷 비교를 실패 조건으로 연결하자. 시각적 버그를 플레이어보다 먼저 발견하는 QA 파이프라인이 된다.