AI로 주문 접수 API를 만들고 호출했더니 성공 응답이 왔다. 그런데 접수 화면의 합계가 주문서와 다르다. 요청이 서버에 도착했다는 사실과, 업무에 쓸 값이 정확하다는 사실은 다르다.
Bruno를 쓰면 요청 본문과 응답 검사 조건을 파일로 묶어 다시 실행할 수 있다. 다만 설치만 하면 잘못된 금액을 찾아주는 것은 아니다. ‘수량 2개 × 단가 12,900원 = 25,800원’ 같은 업무 기준을 검사 조건에 넣어야 한다.
성공 코드 다음에는 주문번호·합계·상태를 확인한다
검증에는 비교용 주문 접수 API를 사용했다. 주문번호 000071, 수량 2, 단가 12,900원을 보낸 뒤 총액 25,800원과 접수 상태 received가 돌아와야 한다. 주문번호를 숫자로 바꿔 앞자리 0을 없애는 것도 오류로 봤다.
| 요청 | 기대하는 결과 |
|---|---|
| 처음 주문 접수 | 201, 주문번호 000071, 합계 25,800원, received |
| 같은 주문 재요청 | 200, 같은 주문 반환, 주문 수는 1건 |
| 기존 주문 조회 | 200, 저장한 번호·금액·상태 유지 |
| 수량 0 / 없는 주문 조회 | 각각 400 / 404로 거절 |
이 기준은 주문 접수 예제의 약속이다. 실제 API가 중복 요청에 409를 반환하도록 설계됐다면 그 규칙으로 바꿔야 한다. AI에게 검사 코드를 만들게 할 때도 성공 코드만 알려주지 말고, 주문번호 타입·금액 계산식·재요청 처리 규칙까지 전달한다.
검사 조건이 같으면 기존 코드도 같은 오류를 찾았다
Bruno CLI 4.2.1과 Node.js의 fetch·assert 검사 코드를 같은 로컬 API에 실행했다. 두 방법 모두 요청은 7개, 확인 조건은 20개로 맞췄다. 정상 상태와 다섯 가지 오류 상태를 각각 실행한 결과는 아래와 같다. 숫자는 잘못된 주문 수가 아니라 실패한 검사 조건 수다.
| API 상태 | 기존 코드 / Bruno |
|---|---|
| 정상 | 0 / 0 |
| 합계를 1,000원 크게 반환 | 3 / 3 |
| 접수 상태 필드 누락 | 3 / 3 |
| 주문번호 앞자리 0 제거 | 4 / 4 |
| 같은 주문 재접수 시 중복 생성 | 1 / 1 |
| 수량 0을 정상 주문으로 접수 | 2 / 2 |
Bruno의 오류 탐지 결과가 더 좋지는 않았다. 기존 코드에도 같은 조건이 들어 있었기 때문이다. 이미 자동 검사가 잘 돌아간다면 도구를 갈아탈 이유는 적다. Bruno는 요청·환경·검사 결과를 한 형식으로 관리하고, 다른 담당자가 어떤 검사가 실패했는지 보고서로 확인해야 할 때 더 의미가 있다.

주문 API에 맞는 요청 파일과 검사 조건을 남긴다
아래는 이번에 실행한 요청 파일의 구성이다. CLI를 쓰려면 Node.js와 npm이 필요하다. 공식 CLI 시작 안내는 설치와 컬렉션 실행, 보고서 출력을 설명한다. 여기서는 전역 설치 대신 작업 폴더에 4.2.1을 설치하는 방식으로 버전을 고정한다.
npm init -y
npm install --save-dev @usebruno/cli@4.2.1
npx --no-install bru --version
새 실습 폴더의 터미널에서 실행한다. 기존 업무 프로젝트에 그대로 실행하지 않는다. 설치 뒤 그 폴더에 bruno.json과 01-create.bru를 만든다. bruno.json에는 아래 내용을 넣는다.
{"version":"1","name":"Order API checks","type":"collection","ignore":["node_modules",".git"]}
01-create.bru는 아래처럼 작성한다. 테스트 API의 /orders가 해당 입력과 응답 약속을 지원해야 실행된다. 독자의 API에서 주소·필드명·상태 값이 다르면 그 부분과 검사 조건을 함께 바꾼다.
meta {
name: 01-create
type: http
seq: 1
}
post {
url: {{baseUrl}}/orders
body: json
auth: none
}
body:json {
{
"order_id": "000071",
"quantity": 2,
"unit_price": 12900
}
}
tests {
test("HTTP status", function() {
expect(res.status).to.equal(201);
});
test("order_id", function() {
expect(res.body.order_id).to.equal("000071");
});
test("total", function() {
expect(res.body.total).to.equal(25800);
});
test("status", function() {
expect(res.body.status).to.equal("received");
});
}
운영 주소가 아니라 테스트 서버의 주소를 baseUrl에 넣는다. 아래 8787은 예시 포트이므로 테스트 API가 실제로 실행 중인 주소로 바꿔야 한다. 이 글의 명령만으로 API 서버가 생기는 것은 아니다.
npx --no-install bru run 01-create.bru --env-var baseUrl=http://127.0.0.1:8787 --sandbox safe --reporter-html result.html --reporter-json result.json
이 파일 하나에는 검사 네 개만 들어 있다. 앞의 비교표처럼 중복·조회·수량 검증까지 보려면 해당 요청 파일을 추가해야 한다. 이미 같은 주문번호가 저장돼 있으면 첫 생성의 201 조건부터 실패할 수 있으므로, 매 실행 전에 테스트 데이터를 초기화하거나 별도 테스트 주문번호를 사용한다.
POST 요청은 실제 주문을 만들 수 있다. 운영 API로 바꿔 실행하지 말고 테스트 환경과 테스트 계정을 사용한다. 보고서에 응답 본문이나 요청 정보가 포함될 수 있으니 공유 전 고객 정보와 인증값도 확인한다.
실패한 검사 이름과 실제 반환값을 같이 읽는다
result.html을 브라우저로 열어 요청과 검사 결과를 확인한다. 총액 오류 상태에서는 201과 200 응답이 정상적으로 왔지만, 콘솔에는 아래 내용이 나타났다.
01-create (201 Created)
✕ total
expected 26800 to equal 25800
✓ HTTP status
✓ order_id
✓ status
이 결과라면 네트워크 연결이나 로그인보다 금액 계산·저장·응답 변환을 먼저 조사할 수 있다. 반대로 연결 오류가 나면 baseUrl, 서버 실행 여부, 포트부터 확인한다. 결과 파일이 만들어졌다는 사실만으로 테스트 통과로 처리하지 않는다.
처음에는 자주 고치는 API 하나에 정상 입력, 중복 입력, 잘못된 입력만 남겨도 된다. 다음 수정 때 같은 요청을 다시 실행하면 이전에 통과하던 조건이 깨졌는지 확인할 수 있다. AI에게 ‘문제없어 보인다’는 답을 받는 것보다, 어떤 입력에서 어떤 값이 나와야 하는지를 파일로 남기는 편이 변경 후 비교에 도움이 된다.
프롬프트의 출력이 바뀌는 문제는 Promptfoo로 프롬프트 변경 전후를 검사하는 글에서 별도로 다뤘다. Bruno는 이 글처럼 HTTP 요청과 응답의 약속을 확인할 때 검토하면 된다.
확인일 2026-10-08 · Bruno CLI 4.2.1 / Node.js 22.19 / Windows · 동일 로컬 API의 7개 요청·20개 조건을 비교했다. 데스크톱 앱 기능은 이번 실행 범위에 포함하지 않았다.

답글 남기기