테스트 피라미드 — 어떤 테스트를 얼마나 써야 하는가
“테스트를 얼마나 써야 하나요?” 이 질문에 “많이”라고 답하면 팀은 E2E 테스트를 잔뜩 만들고 빌드가 30분이 넘어가면서 아무도 테스트를 안 돌리게 된다. 테스트는 양이 아니라 구성이 중요하다. 피라미드는 그 구성의 원칙이다.
Mike Cohn의 테스트 피라미드
2009년 Mike Cohn이 제안한 모델이다. 세 레이어로 테스트를 분류하고, 아래로 갈수록 더 많이 써야 한다는 원칙을 담고 있다.
/\
/ \
/ E2E \ ← 소수, 느림, 비쌈
/--------\
/ 통합 테스트 \ ← 중간
/-------------\
/ 단위 테스트 \ ← 다수, 빠름, 싸고 안정적
/-----------------\
각 레이어의 특성은 명확하다.
단위 테스트 (Unit Test)
- 실행 시간: 밀리초 단위
- 외부 의존성 없음 (DB, 네트워크, 파일시스템)
- 특정 로직 하나를 집중적으로 검증
- 실패 원인이 즉시 명확
통합 테스트 (Integration Test)
- 실행 시간: 초 단위
- DB, HTTP API, 메시지 큐 등과 실제 연결
- 여러 컴포넌트가 함께 동작하는지 검증
- 실패 원인을 찾으려면 약간의 조사가 필요
E2E 테스트 (End-to-End Test)
- 실행 시간: 분 단위
- 전체 시스템을 실제 사용자처럼 구동
- 시나리오 단위 검증
- 실패 원인을 찾기 어렵고, 불안정(flaky)하기 쉬움
피라미드를 따라야 하는 이유
피라미드는 비용과 속도의 트레이드오프에서 도출된다.
테스트가 빠를수록 피드백이 빠르다. 단위 테스트 1,000개가 3초 안에 끝나면 코드를 수정할 때마다 돌릴 수 있다. E2E 테스트 50개가 20분 걸리면 배포 전에나 한 번 돌린다. 피드백이 느리면 사이클이 길어지고 문제를 늦게 발견한다.
테스트가 격리될수록 유지보수가 쉽다. 단위 테스트는 한 로직이 바뀌면 그 테스트만 깨진다. E2E 테스트는 UI 레이아웃이 바뀌어도 깨지고, API 응답 필드 이름이 바뀌어도 깨지고, 테스트 환경의 타임존이 달라도 깨진다. 유지보수 비용이 전혀 다르다.
테스트가 많을수록 실행 비용이 올라간다. CI 파이프라인에서 30분 걸리는 빌드는 PR 리뷰 속도를 늦추고, 개발자 생산성을 떨어뜨리고, 인프라 비용도 올린다.
아이스크림 콘 안티패턴
테스트 피라미드를 거꾸로 뒤집은 모양이다.
/------------------\
/ E2E 테스트 \ ← 아주 많음, 빌드가 죽어감
/--------------------\
\ 통합 테스트 조금 /
\------------------/
\ 단위 테스트 없음 /
\--------------/
\ /
\/ ← 수동 테스트로 떠받침
이 패턴에 빠지는 팀의 공통 서사가 있다.
- “빠르게 개발”하다 보니 단위 테스트를 안 썼다
- 버그가 계속 나와서 E2E를 추가했다
- E2E가 쌓이면서 빌드가 느려졌다
- 빌드가 느리니 테스트를 가끔만 돌린다
- 테스트를 안 돌리니 또 버그가 나온다
악순환이다. 수동 테스트로 보완하게 되고, 릴리즈마다 QA 사이클이 길어진다.
아이스크림 콘 안티패턴의 신호:
- CI 빌드가 15분 이상 걸린다
- “일단 배포하고 모니터링 보자”는 말이 자연스럽게 나온다
- 리팩토링할 때 E2E만 믿는다 (“단위 테스트 없어서 무섭지만 E2E는 있으니까”)
- Flaky test 때문에 CI를 재실행하는 일이 잦다
Kent C. Dodds의 트로피 모양
2019년 Kent C. Dodds는 프론트엔드 맥락에서 피라미드를 수정한 “Testing Trophy”를 제안했다.
/\
/ \
/ E2E \
/--------\
/ 통합 테스트 \ ← 가장 많이
/--------------\
/ 단위 테스트 \
/----------------\
/ 정적 분석 (타입) \ ← 기반
/--------------------\
핵심 주장은 통합 테스트가 ROI가 가장 높다는 것이다. 이유가 설득력 있다.
순수 단위 테스트는 각 컴포넌트가 개별적으로 올바른지 확인하지만, 컴포넌트들이 제대로 연결되었는지는 확인하지 못한다. 반면 통합 테스트는 실제 사용 시나리오에 가깝게 여러 컴포넌트를 함께 구동하므로 “실제로 동작하는가”에 대한 신뢰도가 높다.
TypeScript를 사용하는 경우 타입 검사가 정적 분석 레이어 역할을 한다. 컴파일 타임에 잡히는 버그는 테스트로 잡을 필요가 없다.
실무에서의 비율 결정
“몇 퍼센트씩”이라는 황금 비율은 없다. 도메인과 시스템 특성에 따라 달라진다.
도메인 로직이 복잡한 시스템 (핀테크, 보험, 커머스 계산 엔진)
- 단위 테스트 비율을 높인다
- 할인 계산, 이자율 적용, 정책 분기 등은 단위 테스트가 가장 효율적
- 예시 비율: 단위 70% / 통합 25% / E2E 5%
API 서버 중심 시스템 (CRUD 위주 백엔드)
- 통합 테스트 비율을 높인다
- HTTP 요청 → 유효성 검사 → DB 저장 → 응답의 흐름을 통합 테스트로 검증
- 도메인 로직이 많지 않으면 단위 테스트가 중복 검증이 될 수 있음
- 예시 비율: 단위 30% / 통합 60% / E2E 10%
프론트엔드 애플리케이션
- 컴포넌트 통합 테스트 + 정적 분석 조합
- E2E는 핵심 사용자 여정(로그인, 결제)만
- 예시 비율: 단위 20% / 통합(컴포넌트) 70% / E2E 10%
마이크로서비스
- 서비스 내부는 단위 + 통합
- 서비스 간 연동은 계약 테스트(Contract Test)로
- E2E는 최소화, 계약 테스트로 대체
피드백 속도와 신뢰도의 트레이드오프
피라미드의 세 레이어는 속도 대 신뢰도의 스펙트럼 위에 있다.
빠름 ←————————————————→ 느림
단위 통합 E2E
낮은 신뢰도 ←——————————→ 높은 신뢰도
(격리된 환경) (실제 DB) (실제 시스템)
단위 테스트가 빠른 이유는 격리되어 있기 때문이다. 격리되어 있다는 건 실제 환경과 다르다는 뜻이다. Mock으로 대체된 DB가 실제 DB와 동일하게 동작한다는 보장이 없다.
E2E 테스트가 신뢰도가 높은 이유는 실제 환경에서 돌리기 때문이다. 하지만 실제 환경은 느리고, 불안정하고, 관리가 복잡하다.
이 트레이드오프를 이해하면 “어떤 레이어에 얼마나 투자할 것인가”를 의식적으로 결정할 수 있다. 피라미드는 그 결정의 출발점이지 절대 법칙이 아니다.
결론
테스트 전략의 목표는 “버그를 최대한 일찍, 최소한의 비용으로 발견하는 것”이다. 피라미드는 그 목표를 달성하기 위한 실용적인 가이드라인이다.
아이스크림 콘이 되지 않는 것이 먼저다. E2E만 있다면 단위 테스트와 통합 테스트를 추가하고, 빌드 시간을 줄여라. 피드백 루프가 빨라지면 개발 속도가 올라가고, 코드 품질도 따라온다.