- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며
- 1. 테스트가 사는 이유: 회귀 방어인가 설계 압력인가
- 2. 피라미드와 트로피 — 논쟁의 실제 축
- 3. '단위'라는 말이 합의된 적이 없다
- 4. 비용 함수로 보기: 작성·실행·유지·오탐
- 5. 무엇을 목(mock)으로 대체할 것인가
- 6. 커버리지 숫자를 다루는 법
- 7. 결함이 실제로 어디서 나오는가 — 데이터로 비율 정하기
- 8. 느리고 깨지는 테스트를 관리하는 운영 규칙
- 9. 팀의 테스트 전략을 한 장으로 쓰기
- 퀴즈: 실력을 확인해 보세요
- 마치며
- 참고 자료
- 이어서 읽기
들어가며
이 블로그에는 이미 테스트를 다룬 한국어 완전 가이드가 여러 편 있습니다. 소프트웨어 테스팅 전략 완전 가이드는 어떤 테스트 종류가 있고 어떻게 쓰는지를, 속성 기반 테스트 실전은 특정 기법을, 검증 설계 — 테스트를 신뢰 근거로 보기는 검증을 개인 역량 관점에서 다룹니다. 세 글 모두 어떤 테스트가 존재하고 어떻게 작성하는가에 관한 글입니다.
이 글은 다릅니다. 팀이 자기 비율을 스스로 정하는 방법을 다룹니다. 피라미드와 트로피 중 하나를 고르지 않고, 그 논쟁이 실제로는 어떤 축들 위에서 벌어지는지를 분해합니다. 축을 알면 "우리 스택에서는 어느 쪽인가"를 스스로 답할 수 있고, 그게 남의 결론을 복사하는 것보다 오래갑니다.
먼저 결론을 밝히면 이렇습니다. 비율은 답이 아니라 결과입니다. 결함이 어디서 나오는지, 통합 환경 구성이 얼마나 비싼지, E2E가 얼마나 자주 헛발질하는지를 측정하면 비율은 자동으로 도출됩니다.
1. 테스트가 사는 이유: 회귀 방어인가 설계 압력인가
테스트 논쟁이 겉돌 때는 대개 참여자들이 서로 다른 목적을 전제하고 있습니다. 테스트가 존재하는 이유는 크게 넷이고, 목적이 다르면 좋은 테스트의 모양도 달라집니다.
- 회귀 방어: 어제까지 되던 것이 오늘 깨졌는지 알려 줍니다. 이 목적에서는 테스트가 구현이 아니라 외부에서 관찰 가능한 행동에 붙어야 합니다. 리팩터링할 때 같이 고쳐야 하는 테스트는 회귀 방어 자산이 아니라 부채입니다.
- 설계 압력: 테스트하기 어려운 코드는 대개 결합이 심한 코드입니다. 먼저 테스트를 쓰면 인터페이스가 사용자 관점에서 다듬어집니다. 이 목적에서는 세밀한 단위 테스트가 유리하고, 테스트가 구현에 어느 정도 붙는 것도 감수합니다.
- 명세와 문서: 코드가 무엇을 보장하는지를 실행 가능한 형태로 남깁니다. 읽히는 것이 목적이라 이름과 배치가 중요합니다.
- 배포 신뢰: 사람이 확인하지 않고도 내보낼 수 있게 만듭니다. 여기서는 통합·계약·E2E 쪽 비중이 커집니다.
여기서 첫 번째 갈등이 보입니다. 회귀 방어를 중시하면 테스트를 구현에서 떼어내려 하고, 설계 압력을 중시하면 테스트를 구현 가까이 붙입니다. 같은 코드베이스에서 두 목적이 충돌하면 "이 테스트는 리팩터링 때 같이 고쳐도 되는가"라는 질문 하나로 정리할 수 있습니다. 회귀 방어용이면 안 되고, 설계 압력용이면 괜찮습니다. 팀이 이 구분에 이름을 붙여 두면 리뷰에서 논쟁이 절반으로 줄어듭니다.
2. 피라미드와 트로피 — 논쟁의 실제 축
2-1. 두 주장의 원문
테스트 피라미드는 Martin Fowler의 정리가 널리 인용됩니다. 핵심은 한 문장입니다. "Write lots of small and fast unit tests. Write some more coarse-grained tests and very few high-level tests that test your application from end to end." 위로 갈수록 느리고 비싸므로 수를 줄이라는 것입니다.
주목할 점은 같은 글이 자기 모델의 한계를 스스로 인정한다는 것입니다. "the concept of the test pyramid falls a little short if you take a closer look"이라고 적고, 중간층을 가리키는 "service test is a term that is hard to grasp"이라고 말하며, 최신 프런트엔드 환경에서는 "UI tests don't have to be on the highest level"이라고 덧붙입니다. 최상단 E2E에 대해서는 "notoriously flaky and often fail for unexpected and unforeseeable reasons"이며 "require a lot of maintenance and run pretty slowly"라고 평가합니다.
테스팅 트로피는 Kent C. Dodds의 정리입니다. 정적 검사, 단위, 통합, E2E의 네 층으로 나누고 통합 층을 가장 크게 잡습니다. 근거는 투자 대비 신뢰도이며, 그의 표현으로는 "The more your tests resemble the way your software is used, the more confidence they can give you"입니다.
2-2. 논쟁을 끝내려는 시도가 오히려 문제라는 지적
중요한 것은 트로피 쪽도 이 논쟁을 정답 찾기로 보지 않는다는 점입니다. Dodds는 같은 글에서 단위 테스트의 정의가 "24 different definitions of unit test"만큼 갈린다고 소개하고, "Any attempt to come to a single definition for all these terms is a futile endeavor"라고 씁니다. 그리고 Justin Searls의 말을 인용합니다. "People love debating what percentage of which type of tests to write, but it's a distraction."
Fowler 쪽도 마찬가지입니다. "If you ask three different people what 'unit' means in the context of unit tests, you'll probably receive four different, slightly nuanced answers."
즉 두 진영의 대표 문헌이 모두 "비율 논쟁은 본질이 아니다"라고 말하고 있습니다. 그런데도 현장의 논쟁이 끝나지 않는 이유는, 비율이라는 한 숫자 뒤에 서로 다른 여러 조건이 숨어 있기 때문입니다.
2-3. 실제 갈림 축 네 가지
축 1 '단위'의 경계를 어디로 잡는가
함수 하나 = 단위 → 단위 테스트 수가 폭증, 피라미드처럼 보임
모듈/기능 = 단위 → 같은 테스트가 '통합'으로 분류됨, 트로피처럼 보임
축 2 통합 환경 구성 비용이 얼마인가
컨테이너로 30초 → 통합 비중을 올리는 쪽이 합리적
전용 스테이징 필요 → 통합 비중을 올리면 피드백 주기가 무너짐
축 3 E2E 층의 오탐률이 얼마인가
1% 미만 → E2E를 신뢰 근거로 쓸 수 있음
10% 이상 → 신호가 아니라 소음, 층 자체를 줄여야 함
축 4 테스트의 주된 목적이 무엇인가
회귀 방어 → 행동 중심, 큰 단위, 목 최소화
설계 피드백 → 구현 근처, 작은 단위, 목 허용
네 축에 팀의 실제 값을 넣으면 비율은 논쟁 없이 나옵니다. 컨테이너 기반 통합 환경이 30초에 뜨고, E2E 오탐률이 8%이고, 팀의 주 목적이 회귀 방어라면 결과는 트로피에 가까운 모양이 됩니다. 반대로 도메인 로직이 두껍고 통합 환경이 비싸면 피라미드에 가까워집니다. 어느 쪽도 틀리지 않았고, 두 글이 서로 다른 축 값을 가진 코드베이스를 전제했을 뿐입니다.
2-4. 옮길 때 반드시 확인할 것
Dodds 본인도 트로피가 개별 코드베이스를 대상으로 한 모델이며 마이크로서비스나 서버리스 구성에 그대로 적용되지 않을 수 있다고 밝힙니다. 어떤 모델이든 원문이 전제한 맥락을 함께 옮겨야 합니다. 그림만 복사하면 남의 축 값으로 우리 비율을 정하는 셈이 됩니다.
3. '단위'라는 말이 합의된 적이 없다
3-1. 정의 스펙트럼
비율 논쟁의 절반은 용어 문제입니다. 같은 테스트가 팀에 따라 단위로도 통합으로도 분류됩니다.
좁음 ←──────────────────────────────────────────────→ 넓음
함수 하나 클래스 하나 모듈/기능 하나 프로세스 하나
협력자 전부 목 일부 목 내부는 실제 외부만 대역
DB는 인메모리 DB는 컨테이너
Fowler와 Dodds가 모두 지적하듯 이 스펙트럼 어디에도 표준 경계는 없습니다. 그래서 "단위 테스트 비중 70%"라는 목표는 경계를 정하기 전에는 아무 의미가 없습니다. 경계를 옮기는 것만으로 같은 코드베이스의 숫자가 20%에서 80%로 움직입니다.
3-2. 이름 대신 성질로 부르기
실무에서 잘 통하는 우회로는 분류 이름을 버리고 성질로 부르는 것입니다.
- 격리 수준: 이 테스트가 실제로 실행하는 코드의 범위는 어디까지인가.
- 실행 시간: 밀리초인가, 초인가, 분인가.
- 환경 요구: 프로세스 안에서 끝나는가, 외부 프로세스가 필요한가, 네트워크가 필요한가.
- 결정성: 같은 입력에 항상 같은 결과인가.
이 네 값이 정해지면 이름은 필요 없습니다. CI 파이프라인의 단계 구성도 이름이 아니라 이 값으로 나눕니다. "3초 안에 끝나고 외부 의존이 없는 테스트"는 커밋마다 돌리고, "컨테이너가 필요한 테스트"는 병합 전에 돌리고, "스테이징이 필요한 테스트"는 배포 파이프라인에서 돌립니다.
4. 비용 함수로 보기: 작성·실행·유지·오탐
테스트를 자산이 아니라 비용과 수익이 함께 있는 계약으로 보면 결정이 쉬워집니다. 비용은 네 항목입니다.
- 작성 비용: 한 번만 지불합니다. 가장 눈에 띄지만 총비용에서 차지하는 비중은 대체로 가장 작습니다.
- 실행 비용: 매 커밋마다 지불합니다. 하루 50번 도는 파이프라인에서 테스트 하나가 2초 느려지면 연간 수 시간이 사라집니다. 더 큰 손실은 대기 시간이 길어져 개발자가 컨텍스트를 잃는 것입니다.
- 유지 비용: 코드가 바뀔 때마다 지불합니다. 구현 세부에 붙은 테스트는 리팩터링마다 청구서를 보냅니다. 이 항목이 총비용의 대부분을 차지하는 경우가 흔합니다.
- 오탐 비용: 실패가 진짜인지 확인하는 시간, 그리고 신뢰를 잃는 비용입니다. 마지막 항목이 가장 비쌉니다. 빨간 파이프라인을 습관적으로 재실행하기 시작하면 그 테스트 스위트는 이미 자산이 아닙니다.
수익 항목은 하나입니다. 막아 낸 결함의 기댓값, 즉 그 테스트가 없었다면 프로덕션까지 갔을 결함의 확률과 비용의 곱입니다.
숫자를 넣어 보면 감이 잡힙니다. E2E 테스트 하나를 작성하는 데 4시간, 실행에 40초, 분기마다 두 번 손보는 데 2시간이 든다고 해 봅시다. 하루 30번 도는 파이프라인이면 실행만으로 연간 약 68시간이 사라지고, 유지 비용까지 더하면 1년 총비용은 80시간을 넘습니다. 같은 로직을 검증하는 단위 테스트 대여섯 개의 총비용은 그 절반에도 미치지 않는 경우가 많습니다. 작성 비용만 비교하면 E2E가 싸 보이지만, 총비용에서는 대개 반대입니다.
여기서 실무적으로 유용한 규칙이 나옵니다. 같은 결함을 두 층에서 잡고 있다면 비싼 쪽을 지웁니다. E2E가 검증하는 로직 분기를 단위 테스트가 이미 전부 덮고 있다면 그 E2E는 비용만 남습니다. E2E에는 층 간 연결(라우팅, 인증, 직렬화, 설정)처럼 아래층이 원리적으로 볼 수 없는 것만 남깁니다.
반대 방향의 실수도 흔합니다. 아래층으로 밀 수 없는 검증을 억지로 단위 테스트로 만들면, 목이 실제와 어긋난 순간 전부 초록인데 프로덕션이 깨집니다. 판단 기준은 "이 결함이 이 층에서 원리적으로 보이는가"이지 "이 층이 싼가"가 아닙니다.
5. 무엇을 목(mock)으로 대체할 것인가
목 사용 범위는 이 분야에서 가장 오래된 논쟁 중 하나이고, 여전히 합의가 없습니다.
5-1. 두 입장
- 목을 넓게 쓰는 쪽: 협력자를 대역으로 바꾸면 테스트가 빨라지고 실패 원인이 좁혀집니다. 실패한 테스트 이름만 보고도 어느 단위가 깨졌는지 압니다. 아직 존재하지 않는 협력자에 대해서도 먼저 설계할 수 있습니다.
- 목을 좁게 쓰는 쪽: 목은 내 가정을 검증하는 것이지 상대의 실제 동작을 검증하지 않습니다. 상대가 바뀌어도 목은 조용하므로, 전부 초록인데 프로덕션이 깨지는 상황이 만들어집니다. 또한 호출 순서와 인자를 검증하는 목은 구현에 강하게 붙어 리팩터링을 막습니다.
5-2. 갈림 축과 실무 기준
논쟁의 축은 대체 대상이 통제 가능한가입니다. 다음 기준이 대체로 잘 작동합니다.
목으로 대체한다 실제를 쓴다
───────────────────── ─────────────────────
외부 시스템 (결제, 메일) 같은 코드베이스 안의 협력자
비결정적 요소 (시각, 난수) 순수 계산과 값 객체
재현하기 어려운 실패 컨테이너로 띄울 수 있는 저장소
느린 I/O (수 초 이상) 인메모리로 대체 가능한 저장소
추가로 지킬 규칙 두 가지가 있습니다. 첫째, 내가 소유하지 않은 인터페이스는 직접 목으로 만들지 않습니다. 외부 SDK를 감싸는 얇은 어댑터를 만들고 그 어댑터를 대역으로 바꿉니다. 외부 SDK의 동작을 상상해 목으로 굳히면 그 상상이 틀렸을 때 아무 테스트도 실패하지 않습니다. 둘째, 목을 썼다면 그 목이 실제와 같다는 사실을 다른 층에서 확인해야 합니다. 계약 테스트나 소수의 실통신 테스트가 그 역할을 합니다.
5-3. 목 대신 쓸 수 있는 대역들
목 하나로 뭉뚱그리면 논쟁이 길어집니다. 대역에도 종류가 있고 유지 비용이 다릅니다.
- 스텁: 정해진 값을 돌려주기만 합니다. 호출 방식을 검증하지 않으므로 구현 변경에 강합니다. 대부분의 경우 이것으로 충분합니다.
- 가짜 구현: 실제와 같은 계약을 만족하는 가벼운 구현입니다. 인메모리 저장소가 대표적입니다. 만드는 비용은 크지만 여러 테스트가 공유하므로 총비용은 낮아지고, 계약이 코드로 남아 검증도 가능합니다.
- 엄격한 목: 호출 횟수와 인자를 단언합니다. "부작용이 실제로 일어났는가"가 검증 대상일 때만 씁니다. 그 외에는 구현에 붙어 리팩터링을 막는 쪽으로 작동합니다.
- 실제 컨테이너: 저장소나 메시지 브로커처럼 컨테이너로 수십 초 안에 뜨는 것은 그냥 실제를 쓰는 편이 정확합니다.
"목을 쓸 것인가"보다 "어떤 대역이 이 검증에 필요한 최소인가"가 더 좋은 질문입니다. 대부분의 논쟁은 엄격한 목을 기본값으로 쓰는 습관에서 시작됩니다.
6. 커버리지 숫자를 다루는 법
커버리지 목표치 역시 합의가 없는 영역입니다. 논쟁의 축을 먼저 정리합니다.
- 목표치를 두자는 쪽: 숫자가 없으면 테스트가 없는 영역이 조용히 자랍니다. 하한선은 최소한의 규율을 만듭니다.
- 목표치가 해롭다는 쪽: 커버리지는 실행된 줄을 셀 뿐 검증된 동작을 세지 않습니다. 목표를 지표로 삼는 순간 단언 없는 테스트, 게터 테스트, 생성된 코드 포함 같은 우회로가 등장합니다.
양쪽 다 맞습니다. 그래서 숫자를 쓰되 쓰는 방식을 바꾸는 것이 현실적입니다.
- 절대값 목표 대신 변화량 관문: 전체 80% 같은 목표 대신 "이번 변경에서 새로 추가된 줄의 커버리지"를 봅니다. 기존 부채를 건드리지 않으면서 새 코드의 품질만 관리할 수 있고, 우회 동기도 훨씬 약합니다.
- 낮은 쪽을 조사 신호로만 사용: 커버리지가 낮은 모듈은 벌점 대상이 아니라 조사 대상입니다. 테스트가 어려운 구조라는 신호일 때가 많습니다.
- 높은 숫자를 신뢰의 근거로 쓰지 않기: 90%는 "10%가 위험하다"가 아니라 "90%가 실행되었다"는 뜻일 뿐입니다. 실행과 검증은 다릅니다.
- 분기 커버리지를 함께 보기: 줄 커버리지만 보면 조건문의 한쪽만 검증한 상태가 가려집니다.
커버리지가 답하지 못하는 질문은 "이 테스트가 실제로 결함을 잡아내는가"입니다. 이걸 직접 재는 방법도 있습니다. 코드에 작은 변형을 자동으로 심고 테스트가 그 변형을 잡아내는지 보는 방식입니다. 잡아내지 못한 변형이 많다면 실행은 되지만 검증은 없는 코드가 많다는 뜻입니다. 실행 비용이 커서 전체 스위트에 상시 적용하기는 어렵지만, 핵심 도메인 모듈에 한 번만 돌려도 단언이 비어 있는 구역이 드러납니다.
7. 결함이 실제로 어디서 나오는가 — 데이터로 비율 정하기
7-1. 4주면 충분한 기록
비율을 정하는 가장 확실한 방법은 논쟁이 아니라 기록입니다. 프로덕션 결함과 롤백에 다음 두 필드만 붙이면 4주 안에 방향이 보입니다.
- 어느 층이 잡았어야 했는가: 단위 / 통합 / 계약 / E2E / 어느 층도 아님(설계·요구사항 문제).
- 왜 못 잡았는가: 테스트가 없었다 / 있었지만 목이 실제와 달랐다 / 환경 차이 / 데이터 차이 / 동시성 및 타이밍.
두 필드의 분포가 곧 투자처입니다. "통합 층이 잡았어야 했다"가 절반이면 통합을 늘리는 것이 맞고, "목이 실제와 달랐다"가 절반이면 목을 줄이거나 계약 테스트를 넣는 것이 맞습니다. 어느 층도 아님이 절반이면 테스트를 늘려도 결함은 줄지 않습니다. 그때는 요구사항 정의와 설계 리뷰 쪽으로 투자를 옮겨야 합니다.
7-2. 배포 지표와 함께 보기
DORA는 네 가지 지표를 정의합니다. 배포 빈도, 변경 리드 타임(변경이 "committed to version control"에서 "deployed in production"까지 걸리는 시간), 변경 실패율("The ratio of deployments that require immediate intervention following a deployment"), 실패 배포 복구 시간입니다.
테스트 전략의 효과는 변경 실패율과 리드 타임의 조합으로 확인합니다. 테스트를 늘렸는데 변경 실패율이 그대로면 잘못된 층에 투자한 것이고, 변경 실패율은 내려갔는데 리드 타임이 크게 늘었다면 비용이 수익을 넘어선 것입니다. DORA는 이 둘이 상충 관계가 아니라고 말합니다. "DORA's research has repeatedly demonstrated that speed and stability are not tradeoffs." 둘 중 하나가 나빠졌다면 절충이 아니라 설계 문제로 보는 편이 맞습니다.
7-3. 프로덕션도 검증 층이다
모든 결함을 배포 전에 잡을 수는 없습니다. Google SRE 워크북은 카나리를 "a partial and time-limited deployment of a change in a service and its evaluation"으로 정의하고 나머지 집단을 대조군으로 둡니다. 여기서 나온 계산이 유용합니다. 5% 카나리 집단에서 오류율이 20%라도 전체 오류율은 1%에 그칩니다. 즉 카나리는 "결함을 사용자에게 노출하지 않는 장치"가 아니라 노출 규모를 오류 예산 안에 가두는 장치입니다.
같은 문서는 시간에 따른 변화가 관측값의 큰 교란 요인이므로 배포 전후 비교 대신 동시에 도는 대조군과 비교하라고 권합니다. 테스트 층과 배포 층은 대체재가 아니라 보완재이며, E2E를 늘릴지 카나리를 정교하게 만들지는 같은 예산을 두고 겨루는 선택입니다. 오류 예산 관점의 정리는 SLI/SLO/Error Budget에 있습니다.
8. 느리고 깨지는 테스트를 관리하는 운영 규칙
전략 문서보다 실제 품질을 좌우하는 것은 이 운영 규칙들입니다.
- 플래키 테스트에 소유자와 기한을 붙인다: 재실행으로 넘기는 순간 스위트 전체의 신뢰가 깎입니다. 실패율을 자동 수집해 임계치를 넘으면 이슈를 자동 생성하고, 담당자와 기한 없이는 격리하지 않는 규칙이 필요합니다.
- 격리는 삭제 예약이다: 격리한 테스트에 만료일을 붙입니다. 기한까지 고쳐지지 않으면 지웁니다. 영원히 꺼져 있는 테스트는 커버리지 숫자만 부풀리고 아무것도 지키지 않습니다.
- 단계별 시간 예산을 정한다: "커밋 단계 5분, 병합 전 15분" 같은 상한을 두고, 초과하면 테스트를 추가하기 전에 무엇을 뺄지 먼저 정합니다. 예산이 없으면 파이프라인은 단조 증가합니다.
- 가장 느린 20개를 주기적으로 본다: 실행 시간 상위 목록은 대개 몇 개가 전체를 지배합니다. 상위 항목만 손봐도 총시간이 크게 줄어드는 경우가 많습니다.
- 실패 메시지를 자산으로 취급한다: "expected true but got false"만 남기는 테스트는 실패해도 진단에 도움이 되지 않습니다. 실패 시 어떤 입력과 어떤 상태였는지 남기게 하면 디버깅 시간이 줄어듭니다.
- 테스트도 리뷰 대상이다: 테스트 코드는 리뷰에서 가장 자주 건너뛰는 부분이지만, 유지 비용의 대부분이 여기서 발생합니다.
- 공유 상태를 테스트 사이에 남기지 않는다: 순서에 의존하는 테스트는 병렬 실행을 켜는 순간 무작위로 깨집니다. 실행 순서를 무작위로 섞는 옵션을 켜 두면 이 의존을 조기에 드러낼 수 있습니다.
- 시간과 난수를 주입한다: 현재 시각과 난수를 코드가 직접 읽으면 그 테스트는 언젠가 반드시 깨집니다. 월말, 윤년, 시간대 전환에서 터지는 실패는 대부분 여기서 시작됩니다.
Fowler가 E2E를 "notoriously flaky"라고 표현한 것은 도구 문제가 아니라 그 층의 구조적 성질입니다. E2E 층은 늘리는 것이 아니라 좁게 유지하고 강하게 관리하는 층이라고 보는 편이 현실적입니다.
9. 팀의 테스트 전략을 한 장으로 쓰기
전략은 문서가 아니라 합의입니다. 한 장을 넘기면 아무도 읽지 않습니다.
1. 우리가 테스트로 사려는 것
(회귀 방어 / 설계 피드백 / 배포 신뢰 중 우선순위)
2. 층의 정의와 실행 위치
빠른 층: 3초 이내, 외부 의존 없음 → 커밋마다
통합 층: 컨테이너 필요, 30초 이내 → 병합 전
E2E 층: 스테이징 필요, 10분 이내 → 배포 파이프라인
3. 목을 쓰는 곳과 쓰지 않는 곳
대체: 외부 결제·메일, 시각, 난수
실제: 자체 저장소(컨테이너), 내부 모듈
4. 커버리지 규칙
변경분 커버리지 하한만 관문으로 사용, 전체 목표치 없음
5. 시간 예산
커밋 5분 / 병합 전 15분, 초과 시 추가 전에 제거 먼저
6. 플래키 대응
실패율 임계 초과 시 자동 이슈, 격리에는 담당자와 만료일 필수
7. 재검토 주기
분기 1회, 결함 분포 데이터로 2·3항을 갱신
이 문서의 값어치는 내용보다 갱신 이력에 있습니다. 결함 분포가 바뀌면 비율도 바뀌어야 하고, 그 변경 근거가 남아 있어야 다음 사람이 이유를 이해합니다. 리뷰 관점의 운영 규칙은 소프트웨어 테스팅 전략 완전 가이드와 함께 보면 좋습니다.
퀴즈: 실력을 확인해 보세요
퀴즈 1: 옆 팀이 "우리는 트로피를 쓴다"며 통합 비중을 70%로 옮겼습니다. 그대로 따라가도 되는가?
정답: 통합 환경 구성 비용, E2E 오탐률, 단위의 정의, 테스트의 주 목적 네 가지 값을 우리 팀 기준으로 확인한 뒤에 결정합니다.
설명: 트로피와 피라미드는 서로 다른 축 값을 가진 코드베이스를 전제한 모델입니다. 통합 환경이 컨테이너로 30초에 뜨는 팀과 전용 스테이징이 필요한 팀의 최적 비율은 다를 수밖에 없습니다. Dodds 본인도 트로피가 개별 코드베이스를 대상으로 한 모델이며 마이크로서비스 구성에 그대로 적용되지 않을 수 있다고 밝힙니다. 비율은 복사하는 것이 아니라 축 값에서 도출하는 것입니다.
퀴즈 2: 커버리지가 60%에서 85%로 올랐는데 프로덕션 결함은 그대로입니다. 무엇을 확인하나?
정답: 결함이 어느 층에서 잡혔어야 했는지 분포를 먼저 확인합니다.
설명: 커버리지는 실행된 줄을 셀 뿐 검증된 동작을 세지 않습니다. 단언이 약한 테스트가 늘면 숫자는 오르고 결함은 그대로입니다. 더 흔한 경우는 잘못된 층에 투자한 것입니다. 결함의 절반이 통합 지점에서 나오는데 단위 테스트만 늘리면 숫자만 오릅니다. "어느 층이 잡았어야 했는가"와 "왜 못 잡았는가" 두 필드를 4주만 기록해도 방향이 보입니다.
퀴즈 3: 결제 게이트웨이 연동에서 목을 쓸지 실제를 쓸지 정해야 합니다. 판단 기준은?
정답: 외부 시스템이므로 목으로 대체하되, 직접 목을 만들지 말고 얇은 어댑터를 만들어 그 어댑터를 대역으로 바꿉니다. 그리고 목이 실제와 같다는 사실을 별도 층에서 확인합니다.
설명: 외부 시스템은 통제할 수 없고 느리고 비용이 발생하므로 대부분의 테스트에서 대역이 필요합니다. 문제는 내가 소유하지 않은 인터페이스를 상상해 목으로 굳히면 그 상상이 틀렸을 때 아무 테스트도 실패하지 않는다는 점입니다. 어댑터로 경계를 소유한 뒤 대역화하고, 계약 테스트나 소수의 실통신 테스트로 어댑터의 가정을 정기 검증하는 구성이 안전합니다.
퀴즈 4: E2E 스위트가 하루 3회 무작위로 실패합니다. 재실행 자동화를 넣어야 하는가?
정답: 아닙니다. 재실행은 신호를 지우는 조치이고, 실패율을 지표로 수집해 소유자와 기한이 붙은 격리·수정 절차로 보내야 합니다.
설명: 재실행 자동화는 오탐 비용을 겉으로만 낮추고 실제로는 스위트 전체의 신뢰를 깎습니다. 빨간 파이프라인을 습관적으로 다시 돌리기 시작하면 진짜 결함이 섞여 들어와도 구분되지 않습니다. E2E는 구조적으로 오탐이 잦은 층이므로 층을 좁게 유지하고, 아래층이 원리적으로 볼 수 없는 것만 남기는 편이 낫습니다. 격리에는 반드시 만료일을 붙여 영원히 꺼져 있는 테스트가 생기지 않게 합니다.
퀴즈 5: 테스트를 대거 추가한 뒤 변경 실패율은 절반이 됐지만 리드 타임이 2배가 됐습니다. 어떻게 판단하나?
정답: 절충이 아니라 설계 문제로 보고, 어느 테스트가 시간을 지배하는지와 같은 결함을 두 층에서 중복으로 잡고 있는지를 먼저 확인합니다.
설명: DORA는 속도와 안정성이 상충 관계가 아니라고 반복해서 밝혔습니다. 둘 중 하나가 크게 나빠졌다면 대개 파이프라인 구성 문제입니다. 실행 시간 상위 목록은 소수의 테스트가 지배하는 경우가 많고, E2E가 검증하는 분기를 단위 테스트가 이미 전부 덮고 있으면 그 E2E는 비용만 남습니다. 단계별 시간 예산을 정하고 추가 전에 제거를 먼저 결정하는 규칙이 필요합니다.
마치며
피라미드와 트로피 중 무엇이 맞느냐는 질문에는 답이 없습니다. 두 모델의 원저자가 모두 그렇게 말하고 있고, 특히 Dodds가 인용한 문장은 직설적입니다. 비율 논쟁 자체가 주의를 분산시킨다는 것입니다.
대신 답이 있는 질문은 이렇습니다. 우리 팀에서 단위의 경계는 어디인가. 통합 환경을 띄우는 데 얼마가 드는가. E2E의 오탐률은 몇 퍼센트인가. 지난 분기 결함은 어느 층이 잡았어야 했는가. 이 네 질문에 숫자로 답하면 비율은 결론이 아니라 계산 결과로 나옵니다.
그리고 그 계산은 한 번으로 끝나지 않습니다. 스택이 바뀌고, 도구가 빨라지고, 결함 분포가 이동하면 비율도 따라 움직여야 합니다. 테스트 전략의 품질은 비율의 정확도가 아니라 갱신 주기에 달려 있습니다.
참고 자료
- The Practical Test Pyramid — Martin Fowler — 피라미드의 핵심 문장, 모델의 한계에 대한 자기 지적, 단위의 정의가 사람마다 다르다는 지적, E2E의 오탐과 유지 비용 평가를 인용했습니다. 2026-08-15 확인.
- The Testing Trophy and Testing Classifications — Kent C. Dodds — 네 층 구성과 통합 층 중심 주장, 사용 방식을 닮을수록 신뢰가 커진다는 문장, 단위 정의 통일이 무의미하다는 지적, Justin Searls 인용, 개별 코드베이스 전제를 인용했습니다. 2026-08-15 확인.
- DORA — Four keys metrics — 변경 리드 타임과 변경 실패율의 정의, 속도와 안정성이 상충 관계가 아니라는 연구 결과를 인용했습니다. 2026-08-15 확인.
- Google SRE Workbook — Canarying Releases — 카나리의 정의, 5% 집단에서 20% 오류율이 전체 1%가 된다는 계산, 시간에 따른 교란 때문에 전후 비교 대신 동시 대조군을 쓰라는 권고를 인용했습니다. 2026-08-15 확인.
- 네 가지 갈림 축, 비용 함수 네 항목, 결함 기록 두 필드, 한 장짜리 전략 템플릿은 위 자료에 나오는 것이 아니라 이 글에서 정리한 절차입니다.
이어서 읽기
- 이 블로그의 관련 글: 소프트웨어 테스팅 전략 완전 가이드
- 이 블로그의 관련 글: 속성 기반 테스트 실전
- 이 블로그의 관련 글: 검증 설계 — 테스트를 신뢰 근거로 보기
- 이 블로그의 관련 글: SLI/SLO/Error Budget 기반 신뢰성 엔지니어링
- 관련 도구: SLO 오류 예산 계산기
완전 가이드 시리즈