← 커리큘럼 홈
★ Stage 6 후속 · BONUS

비개발자가
테스트를 짠다

"테스트는 개발자만 짠다"는 옛 통념. AI에게 시키면 비개발자도 30분에 테스트 셋업. 이 한 페이지로 Vitest + Playwright + AI 활용까지 끝냅니다.

"테스트 없는 코드 = 운에 맡긴 코드"

§1. 왜 테스트인가 (비개발자도 알아야 하는 이유)

"AI가 짠 코드를 어떻게 믿나" — 테스트가 그 답입니다

Stage 5에서 Claude Code가 짠 코드, 진짜 작동하는지 어떻게 압니까? 한 번 손으로 클릭해서 확인? 그건 첫 1주만 가능합니다. 기능이 10개 넘으면 매번 손으로 검증 불가능 → 테스트 코드가 그 검증을 자동화합니다.

🎯 테스트의 진짜 가치 — 3가지
1. AI가 짠 코드 검증 자동화 — 사람이 매번 클릭 안 함
2. 리팩토링 안전망 — 다음에 코드 바꿔도 기존 기능 망가지면 즉시 알림
3. AI에게 "정답 명세" — 테스트가 있으면 AI가 더 정확히 짬

§2. 3종 테스트 — 피라미드 모델

Unit 많이 / Integration 중간 / E2E 적게

🧪
Unit Test (단위 테스트)
도구: Vitest · 속도: ms 단위 · 비중: 70% · 대상: 함수 1개
🔗
Integration Test (통합)
도구: Vitest + Supabase test DB · 속도: 초 단위 · 비중: 20% · 대상: API + DB
🌐
E2E Test (실제 브라우저)
도구: Playwright · 속도: 분 단위 · 비중: 10% · 대상: 사용자 흐름
📐 피라미드 비율의 의미
Unit이 빠르고 싸니까 가장 많이. E2E는 진짜 같지만 느리고 깨지기 쉬워서 핵심 흐름만. 1인 SaaS 시작: Unit 20개 + E2E 3개 (가입·결제·발송)면 충분. Integration은 나중.

§3. Vitest — 단위 테스트 셋업 (5분)

Next.js 15 + Vitest 가장 빠른 셋업

설치 + 첫 테스트 5 min
# 1. 설치 $ pnpm add -D vitest @vitejs/plugin-react jsdom @testing-library/react # 2. vitest.config.ts import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], test: { environment: 'jsdom', globals: true } }); # 3. package.json scripts에 추가 "test": "vitest", "test:ci": "vitest run" # 4. 실행 $ pnpm test # watch 모드 (개발 중) $ pnpm test:ci # CI 모드 (1회 실행)
src/lib/__tests__/calc-billing.test.ts 실전 예시
import { describe, it, expect } from 'vitest'; import { calcMonthlyBilling } from '../billing'; describe('calcMonthlyBilling', () => { it('월 구독료 계산 — 9,900원 정상', () => { expect(calcMonthlyBilling('monthly')).toBe(9900); }); it('연 구독료 계산 — 79,000원 (2개월 무료)', () => { expect(calcMonthlyBilling('yearly')).toBe(79000); }); it('얼리버드 50% 할인 적용', () => { expect(calcMonthlyBilling('monthly', { earlyBird: true })).toBe(4950); }); it('잘못된 plan 입력 시 에러', () => { expect(() => calcMonthlyBilling('invalid' as any)).toThrow(); }); });
💡 테스트 작성 패턴 — Arrange · Act · Assert
모든 테스트가 같은 구조: Arrange (준비)Act (실행)Assert (검증). 이 한 가지만 익히면 모든 테스트가 손에 잡힙니다.

§4. Playwright — 진짜 브라우저로 테스트

실제 사용자가 클릭하는 흐름을 자동화 — 가입→결제→발송

설치 + 첫 E2E 10 min
# 1. 설치 (브라우저 자동 다운로드 ~150MB) $ pnpm add -D @playwright/test $ pnpm exec playwright install chromium # 2. playwright.config.ts (자동 생성) $ pnpm exec playwright init # 3. e2e/signup-payment.spec.ts import { test, expect } from '@playwright/test'; test('가입 → 결제 → 첫 메일 도착 흐름', async ({ page }) => { // 1. 랜딩 방문 await page.goto('/'); await expect(page).toHaveTitle(/뉴스레터/); // 2. 가입 버튼 클릭 → 카카오 로그인 (테스트 계정) await page.click('text=카카오로 시작'); // (실제 카카오는 mock — 테스트 환경에서 우회) // 3. 주제 선택 (IT) await page.click('text=IT'); await page.click('text=다음'); // 4. 결제 페이지 → 토스 위젯 (테스트 모드) await page.click('text=월 9,900원 결제'); // 토스 테스트 카드 입력 (자동) await page.fill('[name="cardNumber"]', '4242424242424242'); await page.click('text=결제 완료'); // 5. 영수증 페이지 도달 검증 await expect(page.locator('text=결제가 완료')).toBeVisible(); }); # 4. 실행 $ pnpm exec playwright test # headless $ pnpm exec playwright test --headed # 브라우저 보면서 $ pnpm exec playwright test --ui # 인터랙티브 UI 모드
🎯 Playwright의 진가 — UI 모드
--ui 옵션이 게임 체인저. 실제 브라우저 클릭하듯 테스트를 보면서 디버깅 가능. 비개발자에게 가장 직관적인 도구. codegen 기능으로 클릭만 하면 테스트 코드 자동 생성도.
codegen — 클릭만으로 테스트 코드 생성 ★ 마법
# 브라우저 열림 → 사용자처럼 클릭 → 코드 자동 기록 $ pnpm exec playwright codegen http://localhost:3000 # 결과: spec.ts 파일이 자동 생성됨 # 그대로 e2e/ 폴더에 저장 → 재실행 가능

§5. AI에게 테스트 짜라고 시키기

"이 함수의 테스트 짜줘" — Claude Code가 가장 잘하는 작업 중 하나

테스트는 코드보다 패턴이 명확해서 AI가 매우 잘 짭니다. 구현 코드를 던지면 80%의 테스트가 자동으로 나옵니다. 사람은 엣지 케이스 1~2개만 추가.

⚡ 단위 테스트 작성 프롬프트 (재사용)
아래 함수의 Vitest 단위 테스트를 작성해줘. [함수 코드 또는 파일 경로]: src/lib/billing.ts의 calcMonthlyBilling 함수 테스트 요구사항: 1. Happy path 3개 (정상 입력) 2. Edge case 3개 (빈 입력, 0원, 음수) 3. Error case 2개 (잘못된 type, null) 각 테스트: - describe로 함수 그룹화 - it로 케이스 한 줄 (한국어 OK) - Arrange / Act / Assert 패턴 - expect로 정확한 값 검증 추가 요구: - 외부 의존성(DB, API)은 모두 mock - 테스트 파일 위치: src/lib/__tests__/billing.test.ts - import 경로 절대 (@/lib/...) 실행 가능한 완성 코드만 출력. 설명 X.
⚡ E2E 테스트 작성 프롬프트 (Playwright)
사용자 흐름의 Playwright E2E 테스트를 작성해줘. 흐름: 신규 사용자 가입 → 주제 선택 → 결제 → 첫 메일 발송 확인 각 단계: 1. page.goto() — 시작 URL 2. page.click() / page.fill() — 사용자 액션 3. expect() — 검증 (페이지 텍스트, URL, 요소 visibility) 요구사항: - 카카오 OAuth는 mock 또는 우회 (테스트 환경) - 토스페이먼츠는 테스트 카드 (4242 4242 4242 4242) - 각 단계 사이 timeout 적절히 (waitForLoadState) - 실패 시 스크린샷 자동 저장 - describe로 시나리오 그룹화 파일: e2e/signup-payment.spec.ts 실행 가능한 코드만. TypeScript.

§6. CI/CD 통합 — Stop Hook + GitHub Actions

테스트는 자동으로 돌아야 의미 있음 — 매 커밋·PR마다

Stage 3의 settings.json Stop Hook에 pnpm test를 추가하면 Claude가 작업 끝낼 때마다 자동 실행. GitHub Actions에 추가하면 push마다 검증.

.github/workflows/test.yml YAML
name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20' } - run: corepack enable - run: pnpm install - run: pnpm test:ci # Vitest - run: pnpm exec playwright install chromium - run: pnpm exec playwright test # E2E - if: failure() uses: actions/upload-artifact@v4 with: name: playwright-report path: playwright-report/
🔄 Stage 3의 Stop Hook과 결합
.claude/settings.json의 Stop Hook을:
"pnpm typecheck && pnpm lint && pnpm build"
에서
"pnpm typecheck && pnpm lint && pnpm test:ci && pnpm build"
로 변경 → Claude가 작업 끝낼 때마다 자동으로 모든 테스트 검증.

§7. 비개발자가 빠지는 5가지 안티패턴

모든 함수에 테스트 짜기 — 단순 getter·setter는 테스트 가치 없음. 비즈니스 로직만 (계산·검증·변환)
구현과 똑같이 짜기 — 테스트가 구현을 그대로 따라하면 의미 X. "이 함수가 무엇을 하는가"의 결과만 검증
외부 API 직접 호출 — 테스트가 Anthropic·Supabase 진짜 호출 → 비용 + 느림. 반드시 mock
테스트끼리 의존 — 테스트 1이 만든 데이터를 테스트 2가 사용 → 순서 망가지면 다 실패. 각 테스트는 독립
실패 시 무시·skip — "나중에 고치자" → 절대 안 고침. 즉시 고치거나 즉시 삭제
🚱 가장 큰 함정 — 테스트 커버리지 100% 강박
"100% 커버리지"는 함정. 핵심 비즈니스 로직 (결제·발송·인증) 100% + 나머지 60% = 충분. UI 컴포넌트·CSS·간단한 컴포넌트는 테스트 ROI 낮음.

§8. 1인 SaaS 테스트 우선순위 — 무엇부터?

우선순위대상도구이유
🥇 1순위 결제 금액 계산·검증 Vitest 틀리면 매출 손해 + 분쟁
🥇 1순위 Webhook 처리 (멱등성) Vitest 중복 처리 시 사용자에게 알림 2번
🥈 2순위 가입→결제→발송 E2E 1개 Playwright 핵심 funnel 막히면 매출 0
🥈 2순위 이메일 발송 함수 Vitest (mock) 발송 실패 시 사용자 항의
🥉 3순위 RLS 정책 검증 Integration 다른 사용자 데이터 노출 방지
4순위 UI 컴포넌트 Vitest + RTL 변화 잦아 ROI 낮음
🎯 시작은 5개 테스트만
Day 1: 1순위 2개 (결제 계산 + Webhook 멱등성) — Vitest로 30분
Day 2: 2순위 E2E 1개 (가입→결제→발송) — Playwright codegen으로 1시간
Day 3: Stop Hook + GitHub Actions에 추가 — 30분
그 후엔 새 기능 추가할 때마다 테스트 1개씩 누적.