Communitygithub.com

ai-regression-testing

AI 기반 개발을 위한 회귀 테스트 전략. 데이터베이스 의존성 없는 샌드박스 모드 API 테스트, 자동 버그 검사 워크플로우, 동일 모델이 코드 작성과 리뷰를 모두 수행할 때 발생하는 AI 사각지대를 잡아내는 패턴을 제공합니다. AI 지원 코드에 회귀 테스트 커버리지를 추가하거나, 동일 모델이 변경 사항을 작성하고 리뷰한 경우에 사용하세요.

ai-regression-testing란 무엇인가요?

AI 회귀 테스팅은 동일한 AI 모델이 코드를 작성하고 리뷰할 때 발생하는 체계적인 사각지대를 자동화된 테스트로 잡아내기 위해 설계된 전문 기술입니다. Claude Code, Cursor, Codex와 같은 AI 에이전트가 API 라우트나 백엔드 로직을 수정했거나, 버그가 발견되어 수정된 후 재발을 방지해야 할 때 활성화됩니다. 핵심 문제는 AI가 코드를 작성하고 스스로 리뷰할 때 동일한 가정을 반복하여 샌드박스/프로덕션 경로 불일치, SELECT 절 누락, 오류 상태 누수, 낙관적 업데이트 롤백 누락과 같은 패턴의 버그를 놓친다는 점입니다. 이 기술은 Vitest + Next.js App Router를 위한 설정 가이드, 샌드박스 모드 API 테스트 헬퍼, 수정된 버그 각각에 대한 회귀 테스트 작성 방법, 그리고 `/bug-check` 워크플로우에 테스트를 통합하는 사용자 정의 명령 정의를 제공합니다. 목표는 100% 커버리지가 아니라, 과거에 발견된 버그 범주에 대해 정확한 회귀 방지를 보장하는 것입니다.

지원 대상✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/ai-regression-testing

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

AI 회귀 테스팅

동일한 AI 모델이 코드를 작성하고 리뷰할 때 발생하는 체계적인 사각지대를 자동화된 테스트로만 잡아낼 수 있도록 설계된 테스팅 패턴입니다.

활성화 시점

  • AI 에이전트(Claude Code, Cursor, Codex)가 API 라우트 또는 백엔드 로직을 수정한 경우
  • 버그를 발견하여 수정한 후 재발을 방지해야 하는 경우
  • 프로젝트에 DB 없이 테스트할 수 있는 샌드박스/모의 모드가 있는 경우
  • 코드 변경 후 /bug-check 또는 유사한 리뷰 명령을 실행하는 경우
  • 여러 코드 경로가 존재하는 경우(샌드박스 vs 프로덕션, 기능 플래그 등)

핵심 문제

AI가 코드를 작성하고 자신의 작업을 리뷰할 때, 두 단계 모두에서 동일한 가정을 그대로 가져옵니다. 이는 예측 가능한 실패 패턴을 만듭니다:

AI가 수정 작성 → AI가 수정 리뷰 → AI가 "올바른 것 같다"고 판단 → 버그 여전히 존재

실제 사례(프로덕션에서 관찰됨):

수정 1: API 응답에 notification_settings 추가
  → SELECT 쿼리에 추가하는 것을 누락
  → AI가 리뷰했지만 놓침 (동일한 사각지대)

수정 2: SELECT 쿼리에 추가
  → TypeScript 빌드 오류 (열이 생성된 타입에 없음)
  → AI가 수정 1을 리뷰했지만 SELECT 문제를 잡지 못함

수정 3: SELECT *로 변경
  → 프로덕션 경로는 수정했지만 샌드박스 경로는 누락
  → AI가 리뷰했지만 또 다시 놓침 (4번째 발생)

수정 4: 테스트가 첫 실행에서 즉시 발견하여 통과:

패턴: 샌드박스/프로덕션 경로 불일치는 AI가 도입하는 회귀 중 1순위입니다.

샌드박스 모드 API 테스팅

AI 친화적인 아키텍처를 가진 대부분의 프로젝트에는 샌드박스/모의 모드가 있습니다. 이것이 빠르고 DB 없이 API를 테스트하는 핵심입니다.

설정 (Vitest + Next.js App Router)

// vitest.config.ts
import { defineConfig } from "vitest/config";
import path from "path";

export default defineConfig({
  test: {
    environment: "node",
    globals: true,
    include: ["__tests__/**/*.test.ts"],
    setupFiles: ["__tests__/setup.ts"],
  },
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "."),
    },
  },
});
// __tests__/setup.ts
// 샌드박스 모드 강제 — 데이터베이스 불필요
process.env.SANDBOX_MODE = "true";
process.env.NEXT_PUBLIC_SUPABASE_URL = "";
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = "";

Next.js API 라우트용 테스트 헬퍼

// __tests__/helpers.ts
import { NextRequest } from "next/server";

export function createTestRequest(
  url: string,
  options?: {
    method?: string;
    body?: Record<string, unknown>;
    headers?: Record<string, string>;
    sandboxUserId?: string;
  },
): NextRequest {
  const { method = "GET", body, headers = {}, sandboxUserId } = options || {};
  const fullUrl = url.startsWith("http") ? url : `http://localhost:3000${url}`;
  const reqHeaders: Record<string, string> = { ...headers };

  if (sandboxUserId) {
    reqHeaders["x-sandbox-user-id"] = sandboxUserId;
  }

  const init: { method: string; headers: Record<string, string>; body?: string } = {
    method,
    headers: reqHeaders,
  };

  if (body) {
    init.body = JSON.stringify(body);
    reqHeaders["content-type"] = "application/json";
  }

  return new NextRequest(fullUrl, init);
}

export async function parseResponse(response: Response) {
  const json = await response.json();
  return { status: response.status, json };
}

회귀 테스트 작성

핵심 원칙: 올바르게 작동하는 코드가 아니라 발견된 버그에 대한 테스트를 작성하세요.

// __tests__/api/user/profile.test.ts
import { describe, it, expect } from "vitest";
import { createTestRequest, parseResponse } from "../../helpers";
import { GET, PATCH } from "@/app/api/user/profile/route";

// 계약 정의 — 응답에 반드시 있어야 하는 필드
const REQUIRED_FIELDS = [
  "id",
  "email",
  "full_name",
  "phone",
  "role",
  "created_at",
  "avatar_url",
  "notification_settings",  // ← 버그로 누락된 것이 발견되어 추가됨
];

describe("GET /api/user/profile", () => {
  it("모든 필수 필드를 반환합니다", async () => {
    const req = createTestRequest("/api/user/profile");
    const res = await GET(req);
    const { status, json } = await parseResponse(res);

    expect(status).toBe(200);
    for (const field of REQUIRED_FIELDS) {
      expect(json.data).toHaveProperty(field);
    }
  });

  // 회귀 테스트 — AI가 4번 도입한 정확한 버그
  it("notification_settings가 undefined가 아닙니다 (BUG-R1 회귀)", async () => {
    const req = createTestRequest("/api/user/profile");
    const res = await GET(req);
    const { json } = await parseResponse(res);

    expect("notification_settings" in json.data).toBe(true);
    const ns = json.data.notification_settings;
    expect(ns === null || typeof ns === "object").toBe(true);
  });
});

샌드박스/프로덕션 일치성 테스트

가장 흔한 AI 회귀: 프로덕션 경로는 수정했지만 샌드박스 경로를 잊어버리거나(또는 그 반대).

// 샌드박스 응답이 예상 계약과 일치하는지 테스트
describe("GET /api/user/messages (대화 목록)", () => {
  it("샌드박스 모드에서 partner_name을 포함합니다", async () => {
    const req = createTestRequest("/api/user/messages", {
      sandboxUserId: "user-001",
    });
    const res = await GET(req);
    const { json } = await parseResponse(res);

    // 이 테스트는 partner_name이 프로덕션 경로에는 추가되었지만
    // 샌드박스 경로에는 추가되지 않은 버그를 잡았습니다.
    if (json.data.length > 0) {
      for (const conv of json.data) {
        expect("partner_name" in conv).toBe(true);
      }
    }
  });
});

버그 검사 워크플로우에 테스트 통합

사용자 정의 명령 정의

<!-- .claude/commands/bug-check.md -->
# 버그 검사

## 1단계: 자동화된 테스트 (필수, 건너뛸 수 없음)

코드 리뷰 전에 먼저 다음 명령을 실행하세요:

    npm run test       # Vitest 테스트 스위트
    npm run build      # TypeScript 타입 검사 + 빌드

- 테스트 실패 시 → 최우선 버그로 보고
- 빌드 실패 시 → 타입 오류를 최우선으로 보고
- 두 단계 모두 통과한 경우에만 2단계로 진행

## 2단계: 코드 리뷰 (AI 리뷰)

1. 샌드박스 / 프로덕션 경로 일관성
2. API 응답 형태가 프론트엔드 기대치와 일치하는지
3. SELECT 절 완전성
4. 롤백이 있는 오류 처리
5. 낙관적 업데이트 경쟁 조건

## 3단계: 수정된 각 버그에 대해 회귀 테스트 제안

워크플로우

사용자: "버그 체크해줘" (또는 "/bug-check")
  │
  ├─ 1단계: npm run test
  │   ├─ 실패 → 기계적으로 버그 발견 (AI 판단 불필요)
  │   └─ 통과 → 계속
  │
  ├─ 2단계: npm run build
  │   ├─ 실패 → 기계적으로 타입 오류 발견
  │   └─ 통과 → 계속
  │
  ├─ 3단계: AI 코드 리뷰 (알려진 사각지대를 염두에 두고)
  │   └─ 발견 사항 보고
  │
  └─ 4단계: 각 수정에 대해 회귀 테스트 작성
      └─ 다음 버그 검사에서 수정이 깨지는지 확인

일반적인 AI 회귀 패턴

패턴 1: 샌드박스/프로덕션 경로 불일치

빈도: 가장 흔함 (회귀 4건 중 3건에서 관찰됨)

// 실패: AI가 프로덕션 경로에만 필드를 추가함
if (isSandboxMode()) {
  return { data: { id, email, name } };  // 새 필드 누락
}
// 프로덕션 경로
return { data: { id, email, name, notification_settings } };

// 통과: 두 경로 모두 동일한 형태를 반환해야 함
if (isSandboxMode()) {
  return { data: { id, email, name, notification_settings: null } };
}
return { data: { id, email, name, notification_settings } };

잡아내는 테스트:

it("샌드박스와 프로덕션이 동일한 필드를 반환합니다", async () => {
  // 테스트 환경에서는 샌드박스 모드가 강제로 켜짐
  const res = await GET(createTestRequest("/api/user/profile"));
  const { json } = await parseResponse(res);

  for (const field of REQUIRED_FIELDS) {
    expect(json.data).toHaveProperty(field);
  }
});

패턴 2: SELECT 절 누락

빈도: Supabase/Prisma에서 새 열을 추가할 때 흔함

// 실패: 새 열이 응답에 추가되었지만 SELECT에는 없음
const { data } = await supabase
  .from("users")
  .select("id, email, name")  // notification_settings 없음
  .single();

return { data: { ...data, notification_settings: data.notification_settings } };
// → notification_settings는 항상 undefined

// 통과: SELECT * 사용 또는 새 열을 명시적으로 포함
const { data } = await supabase
  .from("users")
  .select("*")
  .single();

패턴 3: 오류 상태 누수

빈도: 보통 — 기존 컴포넌트에 오류 처리를 추가할 때 발생

// 실패: 오류 상태는 설정되었지만 이전 데이터가 지워지지 않음
catch (err) {
  setError("로드 실패");
  // reservations는 여전히 이전 탭의 데이터를 표시!
}

// 통과: 오류 발생 시 관련 상태를 지움
catch (err) {
  setReservations([]);  // 오래된 데이터 지우기
  setError("로드 실패");
}

패턴 4: 적절한 롤백 없는 낙관적 업데이트

// 실패: 실패 시 롤백 없음
const handleRemove = async (id: string) => {
  setItems(prev => prev.filter(i => i.id !== id));
  await fetch(`/api/items/${id}`, { method: "DELETE" });
  // API가 실패하면 UI에서는 항목이 사라졌지만 DB에는 여전히 존재
};

// 통과: 이전 상태를 캡처하고 실패 시 롤백
const handleRemove = async (id: string) => {
  const prevItems = [...items];
  setItems(prev => prev.filter(i => i.id !== id));
  try {
    const res = await fetch(`/api/items/${id}`, { method: "DELETE" });
    if (!res.ok) throw new Error("API 오류");
  } catch {
    setItems(prevItems);  // 롤백
    alert("삭제에 실패했습니다");
  }
};

전략: 버그가 발견된 곳을 테스트하세요

100% 커버리지를 목표로 하지 마세요. 대신:

/api/user/profile에서 버그 발견  → profile API에 대한 테스트 작성
/api/user/messages에서 버그 발견 → messages API에 대한 테스트 작성
/api/user/favorites에서 버그 발견 → favorites API에 대한 테스트 작성
/api/user/notifications에 버그 없음 → 테스트 작성하지 않음 (아직은)

AI 개발에 이 전략이 효과적인 이유:

  1. AI는 동일한 유형의 실수를 반복하는 경향이 있음
  2. 버그는 복잡한 영역(인증, 다중 경로 로직, 상태 관리)에 집중됨
  3. 일단 테스트되면, 그 정확한 회귀는 다시 발생할 수 없음
  4. 테스트 수는 버그 수정과 함께 자연스럽게 증가 — 낭비되는 노력 없음

빠른 참조

AI 회귀 패턴테스트 전략우선순위
샌드박스/프로덕션 불일치샌드박스 모드에서 동일한 응답 형태 단언높음
SELECT 절 누락응답의 모든 필수 필드 단언높음
오류 상태 누수오류 발생 시 상태 정리 단언중간
롤백 누락API 실패 시 상태 복원 단언중간
타입 캐스트로 인한 null 마스킹필드가 undefined가 아님을 단언중간

DO / DON'T

DO:

  • 버그를 찾은 후 즉시 테스트 작성 (가능하면 수정하기 전에)
  • 구현이 아닌 API 응답 형태를 테스트
  • 모든 버그 검사의 첫 단계로 테스트 실행
  • 테스트를 빠르게 유지 (샌드박스 모드에서 총 < 1초)
  • 방지하는 버그의 이름을 테스트에 지정 (예: "BUG-R1 회귀")

DON'T:

  • 한 번도 버그가 발생하지 않은 코드에 대한 테스트 작성
  • AI 자체 리뷰를 자동화된 테스트의 대체 수단으로 신뢰
  • "그냥 모의 데이터일 뿐"이라며 샌드박스 경로 테스트 건너뛰기
  • 단위 테스트로 충분할 때 통합 테스트 작성
  • 커버리지 백분율을 목표로 삼지 말고 회귀 방지를 목표로 삼기

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

관련 스킬