Hook
CI 파이프라인이 15분 걸립니다. 그중 12분은 E2E 테스트입니다. 그리고 30%는 랜덤하게 실패합니다. “이 실패가 진짜 버그인가, 아니면 타이밍 이슈인가?” 매 PR마다 이 질문을 던집니다.
전통적 E2E 우선 테스트는 AI 주도 개발에서 더 심각합니다. AI가 만든 코드가 많을수록 검증해야 할 것이 많아지는데, 느린 E2E가 병목이 되기 때문입니다. 이 글에서는 바텀업 레이어드 테스팅으로 이 문제를 해결한 방법을 공유합니다.
TL;DR
- E2E 우선 접근은 느리고 불안정합니다: flaky 30%, CI 차단 빈번
- 4계층 바텀업: Unit → Component → Contract → E2E Smoke (3~10개만)
- 컨트랙트 테스트가 통합 테스트를 대체: 실제 API 호출 없이 데이터 모양 검증
- CDC (Consumer-Driven Contracts) 로 컴포넌트와 API가 동일 스키마 검증
Background: 왜 E2E 우선이 실패하는가
전통적 접근의 문제
전통적 테스트 피라미드:
E2E (많음) ← 느림, 불안정, CI 차단
/ \
/ \
/ 통합 테스트 \ ← DB/API 의존, 환경 민감
/______________\
/ Unit (적음) \ ← 빠르지만 커버리지 낮음
/__________________\
| E2E 문제 | 영향 |
|---|---|
| 느림 | 전체 suite 5~15분 → PR 피드백 지연 |
| 불안정 (flaky) | 타이밍, 네트워크, 환경 → ~30% 랜덤 실패 |
| 디버깅 어려움 | 실패 지점 파악이 힘듦 |
| 비즈니스 로직 검증 부적합 | UI 클릭 흐름은 비즈니스 로직을 직접 검증하지 않음 |
| i18n 검증 부적합 | 로케일별 문자열을 E2E로 검증하는 것은 비효율 |
Solution: 4계층 레이어드 테스팅
역전된 피라미드
E2E Smoke (3~10개) ← 핵심 사용자 여정만, 배포 시
/__________________\
/ Contract (100%) \ ← 모든 엔드포인트, 매 PR
/______________________\
/ Component (80%+) \ ← 단일 컴포넌트, 모킹된 API
/__________________________\
/ Unit (90%+) \ ← 순수 함수, 매 커밋
/______________________________\
계층별 상세
| 레이어 | 내용 | 도구 | 속도 | 커버리지 목표 |
|---|---|---|---|---|
| L0 Unit | 순수 함수 (포매터, 파서, 검증자) | Vitest | <100ms | 90%+ |
| L1 Component | 단일 컴포넌트, 모킹된 API | Playwright CT / Vitest | <500ms | 80%+ |
| L2 Contract | API 응답이 스키마 준수 | Zod (OpenAPI 생성) | <500ms/엔드포인트 | 100% 엔드포인트 |
| L3 E2E Smoke | 핵심 사용자 여정만 | Playwright (배포된 환경) | ~2분 | 5~10개/서비스 |
L0 Unit: 순수 함수
순수 함수는 부작용이 없고 결정론적입니다. 가장 빠르고 안정적인 테스트 계층입니다.
// 포매터, 파서, 검증자, 유틸 함수
describe('formatWeight', () => {
it('정수 파운드를 포맷한다', () => {
expect(formatWeight(135, 'lbs')).toBe('135 lbs');
});
});
L1 Component: 격리된 컴포넌트
단일 컴포넌트를 모킹된 API와 함께 테스트합니다. 실제 API 호출 금지: 데이터 모양은 컨트랙트 테스트가 보장합니다.
// React: Playwright CT
test('운동 카드가 로딩 상태를 표시한다', async ({ mount }) => {
const component = await mount(<WorkoutCard loading={true} />);
await expect(component.getByText('로딩 중')).toBeVisible();
});
L2 Contract: 스키마 준수 검증
API 응답이 OpenAPI/Zod 스키마를 준수하는지 검증합니다. 이것이 통합 테스트를 대체합니다.
API (생산자) 컴포넌트 (소비자)
┌──────────┐ ┌──────────┐
│ 실제 API │──── 응답 ────→ │ 컴포넌트 │
│ 응답 │ │ 테스트 │
└────┬─────┘ └────┬─────┘
│ │
│ ┌──────────┐ │
└── 검증 ──→│ Zod 스키마│←── 검증 ───┘
└──────────┘
CDC (Consumer-Driven Contracts)
CDC의 핵심: 컴포넌트(소비자)와 API(생산자) 양쪽이 동일한 Zod 스키마에 대해 검증합니다:
- 컴포넌트 테스트는 스키마에서 생성된 모킹 데이터 사용 (유효한 모양 보장)
- 컨트랙트 테스트는 실제 API 응답 검증
- 양쪽 통과 시 통합이 보장됨
L3 E2E Smoke: 최후의 수단
핵심 사용자 여정만 E2E로 검증합니다:
| 스모크 시나리오 | 설명 |
|---|---|
| 로그인 → 핵심 기능 → 저장 | 가장 기본적인 사용자 흐름 |
| 결제 (있다면) | 결제는 반드시 E2E |
| 오프라인 전환 (PWA) | 오프라인 동작 검증 |
절칙: E2E에서 비즈니스 로직을 테스트하지 않습니다. 비즈니스 로직은 L0/L1에서, 데이터 모양은 L2에서 검증합니다.
i18n 테스트 전략
느린 E2E로 i18n을 검증하지 않습니다:
| 계층 | i18n 검증 내용 |
|---|---|
| Component | gettext 문자열 해상도 검증 |
| Contract | API 에러 메시지가 영어 msgid인지 확인 |
| (선택) | 로케일 전환 후 렌더링 검증 |
분야별 커버리지 기준
| 영역 | 최소 커버리지 | 이유 |
|---|---|---|
| 결제 | 90% | 금전적 영향 |
| 인증 | 90% | 보안 |
| 핵심 비즈니스 로직 | 80% | 핵심 가치 |
| 유틸리티 | 60% | 부작용 적음 |
CI/CD 통합
| 단계 | 트리거 | 최대 소요 | 차단 |
|---|---|---|---|
test:unit | 매 커밋/푸시 | 10초 | PR |
test:components | 매 PR | 30초 | PR |
test:contracts | 매 PR | 20초 | PR |
openapi:validate | 매 PR | 5초 | PR |
test:e2e | main 머지 시 | 2분 | 배포 |
안티패턴 (금지)
- E2E에서 비즈니스 로직 테스트
- E2E에서만 i18n 테스트
- 컴포넌트 테스트에서 실제 API 호출
Result: 전환 전후
| 지표 | E2E 우선 (Before) | 레이어드 (After) |
|---|---|---|
| CI 시간 (PR당) | 12~15분 | 1~2분 (E2E 제외) |
| flaky 비율 | ~30% | 5% 미만 (스모크 10개만) |
| 커버리지 | E2E에 집중, Unit 낮음 | Unit 90%+, Component 80%+ |
| 데이터 모양 검증 | 수동/암묵적 | 100% 자동 (Contract) |
마치며
테스트 전략을 처음 설계할 때, 당연히 E2E 테스트에 가장 많은 비중을 두었습니다. “실제 사용자 환경에서 검증하는 것이 가장 확실하다”는 믿음 때문이었습니다. 하지만 CI가 15분씩 걸리고 30%가 랜덤하게 실패하는 상황이 반복되면서, 그 믿음이 흔들리기 시작했습니다. 매 PR마다 “이 실패가 진짜 버그인가, 아니면 타이밍 이슈인가?”를 판단하는 데 에너지를 소모하고 있었습니다. E2E가 검증의 마지막 보루가 아니라, 개발 속도를 깎아먹는 병목이 되어 있었습니다.
바텀업 레이어드 테스팅으로 전환하면서 가장 큰 깨달음은, 비즈니스 로직은 순수 함수와 컴포넌트 격리로 검증하는 것이 훨씬 빠르고 안정적이라는 점이었습니다. 컨트랙트 테스트로 데이터 모양을 보장하면 실제 통합을 테스트하지 않아도 됩니다. E2E는 “로그인 → 기능 사용 → 저장” 같은 핵심 여정 몇 개만 스모크로 남겨두면 충분했습니다. Unit 10초, Component 30초면 AI가 만든 코드를 즉시 검증할 수 있게 되자, 개발 주기가 비약적으로 짧아졌습니다.
이 전환은 AI 주도 개발에서 특히 중요한 의미를 갖습니다. AI 에이전트가 생성하는 코드량이 많을수록 검증 주기가 짧아야 하는데, 느린 E2E는 AI가 다음 작업을 진행하지 못하게 막습니다. 레이어드 테스팅은 단순한 테스트 기법이 아니라 AI 개발 속도를 뒷받침하는 인프라입니다. 앞으로 AI가 더 빠르게 코드를 쏟아낼수록, 그 코드를 즉시 검증할 수 있는 빠르고 결정론적인 테스트 계층의 가치는 더욱 커질 것입니다.
| ← 이전 | 시리즈: AI 주도 개발 거버넌스 | 다음 → |
|---|---|---|
| (4) gettext 원문 키 | (5) 레이어드 테스팅 | (6) 3-Tier CI 게이트 |
게이트웨이 On-promise 제품 팀에서 시스템 모니터링 및 관리를 쉽게 다가갈 수 있도록 하기 위한 업무를 하고 있습니다.
Contact: lhjnano@gmail.com