테스트케이스를 기획 리뷰 시점에 먼저 뽑기로 했다
- 테스트
- 설계
들어가며
테스트케이스라고 하면 보통 구현된 코드를 검증하는 문서를 떠올린다. 입력을 넣었을 때 기대한 결과가 나오는지 확인하는 문서이니, 보통 검증할 코드가 있어야 작성할 수 있다고 생각한다. 그래서 기능 하나를 끝낼 때마다 해당 기능의 기획 서술과 구현이 맞는지 확인하고, 모든 구현을 마치면 전체 흐름을 케이스로 적어 한 번 더 검증해왔다.
문제는 구현이 끝난 뒤에 터졌다. 전체 흐름을 케이스로 적는 과정에서, 기획서에 따로 적혀 있던 두 규칙이 한 케이스 안에서 만나는 조합이 나왔다. 그 조합에서 어떤 결과가 나와야 하는지는 기획서 어디에도 없었다. 기획팀에 확인하고, 기획서와 구현을 고치고, 다시 검증했다. 빈틈이 코드 위에서 드러나니 그때마다 이 길을 처음부터 다시 걸어야 했다.
되짚어 보면 빈틈이 보인 건 전체 흐름을 "이렇게 하면 이런 결과가 나와야 한다"는 줄로 적어 봤을 때였다. 규칙 하나하나가 분명해 보여 지나쳤지만, 케이스 한 줄로 옮기자 기대 결과를 채울 근거가 기획서에 없었다. 이 사실을 알아내는 데 코드는 필요 없었고, 기획서만 있으면 됐다. 그렇다면 테스트케이스를 구현이 끝난 뒤가 아니라 기획 리뷰 시점에 먼저 작성하면, 같은 빈틈을 구현에 들어가기 전에 찾아낼 수 있지 않을까?
규칙 하나하나는 맞았지만 규칙 사이는 비어 있었다
이전 작업을 첫 주문 할인 기능에 비유하면 이렇다. 기획서에는 규칙 두 개가 따로 적혀 있었다. 하나는 "첫 주문 회원은 결제 시 10% 할인을 받는다"는 것, 다른 하나는 "할인 상품은 추가 할인에서 제외된다"는 것이었다. 규칙 하나하나는 그 자체로 분명했고, 기획 리뷰에서도 질문 없이 넘어갔다. 구현은 규칙마다 브랜치를 따로 파서 진행했으며, 각 브랜치는 자기 규칙 하나만 확인하고 병합했다. 두 구현 모두 기획서와 정확히 맞았으니, 이 정도면 빠진 게 없다고 봤다.
모든 구현을 마친 뒤 전체 흐름을 테스트케이스로 적었다. 회원 조건 둘과 상품 조건 둘을 조합하면 케이스는 아래 표럼 네 줄이 나온다. 이때 출처는 그 줄의 기대 결과를 어디서 가져왔는지 적는 열이다. 기획서에 근거 문장이 있으면 "명시"라고 기입하고, 없어서 짐작으로 채웠으면 "추정"이라고 기입했다.
| 테스트케이스 | 출처 | 검증 여부 |
|---|---|---|
| 재주문 회원이 일반 상품을 장바구니에 담은 뒤 결제 화면으로 가면 정가로 결제된다 | 명시 | ✅ |
| 재주문 회원이 할인 상품을 장바구니에 담은 뒤 결제 화면으로 가면 할인가로 결제된다 | 명시 | ✅ |
| 첫 주문 회원이 일반 상품을 장바구니에 담은 뒤 결제 화면으로 가면 정가에서 10% 할인된 금액으로 결제된다 | 명시 | ✅ |
| 첫 주문 회원이 할인 상품을 장바구니에 담은 뒤 결제 화면으로 가면 할인가 그대로 결제된다 | 추정 |
위 세 줄은 규칙 하나만 보면 답이 나오는 조합이라 기획서에 근거가 있었고 검증도 통과했지만, 마지막 줄만 달랐다. 첫 주문 회원이 할인 상품을 담았을 때 어떤 규칙이 우선하는지는 기획서 어디에도 없었다. 두 규칙은 기획서의 서로 다른 페이지에 있었고 검증도 페이지 단위였으니, 둘이 만나는 이 케이스는 어느 쪽 검증에도 없었다.
뒤늦게 기획팀에 질문을 올리게 됐다.
기획팀의 답은 첫 주문 회원이면 할인 상품에도 10% 할인을 더 받는다는 것이었다. 이 답은 할인 상품 규칙에 예외를 하나 만든 셈인데, 기획서에는 첫 주문 회원 페이지에만 적혔다. 할인 상품 페이지의 "할인 상품은 추가 할인에서 제외된다"는 문장과, 그 문장을 따르는 상품 상세 화면의 "추가 할인 불가" 안내는 그대로였다. 첫 주문 회원에게도 이 안내가 뜨는 걸 발견했고, 기획서와 코드를 또다시 고쳐야만 했다. 규칙 하나하나는 매번 맞았지만, 두 규칙 사이는 늘 비어 있었다.
입력에 따라 결과가 달라지는 문장만 케이스로 삼았다
다음 작업은 입점사 상품 등록 기능이었다. 이번에는 기획서를 받은 날 검토하면서 바로 테스트케이스를 뽑아, 읽고 지나치는 문장이 없게 했다. 다만 기획서의 모든 문장마다 케이스를 하나씩 뽑으면, 버튼 색상이나 페이지 제목처럼 누가 어떻게 하든 똑같이 보이는 것까지 케이스가 된다. 그러면 조건에 따라 결과가 갈리는 줄, 즉 빈틈이 생길 수 있는 줄이 그 사이에 섞여 눈에 띄지 않는다고 봤다. 그래서 입력에 따라 기대 결과가 달라지는 문장인가를 기준으로 삼고, 여기에 드는지 가려 주는 신호를 표로 정리했다.
| 신호 | 예시 | 케이스로 삼는가 |
|---|---|---|
| 상태 전이 (로딩/성공/실패/빈 상태) | 목록 조회 실패 시 에러 메시지 노출 | ✅ |
| 조건 분기와 비즈니스 로직 | 재고 0개면 구매 버튼 비활성화 | ✅ |
| 유효성 검사 | 필수값 미입력 시 저장 버튼 비활성화 | ✅ |
| 권한과 역할별 차이 | 관리자만 삭제 버튼 노출 | ✅ |
| 경계값과 기한 조건 | 현재 시각 이후만 선택 가능 | ✅ |
| 데이터 의존 표시 | 재고 5개 미만이면 "품절임박" 배지 | ✅ |
| 외부 연동 (API, 결제, 네트워크) | 결제 실패 시 재시도 안내 | ✅ |
| 중복 제출과 동시성 | 버튼 연타 시 중복 요청 방지 | ✅ |
| 콘텐츠 길이에 따른 말줄임, 줄바꿈, 스크롤 | 상품명이 두 줄 넘으면 말줄임 | ✅ |
| 정책이 지정한 구체 문구, 버튼, 컬럼, 배지 | 확인 문구 "목록에서 해제하시겠습니까?" | ✅ |
| 기존 화면이나 기능을 재사용한다는 서술 | "상품 승인 관리와 동일" | ✅ |
| 고정 요소의 시각 스타일 (색상, 정렬, 여백, 폰트) | 버튼 색상, 카드 간격 | ❌ |
| 정책에 지정되지 않은 장식성 텍스트와 라벨 | 페이지 제목, 섹션 구분 라벨 | ❌ |
✅로 둔 항목은 응답, 입력값, 역할, 사용자 행동에 따라 결과가 갈리는 것들이다. 앞선 작업의 빈틈은 첫 주문 회원 조건과 할인 상품 조건이 한 주문에 같이 걸리는 자리에서 생겼는데, 이런 겹침은 조건이 있는 문장끼리만 만들 수 있다. 반대로 ❌로 둔 항목은 어떤 입력이든 결과가 같아서 다른 규칙과 부딪힐 일이 없다. 버튼 색상은 디자인 시안이 정하고, 페이드인 효과는 조건 없이 늘 같은 방식으로 일어난다. 화면이 의도대로 그려졌는지는 한 번 보면 확인이 끝난다.
한 문장 안에 결과가 둘 이상 섞여 있다면 케이스도 나눴다. 예를 들어 “필수값이 비면 저장 버튼을 비활성화하고 입력란 아래에 안내를 띄운다”는 버튼 비활성화 케이스와 안내 문구 노출 케이스, 두 개로 분리했다. 결과가 둘이면 출처도 갈릴 수 있기 때문이다. 위 예시만 해도 버튼을 비활성화한다는 건 기획서가 정했지만, 안내에 어떤 문구를 띄울지는 기획서 어디에도 없었다. 이 둘을 한 줄에 담으면 출처 칸에 명시와 추정을 같이 적어야 해서 빈틈이 묻힌다. 결과 하나에 출처 하나를 매기려면 케이스도 하나씩 나눠야 했다.
이 기준으로 5개 화면을 훑으며 케이스를 작성했다. 기대 결과를 기획서에서 찾을 수 없는 문장도 빼지 않고 짐작으로 채워 케이스에 넣되, 출처를 추정으로 적어 구분했다.
화면 사이의 빈틈은 두 화면을 나란히 놓아야 보였다
기획서는 보통 화면 단위로 나온다. 상품 등록 화면, 상품 수정 화면, 목록 화면이 한 장씩이고, 장마다 그 화면의 정책만 적혀 있다. 그래서 케이스를 화면 단위로 뽑으면 화면 하나 안에서는 빈틈이 없다. 하지만 빈틈은 화면과 화면 사이에서 생긴다.
이전 작업의 첫 주문 할인 빈틈도 서로 다른 자리에 적힌 두 규칙 사이에서 생겼다. 이번 기획서도 화면이 5장이라 같은 빈틈이 있을 수 있다고 보고, 화면별 케이스를 모두 뽑은 뒤 한 단계를 더 뒀다. 한 화면의 조건이 다른 화면의 조건과 같은 대상에 걸리면 두 서술을 나란히 놓고 조합을 펼쳐, 기대 결과가 하나로 정해지는지 확인했다. 정해지지 않으면 한쪽으로 정해 두되 출처를 추정으로 적었다.
판매가 수정이 그런 자리였다. 상품 수정 화면 문서에는 판매 중인 상품의 판매가를 고치면 바로 반영된다고 적혀 있고, 승인 관리 화면 문서에는 상품 정보를 고치면 승인 대기로 돌아가 승인 전까지 반영되지 않는다고 적혀 있다. 화면 하나만 보면 질문이 생기지 않지만, 판매 중인 상품의 판매가를 고치는 순간 두 규칙이 한 입력에서 만나고, 바로 반영인지 승인 뒤 반영인지는 어느 문서에도 없다.
기획서의 빈틈을 구현 전에 기획 리뷰에서 메웠다
화면 5개의 케이스를 모두 뽑고 나니 출처가 추정인 줄이 14건이었다. 기획서에 서술이 없거나, 있어도 두 가지로 해석되거나, 두 화면의 조건이 겹치거나, 기존 화면의 동작과 어긋나는 경우였다. 조건이 겹친 경우가 앞 절의 판매가 수정이었고, 기존 화면과 어긋난 경우로는 오류 문구의 위치가 있었다. 등록 버튼을 누른 뒤 서버가 오류를 돌려주면 안내를 띄운다는 서술은 있었는데, 기존 화면들은 서버 오류를 토스트로 띄우고 있었고 이번 목업에는 입력란 아래 폼 에러로 그려져 있었다. 기획서만 읽을 때는 지나쳤던 차이가 케이스 표로 옮기자 출처 칸을 채울 수 없는 줄로 드러났다.
| 테스트케이스 | 출처 | 검증 여부 |
|---|---|---|
| 입점사가 상품 등록 화면에서 항목을 입력하고 등록 버튼을 누른 뒤 서버가 유효성 오류를 응답하면 화면 위에 토스트로 오류 문구가 뜬다 | 추정 |
이 14건을 기획 리뷰에 가져가 하나씩 논의했다. 논의가 끝나면 기획자가 기획서를 고쳐 보냈고, 고친 기획서로 케이스를 다시 뽑았다. 판매가 수정은 승인 뒤 반영으로 정해져 상품 수정 화면 문서와 승인 관리 화면 문서 양쪽에 그 문장이 들어갔고, 추정으로 적어 둔 줄은 명시로 바뀌었다. 오류 문구의 위치도 같은 과정을 거쳐 폼 에러로 정해졌다. 이렇게 모인 14건의 답은 새 케이스 36건을 만들고 기존 케이스 6건을 바꿔, 최종 문서는 173건이 됐다. 답 하나가 줄 하나로 끝나지 않고 여러 줄에 걸쳐 퍼진 셈이다.
이전 작업이었다면 이 14건은 구현 중이거나 구현을 마친 뒤 하나씩 드러났을 것이고, 그때마다 기획자는 기획서를, 개발자는 만들던 코드를 되돌려야 했을 것이다. 이번에는 그 되돌림이 기획서와 케이스 문서 사이에서 끝났다. 기획자는 리뷰에서 모인 질문을 한 번에 반영했고, 개발자는 개정이 끝난 기획서로 한 번만 구현했다. 구현이 끝난 뒤 173건을 전부 돌려 모두 통과한 상태로 QA팀에 넘겼고, QA팀이 자체 케이스로 돌려 나온 이슈는 0건이었다.
정리하며
처음에는 테스트케이스를 구현이 끝난 뒤에 만들었다. 검증할 코드가 있어야 만들 수 있다고 봤기 때문이다. 이번에는 기획서를 받은 날 케이스부터 뽑았고, 출처가 추정인 줄 14건을 기획 리뷰에서 명시로 바꾼 뒤에 구현에 들어갔다. 구현이 끝난 뒤 173건을 돌려 QA팀에 넘기기까지 문서는 이 하나였고, QA에서 되돌아온 이슈는 없었다. 테스트케이스는 코드를 검증하는 문서이기 전에 기획서를 검증하는 문서였다.
기획서를 검증한다는 말이 기획의 실수를 개발이 잡아낸다는 뜻은 아니다. 14건은 기획이 틀린 자리가 아니라, 기획서만 보는 사람에게는 안 보이고 기획서와 코드를 함께 보는 사람에게만 보이는 자리였다. 오류 문구 위치는 기존 코드를 아는 쪽이, 판매가 수정은 기획서 두 장을 같이 읽는 쪽이 먼저 볼 수 있는 자리였다.
기획 리뷰는 모두가 함께 읽고 넘기는 단계이기도 하다. 그 단계를 지나고도 남은 빈틈은 기획 혼자의 몫이 아니라 리뷰에 들어간 모두의 몫이고, 코드 리뷰에서 PR을 승인한 사람이 그 코드에 책임을 나눠 지는 것과 같다. 그래서 개발은 그 자리를 찾아내고, 기획은 그 자리를 검토해 기획서에 반영하고, 개발은 반영된 기획서로 구현한다. 추정으로 채운 줄을 개발이 확정하지 않고 기획 리뷰로 가져간 것도 그런 이유였다. 케이스 문서가 상대의 답을 대신 정하는 통로가 아니라 상대가 더 정확하게 결정할 재료를 건네는 데 쓰일 때, 각자 잘하는 일에 집중하게 되고 그게 팀 전체의 효율로 이어진다고 생각한다.