문제와 판단 기준

검수할 테스트 목록에 “10MB보다 큰 파일을 올리면 오류가 표시된다”라고 적혀 있습니다. 정상·오류 시나리오가 모두 있고 문장도 자연스럽습니다. 그런데 무엇을 올려야 하는지, 어떤 오류여야 하는지, 파일이 실제로 저장되지 않았는지는 알 수 없습니다. 이대로는 잘못된 동작을 놓쳐도 테스트가 통과할 수 있습니다.

AI가 만든 테스트 케이스는 검증할 요구사항의 근거, 통제할 조건, 관찰할 결과, 실패로 판정할 기준부터 확인해야 합니다. 기대 결과의 근거가 모호하면 케이스를 더 생성하기보다 확인 질문으로 돌려보내야 합니다. 문장이 아니라 검증의 내용을 검수하는 일입니다.

이 글의 파일 업로드 기능과 테스트 목록은 모두 설계 예시입니다. 실제 모델이나 Qoretix가 생성한 결과, 고객 사례, 실행 성과가 아닙니다. 생성 도구를 비교하는 대신, 생성된 개별 케이스를 그대로 쓸지, 고칠지, 요구사항부터 확인할지 판단하는 방법을 살펴봅니다.

기대 결과는 누가 정했을까

테스트에서 실제 결과가 옳은지 판단하는 근거를 테스트 오라클(test oracle)이라고 합니다. 승인된 요구사항, 합의된 계산 규칙, 검증된 참조 결과 등이 그 역할을 합니다. 기대 결과를 정할 수 없다면 실행에 성공해도 올바르게 검증했다고 말하기 어렵습니다. ISTQB의 Test Analyst 지침도 케이스에 명시적인 통과·실패 기준을 두고, 화면 출력뿐 아니라 데이터와 실행 후 상태까지 고려하도록 설명합니다. [1]

검수할 때는 기대 결과 옆에 “왜 이것이 정답인가”를 짧게 적어 봅니다. 요구사항 R-UP-01 v1.0의 상한 포함 규칙처럼 근거가 나오면 다음 단계로 진행합니다. 현재 코드가 그렇게 동작해서, AI가 일반적인 정책이라고 답해서만 남는다면 아직 사업적으로 옳은 결과인지 확인되지 않은 것입니다.

여기서 코드 기반 생성 자체를 배제할 필요는 없습니다. 기존 동작을 기록해 변경 전후의 차이를 찾는 테스트는 유용합니다. 다만 현재 동작을 보존하는 테스트와 합의한 요구를 만족하는지 확인하는 테스트를 구분해야 합니다. 현재 동작이 알려진 결함이라면 그것을 승인된 기대 결과로 승격하지 않습니다.

예를 들어 합의한 규칙은 파일 크기 ≤ 상한인데 구현이 파일 크기 < 상한이라고 가정해 보겠습니다. 구현을 읽고 “상한과 같은 파일은 거절된다”는 기대 결과를 만들면, 테스트는 기존 버그를 보존하게 됩니다. 기대 결과를 계산할 때 테스트 대상과 같은 함수를 다시 호출하는 방식도 이 오류를 독립적으로 드러내지 못합니다. 이 경우에는 승인된 규칙에서 계산한 경계값으로 기대 결과를 정해야 합니다.

서비스 작동 방식

요구사항에서 승인까지 이어지는 근거 — 요구사항과 테스트, 결함 조치와 승인 기록을 연결해 같은 기준으로 품질을 판단합니다.

모호한 요구사항은 테스트가 아니라 질문으로 남긴다

“10MB까지 업로드할 수 있다”는 문장만으로는 정확한 경계 테스트를 만들기 어렵습니다. 10MB가 10,000,000바이트인지, 10MiB인 10,485,760바이트인지부터 정해야 합니다. 상한과 같은 크기를 허용하는지, 제한 대상이 파일 본문인지 전체 HTTP 요청인지도 다릅니다. 빈 파일, 업로드 권한, 실패 후 저장 데이터의 처리도 별도 정책입니다.

이 빈칸을 생성기가 임의로 채우지 않도록, 요구사항 검토 결과를 확정된 규칙·승인 대기 가정·미결정 질문으로 나누는 방식을 권합니다. 예를 들면 다음과 같습니다.

요구사항 확인 기록 — 설계 예시
질문 Q-UP-01: 크기 상한의 단위와 상한 포함 여부는 무엇인가?
결정 담당: 제품 담당자와 API 계약 담당자.
영향받는 케이스: 상한 직전·상한·상한 직후 업로드.
임시 가정: 10MiB 이하 허용. 승인 전에는 인수 기준으로 사용하지 않음.
다음 행동: 규칙과 적용 버전을 확정한 뒤 기대 결과를 갱신합니다.

질문이 생겼다고 관련 작업을 모두 멈출 필요는 없습니다. 단위와 무관하게 준비할 수 있는 테스트 데이터 생성이나 격리 환경 구성은 진행해도 됩니다. 하지만 미결정 규칙에 의존하는 케이스를 통과 처리해서는 안 됩니다. 요구사항의 모호함과 제품의 결함은 다른 상태입니다.

요구사항과 테스트의 버전을 연결하는 구조 자체가 필요하다면 요구사항–테스트 추적성 설계를 함께 참고하십시오. 여기서는 그 연결을 따라 찾은 케이스 하나의 판정 내용에 집중합니다.

약한 케이스를 실제로 판정할 수 있는 케이스로 바꾸기

앞선 질문에 답이 정해졌다는 가정 아래, 다음과 같은 가상 API 계약을 만들어 보겠습니다. 이 규칙과 응답 코드는 예시를 위한 선택이지, 모든 업로드 서비스가 따라야 하는 표준은 아닙니다.

설계 예시의 합의 규칙 R-UP-01 v1.0

편집 권한이 있는 사용자는 준비된 폴더에 파일 업로드가 허용됩니다. 파일 본문의 허용 크기는 1바이트 이상, 10MiB 이하입니다. 이 예시에서는 ASCII 문자로 만든 .txt 파일과 올바른 메타데이터를 사용하고 파일 형식·사용자 용량 한도·저장소 장애 등 다른 거절 사유는 제거합니다.

정상 업로드는 저장을 완료한 뒤 HTTP 201과 비어 있지 않은 fileId, 실제 파일 크기인 sizeBytes, READY 상태를 반환합니다. 파일 메타데이터와 저장 객체는 각각 한 개여야 하고 내려받은 바이트는 원본과 같아야 합니다.

상한을 넘으면 HTTP 413, 오류 코드 FILE_TOO_LARGE를 반환하고 fileId를 발급하지 않습니다. 해당 요청의 파일 메타데이터와 영속 저장 객체는 남기지 않습니다. 응답 이후 뒤늦게 파일을 저장하는 비동기 작업은 없는 동기 처리 계약이며 테스트의 저장 결과 조회는 쓰기 완료 상태를 확인할 수 있는 수단을 사용합니다.

처음의 약한 케이스는 이렇게 적혀 있었습니다.

수정 전 — 설계 예시
입력: 큰 파일.
행위: 업로드합니다.
기대 결과: 오류가 정상적으로 표시됩니다.

이 케이스를 서버의 크기 제한과 저장 결과를 검증하는 목적으로 고치면 다음과 같습니다. 표는 실행 결과가 아니라 테스트 명세입니다.

구분수정 후 케이스 TC-UP-MAX-PLUS-ONE — 설계 예시
검증할 요구R-UP-01 v1.0: 상한을 초과한 파일은 지정한 오류로 거절하고 파일 데이터는 남기지 않습니다.
전제편집 권한의 테스트 계정, 준비된 폴더, 케이스 전용 데이터 영역. 시작 시 해당 영역의 파일 메타데이터와 저장 객체는 각각 0개. 중간 프록시가 파일 본문과 요청 부가 정보를 포함한 크기를 허용하고 크기 규칙을 검사할 대상 API에 요청이 도달하는 테스트 경로임을 확인합니다.
입력limit-plus-one.txt: ASCII a로 채운 10,485,761바이트 파일. BOM과 추가 줄바꿈 없음. 파일 이름이나 문자열 길이가 아니라 생성한 파일의 바이트 수를 확인합니다.
행위소유하거나 허가받은 테스트 환경에서 실제 업로드 API에 파일을 한 번 전송합니다. 이 검증에서는 대상 API 응답과 파일 저장 경로를 테스트용 대역(mock)으로 대체하지 않습니다.
기대 결과413, code = FILE_TOO_LARGE, fileId 없음. 동기 처리 종료 뒤 해당 요청의 파일 메타데이터와 영속 저장 객체가 각각 0개입니다. 다른 사유의 오류는 이 케이스의 통과로 인정하지 않습니다.
확인 증거입력 바이트 수, 테스트 실행 식별자, 요청과 응답, 실행 전후의 메타데이터·저장 객체 조회 결과. 조회 실패나 권한 오류를 “0개”로 바꾸지 않습니다. 개인정보와 인증정보는 증거에서 제외합니다.
정리·격리증거 확인 뒤 케이스 전용 데이터를 정리합니다. 실패한 경우에도 잔여 데이터와 변경한 테스트 설정을 복구합니다. 정리 실패는 별도 기록하며 다음 실행의 초기 상태가 깨끗하다고 가정하지 않습니다.

차이는 문장이 길어졌다는 데 있지 않습니다. 잘못된 결과가 구체적으로 드러납니다. 서버가 파일을 받아들이면 상태 코드에서, 엉뚱한 이유로 거절하면 오류 코드에서, 거절 응답만 보내고 파일을 저장하면 저장 결과에서 실패합니다.

반대로 “저장 객체 0개”라는 조회 결과만 믿어서도 안 됩니다. 같은 관찰 수단으로 정상 업로드의 객체 한 개가 실제 보이는지 먼저 확인해야 합니다. 조회 대상이 잘못됐거나 조회 권한이 없는 도구는 잘못된 부재 증거를 만들 수 있기 때문입니다. 정리 작업이 먼저 파일을 지워 버리지 않도록 결과 확인과 정리의 순서도 분리합니다.

경계값을 추가할 때는 입력과 판정이 함께 달라져야 한다

ISTQB CTFL은 동등 분할로 같은 방식으로 처리될 입력 집합을 구분하고 순서가 있는 분할의 경계에서는 경계값 분석을 적용하도록 설명합니다. 상태에 따라 동작이 달라지는 기능에는 상태 전이 테스트를 적용합니다. 따라서 생성된 목록에서 정상 값 몇 개만 바뀌었는지, 실제 경계나 상태 변화가 검증되는지 살펴봐야 합니다. [2]

앞선 계약의 상한 주변만 펼치면 다음과 같습니다. 각 행은 초기 상태가 같은 독립 실행입니다.

입력 파일 크기기대 응답함께 확인할 결과
10,485,759바이트 — 상한보다 1바이트 작음201sizeBytes 일치, READY, 메타데이터·저장 객체 각각 1개, 내려받은 바이트 일치
10,485,760바이트 — 상한과 같음201위와 동일. 상한을 잘못 제외하는 구현을 드러내는 케이스
10,485,761바이트 — 상한보다 1바이트 큼413, FILE_TOO_LARGEfileId 없음, 파일 메타데이터·영속 저장 객체 각각 0개

이 표만으로 업로드 검증이 완성되지는 않습니다. 하한과 빈 파일, 권한, 전송 중단, 저장 실패는 다루지 않았습니다. 상한 주변의 예시를 전체 경계값 커버리지나 전체 기능 커버리지라고 부르지 않는 것이 중요합니다.

생성된 목록에서 찾아야 할 결함

다음 검수표는 형식 충족 점수를 매기기 위한 표가 아닙니다. 문제를 발견했을 때 무엇을 고칠지 정하기 위한 편집·검수 제안입니다. 요구와 기대 결과가 미결정인 항목을 다른 항목의 높은 점수로 상쇄하지 않습니다.

발견한 문제검수 질문과 보완 방향다음 처리
기대 결과의 근거가 없음요구사항·계약·참조 결과 중 무엇을 근거로 했는가? 출처와 적용 버전을 연결할 수 없다면 먼저 질문을 만듭니다.요구사항 확인
정상 경로만 반복됨유효·무효 입력, 상·하한, 거절·중단·복구 조건이 필요한가? 상태가 있는 기능은 초기 상태·행위·다음 상태와 금지 전이를 확인합니다.수정 후 사용
검증문(assertion)이 빈약함“응답이 있다”, “페이지가 열린다”만 검사하지 않는가? 검증 목적에 필요한 값·상태·부작용을 명시합니다. 단위 테스트 한 개에 모든 계층의 검증을 억지로 넣지는 않습니다.수정 후 사용
현재 버그를 정답으로 복제함기대 결과가 승인된 규칙이 아니라 현재 구현이나 같은 계산 함수에서 나온 것은 아닌가? 현재 동작 기록과 요구 충족 판정을 분리합니다.요구사항 확인 또는 수정
mock이 검증 대상을 가림오류 처리 화면을 검증하는가, 서버가 실제로 오류를 감지하는지 검증하는가? 대체한 경로 밖까지 확인했다고 쓰지 않습니다.목적 수정 또는 검증 분리
의미가 같은 케이스가 중복됨요구·초기 조건·입력 분류·기대 결과가 같은가? 같은 목적은 매개변수화하거나 통합하되, 상한 허용과 상한 초과 거절처럼 다른 판정은 보존합니다.폐기·통합
권한·실패 경로가 빠짐로그인 여부만 확인하는가? 허용된 행위와 금지된 행위, 실패 뒤 데이터 상태의 기준이 있는지 확인합니다.요구 확인 후 보완
데이터가 조건을 충족하지 않음파일 크기·인코딩·시간대·역할·기존 데이터가 명세와 맞는가? 다른 유효성 오류 때문에 먼저 거절되는 케이스를 구분합니다.수정 후 사용
실행 결과가 조건에 따라 흔들림시각·난수·외부 응답·공유 데이터·실행 순서에 의존하는가? 조건을 통제하고 통제가 불가능한 부분은 관찰 방법과 제한을 남깁니다.수정 후 사용 또는 실행 보류

권한 검수에서는 버튼이 숨겨졌다는 사실만으로 서버의 접근 통제가 검증되었다고 판단하지 않습니다. OWASP WSTG v4.2의 WSTG-ATHZ-02도 역할과 신원에 따른 기능·자원 접근을 구분해 확인합니다. 여기서는 개별 케이스의 목적과 판정 근거를 검토하고 상세한 멀티테넌트 검증 조합은 별도 범위로 다룹니다. 보안 관련 실행은 허가된 환경과 합성 계정·데이터로 제한합니다. [3]

mock은 많고 적음보다 어디를 대신했는지가 중요하다

Playwright의 공식 mock 예시는 네트워크 요청에 미리 정한 응답을 돌려주며, 그 경우 실제 API를 호출하지 않는다고 명시합니다. 이런 테스트는 해당 응답을 받은 화면의 동작을 확인하는 데 쓸 수 있지만 서버가 파일 크기를 검사하거나 저장을 취소했다는 증거는 아닙니다. [4]

외부 시스템의 시간 초과를 재현하거나 특정 화면 상태를 안정적으로 만들려면 mock을 사용하는 방법도 적절합니다. 반대로 이 글의 예시처럼 서버의 크기 제한과 저장 결과를 확인하려면 그 경로를 실제로 실행하는 별도 검증이 필요합니다. 대체한 의존성과 실제로 실행한 경로를 케이스에 남기면, mock이 있는 테스트를 무조건 버리거나 그 결과를 전체 통합 검증으로 과장하는 일을 피할 수 있습니다.

서비스 작동 방식

정상 흐름 너머의 예외까지 확인 — 구현된 동작에서 요구사항을 복원하고 입력과 권한, 외부 호출의 예외 경로를 실행해 확인합니다.

사람이 판단할 부분과 도구로 확인할 부분을 나눈다

사람이 모든 케이스를 처음부터 다시 작성할 필요는 없습니다. 반복되는 형식·참조·코드 오류는 도구로 먼저 걸러내고, 검수자는 기대 결과가 옳은지, 그 결과를 확인할 방법이 충분한지에 시간을 쓰는 편이 낫습니다.

정적 검사에서는 필수 필드 누락, 존재하지 않는 참조, 타입·문법 오류, 불필요하게 남은 실행 제외 표시, 누락된 비동기 대기 같은 문제를 찾도록 규칙을 구성합니다. 다만 검증문이 있다는 사실과 검증문이 올바른 요구를 검사한다는 사실은 다릅니다. 의미가 비슷한 케이스의 자동 묶음도 통합 후보를 찾는 데 사용하고 실제로 같은 검증인지 판단한 뒤 정리합니다.

실행 검사는 테스트가 발견되고 시작되는지, 데이터 준비와 정리가 작동하는지, 의도한 경로와 판정문에 도달하는지 확인하는 단계입니다. 반복 실행이나 순서 변경으로 의존성을 발견할 수 있지만 몇 번 통과했다는 결과를 비결정성이 없다는 증명으로 보지는 않습니다.

도구가 제공하는 안전장치의 범위도 구분해야 합니다. 테스트에 필요한 환경과 자원을 준비하는 구조인 fixture(픽스처)도 적용 범위를 확인해야 합니다. Playwright Test의 기본 page·context는 테스트 단위로 제공되지만 worker 범위의 fixture도 존재합니다. 브라우저 컨텍스트가 분리됐다고 외부 DB나 파일 저장소까지 자동으로 초기화되는 것은 아닙니다. 공용 계정·공유 데이터의 격리는 별도로 설계해야 합니다. [5][6]

화면이 바뀌기를 기다릴 때도 고정 시간만 재우기보다 확인하려는 조건을 기다리도록 설계합니다. Playwright Test에서는 await expect(locator).toHaveText(...) 같은 웹 전용 assertion이 조건을 재확인합니다. 모든 expect가 자동 재시도하는 것은 아니며, 자동 대기가 잘못된 기대 결과를 올바르게 만들어 주는 것도 아닙니다. [7]

통과하는지뿐 아니라, 틀렸을 때 실패하는지 확인한다

중요한 케이스에는 작은 반증 검사를 추가하는 방법을 권합니다. 앞선 설계 예시라면 격리된 로컬 복제본에서 크기 상한 검사를 잠시 제거했을 때 초과 케이스가 실패해야 합니다. 상한 비교를 잘못 바꿨을 때는 상한과 같은 크기의 정상 케이스가 실패해야 합니다. 변경은 시험 후 되돌리고 운영 코드에는 반영하지 않습니다.

이 검사는 테스트가 의도한 오류를 감지하는지 확인하는 보조 수단입니다. 의도적으로 만든 오류를 잡았다고 다른 결함까지 모두 검출한다는 뜻은 아닙니다. 테스트 대상이나 판정 경로를 실제로 바꾸지 못한 실험은 반증 검사로 인정하지 않는 편이 좋습니다.

NIST SSDF v1.1의 PW.8.2도 보안 테스트에서 범위를 정하고 설계·수행·결과 기록과 발견 사항의 분류·처리를 연결하도록 권고합니다. 이를 참고해 이 글에서는 생성된 케이스의 검토, 실행 결과, 후속 조치를 별도 기록으로 남기는 방식을 제안합니다. 이것이 AI 테스트 생성의 품질 인증이나 SSDF 전체 준수를 의미하지는 않습니다. [8]

실행한 뒤 실패 원인이나 불안정성을 관리해야 한다면 자동화 실패 분석·격리·복귀 운영 양식을 이어서 활용할 수 있습니다. 케이스의 설계 검수와 실행 후 실패 분석은 같은 판단이 아닙니다.

검수 결과는 네 가지 다음 행동으로 끝낸다

아래는 케이스의 설계 검수 결과입니다. 테스트 실행의 PASS·FAIL이나 출시 승인과 혼합하지 않습니다.

검수 결과판단 조건다음 행동
그대로 사용요구의 근거, 입력, 판정, 증거와 격리가 목적에 맞고 수정할 사항이 없음검토한 버전을 확정하고 합의한 환경에서 실행 검증. 아직 실행하지 않았다면 실행 결과는 미실행으로 유지
수정 후 사용요구는 정해졌지만 데이터·판정·mock·격리 등에 수정할 부분이 있음담당자가 수정하고 변경한 부분을 재검토한 뒤 실행
요구사항 확인옳은 결과를 결정할 근거가 없거나 문서·코드·정책이 충돌함결정할 질문과 담당자, 영향받는 케이스를 기록. 답이 필요한 판정은 보류
폐기·통합같은 검증이 중복되거나 검증 목적이 없고 다른 케이스로 충분히 대체됨남길 케이스와 이유를 기록하고 통합. 단지 실행하기 어렵거나 결함을 드러낸다는 이유로 폐기하지 않음

새로운 기대 결과나 위험한 데이터 변경이 포함된 케이스는 사람이 먼저 살펴보고, 이미 검토한 같은 규칙의 반복 입력은 공통 명세와 자동 검사로 중복 작업을 줄이는 방법도 있습니다. 검수 시간을 줄이겠다고 미결정 요구나 확인되지 않은 권한 조건까지 일괄 승인하지는 않습니다.

기존 저장소와 이슈 관리 도구만으로도 이 흐름을 시작해도 됩니다. 요구를 결정할 담당자가 있고, 데이터를 통제하며 결과를 관찰할 수 있다면 새로운 생성 도구가 선행 조건은 아닙니다. 여러 팀이 서로 다른 기대 결과를 사용하거나 실패 뒤 저장 상태를 확인할 수 없다면, 생성량을 늘리기 전에 계약과 관찰 수단부터 정리해야 합니다.

다음 검수에서는 케이스 하나를 골라 이렇게 물어보면 됩니다. “어떤 잘못된 동작을 넣었을 때, 이 케이스의 어느 확인이 실패해야 하는가?” 답이 명확해지면 무엇을 확인할지 정해집니다. 그다음 데이터·환경·실행 조건까지 갖추어 검증합니다. 답이 모호하면 그 케이스는 아직 더 많은 문장이 아니라 더 정확한 근거가 필요합니다.

검수 기준과 담당 역할을 여러 팀 사이에서 합의해야 한다면 IXC의 QA 프로세스 구축 지원 범위를 확인할 수 있습니다. 품질·테스트 제품을 함께 검토한다면 Qoretix의 제공 범위를 확인하십시오.