• AI에게 구현이나 테스트를 위임하고 있지만, 완료 보고서를 어떻게 평가해야 할지 막막하다
• 테스트가 성공한 범위와 아직 확인하지 않은 범위를 분리하여 파악하고 싶다
• 코드를 승인하기 전에 사람이 무엇을 판단해야 하는지 정리하고 싶다
'테스트가 통과했다'는 사실만으로 어디까지 결론을 내릴 수 있는지, 작은 JavaScript 예제를 통해 확인해 보겠습니다. 완료 조건, 실행한 검증(validation), 놓치기 쉬운 버그(bug), 미확인 사항의 처리라는 4가지 포인트를 자신의 개발 경험에 적용할 수 있도록 정리합니다.
AI로부터 '테스트가 통과했습니다'라는 보고를 받았을 때, 무엇을 확인해야 구현을 받아들여도 좋다고 판단할 수 있을까요?
성공했다는 결론만으로는 실행된 조건이나 아직 검증하지 않은 동작까지 알 수 없습니다. 비공개 노트를 가져오는 작은 JavaScript 예제를 사용하여, 완료 보고서를 읽을 때 확인해야 할 포인트를 정리합니다.
| 포인트 | 자신의 개발에서 확인할 것 |
|---|
| 1. 완료 조건 | 기대하는 동작과 거부하는 조건이 명확하게 정해져 있는가 |
| ... | |
| 예를 들어, 비공개 노트 가져오기(read private note) 처리에 대해 AI로부터 '테스트가 통과했습니다'라는 보고를 받았다고 가정해 보겠습니다. | |
본인 노트를 읽을 수 있다는 것만 확인했다면, 거기서 말할 수 있는 것은 해당 입력값으로 기대했던 결과가 나왔다는 것입니다. 타인의 노트를 반환하지 않는지는 이 테스트만으로는 알 수 없습니다.
테스트의 성공은 실행된 조건과 기대값에 비추어 본 결과입니다. '기능 전체가 올바르다'고 판단하기 전에, 입력(Input)과 기대하는 결과(Expected result)를 반드시 확인해야 합니다.
설명을 위해 '비공개 노트는 작성자 본인만 읽을 수 있다'라는 규칙을 설정합니다. 공유 기능이나 관리자의 예외 사항은 없다고 가정합니다.
함수에 전달되는 사용자 ID는 호출하는 쪽에서 확정된 문자열이거나, 이용자를 지정하지 않음을 나타내는 null입니다. 이 예제에서 검증하는 것은 배열을 검색하는 함수의 반환값입니다. 실제 로그인 처리나 HTTP 상태 코드 설계는 포함하지 않습니다.
| 입력 조건 | 기대 결과 |
|---|
| A가 A의 노트를 요청 | A의 노트를 반환한다 |
| A가 B의 노트를 요청 | null을 반환한다 |
| 존재하지 않는 노트를 요청 | null을 반환한다 |
사용자 ID가 null | null을 반환한다 |
'가져올 수 있는 경우'만을 완료 조건으로 삼으면, 거부해야 할 경우를 확인할 이유가 빠지게 됩니다. 이 표의 기대값은 사전에 정한 규칙에서 도출됩니다. 구현의 현재 출력에 맞춰 결정하면, 버그(bug)조차 올바른 결과로 고정해버릴 가능성이 있습니다.
AI에게 요청할 때도 대상, 허용하는 조건, 거부하는 조건, 이번에 다루지 않을 범위를 제시하면 무엇을 완료로 할 것인지 공유하기가 수월합니다. 조건 자체를 AI와 상의할 수는 있지만, 채택할 조건이 사용자나 서비스의 요구사항에 맞는지 여부는 사람이 판단해야 합니다.
실행하는 테스트에 따른 결과 차이를 확인하기 위해, before/와 after/ 두 개의 폴더를 준비합니다. 다음 코드는 소유자(owner) 조건을 제외한 학습용 반례입니다. 이를 before/read-note.mjs로 저장합니다.
export function readNote(notes, viewerId, noteId) {
if (viewerId === null) return null;
...
위에서 설명한 4가지 조건을 확인하는 테스트를 before/read-note.test.mjs로 저장합니다. 데이터는 가상의 메모 2건입니다.
import test from "node:test";
import assert from "node:assert/strict";
import { readNote } from "./read-note.mjs";
...
이 예제의 실행 환경은 Node.js v26.13.0입니다. Node.js 표준의 node:test와 node:assert/strict를 사용하므로 추가 패키지는 필요 없습니다. 다음 명령어는, before/와 after/ 폴더가 위치한 디렉토리에서 실행해 봅니다.
node --test --test-name-pattern="본인" before/read-note.test.mjs
이 구현에는 타인의 노트를 가져올 수 있는 버그가 있지만, 본인(자신)의 노트는 성공적으로 가져옵니다. 이 예시에서는 1건 성공, 실패 0건, 종료 코드 0으로 나옵니다. '타인의 노트는 반환하지 않는다'라는 테스트는 실행 대상에 포함되지 않기 때문에, 이 버그를 감지할 수 없습니다.
다음으로, 동일한 구현으로 4건 모두를 실행합니다.
node --test before/read-note.test.mjs
이것은 3건 성공, 1건 실패, 종료 코드 1로 나옵니다. '타인의 노트는 반환하지 않는다'라는 테스트에서 기대값인 null과 실제로 반환된 B의 노트가 일치하지 않기 때문입니다.
테스트 결과를 볼 때는 성공/실패 표시 외에도 실행한 명령어, 대상 파일, 이름 등의 필터, 실행 건수, 입력 값 및 기대값을 확인해야 합니다. 이번 경우, 첫 번째 실행이 성공했던 이유는 소유자 조건(owner condition)이 정확했기 때문이 아니라, 그 버그를 감지하는 테스트를 실행하고 있지 않았기 때문입니다.
수정된 코드를 after/read-note.mjs로 저장합니다. 노트 ID와 소유자 ID 모두를 검색 조건으로 만듭니다.
export function readNote(notes, viewerId, noteId) {
if (viewerId === null) return null;
return notes.find(note =>
...
before/read-note.test.mjs를 내용 변경 없이 after/read-note.test.mjs로 복사하여 실행합니다.
node --test after/read-note.test.mjs
동일한 4가지 조건 모두가 성공하고, 종료 코드는 0이 됩니다. 비교할 때는 수정 전후로 기대값을 바꾸지 않고, 구현 변경으로 인해 결과가 달라졌는지 확인합니다.
| 구현 | 실행한 조건 | 성공 | 실패 | 종료 코드 |
|---|
| 변경 전 | 본인 노트만 가져오기 | 1 | 0 | 0 |
| ... | | | | |
이 비교에서는, 소유자 조건이 빠진 구현으로 인해 실패하고, 조건을 추가하자 성공합니다. 자신의 개발 과정에서도 예상했던 버그가 있을 때 실패하는 테스트인지 확인해 보면 검증의 의미를 판단하기 쉬워집니다. 테스트를 통과시키기 위해서만 요구사항에서 정한 기대값에 맞추는 것은 중요하지 않습니다.
이 예시의 테스트가 성공하더라도, 확인할 수 있는 것은 배열을 검색하는 함수의 동작일 뿐입니다. 실제 서비스에서는 사용자 ID 획득 방법, 라우트(route)를 통한 호출, DB 검색 조건 등 다른 오류가 있을 수 있습니다.
예를 들어, 가져오는 함수가 정확하더라도, 다른 API가 소유자 조건을 붙이지 않고 DB를 검색하고 있다면, 이번 함수 테스트만으로는 감지할 수 없습니다. 실제 라우트와 DB를 거치는 검증이 필요합니다.
또한, 모든 코드 라인을 실행했다는 커버리지(coverage) 정보만으로는 '타인의 노트를 반환하지 않는다'라는 기대값을 확인했는지까지는 알 수 없습니다. 코드를 실행하는 것과 그 결과가 요구사항에 맞는지 확인하는 것은 별개입니다.
추가로 확인할 것을 생각할 때는, 단순히 테스트 수를 늘리기보다는 남아있는 버그 후보군 중에서 검증 방법을 선택합니다. 인증 결과를 사용하지 않은 의심이라면 인증부터 획득까지의 경로를, DB 조건이 빠졌을지 의심이라면 실제 쿼리(query)를 거친 결과를 확인하는 식의 대응입니다.
여기서의 '미확인'은 반드시 모든 것을 당장 검증한다는 의미는 아닙니다. 무엇을 확인하지 못했는지 알면, 변경의 영향에 따라 추가 확인 범위를 정할 수 있습니다.
AI가 완료 보고를 할 때는 결론과 함께 그 근거와 한계를 제시하도록 합니다. 이번 샘플이라면 다음과 같이 보고할 수 있습니다.
대상: 배열에서 비공개 노트를 가져오는 함수
변경: 검색 조건에 소유자 ID 일치 추가
검증: 변경 전은 4건 중 1건 실패, 수정 후는 동일한 4건이 성공
...
AI에게는 여기에 실제로 실행한 명령어와 출력물도 함께 첨부하도록 합니다. 요약에 '성공'이라고만 되어 있다면, 그 보고가 어떤 실행에 대응하는지 확인해야 합니다. 다른 AI의 리뷰도 참고가 되지만, 평가하는 관점은 요구사항이나 실행 결과에 비추어 판단하는 것이 같습니다.
미확인된 사항이 이번 변경을 수용하는 데 문제가 되는지는 사람이 판단합니다. 가져오는 API를 변경했는데 함수 테스트만 했다면, 실제 호출 경로를 확인할 이유가 있습니다. 반면에 업데이트/삭제/목록까지 변경하지 않았다면, 이번 결과로 그것들까지 검증되었다고 쓰지 않고, 필요한 확인은 변경의 영향에 맞춰 판단해야 합니다.
다음 템플릿은 AI에게 구현이나 검증을 맡길 때 사용할 수 있습니다. 대상과 완료 조건을 자신의 제품(프로덕트)에 맞게 구체화하고, 실행하지 못한 검증도 보고에 포함하도록 요청합니다.
대상: [변경할 기능/처리]
완료 조건: [기대하는 동작과, 허용하지 않는 동작]
구현 후에는 다음을 보고해 주세요.
...
완료 보고를 받았다면,
먼저 자신이 정한 조건에 대응하는지 확인합니다.
다음으로, 그 검증에서는 발견되지 않는 결함(bug)을 하나 생각해 보고, 영향도에 따라 추가 확인 범위를 정합니다.
AI에게 구현이나 테스트 작성을 맡기는 경우에도, 성공 표시만으로 판단하지 않고, 완료 조건/실행한 검증/놓치기 쉬운 결함/미확인 사항을 연결 지어 읽는 것이 중요합니다.