YAML 기반 테스트: E2E에 대한 새로운 접근

발행: (2026년 4월 8일 AM 11:10 GMT+9)
12 분 소요
원문: Dev.to

Source: Dev.to

Hai Huang

엔드‑투‑엔드 테스트의 유지보수 문제

전통적인 테스트 스크립트는 깨지기 쉽고, 장황하며, DOM에 밀접하게 결합되어 있습니다. UI를 한 번 리팩터링하면 전날까지 정상적으로 동작하던 수십 개의 테스트가 모두 실패할 수 있습니다. 팀은 새로운 테스트를 작성하기보다 테스트를 고치는 데 더 많은 시간을 소비하게 됩니다.

YAML 기반 테스트는 다른 접근 방식을 취합니다. 요소와 어떻게 상호작용할지를 서술하는 절차적 스크립트를 작성하는 대신, 무엇을 테스트하고 싶은지를 서술하는 선언적 파일을 작성합니다. 실행 엔진이 어떻게 수행할지를 담당합니다.

이것은 이론적인 개념이 아니라, Shiplight 가 YAML을 기본 테스트 형식으로 사용하고 있으며, 이 접근 방식은 팀이 E2E 테스트 유지보수를 생각하는 방식을 근본적으로 바꾸어 놓습니다.

왜 테스트에 YAML을 사용하는가

Readability – YAML 테스트 파일은 사용자 행동 체크리스트처럼 읽힙니다. 팀의 누구든지 — 개발자, QA 엔지니어, 제품 매니저 — YAML 테스트를 읽고 어떤 내용을 다루는지 이해할 수 있습니다. Playwright 스크립트는 JavaScript 지식과 Playwright API에 대한 친숙함이 필요합니다. YAML은 애플리케이션이 해야 할 일을 아는 것만으로 충분합니다.

Separation of intent from implementation – 기존 스크립트는 테스트 로직과 DOM‑상호작용 코드를 뒤섞습니다. 버튼의 셀렉터가 바뀌면 사용자 의도는 변하지 않았음에도 테스트가 깨집니다. YAML 기반 테스트는 무엇을 테스트하고 싶은지(의도)와 어떻게 도구가 요소를 찾고 상호작용하는지(캐시된 로케이터)를 분리합니다.

Version‑control friendliness – YAML 차이는 깔끔하고 의미가 명확합니다. 테스트가 변경될 때, diff는 정확히 어떤 동작이 바뀌었는지를 보여줍니다. JavaScript 테스트 diff는 셀렉터 업데이트, async‑처리 변경, 프레임워크 보일러플레이트 등 잡음이 많이 섞여 있습니다.

How YAML Tests Differ from Playwright Scripts

Playwright script (JavaScript)

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

test('user can log in and see dashboard', async ({ page }) => {
  await page.goto('https://app.example.com/login');
  await page.fill('[data-testid="email-input"]', 'user@example.com');
  await page.fill('[data-testid="password-input"]', 'securepass123');
  await page.click('[data-testid="login-button"]');
  await page.waitForURL('**/dashboard');
  await expect(page.locator('[data-testid="welcome-message"]'))
    .toContainText('Welcome back');
  await expect(page.locator('[data-testid="project-list"]'))
    .toBeVisible();
});

Equivalent YAML test

name: User login and dashboard
url: https://app.example.com/login
statements:
  - action: FILL
    target: email input
    value: user@example.com
  - action: FILL
    target: password input
    value: securepass123
  - action: CLICK
    target: login button
  - action: VERIFY
    assertion: page contains "Welcome back"
  - action: VERIFY
    assertion: project list is visible

YAML 버전은 더 짧지만 길이가 핵심은 아닙니다. 구조적 차이가 더 중요합니다.

  • Playwright 스크립트는 7개의 셀렉터([data-testid="email-input"] 등)를 포함하고 있으며, 프론트엔드 팀이 해당 테스트 ID를 바꾸면 깨집니다.
  • YAML 버전은 email input, login button과 같은 의도 기반 타깃을 사용합니다 — 요소가 무엇인지에 대한 설명이며, 찾는 방법이 아닙니다.
  • Playwright 스크립트는 async/await, Playwright API, 그리고 JavaScript 구조 분해 할당에 대한 지식이 필요합니다.
  • YAML 버전은 애플리케이션이 무엇을 하는지만 알면 됩니다.

인텐트 문장

YAML 기반 테스트의 핵심 개념은 인텐트 문장입니다. 인텐트 문장은 무엇이 일어나길 원하는지를 설명하고, 도구가 어떻게 수행해야 하는지는 지정하지 않습니다.

target: login button을 작성하면, 당신은 인텐트를 표현하는 것입니다: “사용자가 로그인 버튼이라고 식별할 요소와 상호작용하고 싶다.” 테스트 엔진은 AI 기반 요소 매칭을 사용해 이를 실제 DOM 요소로 해석합니다.

이는 button.btn-primary.auth-submit 같은 선택자나 [data-testid="login-btn"]와 근본적으로 다릅니다. 선택자는 구현 세부 사항이며, 인텐트는 사용자 입장에서의 설명입니다.

intent‑cache‑heal 패턴은 대규모 환경에서도 이를 실용적으로 만듭니다:

  1. First run – 엔진은 각 인텐트를 특정 로케이터로 해석하고 이를 캐시합니다.
  2. Subsequent runs – 캐시된 로케이터를 직접 사용하여 속도를 높입니다.
  3. Cache miss – 캐시된 로케이터가 실패하면(UI가 변경되었기 때문) 엔진이 AI를 사용해 인텐트를 다시 해석합니다.

캐시된 선택자의 속도와 인텐트 기반 매칭의 복원력을 동시에 얻을 수 있습니다.

캐시된 로케이터

내부적으로, 모든 인텐트 대상은 캐시된 로케이터에 의해 지원됩니다. Shiplight가 login buttonbutton[type="submit"]으로 처음 해석하면, 해당 매핑을 테스트와 함께 로케이터‑캐시 파일에 저장합니다.

# .shiplight/cache/login-test.locators.yml
- intent: email input
  locator: 'input[name="email"]'
  resolved_at: 2026-03-28T14:22:00Z
- intent: password input
  locator: 'input[name="password"]'
  resolved_at: 2026-03-28T14:22:01Z
- intent: login button
  locator: 'button[type="submit"]'
  resolved_at: 2026-03-28T14:22:01Z

이 캐시 파일들은 리포지토리 내에 존재하며 — 버전‑관리되고 검토 가능한 아티팩트입니다. 로케이터가 치유(UI 변경 후 재해석)되면 캐시 파일이 업데이트되고, 차이점(diff)에는 정확히 어떤 부분이 변경되었는지 표시됩니다. 각 로케이터가 언제 마지막으로 해석되었는지, 그리고 어떻게 진화했는지를 확인할 수 있습니다.

투명성은 테스트 인프라를 감사해야 하는 팀에게 중요합니다. 숨겨진 것이 없습니다.

VERIFY 어설션

YAML 기반 테스트는 VERIFY 단계를 사용하여 어설션을 수행합니다. 특정 DOM 속성을 확인하는 전통적인 어설션과 달리, VERIFY 단계는 페이지에 대해 사실이어야 하는 내용을 자연어로 표현합니다.

- action: VERIFY
  assertion: page contains "Welcome back"
- action: VERIFY
  assertion: project list shows at least 3 items
- action: VERIFY
  assertion: navigation menu is visible
- action: VERIFY
  assertion: error message is not displayed

테스트 엔진은 수행해야 할 적절한 DOM 검사를 자동으로 결정합니다. 어설션은 UI 프레임워크가 콘텐츠를 <div>, <span>, <p> 등 어떤 요소로 렌더링하든 상관없이 동작합니다. 구현이 아니라 결과를 기술하는 것입니다.

전체 YAML 테스트 예시

다음은 전자상거래 결제 흐름에 대한 전체 YAML 테스트이며, 사용 가능한 모든 액션과 어설션을 보여줍니다:

name: Complete checkout flow
url: https://store.example.com
tags:
  - checkout
  - critical-path
statements:
  - action: CLICK
    target: first product card
  - action: VERIFY
    assertion: product detail page is displayed
  - action: CLICK
    target: add to cart button
  - action: VERIFY
    assertion: cart badge shows "1"
  - action: CLICK
    target: cart icon
  - action: VERIFY
    assertion: cart contains 1 item
  - action: CLICK
    target: proceed to checkout
  - action: FILL
    target: shipping address
    value: 123 Test Street, San Francisco, CA 94102
  - action: FILL
    target: card number
    value: "4242424242424242"
  - action: FILL
    target: expiration date
    value: "12/28"
  - action: FILL
    target: CVV
    value: "123"
  - action: CLICK
    target: place order button
  - action: VERIFY
    assertion: order confirmation page is displayed
  - action: VERIFY
    assertion: page contains "Thank you for your order"
  - action: VERIFY
    assertion: order number is displayed

이 테스트는 결제 흐름 자체가 변경되지 않는 한, 전체 프론트엔드 디자인이 새로워져도 살아남을 가능성이 높습니다. 동일한 흐름을 Playwright 스크립트로 구현하면 선택자, 대기 및 어설션이 현재 DOM에 밀접하게 결합된 60~80줄의 JavaScript 코드가 될 것입니다.

Getting Started

현재 Playwright 스크립트를 작성하고 있다면, 모든 것을 한 번에 다시 작성할 필요는 없습니다. Shiplight는 기존 테스트 스위트와 함께 plugin system을 통해 실행됩니다.

실용적인 시작점:

  1. UI 변경으로 인해 자주 깨지는, 가장 많이 유지 관리되는 테스트를 식별합니다.
  2. 해당 테스트를 먼저 YAML 형식으로 변환하고 기존 스크립트와 병행하여 실행합니다.
  3. 유지 관리 부담이 감소하는지 관찰합니다.

AI 코딩 에이전트를 사용해 테스트를 생성하는 팀에게는 YAML이 자연스러운 출력 형식입니다. AI가 생성한 JavaScript 테스트 스크립트는 검토가 어렵고 유지 비용이 높습니다. AI가 만든 YAML 테스트 파일은 사양처럼 읽히며 시간이 지나도 가독성을 유지합니다.

참조

0 조회
Back to Blog

관련 글

더 보기 »