실시간 API에 의존하는 테스트는 잘못된 이유로 실패하는 테스트입니다. 스테이징 서버가 다운되거나, 서드파티 속도 제한이 걸리거나, 팀원이 레코드를 변경하면, 코드는 괜찮은데도 갑자기 테스트 스위트가 빨간색으로 변합니다. API를 모의(mocking)하는 것은 이러한 취약성을 제거합니다. 실제 엔드포인트를 제어 가능한 대역으로 대체하여, 요청하는 응답을 매번 정확히 반환하도록 합니다.
이 가이드는 테스트를 위해 API를 모의하는 실제 단계를 안내합니다. 스키마를 정의하고, 모의 응답을 생성하고, 테스트 코드가 모의를 가리키도록 설정하며, 실제 서버가 거의 요청 시 생성하지 않는 오류 경로를 다룰 것입니다. 예제는 작은 주문 관리 API를 사용하지만, 이 워크플로우는 모든 REST 또는 GraphQL 서비스에 적용됩니다.
API 모의가 적절한 경우
테스트하려는 것이 네트워크가 아닌 자신의 코드일 때 API를 모의하십시오. 단위 테스트와 대부분의 통합 테스트가 이 범주에 속합니다. 클라이언트가 200을 올바르게 파싱하고, 503에서 재시도하며, 404에서 명확한 메시지를 표시하는지 알고 싶을 것입니다. 이 중 어떤 것도 실제 서버를 필요로 하지 않습니다.
실제 API는 계약 테스트와 얇은 계층의 엔드투엔드 검사에 사용하십시오. 이들은 모의가 여전히 실제와 일치하는지 확인하기 위해 존재합니다. 모든 것을 모의하고 라이브 서비스에 대해 전혀 검증하지 않으면, 프로덕션은 고장 나는데도 스위트는 계속 녹색으로 유지됩니다. 대략적으로는 다음과 같습니다: 속도와 격리를 위해 모의하고, 계약 확인을 위해 실제 서비스를 호출합니다. 각 방법이 어디에 적합한지 더 자세히 알아보려면, API 모의가 효과적인 시나리오 및 모의 서버와 실제 서버의 차이점에 대한 분석을 참조하십시오.
한눈에 보는 5단계 워크플로우
테스트를 위해 API를 모의하는 것은 언어나 프레임워크에 관계없이 항상 동일한 5단계입니다:
- 스키마 정의: 모의가 실제 응답 형태를 반영하도록 합니다.
- 모의 응답 생성: 해당 스키마에서 정적 또는 동적 모의 응답을 생성합니다.
- 모의 서버 실행: 해당 응답을 URL에서 제공하는 모의 서버를 실행합니다.
- 테스트를 모의로 지정: 기본 URL을 구성 가능하게 하여 테스트가 모의를 가리키도록 합니다.
- 오류 경로 테스트: 실제 서버가 요청 시 생성하지 않을 오류 경로를 테스트합니다.
이 가이드의 나머지 부분에서는 GET /orders/{id} 엔드포인트를 가진 작은 주문 관리 API라는 구체적인 예시를 통해 각 단계를 설명합니다. 이 엔드포인트를 계속해서 예시로 기억하십시오.
1단계: 스키마 정의
모의는 실제 응답 형태를 반영할 때만 유용합니다. 스키마부터 시작하십시오. API에 이미 OpenAPI 문서가 있다면, 그것을 사용하십시오. 없다면, 테스트 중인 엔드포인트에 대한 스키마를 작성하십시오. 다음은 GET /orders/{id}에 대한 간략화된 정의입니다:
paths:
/orders/{id}:
get:
parameters:
- name: id
in: path
required: true
schema: { type: string }
responses:
'200':
content:
application/json:
schema:
type: object
properties:
id: { type: string }
status: { type: string, enum: [pending, shipped, delivered] }
total: { type: number }
items: { type: array, items: { type: object } }
'404':
description: Order not found
스키마는 두 가지 역할을 합니다. 모의에게 어떤 필드를 반환해야 하는지 알려주고, 단일 진실의 원천을 제공합니다. 백엔드가 계약을 변경하면, 스키마를 업데이트하고 거기서 파생된 모든 모의는 정확하게 유지됩니다. 스키마 우선 모의는 API 계약 테스트를 정직하게 유지하는 비결입니다.
2단계: 모의 응답 생성
응답 본문을 생성하는 두 가지 방법이 있습니다.
정적 응답은 고정된 JSON입니다. 페이로드를 한 번 작성하면 모의는 그것을 변경 없이 반환합니다. 예측 가능하고 단언하기 쉬워서, 알려진 하나의 값을 원하는 단위 테스트에 이상적입니다.
동적 응답은 요청마다 생성됩니다. "total": 149.99를 하드코딩하는 대신, 모의는 id에 UUID, status에 무작위 열거형 멤버, total에 그럴듯한 통화 금액과 같이 현실적인 생성 값으로 필드를 채웁니다. 동적 데이터는 긴 문자열에서 작동하지 않거나 예기치 않은 null 필드와 같은, 단일 고정 페이로드가 숨기는 버그를 찾아냅니다.
대부분의 팀은 둘 다 사용합니다. 단언이 중요한 테스트에는 정적 페이로드를, 퍼즈 스타일 커버리지에는 동적 생성을 사용합니다. Apidog를 사용하면, 이 둘 중 어느 것도 직접 작성할 필요가 없습니다. Apidog는 스키마를 읽고, email, phone, avatar와 같은 필드 이름을 올바른 데이터 유형과 일치시켜 모의 엔드포인트를 자동으로 생성합니다. 브라우저를 모의 URL로 가리키면 유효한 응답을 즉시 얻을 수 있습니다.
페이로드를 직접 작성할 때는 현실적으로 유지하십시오. "total": 0과 비어있는 items 배열을 가진 테스트 주문은 순진한 파서(parser)는 통과하지만 버그를 숨깁니다. 프로덕션과 유사한 값을 사용하십시오: 실제와 같은 총액, 두세 개의 품목, 열거형에 실제로 있는 상태. 모의 데이터가 실제 요청이 반환하는 것과 가까울수록 테스트의 가치는 더 커집니다.
3단계: 모의 서버 실행
모의 응답은 그것을 제공하는 무언가가 있기 전까지는 소용이 없습니다. 두 가지 호스팅 선택지가 있습니다.
로컬 모의 서버는 일반적으로 localhost:4010과 같은 포트에서 자신의 머신에서 실행됩니다. 빠르고 오프라인에서도 작동하며, 단위 및 통합 테스트의 기본입니다. Prism과 같은 경량 도구는 OpenAPI 파일에서 직접 모의 서버를 시작합니다:
prism mock openapi.yaml
# Mock server listening on http://127.0.0.1:4010
클라우드 모의 서버는 공개 URL을 가집니다. 모바일 앱, CI 러너 또는 외부 협업자가 노트북에 접근할 수 없이 모의를 호출해야 할 때 사용하십시오. Apidog는 모든 프로젝트에 호스팅된 클라우드 모의 URL을 제공하므로, 다른 네트워크의 팀원이 당신과 동일한 엔드포인트를 호출할 수 있습니다.
테스트 실행 시에는 로컬을 선호하십시오. 네트워크 지연이 없고 공유 상태가 없으므로, 두 빌드가 절대 충돌하지 않습니다. 데모 및 교차 기기 테스트에는 클라우드 옵션을 사용하십시오.
4단계: 테스트를 모의로 지정
이제 프로덕션 대신 모의에 테스트 코드를 연결하십시오. 가장 깔끔한 접근 방식은 구성 가능한 기본 URL입니다. 환경 변수에서 읽어와서 동일한 테스트 파일이 로컬에서는 모의에 대해 실행되고, 계약 작업에서는 실제 API에 대해 실행되도록 합니다.
// orderClient.test.js
import { getOrder } from './orderClient.js';
const BASE_URL = process.env.API_BASE_URL || 'http://127.0.0.1:4010';
test('parses a shipped order', async () => {
const order = await getOrder('order_8842', BASE_URL);
expect(order.status).toBe('shipped');
expect(typeof order.total).toBe('number');
});
클라이언트는 기본 URL을 인수로 받습니다; 프로덕션 코드에서는 모의되고 있다는 것을 알지 못합니다. CI에서는 스위트가 실행되기 전에 API_BASE_URL을 모의 주소로 설정합니다. 이 패턴은 모의를 애플리케이션 로직에서 완전히 분리하며, 이것이 올바른 위치입니다. 파이프라인을 통해 테스트를 실행하는 경우, CI/CD에서 API 테스트를 자동화할 때도 동일한 아이디어가 적용됩니다.
5단계: 오류 경로 테스트
이것은 대부분의 팀이 건너뛰지만, 가장 효과적인 단계입니다. 실제 서버는 당신이 원할 때 500을 거의 반환하지 않습니다. 모의는 명령에 따라 500을 반환합니다.
모의가 특정 실패 응답을 제공하도록 구성한 다음, 클라이언트가 각 응답을 처리하는지 단언하십시오:
| 시나리오 | 모의 반환 | 단언 내용 |
|---|---|---|
| 기록 없음 | 404 |
클라이언트가 명확한 "찾을 수 없음" 오류를 발생시킴 |
| 서버 오류 | 500 |
클라이언트가 재시도한 후, 폴백(fallback)을 표시함 |
| 속도 제한됨 | Retry-After 헤더와 함께 429 |
클라이언트가 적절히 후퇴함 |
| 느린 응답 | 5초 지연 후 200 |
클라이언트가 타임아웃되고 복구됨 |
| 잘못된 형식의 본문 | 손상된 JSON과 함께 200 |
클라이언트가 정상적으로 실패하고, 충돌하지 않음 |
Apidog의 고급 모의 규칙을 사용하면 요청에 따라 다른 응답을 반환할 수 있으므로, order_404에 대한 요청은 404를 반환하고 다른 모든 ID는 정상적인 200을 반환합니다. 이를 통해 성공 경로와 실패 경로를 모두 다루는 하나의 모의 엔드포인트를 가질 수 있습니다. 이를 강력한 API 단언과 결합하면, 스위트는 단순히 상태 코드가 아니라 동작을 검증합니다.
성장하는 테스트 스위트에서 모의 정리하기
단일 모의 엔드포인트는 쉽습니다. 스위트 전체에 걸쳐 백 개의 모의 엔드포인트가 있으면 팀은 통제력을 잃습니다. 몇 가지 습관은 세트를 관리 가능하게 유지합니다.
모의를 사용하는 테스트가 아닌, 모의가 대신하는 실제 서비스별로 그룹화하십시오. 결제 API가 변경될 때, 20개의 테스트 파일이 아닌 한 곳만 업데이트하고 싶을 것입니다. 실패한 테스트를 명확하게 읽을 수 있도록, 모의 픽스처(mock fixture)의 이름을 order-shipped 또는 order-rate-limited와 같이 나타내는 시나리오별로 지정하십시오. 모의는 테스트의 일부이며 동일한 검토를 받을 가치가 있으므로, 테스트 옆에 버전 관리 시스템에 모의 정의를 보관하십시오.
모든 테스트에 자체 맞춤형 페이로드를 제공하려는 충동을 참으십시오. 대부분의 테스트는 하나의 필드만 변경된 동일한 현실적인 주문 객체를 원합니다. 기본 응답을 한 번 정의하고 테스트 중인 필드만 재정의하십시오. 이렇게 하면 스위트가 읽기 쉽고, 계약 변경 시 흩어져 있는 복사본 대신 하나의 기본 정의만 건드리게 됩니다. API 자동화를 위한 테스트 스위트를 유지보수 가능하게 만드는 것과 동일한 원칙이 그 뒤에 있는 모의에 직접 적용됩니다.
모의를 정직하게 유지하기
모의는 점차 멀어집니다. 백엔드가 필드를 추가하거나, total을 amount로 이름을 바꾸거나, 열거형을 변경해도 모의는 계속 이전 형태를 반환합니다. 테스트는 통과하지만, 프로덕션은 실패합니다. 이것은 모의가 잘못되는 가장 흔한 방법이며, 조용히 발생합니다. 스위트가 스스로 모의를 측정하기 때문에 스위트에서는 아무것도 불평하지 않습니다.
두 가지 습관이 이를 방지합니다. 첫째, 백엔드가 게시하는 동일한 스키마에서 모의를 파생시켜 계약 변경 시 둘 다 동시에 업데이트되도록 합니다. OpenAPI 파일에서 생성된 모의는 해당 파일이 변경될 때 재생성되지만, 직접 작성한 모의는 그렇지 않습니다. 둘째, 실제 API에 대해 정기적으로 소규모 계약 테스트 세트를 실행하십시오. 이들의 유일한 역할은 라이브 응답이 모의가 사용하는 스키마와 여전히 일치하는지 확인하는 것입니다. 테스트가 실패하면, 사용자들이 알기 전에 모의가 오래되었다는 것을 알게 됩니다.
코드 검토 중에 모의를 검토하는 것도 도움이 됩니다. 풀 리퀘스트가 API 응답을 변경할 때, 검토자는 일치하는 모의도 변경되었는지 확인해야 합니다. 모의를 일회용 테스트 도우미가 아닌 계약의 일부로 취급하는 것이, 수개월에 걸친 변경에도 모의된 스위트를 신뢰할 수 있게 유지하는 비결입니다.
스키마를 보유하고, 모의를 생성하며, 그에 대해 테스트를 실행하는 단일 환경을 원한다면, Apidog를 다운로드하십시오. Apidog는 디자인, 모의 서버 및 테스트 스위트를 동기화하여, 테스트하는 모의가 항상 현재 계약이 되도록 합니다. 더 넓은 선택지를 원하시면, REST API 모의 도구에 대한 이 요약을 통해 해당 분야를 비교하십시오.
자주 묻는 질문
모든 테스트에 API를 모의해야 할까요?
아닙니다. 자신의 코드를 확인하는 단위 및 통합 테스트에만 모의를 사용하십시오. 모의가 여전히 프로덕션과 일치하는지 확인하는 계약 및 엔드투엔드 테스트 세트는 소규모로 실제 API를 호출하도록 유지하십시오. 모든 것을 모의하는 것은 계약 불일치를 숨깁니다.
정적 모의 응답과 동적 모의 응답의 차이점은 무엇인가요?
정적 응답은 결코 변하지 않는 고정된 JSON 페이로드로, 예측 가능한 단언에 좋습니다. 동적 응답은 요청마다 현실적인 값으로 생성되며, 단일 고정 페이로드가 놓칠 수 있는 버그를 잡아냅니다. 대부분의 팀은 둘 다 사용합니다.
모의가 정확하게 유지되도록 하려면 어떻게 해야 하나요?
백엔드가 사용하는 동일한 스키마(이상적으로는 OpenAPI 문서)에서 모의를 생성하십시오. 그런 다음 실제 API에 대해 예약된 계약 테스트를 실행하여 라이브 응답이 해당 스키마와 여전히 일치하는지 확인하십시오. 테스트가 실패하면, 모의를 업데이트해야 합니다.
모의가 느리거나 실패하는 응답을 시뮬레이션할 수 있나요?
예, 그리고 이것이 모의를 사용하는 가장 강력한 이유 중 하나입니다. 모의를 500, Retry-After 헤더가 포함된 429, 또는 지연된 200을 반환하도록 구성할 수 있습니다. 이를 통해 정상적인 실제 서버가 요청 시 절대 트리거하지 않을 재시도 로직과 타임아웃을 검증할 수 있습니다.
테스트를 위한 로컬 모의 서버와 클라우드 모의 서버 중 어떤 것을 사용할까요?
테스트 실행에는 로컬 모의 서버를 사용하십시오. 빠르고, 네트워크 지연이 없으며, 빌드 간의 공유 상태를 피할 수 있습니다. 모바일 기기, CI 러너 또는 외부 협업자가 당신의 머신에 접근하지 않고 모의에 도달해야 할 때 클라우드 호스팅 모의를 사용하십시오.
