개념
"테스트 100개 있습니다"가 답하지 못하는 것
API 자동화를 어느 정도 쌓은 팀에서 이런 대화가 오간다.
팀장: 우리 API 테스트 얼마나 돼요? QA: 100개쯤 있고 다 통과합니다. 팀장: 그럼 안심해도 되나요? QA: ...
마지막 침묵의 이유는 분명하다. 100이라는 숫자가 "무엇을 덮었는가"를 말해주지 않기 때문이다. 100개가 전부 로그인 API의 정상 케이스일 수도 있고, 결제 API는 하나도 없을 수도 있다.
모듈 6(레슨 5)에서 품질 지표를 배울 때 같은 문제를 봤다 — 개수는 지표가 아니다. 이 레슨은 API 테스트에서 "얼마나 덮었는가"를 실제로 재는 방법을 다룬다.
커버리지는 네 층으로 나뉜다
API 커버리지를 하나의 숫자로 만들려는 시도가 실패하는 이유는, 서로 다른 질문 네 개가 섞여 있기 때문이다.
① 엔드포인트 커버리지 어떤 API를 건드려는 봤는가
↓ (가장 얕다. 그러나 여기가 비면 나머지는 의미 없다)
② 응답·상태 커버리지 각 API의 어떤 결과 경로를 봤는가
↓ (200만 보고 있지 않은가)
③ 파라미터·입력 커버리지 어떤 입력 조합을 봤는가
↓ (경계값·필수/선택·타입)
④ 시나리오 커버리지 실제 사용자 흐름을 이어서 봤는가
(개별 API는 통과하는데 흐름은 깨지는 경우)각 층이 답하는 질문과 재는 법을 표로 정리하면 이렇다.
| 층 | 분모 | 분자 | 재기 난이도 |
|---|---|---|---|
| ① 엔드포인트 | 스펙에 정의된 (경로 × 메서드) 수 | 테스트가 한 번이라도 호출한 수 | 낮음 |
| ② 응답·상태 | 스펙에 정의된 응답 코드 수 | 실제로 발생시킨 응답 수 | 중간 |
| ③ 파라미터 | 필드 × 유효/무효/경계 | 실제로 시도한 조합 | 높음 |
| ④ 시나리오 | 핵심 사용자 여정 수 | 끝까지 검증한 여정 수 | 중간(정의하기 나름) |
①부터 시작하는 것이 정석이다. 재기 쉽고, 여기가 비어 있으면 아래 층을 논할 이유가 없기 때문이다.
엔드포인트 커버리지 — 스펙과 대조한다
모듈 12(레슨 4)에서 OpenAPI 스펙으로 계약을 검증하는 법을 배웠다. 같은 스펙이 커버리지의 분모 로도 쓰인다.
OpenAPI 스펙에 정의된 것 우리 테스트가 호출하는 것
GET /orders ✓
POST /orders ✓
GET /orders/{id} ✓
PATCH /orders/{id} ✗ ← 빈칸
DELETE /orders/{id} ✗ ← 빈칸
POST /orders/{id}/cancel ✗ ← 빈칸
GET /coupons ✓
POST /coupons/apply ✗ ← 빈칸
엔드포인트 커버리지 = 4 / 8 = 50%재는 방법은 두 갈래다.
(a) 정적 대조 — 스펙의 경로 목록과 테스트 코드에서 호출하는 경로 목록을 뽑아 비교한다. 간단한 스크립트로도 된다.
(b) 실행 기록 기반 — 테스트를 돌릴 때 프록시나 서버 로그로 실제 호출된 경로를 수집해 스펙과 대조한다. 정확하지만 준비가 조금 더 든다.
(b)가 더 정직하다. 테스트 코드에 호출 코드가 있어도 조건 분기로 실제로는 안 돌 수 있기 때문이다.
중요한 전제: 이 측정은 스펙이 최신일 때만 의미가 있다. 스펙에 없는 엔드포인트가 운영에 떠 있으면, 커버리지 100%가 나와도 실제로는 절반만 덮은 것이다. 모듈 25(레슨 3)에서 배운 "설명서와 제품의 불일치" 가 여기서 커버리지 지표를 오염시킨다.
실패 경로가 진짜 커버리지다
②층에서 가장 흔한 함정이 이것이다.
200만 테스트하고 있다.
정상 응답은 개발자도 확인한다. QA가 봐야 할 것은 그 바깥이다.
| 확인해야 할 응답 | 구체적으로 |
|---|---|
| 400 검증 오류 | 필수 필드 누락, 타입 불일치, 형식 위반, 길이 초과 |
| 401 / 403 인증·인가 | 토큰 없음·만료·위조, 다른 사용자 자원 접근 |
| 404 | 없는 리소스, 삭제된 리소스 |
| 409 충돌 | 중복 생성, 동시 수정 |
| 422 비즈니스 규칙 위반 | 재고 부족, 쿠폰 만료, 한도 초과 |
| 429 | 레이트 리밋 |
| 5xx 처리 | 의존 서비스 장애 시 어떻게 되는가 |
403이 특히 자주 빠진다. "다른 사용자의 주문을 조회하면 어떻게 되나" 같은 케이스다. UI에서는 그 화면에 갈 방법이 없으니 테스트하지 않는데, API는 직접 호출할 수 있다. 모듈 16(레슨 5)의 OWASP 계열 취약점 중 접근 통제 실패 가 이 지점에서 나온다.
그 밖에 API 테스트에서 놓치기 쉬운 검증 항목들이다.
- 멱등성 — 같은 요청을 두 번 보내면? 결제·주문 생성에서 치명적이다
- 페이지네이션 경계 — 0건, 1건, 마지막 페이지, 범위 밖 페이지
- 정렬·필터 조합 — 값이 없을 때, 잘못된 필드명일 때
- 부분 실패 — 벌크 요청에서 일부만 실패하면 응답이 어떻게 되나
- 필드 누락 vs null vs 빈 문자열 — 모듈 4(레슨 6)에서 본 그 구분
파라미터·입력 커버리지 — 모듈 3이 여기서 쓰인다
③층은 조합 폭발이 일어나는 곳이다. 필드가 10개면 조합은 감당 불가능해진다. 그래서 모듈 3에서 배운 설계 기법을 그대로 적용 한다.
| 기법 | API 테스트에서 |
|---|---|
| 동등분할(레슨 1) | 유효 그룹 / 무효 그룹 / 경계 그룹으로 나눈다 |
| 경계값 분석(레슨 1) | 최소·최대 길이, 0, 음수, 최대 정수 |
| 결정 테이블(레슨 3) | 여러 파라미터의 조합 규칙 |
| 페어와이즈(레슨 5) | 조합 수를 현실적으로 줄인다 |
페어와이즈가 API에서 특히 값을 한다. 파라미터 6개에 각 3값이면 전조합은 729개지만, 페어와이즈로는 20개 안팎으로 줄어든다. 그러면서도 "두 파라미터의 조합에서 나는 결함"의 대부분을 잡는다.
스펙 기반 자동 생성 도구를 쓰는 방법도 있다. OpenAPI 스펙에서 타입· 제약을 읽어 자동으로 무효 입력을 만들어 던지는 방식이다. 사람이 생각하지 못한 입력을 시도한다는 게 장점이고, 생성된 케이스가 우리 비즈니스 규칙까지는 모른다 는 게 한계다. 둘을 함께 쓰는 것이 현실적이다.
실사용 분포와 대조하기 — 가장 값진 확인
여기가 이 레슨에서 가장 실용적인 부분이다. 커버리지를 스펙하고만 대조하면 놓치는 것이 있다.
아무도 안 쓰는 API를 열심히 테스트하고, 가장 많이 쓰이는 API를 대충 테스트하고 있을 수 있다.
운영 로그(또는 API 게이트웨이 통계)에서 엔드포인트별 호출량 을 뽑아 우리 테스트 분포와 나란히 놓아본다.
| 엔드포인트 | 월 호출량 | 테스트 수 | 판정 |
|---|---|---|---|
GET /products | 4,200만 | 3 | 불균형 — 보강 필요 |
POST /orders | 180만 | 12 | 적절 |
GET /orders/{id} | 320만 | 4 | 보강 검토 |
POST /admin/batch | 40 | 15 | 과투자 |
DELETE /coupons/{id} | 12,000 | 0 | 공백 |
이 표를 만들면 대화가 달라진다. "테스트를 늘려야 합니다"가 아니라 "월 4,200만 건 호출되는 상품 조회에 테스트가 3개입니다" 가 된다. 모듈 4에서 배운 리스크 기반 사고에 실사용 데이터 라는 축을 더하는 것이다.
호출량만이 아니라 장애 이력 을 함께 보면 더 좋다. 과거에 사고가 났던 엔드포인트는 다시 날 가능성이 높다.
커버리지를 올리는 순서
무작정 개수를 늘리면 유지보수 부채만 늘어난다(모듈 11 레슨 4). 순서가 있다.
1순위 스펙에 있는데 테스트가 0개인 엔드포인트
→ 빈칸부터 메운다. 가장 값싸고 효과가 크다
2순위 실사용 상위 엔드포인트의 실패 경로
→ 많이 쓰이는 곳의 400·401·403·409
3순위 계약 위반 케이스
→ 스펙과 실제 응답의 불일치 (모듈 12 레슨 4)
4순위 핵심 시나리오(여정) 연결 테스트
→ 개별 API는 통과하는데 흐름이 깨지는 경우
5순위 파라미터 조합 확장
→ 페어와이즈로 현실적인 개수만1순위와 2순위에서 대부분의 이득이 나온다. 3순위 이하는 팀의 여력에 따라 진행한다. 이 순서를 지키지 않고 5순위부터 하는 팀이 의외로 많은데, 조합을 아무리 늘려도 테스트가 0개인 엔드포인트는 여전히 0개 다.
시나리오 커버리지 — 개별 통과와 흐름 통과는 다르다
④층은 개수로 재기 어렵지만 중요하다. 개별 API가 전부 200을 반환해도 이어 붙이면 깨지는 경우가 있다.
개별 테스트 (전부 통과)
POST /cart/items → 201
POST /orders → 201
POST /payments → 201
시나리오 테스트 (실패)
장바구니 담기 → 주문 생성 → 결제
└ 주문 생성 시 장바구니가 비워지지 않아
결제 후에도 장바구니에 상품이 남는다상태 전이와 데이터 일관성 이 시나리오에서만 드러난다. 모듈 3(레슨 3)의 상태 전이 테스트를 API 수준에서 하는 것이라고 봐도 된다.
시나리오 커버리지는 "핵심 사용자 여정 N개 중 M개를 API로 검증"처럼 재는 것이 현실적이다. 모듈 14(레슨 7)에서 SLI를 핵심 여정 단위로 잡은 것과 같은 분류를 쓰면 지표들이 서로 맞물린다.
API 커버리지가 UI 테스트를 줄여주는가
자주 나오는 질문이다. 답은 "부분적으로, 조건부로" 다.
| API로 덮으면 UI에서 줄일 수 있는 것 | UI에서 반드시 남겨야 하는 것 |
|---|---|
| 비즈니스 규칙 검증(할인 계산, 권한) | 화면 렌더링과 레이아웃 |
| 데이터 유효성 검사 | 사용자 상호작용 흐름 |
| 오류 응답 처리 | 브라우저·기기별 동작 |
| 다양한 입력 조합 | 접근성 |
| 상태 전이 | API 응답을 화면이 제대로 그리는가 |
마지막 줄이 핵심이다. API가 옳게 응답해도 화면이 잘못 그릴 수 있다. 그래서 API 커버리지가 올라가면 UI 테스트의 개수 는 줄일 수 있지만 종류 를 없앨 수는 없다. 모듈 11(레슨 1)의 자동화 피라미드가 말하는 바가 정확히 이것이다 — 아래층을 두껍게 하면 위층을 얇게 할 수 있지만, 위층이 0이 되지는 않는다.
숫자를 어디에 보고할 것인가
측정했으면 보이게 만든다. 모듈 22(레슨 8)에서 만든 대시보드에 얹거나, CI 리포트로 남긴다.
보고할 때 좋은 형태는 단일 숫자가 아니라 층별 + 빈칸 목록 이다.
API 테스트 커버리지 (v2.4.0 기준)
엔드포인트 38 / 52 (73%)
실패 경로 주요 엔드포인트 20개 중 14개에 4xx 케이스 존재
시나리오 핵심 여정 8개 중 6개 검증
테스트가 없는 엔드포인트 (14개)
DELETE /coupons/{id} 월 호출 12,000
PATCH /orders/{id} 월 호출 84,000 ← 우선 보강
...빈칸 목록이 함께 있어야 행동으로 이어진다. 모듈 22 레슨 8에서 배운 대로, 행동을 부르지 않는 숫자는 대시보드에 둘 이유가 없다.
함정 — 커버리지가 목표가 되는 순간
마지막으로 경고를 하나 남긴다. 커버리지 지표에는 잘 알려진 부작용이 있다.
1) 숫자가 목표가 되면 얕은 테스트가 늘어난다. 엔드포인트를 한 번씩 호출만 하고 응답을 제대로 검증하지 않는 테스트로도 ①층 커버리지는 100%가 된다. 모듈 18c(레슨 3)에서 배운 가짜 테스트 의 API 버전이다.
2) 스펙이 낡으면 커버리지도 거짓말이 된다. 분모가 틀렸기 때문이다. 스펙 갱신이 커버리지 측정의 전제다.
3) 커버리지 100%가 품질 100%가 아니다. 모듈 25(레슨 3)에서 인증에 대해 한 이야기와 같다 — 최소선을 목표로 삼으면 팀은 최소선으로 수렴한다.
그래서 커버리지는 목표가 아니라 진단 도구 로 쓰는 것이 맞다. "어디가 비어 있는가"를 찾는 데 쓰고, 숫자 자체를 KPI로 걸지 않는다.
실무에서 왜 필요한가
API 테스트를 쌓다 보면 반드시 "이제 충분한가"라는 질문이 온다. 그때 개수로 답하면 대화가 끝나지 않는다. 층별 커버리지와 빈칸 목록으로 답하면 다음에 무엇을 할지가 함께 나온다.
실사용 분포와 대조하는 습관은 특히 값이 크다. 대부분의 팀에서 테스트 분포는 만들기 쉬운 순서 로 형성되어 있지, 중요한 순서가 아니다. 한 번 대조해보면 우선순위가 크게 바뀌는 경우가 많다.
그리고 이 작업은 QA가 조직에 근거를 제시하는 통로다. "테스트 인력이 더 필요합니다"보다 "월 4,200만 건 호출되는 엔드포인트에 실패 경로 테스트가 없습니다" 가 훨씬 강한 문장이다. 모듈 4에서 배운 리스크 기반 논증에 데이터가 붙는 셈이다.