유저 스토리에서 Gherkin 시나리오를 거쳐 실행 가능한 테스트로 자동 변환하는 Agentic QA의 아키텍처, 자율 탐색 테스트, Human-on-the-loop 감독 체계, 그리고 품질 게이트 통합을 다룹니다.
1장에서 간략히 소개한 Agentic QA(자율 QA)를 이 장에서 본격적으로 다룹니다. Agentic QA는 AI 에이전트가 테스트의 계획, 생성, 실행, 분석, 유지보수를 자율적으로 수행하는 패러다임입니다.
| 특성 | 수동 QA | 자동화 QA | Agentic QA |
|---|---|---|---|
| 테스트 설계 | 사람 | 사람 | AI 에이전트 |
| 테스트 코드 작성 | 사람 | 사람 | AI 에이전트 |
| 테스트 실행 | 사람/도구 | 도구 | AI 에이전트 |
| 결과 분석 | 사람 | 사람 | AI 에이전트 |
| 유지보수 | 사람 | 사람 | AI 에이전트 |
| 사람의 역할 | 모든 단계 수행 | 설계 + 코드 작성 | 감독 + 승인 |
| 속도 | 느림 | 보통 | 빠름 |
핵심은 사람의 역할이 "수행자"에서 "감독자"로 전환된다는 점입니다. 에이전트가 자율적으로 동작하되, 사람은 결과를 검토하고 방향을 조정합니다.
Agentic QA의 가장 강력한 기능은 요구사항에서 실행 가능한 테스트까지의 자동 변환입니다.
[US-1234] 장바구니 할인 쿠폰 적용
사용자로서,
장바구니에 할인 쿠폰을 적용하여
최종 결제 금액에서 할인을 받고 싶습니다.
인수 조건:
- 유효한 쿠폰 코드를 입력하면 할인이 적용됩니다
- 만료된 쿠폰은 에러 메시지를 표시합니다
- 최소 주문 금액 미달 시 쿠폰 적용이 거부됩니다
- 하나의 주문에 쿠폰은 1개만 적용 가능합니다
- 할인 적용 후 최종 금액이 실시간으로 업데이트됩니다AI 에이전트는 이 유저 스토리에서 다음을 추출합니다.
Feature: 장바구니 할인 쿠폰 적용
Background:
Given 사용자가 로그인되어 있습니다
And 장바구니에 "노트북" 1개(1,500,000원)가 담겨 있습니다
Scenario: 유효한 쿠폰 적용 성공
When 쿠폰 코드 "SAVE10"을 입력합니다
And "쿠폰 적용" 버튼을 클릭합니다
Then "10% 할인이 적용되었습니다" 메시지가 표시됩니다
And 최종 금액이 "1,350,000원"으로 업데이트됩니다
Scenario: 만료된 쿠폰 적용 실패
When 쿠폰 코드 "EXPIRED2025"를 입력합니다
And "쿠폰 적용" 버튼을 클릭합니다
Then "만료된 쿠폰입니다" 에러 메시지가 표시됩니다
And 최종 금액이 변경되지 않습니다
Scenario: 최소 주문 금액 미달
Given 장바구니에 "마우스" 1개(30,000원)만 담겨 있습니다
When 쿠폰 코드 "MIN50000"을 입력합니다
And "쿠폰 적용" 버튼을 클릭합니다
Then "최소 주문 금액(50,000원)을 충족하지 않습니다" 에러 메시지가 표시됩니다
Scenario: 쿠폰 중복 적용 방지
Given 쿠폰 "SAVE10"이 이미 적용되어 있습니다
When 쿠폰 코드 "SAVE20"을 입력합니다
And "쿠폰 적용" 버튼을 클릭합니다
Then "이미 쿠폰이 적용되어 있습니다" 메시지가 표시됩니다
Scenario: 쿠폰 적용 후 금액 실시간 업데이트
When 쿠폰 코드 "SAVE10"을 입력합니다
And "쿠폰 적용" 버튼을 클릭합니다
Then 할인 금액 "-150,000원"이 표시됩니다
And 최종 금액이 페이지 새로고침 없이 업데이트됩니다AI가 생성한 Gherkin 시나리오는 반드시 제품 관리자 또는 QA 담당자의 검토를 거쳐야 합니다. AI는 인수 조건에서 시나리오를 잘 도출하지만, 비즈니스 맥락에서 추가로 필요한 시나리오(예: VIP 고객 추가 할인, 특정 카테고리 제외 쿠폰 등)를 놓칠 수 있습니다.
import { test, expect } from "@playwright/test";
test.describe("장바구니 할인 쿠폰 적용", () => {
test.beforeEach(async ({ page }) => {
// 로그인
await page.goto("/login");
await page.getByLabel("이메일").fill("test@example.com");
await page.getByLabel("비밀번호").fill("password123");
await page.getByRole("button", { name: "로그인" }).click();
await expect(page).toHaveURL(/dashboard/);
// 장바구니에 상품 추가
await page.goto("/products/notebook");
await page.getByRole("button", { name: "장바구니에 추가" }).click();
await expect(page.getByText("장바구니에 추가되었습니다")).toBeVisible();
// 장바구니 페이지로 이동
await page.goto("/cart");
});
test("유효한 쿠폰 적용 성공", async ({ page }) => {
await page.getByLabel("쿠폰 코드").fill("SAVE10");
await page.getByRole("button", { name: "쿠폰 적용" }).click();
await expect(page.getByText("10% 할인이 적용되었습니다")).toBeVisible();
await expect(page.getByTestId("final-price")).toHaveText(/1,350,000/);
});
test("만료된 쿠폰 적용 실패", async ({ page }) => {
await page.getByLabel("쿠폰 코드").fill("EXPIRED2025");
await page.getByRole("button", { name: "쿠폰 적용" }).click();
await expect(page.getByText("만료된 쿠폰입니다")).toBeVisible();
await expect(page.getByTestId("final-price")).toHaveText(/1,500,000/);
});
test("최소 주문 금액 미달", async ({ page }) => {
// 장바구니를 저가 상품으로 교체
await page.getByRole("button", { name: "전체 삭제" }).click();
await page.goto("/products/mouse");
await page.getByRole("button", { name: "장바구니에 추가" }).click();
await page.goto("/cart");
await page.getByLabel("쿠폰 코드").fill("MIN50000");
await page.getByRole("button", { name: "쿠폰 적용" }).click();
await expect(
page.getByText("최소 주문 금액(50,000원)을 충족하지 않습니다")
).toBeVisible();
});
test("쿠폰 중복 적용 방지", async ({ page }) => {
// 첫 번째 쿠폰 적용
await page.getByLabel("쿠폰 코드").fill("SAVE10");
await page.getByRole("button", { name: "쿠폰 적용" }).click();
await expect(page.getByText("10% 할인이 적용되었습니다")).toBeVisible();
// 두 번째 쿠폰 시도
await page.getByLabel("쿠폰 코드").fill("SAVE20");
await page.getByRole("button", { name: "쿠폰 적용" }).click();
await expect(
page.getByText("이미 쿠폰이 적용되어 있습니다")
).toBeVisible();
});
});Agentic QA를 구현하는 에이전트는 다음과 같은 컴포넌트로 구성됩니다.
Planner(계획 수립기)
유저 스토리, 요구사항 문서, 기존 테스트 커버리지를 분석하여 어떤 테스트가 필요한지 계획합니다. 기존 테스트와의 중복을 피하고, 커버리지 갭을 우선적으로 채웁니다.
Generator(테스트 생성기)
계획에 따라 Gherkin 시나리오와 실행 가능한 테스트 코드를 생성합니다. 프로젝트의 기존 테스트 패턴(네이밍 컨벤션, 헬퍼 함수, 페이지 객체 등)을 학습하여 일관된 스타일을 유지합니다.
Executor(테스트 실행기)
생성된 테스트를 실행하고 결과를 수집합니다. 단순히 통과/실패뿐 아니라, 실행 시간, 스크린샷, 네트워크 로그 등을 함께 기록합니다.
Analyzer(결과 분석기)
실행 결과를 분석하여 실패 원인을 분류합니다. 실제 결함인지, 테스트 코드의 문제인지, 환경 문제인지를 판단합니다.
Healer(자동 복구기)
테스트 코드의 문제로 실패한 경우 자동으로 수정합니다. 셀렉터 변경, 대기 조건 추가, 데이터 설정 수정 등을 수행합니다.
Memory(학습 기억)
에이전트의 모든 경험을 저장하고 학습합니다. 어떤 패턴의 테스트가 효과적이었는지, 어떤 유형의 실패가 자주 발생하는지를 기억하여 점점 나은 테스트를 생성합니다.
Agentic QA의 또 다른 강력한 기능은 Exploratory Testing(탐색 테스트)의 자동화입니다. 사전 정의된 시나리오 없이 AI가 애플리케이션을 자율적으로 탐색하며 결함을 찾습니다.
AI 탐색 에이전트가 감지하는 이상 유형은 다음과 같습니다.
| 이상 유형 | 감지 방법 |
|---|---|
| 콘솔 에러 | JavaScript 에러 로그 모니터링 |
| HTTP 에러 | 4xx/5xx 응답 감지 |
| 접근성 위반 | ARIA 규칙 검사 |
| 성능 이상 | 응답 시간 임계치 초과 |
| 시각적 이상 | 요소 겹침, 오버플로우, 깨진 이미지 |
| 데드 링크 | 404 응답 페이지 |
| 보안 취약점 | XSS, CSRF 패턴 감지 |
interface ExplorationResult {
pagesVisited: number;
actionsPerformed: number;
issuesFound: Issue[];
coverageMap: Map<string, number>;
duration: number;
}
interface Issue {
type: "error" | "accessibility" | "performance" | "visual" | "security";
severity: "critical" | "major" | "minor";
page: string;
description: string;
screenshot?: string;
reproduction: string[];
}
async function explore(baseUrl: string, options: ExploreOptions): Promise<ExplorationResult> {
const visited = new Set<string>();
const issues: Issue[] = [];
const queue: string[] = [baseUrl];
while (queue.length > 0 && visited.size < options.maxPages) {
const url = queue.shift()!;
if (visited.has(url)) continue;
visited.add(url);
// 페이지 로드 및 분석
const pageAnalysis = await analyzePage(url);
// 이상 감지
issues.push(...pageAnalysis.issues);
// 상호작용 가능 요소에 대해 행동 수행
for (const action of pageAnalysis.possibleActions) {
const result = await performAction(action);
if (result.newUrl && !visited.has(result.newUrl)) {
queue.push(result.newUrl);
}
issues.push(...result.issues);
}
}
return {
pagesVisited: visited.size,
actionsPerformed: issues.length,
issuesFound: issues,
coverageMap: buildCoverageMap(visited),
duration: Date.now() - startTime,
};
}자율 탐색 테스트는 강력하지만, 부작용에 주의해야 합니다. 에이전트가 삭제 버튼을 클릭하거나, 결제를 시도하거나, 대량 데이터를 생성할 수 있습니다. 반드시 격리된 테스트 환경에서 실행하고, 위험한 행동(삭제, 결제 등)은 제외 목록에 추가해야 합니다.
Agentic QA에서 사람의 역할은 "모든 것을 수행하는 것"에서 "중요한 결정을 내리는 것"으로 전환됩니다.
| 감독 수준 | 에이전트 자율성 | 사람 개입 시점 | 적용 상황 |
|---|---|---|---|
| Level 1: 보조 | 테스트 제안만 | 모든 단계에서 승인 | 도입 초기 |
| Level 2: 반자율 | 생성 + 실행 자율 | 결과 검토에서 승인 | 안정화 단계 |
| Level 3: 자율 | 전체 파이프라인 자율 | 이상 감지 시에만 개입 | 성숙 단계 |
| Level 4: 완전 자율 | 배포 게이트까지 자율 | 주기적 감사만 | 고도 신뢰 |
에이전트가 생성한 테스트는 품질 게이트를 통과해야 테스트 스위트에 추가됩니다.
interface QualityGateResult {
passed: boolean;
checks: Array<{
name: string;
status: "pass" | "fail" | "warn";
message: string;
}>;
}
function evaluateTestQuality(generatedTest: TestFile): QualityGateResult {
const checks = [];
// 1. 의미 있는 단언이 있는가
const assertionCount = countAssertions(generatedTest);
checks.push({
name: "단언 존재",
status: assertionCount > 0 ? "pass" : "fail",
message: `${assertionCount}개의 단언 감지`,
});
// 2. 에러 경로를 테스트하는가
const hasErrorTests = containsErrorScenarios(generatedTest);
checks.push({
name: "에러 경로 테스트",
status: hasErrorTests ? "pass" : "warn",
message: hasErrorTests ? "에러 시나리오 포함" : "에러 시나리오 없음",
});
// 3. 하드코딩된 대기 시간이 없는가
const hasHardWaits = containsHardWaits(generatedTest);
checks.push({
name: "하드 대기 없음",
status: hasHardWaits ? "fail" : "pass",
message: hasHardWaits ? "waitForTimeout 사용 감지" : "안정적 대기 사용",
});
// 4. 테스트 간 독립성
const hasSharedState = detectSharedState(generatedTest);
checks.push({
name: "테스트 독립성",
status: hasSharedState ? "warn" : "pass",
message: hasSharedState ? "공유 상태 감지" : "테스트 간 독립적",
});
return {
passed: checks.every((c) => c.status !== "fail"),
checks,
};
}이 장에서는 Agentic QA의 핵심 개념, 아키텍처, 그리고 실전 적용 방법을 살펴보았습니다.
핵심 내용을 정리하면 다음과 같습니다.
10장에서는 이 시리즈의 마무리로 실전 프로젝트를 진행합니다. 지금까지 배운 단위 테스트, 통합 테스트, E2E 테스트, 시각적 회귀 테스트, 변이 테스트를 하나의 AI 테스트 자동화 파이프라인으로 통합하고, 커버리지/변이 점수 대시보드와 도입 로드맵을 수립합니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
단위, 통합, E2E, 시각적, 변이 테스트를 하나의 AI 테스트 자동화 파이프라인으로 통합합니다. Codium, Playwright, Applitools를 결합한 CI/CD 파이프라인과 대시보드, 도입 로드맵, ROI 측정을 다룹니다.
변경 영향 분석 기반 테스트 선택, 위험 기반 우선순위, 플레이키 테스트 자동 격리, 병렬 실행 최적화, 결함 예측, GitHub Actions/GitLab CI 통합을 다루는 AI QA 파이프라인 구축 가이드입니다.
테스트 로트(Test Rot) 문제의 근본 원인과 AI 기반 셀프 힐링, 셀렉터 자동 재바인딩, 테스트 코드 리팩터링, 중복 테스트 감지, 커버리지 갭 분석 등 유지보수 비용 절감 전략을 다룹니다.