본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 8장: AI QA 파이프라인 구축
2026년 3월 18일·AI / ML·

8장: AI QA 파이프라인 구축

변경 영향 분석 기반 테스트 선택, 위험 기반 우선순위, 플레이키 테스트 자동 격리, 병렬 실행 최적화, 결함 예측, GitHub Actions/GitLab CI 통합을 다루는 AI QA 파이프라인 구축 가이드입니다.

17분762자9개 섹션
testingautomationquality-assuranceai
공유
ai-testing8 / 10
12345678910
이전7장: 테스트 유지보수 자동화다음9장: Agentic QA -- 자율 테스트 에이전트

학습 목표

  • 변경 영향 분석 기반 테스트 선택의 원리를 이해합니다
  • 위험 기반 테스트 우선순위 지정 전략을 파악합니다
  • 플레이키 테스트 자동 격리와 병렬 실행 최적화를 학습합니다
  • AI 기반 결함 예측의 가능성과 한계를 이해합니다
  • GitHub Actions에서 AI QA 파이프라인을 구축하는 방법을 실습합니다

AI QA 파이프라인 개요

전통적인 CI 파이프라인은 모든 PR에 대해 전체 테스트 스위트를 동일한 방식으로 실행합니다. AI QA 파이프라인은 이를 지능적으로 최적화합니다.

기존 파이프라인과의 비교

영역기존 파이프라인AI QA 파이프라인
테스트 선택전체 실행변경 영향 분석 기반 선택
실행 순서알파벳 또는 정의 순위험도/실패 확률 순
플레이키 대응고정 횟수 재시도패턴 기반 자동 격리
실행 시간30-60분10-20분
실패 분석로그 수동 확인AI 자동 분류 및 근본 원인 제시

변경 영향 분석 기반 테스트 선택

모든 코드 변경이 모든 테스트에 영향을 주는 것은 아닙니다. Change Impact Analysis(변경 영향 분석)는 변경된 코드가 어떤 테스트에 영향을 미치는지를 정적/동적으로 분석합니다.

분석 기법

구현 예시

impact-analyzer.ts
typescript
interface ChangeImpact {
  changedFiles: string[];
  directlyAffectedTests: string[];
  indirectlyAffectedTests: string[];
  riskScore: number;
  recommendation: "full" | "selective" | "smoke";
}
 
async function analyzeImpact(prDiff: string): Promise<ChangeImpact> {
  const changedFiles = parseDiffFiles(prDiff);
 
  // 1단계: 정적 의존성 분석
  const importGraph = await buildImportGraph("src/");
  const directDeps = changedFiles.flatMap((f) =>
    importGraph.getReverseDependencies(f)
  );
 
  // 2단계: 테스트-코드 매핑
  const testMap = await loadTestCoverageMap();
  const directlyAffectedTests = directDeps
    .flatMap((dep) => testMap.getTestsForFile(dep))
    .filter(unique);
 
  // 3단계: AI 기반 간접 영향 추론
  const indirectlyAffectedTests = await predictIndirectImpact(
    changedFiles,
    directlyAffectedTests
  );
 
  // 4단계: 위험도 계산
  const riskScore = calculateRiskScore(changedFiles, {
    defectHistory: await getDefectHistory(changedFiles),
    changeFrequency: await getChangeFrequency(changedFiles),
    complexity: await getComplexity(changedFiles),
  });
 
  return {
    changedFiles,
    directlyAffectedTests,
    indirectlyAffectedTests,
    riskScore,
    recommendation:
      riskScore > 0.8 ? "full" : riskScore > 0.4 ? "selective" : "smoke",
  };
}
Info

변경 영향 분석의 정확도는 테스트-코드 매핑의 품질에 크게 의존합니다. 초기에는 커버리지 데이터를 수집하여 매핑을 구축하고, 시간이 지남에 따라 실제 실패 패턴을 학습하여 정확도를 높여갑니다.


위험 기반 테스트 우선순위

선택된 테스트를 실행할 때, 위험도가 높은 테스트를 먼저 실행하면 결함을 더 빨리 발견할 수 있습니다.

우선순위 결정 요소

요소가중치설명
최근 실패 이력높음최근에 실패한 테스트는 다시 실패할 확률이 높음
코드 변경 밀접도높음변경된 코드와 직접 관련된 테스트
결함 발견 빈도중간과거에 실제 결함을 발견한 테스트
실행 시간중간빠른 테스트를 먼저 실행하여 빠른 피드백
비즈니스 중요도높음결제, 인증 등 핵심 기능 관련 테스트

우선순위 알고리즘

test-prioritizer.ts
typescript
interface TestPriority {
  testId: string;
  testFile: string;
  priority: number;
  reasons: string[];
}
 
function prioritizeTests(
  tests: string[],
  context: PrioritizationContext
): TestPriority[] {
  return tests
    .map((testId) => {
      let priority = 0;
      const reasons: string[] = [];
 
      // 최근 실패 이력 (+30점)
      if (context.recentFailures.has(testId)) {
        priority += 30;
        reasons.push("최근 실패 이력 있음");
      }
 
      // 변경 코드 직접 관련 (+25점)
      if (context.directlyAffected.has(testId)) {
        priority += 25;
        reasons.push("변경 코드와 직접 관련");
      }
 
      // 결함 발견 빈도 (+20점)
      const defectCount = context.defectDiscoveryCount.get(testId) ?? 0;
      priority += Math.min(defectCount * 5, 20);
      if (defectCount > 0) {
        reasons.push(`과거 ${defectCount}건 결함 발견`);
      }
 
      // 빠른 테스트 우선 (+15점)
      const duration = context.avgDuration.get(testId) ?? 10000;
      if (duration < 1000) {
        priority += 15;
        reasons.push("빠른 실행 (1초 미만)");
      } else if (duration < 5000) {
        priority += 10;
      }
 
      // 비즈니스 중요도 (+10점)
      if (context.criticalPaths.has(testId)) {
        priority += 10;
        reasons.push("핵심 비즈니스 경로");
      }
 
      return { testId, testFile: testId, priority, reasons };
    })
    .sort((a, b) => b.priority - a.priority);
}

플레이키 테스트 자동 격리

Flaky Test(플레이키 테스트)는 CI 파이프라인의 가장 큰 적입니다. AI 기반 자동 격리는 플레이키 테스트를 감지하고, 일반 테스트와 분리하여 별도로 관리합니다.

플레이키 감지 알고리즘

격리 및 관리 전략

.github/workflows/ai-qa-pipeline.yml (플레이키 관리)
yaml
name: AI QA Pipeline
 
on:
  pull_request:
    branches: [main]
 
jobs:
  classify-tests:
    runs-on: ubuntu-latest
    outputs:
      stable-tests: ${{ steps.classify.outputs.stable }}
      flaky-tests: ${{ steps.classify.outputs.flaky }}
    steps:
      - uses: actions/checkout@v4
      - name: Classify tests
        id: classify
        run: |
          node scripts/classify-tests.js \
            --history=test-history.json \
            --threshold=0.2
 
  run-stable:
    needs: classify-tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run stable tests
        run: |
          npx jest --testPathPattern="${{ needs.classify-tests.outputs.stable-tests }}"
 
  run-flaky:
    needs: classify-tests
    runs-on: ubuntu-latest
    continue-on-error: true
    steps:
      - uses: actions/checkout@v4
      - name: Run flaky tests with retries
        run: |
          npx jest \
            --testPathPattern="${{ needs.classify-tests.outputs.flaky-tests }}" \
            --retries=3
      - name: Report flaky results
        if: always()
        run: node scripts/report-flaky.js
Warning

플레이키 테스트를 격리하는 것은 "무시하는 것"과 다릅니다. 격리된 테스트는 별도의 대시보드에서 추적하고, 주기적으로 원인을 분석하여 수정해야 합니다. 격리만 하고 방치하면 테스트 로트의 또 다른 형태가 됩니다.


병렬 실행 최적화

테스트 실행 시간을 줄이는 가장 직접적인 방법은 병렬 실행입니다. AI는 테스트 간 의존성과 리소스 사용 패턴을 분석하여 최적의 병렬화 전략을 수립합니다.

샤딩 전략

shard-optimizer.ts
typescript
interface ShardPlan {
  shards: Array<{
    id: number;
    tests: string[];
    estimatedDuration: number;
  }>;
  totalEstimatedDuration: number;
  parallelSpeedup: number;
}
 
function optimizeShards(
  tests: TestMetadata[],
  maxShards: number
): ShardPlan {
  // 실행 시간 기준으로 테스트를 내림차순 정렬
  const sorted = [...tests].sort(
    (a, b) => b.avgDuration - a.avgDuration
  );
 
  // 최소 힙(min-heap) 기반 로드 밸런싱
  const shards: Array<{ id: number; tests: string[]; totalDuration: number }> =
    Array.from({ length: maxShards }, (_, i) => ({
      id: i,
      tests: [],
      totalDuration: 0,
    }));
 
  for (const test of sorted) {
    // 가장 여유로운 샤드에 테스트 배정
    const lightest = shards.reduce((min, shard) =>
      shard.totalDuration < min.totalDuration ? shard : min
    );
    lightest.tests.push(test.id);
    lightest.totalDuration += test.avgDuration;
  }
 
  const maxDuration = Math.max(...shards.map((s) => s.totalDuration));
  const sequentialDuration = tests.reduce((sum, t) => sum + t.avgDuration, 0);
 
  return {
    shards: shards.map((s) => ({
      id: s.id,
      tests: s.tests,
      estimatedDuration: s.totalDuration,
    })),
    totalEstimatedDuration: maxDuration,
    parallelSpeedup: sequentialDuration / maxDuration,
  };
}

결함 예측

AI는 과거 데이터를 분석하여 결함이 발생할 가능성이 높은 코드 영역을 예측할 수 있습니다.

예측 모델의 입력

특성설명
코드 복잡도순환 복잡도, 인지 복잡도
변경 빈도최근 N일간 변경 횟수
변경 크기추가/삭제된 라인 수
개발자 경험해당 코드에 대한 개발자의 친숙도
과거 결함해당 파일/모듈의 결함 이력
코드 연령마지막 대규모 리팩터링 이후 경과 시간
결함 예측 보고서 예시
plaintext
=== PR #1234 결함 예측 ===
 
[위험도 0.87] src/services/payment-processor.ts
  - 순환 복잡도: 28 (권장: 15 이하)
  - 최근 30일 변경: 12회
  - 과거 결함: 4건
  - 이번 변경: +45 / -12 라인
  권장: 집중 코드 리뷰 + 추가 테스트
 
[위험도 0.62] src/api/routes/orders.ts
  - 순환 복잡도: 15
  - 최근 30일 변경: 8회
  - 과거 결함: 2건
  - 이번 변경: +23 / -5 라인
  권장: 일반 코드 리뷰
 
[위험도 0.23] src/utils/format.ts
  - 순환 복잡도: 5
  - 최근 30일 변경: 1회
  - 과거 결함: 0건
  - 이번 변경: +8 / -2 라인
  권장: 표준 프로세스
Info

결함 예측 모델은 확률적 도구이지, 결정론적 판단이 아닙니다. "이 코드에 반드시 결함이 있다"가 아니라, "이 코드에 주의를 기울여야 한다"는 신호로 활용해야 합니다. 위험도가 높은 영역에 더 많은 리뷰 시간과 테스트를 할당하는 전략입니다.


GitHub Actions 통합 전체 파이프라인

지금까지 다룬 기능을 통합한 전체 AI QA 파이프라인입니다.

.github/workflows/ai-qa-full.yml
yaml
name: AI QA Pipeline
 
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]
 
env:
  NODE_VERSION: "22"
 
jobs:
  # 1단계: 변경 분석 및 테스트 계획
  analyze:
    runs-on: ubuntu-latest
    outputs:
      test-plan: ${{ steps.plan.outputs.plan }}
      risk-level: ${{ steps.plan.outputs.risk }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
 
      - name: Analyze changes and create test plan
        id: plan
        run: |
          node scripts/ai-test-planner.js \
            --base=${{ github.event.pull_request.base.sha }} \
            --head=${{ github.event.pull_request.head.sha }}
 
  # 2단계: 단위 + 통합 테스트 (병렬)
  unit-integration:
    needs: analyze
    runs-on: ubuntu-latest
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: "pnpm"
 
      - run: pnpm install --frozen-lockfile
      
      - name: Run tests (shard ${{ matrix.shard }})
        run: |
          pnpm test -- \
            --shard=${{ matrix.shard }}/4 \
            --forceExit \
            --ci
 
  # 3단계: E2E 테스트 (위험도 높을 때만)
  e2e:
    needs: [analyze, unit-integration]
    if: needs.analyze.outputs.risk-level == 'high'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: "pnpm"
 
      - run: pnpm install --frozen-lockfile
      - run: npx playwright install --with-deps
 
      - name: Run E2E tests
        run: pnpm test:e2e
 
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: e2e-results
          path: test-results/
 
  # 4단계: 품질 게이트
  quality-gate:
    needs: [unit-integration, e2e]
    if: always()
    runs-on: ubuntu-latest
    steps:
      - name: Evaluate quality gate
        run: |
          echo "Checking quality gate..."
          node scripts/quality-gate.js \
            --coverage-threshold=80 \
            --mutation-threshold=70

정리

이 장에서는 AI QA 파이프라인의 핵심 구성 요소와 구축 방법을 살펴보았습니다.

핵심 내용을 정리하면 다음과 같습니다.

  • 변경 영향 분석은 정적 분석, 동적 분석, AI 추론을 결합하여 필요한 테스트만 선택합니다
  • 위험 기반 우선순위는 실패 이력, 코드 변경 밀접도, 비즈니스 중요도를 종합하여 테스트 순서를 결정합니다
  • 플레이키 테스트는 자동 감지, 격리, 별도 관리하되 방치해서는 안 됩니다
  • 병렬 실행은 로드 밸런싱 기반 샤딩으로 최적화합니다
  • 결함 예측은 코드 복잡도, 변경 빈도, 결함 이력을 분석하여 주의가 필요한 영역을 사전에 파악합니다

다음 장 미리보기

9장에서는 Agentic QA -- 자율 테스트 에이전트를 다룹니다. 유저 스토리에서 Gherkin 시나리오를 거쳐 실행 가능한 테스트 코드까지 자동 변환하는 에이전트 아키텍처와, Human-on-the-loop 감독 체계를 깊이 살펴봅니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#testing#automation#quality-assurance#ai

관련 글

AI / ML

9장: Agentic QA -- 자율 테스트 에이전트

유저 스토리에서 Gherkin 시나리오를 거쳐 실행 가능한 테스트로 자동 변환하는 Agentic QA의 아키텍처, 자율 탐색 테스트, Human-on-the-loop 감독 체계, 그리고 품질 게이트 통합을 다룹니다.

2026년 3월 20일·22분
AI / ML

7장: 테스트 유지보수 자동화

테스트 로트(Test Rot) 문제의 근본 원인과 AI 기반 셀프 힐링, 셀렉터 자동 재바인딩, 테스트 코드 리팩터링, 중복 테스트 감지, 커버리지 갭 분석 등 유지보수 비용 절감 전략을 다룹니다.

2026년 3월 16일·16분
AI / ML

10장: 실전 프로젝트 -- AI 테스트 자동화 파이프라인

단위, 통합, E2E, 시각적, 변이 테스트를 하나의 AI 테스트 자동화 파이프라인으로 통합합니다. Codium, Playwright, Applitools를 결합한 CI/CD 파이프라인과 대시보드, 도입 로드맵, ROI 측정을 다룹니다.

2026년 3월 22일·23분
이전 글7장: 테스트 유지보수 자동화
다음 글9장: Agentic QA -- 자율 테스트 에이전트

댓글

목차

약 17분 남음
  • 학습 목표
  • AI QA 파이프라인 개요
    • 기존 파이프라인과의 비교
  • 변경 영향 분석 기반 테스트 선택
    • 분석 기법
    • 구현 예시
  • 위험 기반 테스트 우선순위
    • 우선순위 결정 요소
    • 우선순위 알고리즘
  • 플레이키 테스트 자동 격리
    • 플레이키 감지 알고리즘
    • 격리 및 관리 전략
  • 병렬 실행 최적화
    • 샤딩 전략
  • 결함 예측
    • 예측 모델의 입력
  • GitHub Actions 통합 전체 파이프라인
  • 정리
    • 다음 장 미리보기