API 테스트는 노트북에서 잘 통과할 수 있습니다. 진짜 문제는 사람이 아무것도 클릭하지 않고 모든 풀 리퀘스트, 모든 병합, 모든 야간 빌드에서 테스트가 실행되는지 여부입니다. 그 역할은 명령줄 러너의 몫입니다. 명령줄 러너는 이미 작성한 테스트를 가져와 파이프라인 내에서 헤드리스로 실행하고, 깨끗한 상태 코드로 종료하며, CI 대시보드가 읽을 수 있는 보고서를 작성합니다.
팀이 이를 설정할 때 항상 언급되는 두 가지 러너는 Bruno CLI와 Apidog CLI입니다. 이들은 서로 다른 시작점에서 동일한 문제를 해결합니다. Bruno는 Git 네이티브, 오프라인 우선, 오픈 소스 API 클라이언트이며, 해당 CLI는 저장소에 있는 .bru 파일을 실행합니다. Apidog는 올인원 API 플랫폼이며, 해당 CLI는 앱에서 구축한 시각적 테스트 시나리오를 실행합니다. 둘 다 GitHub Actions, GitLab CI, Jenkins 및 Node.js가 있는 다른 모든 것에 플러그인할 수 있습니다. 둘 다 테스트가 실패하면 빌드를 실패 처리합니다. 차이점은 테스트를 작성하는 방법, 인증하는 방법, 그리고 테스트 정의가 CI로 전달되는 방식에서 나타납니다.
이것은 두 도구에 대한 정직한 명령 수준 비교입니다. 불필요한 논쟁은 없습니다. Bruno는 여러 가지를 진정으로 잘 수행하며, 각 러너가 파이프라인에 연결되기 전에 어디에 적합한지 정확히 알 수 있을 것입니다.
요약
- Bruno CLI (
@usebruno/cli, 바이너리bru)는 Git 저장소 폴더에 있는.bru파일을 직접 실행합니다. 오픈 소스이고 오프라인이며 계정이나 토큰이 필요 없습니다. 디렉토리를 가리키면 해당 디렉토리에서 찾은 모든 요청과 어설션을 실행합니다. - Apidog CLI (
apidog-cli, 바이너리apidog)는 Apidog 앱에서 설계한 테스트 시나리오를 액세스 토큰을 사용하여 ID로 가져와 실행합니다. 테스트를 코드로 다시 작성할 필요가 없습니다. 시각적 시나리오가 테스트이고, CLI가 이를 헤드리스로 실행합니다. - 둘 다 JUnit, JSON, HTML 보고서를 생성하며, 테스트가 실패하면 빌드를 실패 처리합니다. 어느 경우든 JUnit XML은 CI 대시보드에 연결됩니다.
- 모든 것을 저장소에 두고, 계정 없이, 일반 텍스트 테스트 파일을 완벽하게 오프라인으로 제어하고 싶다면 Bruno를 선택하세요.
- 수동으로 테스트 코드를 유지보수하지 않고 시각적 작성, 시나리오 체이닝, 환경 관리 및 데이터 기반 실행을 원한다면 Apidog를 선택하세요.
실제 문제: 존재하지만 실행되지 않는 테스트
수동으로 실행하는 테스트는 썩어가는 테스트입니다. 누군가가 만들었고 한 번 통과했지만, API가 그 밑에서 변경되는 동안 방치되었습니다. 해결책은 더 많은 테스트가 아닙니다. 모든 변경 사항에 대해 자동으로 실행되고, 파이프라인이 조치할 수 있는 합격/불합격 신호를 제공하는 테스트입니다.
CLI 러너가 바로 이 간극을 메웁니다. CI에서 유용하려면 세 가지가 필요합니다. GUI 없이 실행되어야 하고, 무언가 실패하면 빌드가 빨간색으로 바뀌도록 0이 아닌 값으로 종료되어야 하며, 검토자가 무엇이 문제인지 확인할 수 있도록 기계가 읽을 수 있는 보고서를 작성해야 합니다. Bruno와 Apidog는 모두 이 기준을 충족합니다. 차이점은 실행 명령 이전, 즉 테스트가 어떻게 작성되었고 어디에 존재하는지에 있습니다.
CI를 처음부터 설정하는 경우, 이 비교와 함께 CI/CD에서 API 테스트 자동화의 더 넓은 패턴을 읽어볼 가치가 있습니다. 여기서는 두 러너 자체에 중점을 둡니다.
Bruno CLI가 잘하는 점
Bruno의 전체 디자인은 Git 네이티브입니다. 모든 요청, 환경 및 어설션은 저장소 내 디스크에 있는 일반 텍스트 .bru 파일이며, 다른 소스 파일과 마찬가지로 버전 관리됩니다. 이 모델은 진정한 장점을 가지고 있으며, 비교하기 전에 이를 명확하게 언급할 가치가 있습니다.
테스트는 코드와 함께 존재합니다. 엔드포인트를 변경하는 풀 리퀘스트는 동일한 변경 내용에 해당 엔드포인트에 대한 테스트를 변경할 수 있으며, 동일한 사람이 검토합니다. 동기화할 별도의 시스템도 없고, 저장소의 내용과 다를 수 있는 클라우드 복사본도 없습니다. 형식이 텍스트이므로 차이점은 읽기 쉽습니다. 테스트를 grep하고, 코드에 사용하는 것과 동일한 도구로 리팩토링하며, 편집기에서 병합 충돌을 해결할 수 있습니다.
또한 오픈 소스이며 오프라인입니다. CLI는 계정, 로그인, 토큰 없이 컴퓨터 또는 CI 러너에서 전적으로 실행됩니다. 엄격한 데이터 처리 규칙이나 에어갭(망 분리) 환경을 가진 팀에게는 이것이 중요합니다. Bruno의 유료 서비스인 Bruno Ultimate는 SSO 및 SCIM, 비밀 관리자 통합, 감사 기능과 같은 팀 기능을 추가하여 프로젝트가 단순한 취미 도구가 아님을 보여줍니다. 그러나 핵심 클라이언트와 CLI는 무료이며 독립적이며, 이는 정당한 강점입니다.
설치는 하나의 명령으로 가능합니다:
npm install -g @usebruno/cli
바이너리는 bru입니다. .bru 파일이 있는 폴더를 가리켜 컬렉션을 실행합니다:
bru run --env staging
컬렉션 디렉토리 내에서 실행하면 bru run이 해당 디렉토리에서 찾은 요청을 실행합니다. 하위 폴더를 재귀적으로 탐색하여 중첩된 요청을 선택하려면 -r을 추가하세요:
bru run -r --env staging
이것이 핵심 루프입니다. ID, 토큰, 원격 가져오기(fetch)가 없습니다. 폴더의 파일이 테스트이고, CLI가 이를 실행합니다.
우리는 Git 네이티브 API 클라이언트로서 Bruno를 다르게 만드는 요소와 대규모 팀에서 Bruno의 한계가 나타나는 지점에서 더 광범위한 Bruno 이야기를 다루었습니다. 특히 CI의 경우 위에서 언급한 강점들이 중요합니다.
Apidog CLI가 잘하는 점
Apidog는 동일한 파이프라인에 다른 경로를 취합니다. Apidog 앱에서 시각적으로 테스트를 구축합니다: 요청을 시나리오로 연결하고, 어설션을 추가하고, 한 응답에서 다음 요청으로 값을 가져오고, 데이터 파일을 통해 전체를 반복합니다. CLI는 이러한 시나리오를 헤드리스로 실행하는 도구입니다. 자체 파일 형식이 없습니다. Apidog 프로젝트에서 ID로 명명된 시나리오를 가져와 앱에서 실행하는 것과 똑같이 실행합니다.
장점은 아무도 동일한 테스트의 두 가지 표현을 유지보수하지 않는다는 것입니다. 프로젝트의 시나리오가 곧 테스트입니다. 스크립트 파일을 작성하고 디버깅할 필요 없이 요청 체이닝, 변수 추출 및 어설션을 처리하는 시각적 빌더에서 이를 작성합니다. 그런 다음 CLI는 CI에서 동일한 시나리오를 실행합니다. 빠른 작성 루프와 자동화 루프는 하나의 진실의 원천을 사용합니다.
설치는 하나의 npm 명령으로 가능합니다:
npm install -g apidog-cli
바이너리는 apidog입니다. 일반적인 실행은 ID로 시나리오를 지정하고, 환경을 선택하고, 액세스 토큰으로 인증합니다:
apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -n 1 -r html,junit
이러한 ID를 직접 입력할 필요는 없습니다. 테스트 시나리오를 열고 CI/CD 탭으로 전환하여 액세스 토큰을 생성하면 Apidog가 시나리오 ID와 환경 ID가 이미 채워진 전체 명령을 생성해 줍니다. 한 번 복사한 다음 토큰을 CI 시크릿으로 옮겨 $APIDOG_ACCESS_TOKEN으로 참조합니다. 모든 옵션을 한곳에서 보려면 Apidog CLI 전체 가이드에 전체 플래그 참조가 있습니다.
토큰 기반 모델은 Bruno와의 가장 명확한 차이점입니다. Apidog는 CLI가 네트워크를 통해 인증되어 접근하는 프로젝트에 저장된 테스트를 실행합니다. Bruno는 CLI가 디스크에서 읽는 파일로 저장된 테스트를 실행합니다. 둘 다 틀리지 않습니다. 이들은 서로 다른 팀 설정에 적합합니다.
측면 비교
| 차원 | Bruno CLI (bru) |
Apidog CLI (apidog) |
|---|---|---|
| 패키지 | @usebruno/cli |
apidog-cli |
| 실행 명령 | bru run |
apidog run |
| 테스트 소스 | Git 저장소의 .bru 파일 |
Apidog 프로젝트의 테스트 시나리오, ID로 가져옴 |
| 작성 방식 | 텍스트 파일을 수동 편집하거나 Bruno 앱 사용 | Apidog 앱의 시각적 시나리오 빌더 |
| CI에서의 인증 | 없음; 오프라인 실행 | 액세스 토큰 (--access-token) |
| 실행할 대상 선택 | 폴더 경로, -r 재귀, --tags |
-t 시나리오, -f 폴더, --test-suite |
| 환경 | --env <이름> |
-e <환경ID> |
| 데이터 기반 | --csv-file-path, --json-file-path |
-d <경로> (CSV 또는 JSON) |
| 반복 | --iteration-count <n> |
-n <n> |
| 리포터 | JSON, JUnit, HTML | cli, html, json, junit |
| 실패 즉시 중단 | --bail |
--on-error end (기본값은 첫 번째 오류에서 중단) |
| 오픈 소스 | 예 | 아니요 (무료 npm CLI; 플랜에서 시나리오 실행) |
| 라이선스/계정 | CLI에는 없음 | 프로젝트용 Apidog 계정 |
두 가지가 눈에 뜁니다. 첫째, 두 러너 모두 동일한 CI 필수 요소(환경 선택, 데이터 기반 반복, 중요한 세 가지 보고서 형식, 실패 시 0이 아닌 종료)를 다룹니다. 둘째, 차이점은 원시 기능이 아니라 테스트가 어디에 있고 어떻게 작성되었는지에 있습니다. Bruno는 테스트를 텍스트로 저장소에 유지합니다. Apidog는 테스트를 시각적 시나리오로 프로젝트에 유지하고 참조를 통해 실행합니다.
리포터와 종료 코드: CI가 실제로 읽는 부분
러너는 두 가지 동작, 즉 작성하는 보고서와 반환하는 종료 코드를 통해 파이프라인에서 자리를 얻습니다. 이것들을 제대로 하면 나머지는 연결하는 일일 뿐입니다.
Bruno는 형식별 플래그를 사용하여 보고서를 작성합니다. 원하는 각 형식에 대해 경로를 전달합니다:
bru run -r --env staging \
--reporter-junit ./results/junit.xml \
--reporter-html ./results/report.html \
--reporter-json ./results/report.json
JUnit XML은 CI 대시보드가 합격/불합격 트리로 파싱하는 기계가 읽을 수 있는 결과입니다. HTML 보고서는 탐색 가능한 아티팩트입니다. Bruno의 --bail은 첫 번째 실패한 요청, 테스트 또는 어설션 후에 실행을 중지하여 스모크 테스트에서 빠른 피드백을 유지합니다. --bail이 없으면 모든 것을 실행하고 모든 실패를 한 번에 보고합니다.
Apidog는 쉼표로 구분된 목록과 함께 단일 -r 플래그를 사용하고, 모든 것을 하나의 출력 디렉토리 아래에 작성합니다:
apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 \
-r html,junit --out-dir ./apidog-reports
--on-error 플래그는 시나리오 중간 동작을 형성합니다: end는 첫 번째 실패에서 중지(기본값), continue는 모든 단계를 실행하여 모든 실패를 하나의 보고서에 수집하고, ignore는 알려진 불안정한 단계를 건너뜁니다. 어떤 식으로든 무언가 실패하면 실행은 0이 아닌 값으로 종료됩니다.
종료 코드 계약은 양쪽 모두 동일하며 이것이 가장 중요한 부분입니다. 어설션이 실패하면 러너는 0이 아닌 코드로 종료됩니다. CI는 해당 코드를 읽고 단계를 실패로 표시하고 작업을 실패 처리하며 병합 또는 배포를 차단합니다. 추가로 구성할 것은 없습니다. 둘 모두에게 동일한 유일한 함정은 종료 코드를 무시하는 것입니다. 실행을 셸 파이프라인으로 래핑하거나 || true를 추가하면 0이 아닌 종료가 무시되고 게이트가 조용히 작동을 멈춥니다. 그렇게 하지 마세요.
GitHub Actions의 Bruno CLI
.bru 파일이 이미 저장소에 있기 때문에 워크플로는 짧습니다. 코드를 체크아웃하고, CLI를 설치하고, 컬렉션을 실행하고, 보고서를 업로드합니다.
name: API tests
on:
pull_request:
branches: [main]
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Bruno CLI
run: npm install -g @usebruno/cli
- name: Run API tests
working-directory: ./api-tests
run: bru run -r --env staging --reporter-junit ./results/junit.xml
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: bruno-report
path: ./api-tests/results
working-directory는 컬렉션을 담고 있는 폴더를 가리킵니다. if: always()는 테스트가 실패하더라도 보고서 업로드가 실행되도록 하는데, 이는 보고서를 읽고 싶을 때와 정확히 일치합니다. 원격 서비스에 아무것도 인증할 필요가 없으므로 시크릿이 필요하지 않습니다.
GitHub Actions의 Apidog CLI
Apidog 워크플로는 구조적으로 동일하지만 한 가지 추가 사항이 있습니다. 액세스 토큰은 저장소 시크릿에서 가져오고, 폴더 경로 대신 ID로 시나리오를 선택합니다.
name: API tests
on:
pull_request:
branches: [main]
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Apidog CLI
run: npm install -g apidog-cli
- name: Run API test scenario
run: |
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t 605067 \
-e 1629989 \
-r html,junit \
--out-dir ./apidog-reports
env:
APIDOG_ACCESS_TOKEN: ${{ secrets.APIDOG_ACCESS_TOKEN }}
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: apidog-report
path: ./apidog-reports
대칭성을 주목하세요. 동일한 체크아웃, 동일한 Node 설정, 동일한 설치 후 실행 형태, 동일한 항상 업로드. 유일한 실제 차이점은 시크릿으로 연결된 토큰과 ID로 선택된 시나리오입니다. GitLab CI 및 Jenkins 변형으로도 확장된 것을 원한다면, Apidog CLI 전체 가이드가 러너 전반에 걸쳐 동일한 패턴을 제공합니다.
선택 방법
결정은 어떤 러너가 "더 나은지"에 따라 내려지는 경우는 거의 없습니다. 팀이 테스트를 작성하고 저장하는 방법에 따라 달라집니다.
저장소가 진실의 원천일 때 Bruno를 선택하세요. 모든 테스트를 코드 옆에 있는 일반 텍스트 파일로 유지하고, 동일한 풀 리퀘스트에서 검토하며, 실행 시 계정이나 네트워크 호출 없이 사용하고 싶다면 Bruno는 이 모델에 정확히 맞습니다. 테스트를 코드로 취급하고, 오프라인 및 오픈 소스 도구를 중요하게 여기며, .bru 파일을 직접 편집하는 데 익숙한 팀에게 자연스러운 선택입니다. Bruno Ultimate는 나중에 팀 및 거버넌스 계층이 필요할 경우 SSO, SCIM, 비밀 관리자 통합 및 감사 기능을 추가하므로, 성장은 장벽이 아닌 선택 사항입니다.
파일 수준 제어보다 작성 속도와 통합된 워크플로가 더 중요할 때 Apidog를 선택하세요. 테스트 시나리오를 시각적으로 구축하고, 요청을 연결하고, 변수를 추출하며, 테스트 코드를 수동으로 작성하고 디버깅할 필요 없이 여러 환경에서 동일한 시나리오를 실행하는 것을 선호한다면 Apidog의 모델은 그러한 마찰을 제거합니다. 설계, 디버그, 모의(mock) 및 테스트가 하나의 작업 공간에 존재하며, CLI는 사용자가 구축한 정확한 시나리오를 실행합니다. Postman 설정을 사용하던 팀에게는 멘탈 모델이 명확하게 매핑되며, Apidog는 API 테스트를 위한 Postman 대체재로서 마이그레이션 경로를 제공합니다.
두 도구를 모두 사용하는 답도 있습니다. 일부 팀은 낮은 수준의 요청 확인을 위해 Bruno의 Git 네이티브 파일을 유지하고, 더 큰 체인 시나리오와 환경에 의존하는 회귀 테스트 실행을 위해 Apidog를 사용합니다. 두 CLI는 하나의 파이프라인에서 잘 공존합니다. 별도의 종료 코드를 가진 별도의 단계입니다.
CLI뿐만 아니라 더 넓은 범위에서 플랫폼 간의 결정을 내리고 있다면, Apidog vs Bruno 비교는 명령줄 외의 설계, 모의(mocking) 및 협업을 다룹니다. 첫 번째 자동화된 시나리오를 설정하고 같은 날 오후 터미널에서 실행하려면 Apidog를 다운로드하고 시나리오의 CI/CD 탭에서 생성된 명령을 복사하세요.
자주 묻는 질문
Bruno CLI는 무료인가요?
네. Bruno CLI는 오픈 소스이며 @usebruno/cli npm 패키지로 제공됩니다. 계정이나 토큰 없이 컴퓨터 또는 CI 러너에서 전적으로 실행됩니다. Bruno Ultimate는 SSO, SCIM, 비밀 관리자 통합 및 감사와 같은 팀 및 거버넌스 기능을 추가하는 별도의 유료 서비스이지만, CLI 자체는 무료입니다.
Apidog CLI는 무료인가요?
CLI는 무료 npm 패키지인 apidog-cli입니다. Apidog 프로젝트의 테스트 시나리오를 실행하므로, 실행할 수 있는 내용은 Apidog 플랜에 따라 다르지만, 명령줄 러너는 별도의 유료 제품이 아닙니다.
두 러너 중 어떤 러너라도 테스트를 코드로 작성해야 하나요?
Bruno의 경우 테스트는 수동으로 편집하거나 Bruno 앱에서 작성할 수 있는 .bru 파일이므로, 직접 다루게 될 텍스트 형식이 있습니다. Apidog의 경우 앱에서 시각적으로 시나리오를 구축하고 CLI가 ID로 실행하므로 테스트 코드를 수동으로 유지보수할 필요가 없습니다. 이것이 두 도구 간의 핵심적인 작성 방식 차이입니다.
테스트가 실패하면 두 러너 모두 빌드를 실패 처리하나요?
네. 둘 다 어설션이 실패하면 0이 아닌 코드로 종료되며, CI는 이를 읽고 단계를 실패로 표시하고 병합 또는 배포를 차단합니다. Bruno의 --bail은 첫 번째 실패에서 중지하고, Apidog의 --on-error end도 동일하게 작동하며 기본값입니다. 종료 코드를 무시하고 게이트를 망가뜨리는 || true로 실행을 래핑하는 것을 피하세요.
CI에서 어떤 보고서 형식을 사용해야 하나요?
CI 대시보드가 합격/불합격 트리로 파싱하는 기계가 읽을 수 있는 결과에는 JUnit XML을 사용하고, 탐색 가능한 아티팩트를 원한다면 HTML을 추가하세요. Bruno는 --reporter-junit 및 --reporter-html로 작성하고, Apidog는 -r html,junit를 사용합니다. 둘 다 사용자 정의 후처리를 위해 JSON도 지원합니다.
Bruno CLI는 실행하는 데 인터넷 연결이나 계정이 필요한가요?
아니요. Bruno는 저장소의 .bru 파일을 로그인이나 원격 가져오기(fetch) 없이 로컬로 실행하므로, 오프라인 또는 에어갭(망 분리)된 CI에 적합합니다. Apidog의 CLI는 액세스 토큰으로 인증하고 프로젝트에서 시나리오를 가져오므로, 실행 시 Apidog 서비스에 대한 네트워크 접근이 필요합니다.
전역 설치 없이 CLI를 실행할 수 있나요?
네, 둘 다 가능합니다. 임시 CI 러너에서 편리하게 사용할 수 있도록 영구적인 전역 설치 없이 실행하려면 npx @usebruno/cli run ... 또는 npx apidog-cli run ...을 사용하세요. 설치된 버전에서 사용 가능한 정확한 옵션을 확인하려면 bru run --help 또는 apidog run --help를 실행하세요.
