ChatGPT API는 빠르게 배포할 수 있지만, 계약 위반을 늦게 발견하면 토큰 비용과 디버깅 시간이 동시에 늘어납니다. 스트리밍 응답은 일반 JSON 응답과 다르게 실패하고, 함수 호출은 JSON 스키마와 실제 모델 응답이 어긋날 수 있으며, 속도 제한은 로컬보다 프로덕션에서 먼저 문제가 됩니다. Python REPL이나 curl 루프만으로 이를 검증하면 반복 비용이 커집니다.
이 글에서는 Apidog에서 ChatGPT API 테스트 워크플로를 구성하는 방법을 단계별로 다룹니다. 인증, 기본 채팅 완성, SSE 스트리밍, 함수 호출, 오류 처리, 속도 제한 검사, 프론트엔드 병렬 개발을 위한 Mock 응답까지 하나의 재사용 가능한 프로젝트로 묶습니다.
TL;DR
- ChatGPT 기본 URL
https://api.openai.com/v1을 Apidog 환경으로 추가합니다. - OpenAI API 키는 시크릿 변수로 저장하고, 폴더 수준에서 Bearer 인증을 적용합니다.
-
/chat/completions요청을 하나 만든 뒤 여러 모델에 재사용합니다. - Apidog 응답 패널에서 SSE 스트리밍 청크를 바로 확인합니다.
- 함수 호출은 요청 본문의
tools배열로 테스트하고, 반환된tool_calls를 검증합니다. - OpenAI 토큰을 쓰기 전에 Apidog Mock으로 프론트엔드 개발을 병렬화합니다.
- 상태 코드,
choices[0].message.content,usage.total_tokens, 도구 호출 스키마를 테스트 시나리오로 저장하고 CI에서 실행합니다.
ChatGPT API를 테스트해야 하는 이유
OpenAI API는 표면적으로 안정적으로 보이지만, 실제 통합에서는 다음 변경 사항이 영향을 줄 수 있습니다.
-
function_call에서tool_calls로의 전환 - 도구 스키마의 strict 모드
-
temperature,top_p등을 지원하지 않는 추론 모델 (o1,o3) response_format: { type: "json_schema" }- 스트리밍 도구 호출에서 조각 단위로 도착하는 delta
-
/v1/chat/completions와 겹치는/v1/responses엔드포인트
이런 변경이 애플리케이션 코드에 바로 연결되어 있으면 프롬프트 변경 PR 하나가 회귀를 만들 수 있습니다. Apidog 컬렉션을 사용하면 요청을 고정하고, 응답 형태를 검증하고, 계약이 깨질 때 테스트가 실패하도록 만들 수 있습니다.
단계 1: Apidog에 OpenAI 환경 추가
Apidog에서 새 프로젝트를 만들고 환경 관리 메뉴에서 OpenAI Prod 환경을 추가합니다.
| 변수 | 값 |
|---|---|
baseUrl |
https://api.openai.com/v1 |
OPENAI_API_KEY |
sk-proj-... |
defaultModel |
gpt-5.5 |
OPENAI_API_KEY는 시크릿으로 표시하세요. 공유 워크스페이스에서 값이 마스킹되고, 컬렉션을 내보낼 때 키가 포함되지 않습니다. 팀원은 변수 이름만 보고 자신의 키를 입력하면 됩니다.
단계 2: 폴더 수준에서 Bearer 인증 설정
프로젝트 안에 ChatGPT 폴더를 만듭니다.
-
ChatGPT폴더 설정을 엽니다. - Auth로 이동합니다.
- 인증 타입으로 Bearer Token을 선택합니다.
- 토큰 값에 다음을 입력합니다.
{{OPENAI_API_KEY}}
이제 폴더 안의 모든 요청이 Authorization 헤더를 상속합니다.
Authorization: Bearer {{OPENAI_API_KEY}}
각 요청마다 Authorization: Bearer sk-...를 붙여넣지 않아도 되고, 키를 교체할 때도 환경 변수만 수정하면 됩니다.
단계 3: 첫 번째 채팅 완성 요청 만들기
ChatGPT 폴더에서 새 요청을 만듭니다.
- Method:
POST - URL:
{{baseUrl}}/chat/completions - Body: JSON
{
"model": "{{defaultModel}}",
"messages": [
{
"role": "system",
"content": "You are a senior backend engineer. Answer in under 100 words."
},
{
"role": "user",
"content": "What's the difference between idempotent and safe HTTP methods?"
}
],
"temperature": 0.2
}
전송 후 다음을 확인합니다.
- 상태 코드:
200 - 응답 본문:
choices[0].message.content - 토큰 사용량:
usage.total_tokens
요청 이름은 chat-completion-basic으로 저장합니다.
문제가 발생하면 아래를 먼저 확인하세요.
| 상태 코드 | 확인할 항목 |
|---|---|
401 |
환경이 OpenAI Prod인지, OPENAI_API_KEY가 설정되어 있는지 확인 |
429 |
속도 제한 도달 여부 확인 |
404 |
모델 이름이 올바른지 확인 |
단계 4: 스트리밍 응답 테스트하기
스트리밍 응답은 일반 JSON이 아니라 text/event-stream입니다. 각 청크는 다음과 같은 SSE 프레임으로 도착합니다.
data: {...}
마지막 프레임은 다음 문자열입니다.
data: [DONE]
chat-completion-basic 요청을 복제하고 이름을 chat-completion-stream으로 바꾼 뒤, 본문에 "stream": true를 추가합니다.
{
"model": "{{defaultModel}}",
"stream": true,
"messages": [
{
"role": "user",
"content": "Stream the first 100 prime numbers, comma-separated."
}
]
}
Apidog에서 전송하면 응답 패널에서 각 data: 청크가 도착하는 순서대로 표시됩니다. 이 뷰는 다음 문제를 디버깅할 때 유용합니다.
- 누락된
[DONE]종료자 - 잘못 조립된 delta
- 클라이언트의 SSE 파서 오류
- 스트리밍 중 도구 호출 인수 조립 문제
스트리밍 테스트 시 주의할 점은 다음과 같습니다.
{
"stream_options": {
"include_usage": true
}
}
usage 정보가 필요하다면 위 옵션을 추가하세요. 이 옵션이 없으면 스트리밍 응답에 토큰 사용량이 포함되지 않을 수 있습니다.
도구 호출 delta는 다음처럼 조각 단위로 도착할 수 있습니다.
indexidfunction.namefunction.arguments
특히 function.arguments는 문자열 조각으로 누적되므로 클라이언트 코드에서 전체 문자열을 조립한 뒤 JSON으로 파싱해야 합니다.
단계 5: 함수 호출 및 도구 사용 테스트
함수 호출은 프롬프트 변경이 다운스트림 코드를 깨뜨리기 쉬운 지점입니다. 모델은 tool_calls 배열을 반환하고, 애플리케이션은 그 안의 function.arguments를 JSON으로 파싱합니다.
다음 요청을 chat-completion-tools로 저장합니다.
{
"model": "{{defaultModel}}",
"messages": [
{
"role": "user",
"content": "What is the weather in Singapore right now?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"unit": {
"type": "string",
"enum": ["c", "f"]
}
},
"required": ["city"]
},
"strict": true
}
}
],
"tool_choice": "auto"
}
기대하는 응답 형태는 다음과 같습니다.
choices[0].message.tool_calls[0].function.name === "get_weather"
그리고 function.arguments는 JSON 문자열이어야 합니다.
예시:
{
"city": "Singapore",
"unit": "c"
}
Apidog의 Tests 탭에 다음 어설션을 추가합니다.
pm.test("Tool was called", () => {
const body = pm.response.json();
const call = body.choices[0].message.tool_calls?.[0];
pm.expect(call?.function?.name).to.eql("get_weather");
});
pm.test("Arguments parse as valid JSON", () => {
const body = pm.response.json();
const args = JSON.parse(
body.choices[0].message.tool_calls[0].function.arguments
);
pm.expect(args.city).to.be.a("string");
});
이 테스트가 통과하면 도구 호출 응답 형태에 대한 최소 계약이 생깁니다. 모델 응답 구조가 바뀌거나 인수가 JSON으로 파싱되지 않으면 프로덕션 전에 테스트가 실패합니다.
단계 6: 오류 및 속도 제한 테스트
프로덕션 ChatGPT 통합은 보통 다음 시나리오에서 실패합니다. 각 케이스를 별도 요청 또는 테스트 시나리오로 만들어 두세요.
| 시나리오 | 트리거 방법 | 예상 결과 |
|---|---|---|
| 유효하지 않은 키 |
Sandbox 환경에서 OPENAI_API_KEY를 sk-bad로 설정 |
401, error.code = "invalid_api_key"
|
| 속도 제한 | Apidog 컬렉션 러너에서 요청을 반복 실행 |
429, Retry-After 헤더 |
| 토큰 한도 초과 | 컨텍스트 한도를 넘는 프롬프트 전송 |
400, error.code = "context_length_exceeded"
|
| 잘못된 모델 이름 | "model": "gpt-99" |
404 |
| 스키마 위반 |
strict: true 도구에 잘못된 입력 유도 |
도구 호출 거부 또는 일반 텍스트 응답 |
예를 들어 상태 코드와 오류 코드를 검증하려면 Tests 탭에 다음을 추가합니다.
pm.test("Returns 401 for invalid API key", () => {
pm.response.to.have.status(401);
const body = pm.response.json();
pm.expect(body.error.code).to.eql("invalid_api_key");
});
속도 제한 테스트에서는 Retry-After 헤더도 확인하세요.
pm.test("429 includes Retry-After", () => {
pm.response.to.have.status(429);
const retryAfter = pm.response.headers.get("Retry-After");
pm.expect(retryAfter).to.not.be.undefined;
});
Retry-After는 초 단위이며 소수 값일 수 있습니다. 프로덕션 코드에서는 고정 백오프만 쓰지 말고 이 헤더를 읽어 재시도 간격에 반영해야 합니다.
단계 7: 프론트엔드 개발을 위한 ChatGPT Mock 만들기
프론트엔드가 다음 UI를 먼저 구현해야 할 수 있습니다.
- 스트리밍 토큰 렌더링
- 추천 후속 질문 표시
- 도구 호출 카드 표시
- 로딩 및 중단 상태 처리
이때 실제 OpenAI 토큰을 계속 쓰지 말고 Apidog Mock을 사용합니다.
-
ChatGPT폴더에서chat-completion-basic요청을 우클릭합니다. - Smart Mock을 선택합니다.
- Mock을 활성화합니다.
Apidog는 OpenAI 응답 형태와 맞는 합성 응답을 반환합니다.
{
"id": "chatcmpl_mock",
"object": "chat.completion",
"created": 1710000000,
"model": "gpt-5.5",
"choices": [],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}
Mock URL은 다음 형태입니다.
https://mock.apidog.com/m1/<projectId>/chat/completions
프론트엔드는 동일한 요청 본문을 이 Mock URL로 보내면 됩니다.
스트리밍 Mock이 필요하다면 Advanced Mock에서 50ms 간격으로 다음 형태의 청크를 반환하는 스크립트를 구성합니다.
data: { ... }
data: { ... }
data: [DONE]
프론트엔드는 실제 OpenAI 호출 없이 SSE 스트림을 처리하는 코드를 구현하고 테스트할 수 있습니다. 실제 프롬프트가 준비되면 기본 URL만 다시 https://api.openai.com/v1로 바꾸면 됩니다.
단계 8: CI 테스트 시나리오로 저장
Apidog 테스트 시나리오를 사용하면 여러 요청과 어설션을 묶어서 실행할 수 있습니다.
다음 흐름으로 시나리오를 만드세요.
-
chat-completion-basic실행status === 200usage.total_tokens > 0
-
chat-completion-stream실행- SSE가
[DONE]으로 끝나는지 확인
- SSE가
-
chat-completion-tools실행-
tool_calls[0].function.name검증 -
function.argumentsJSON 파싱 검증
-
-
오류 시나리오 실행
401429400404
예시 테스트:
pm.test("Basic completion returns content", () => {
pm.response.to.have.status(200);
const body = pm.response.json();
pm.expect(body.choices[0].message.content).to.be.a("string");
pm.expect(body.usage.total_tokens).to.be.greaterThan(0);
});
시나리오를 내보낸 뒤 CI에서 실행합니다.
apidog-cli run scenario.json --env "OpenAI Prod"
프롬프트 파일이 포함된 PR 파이프라인에 이 명령을 연결하세요. 그러면 프롬프트 변경이 병합되기 전에 실제 OpenAI API를 대상으로 계약 테스트를 실행할 수 있습니다.
자주 묻는 질문
Azure OpenAI와도 작동합니까?
예. baseUrl을 Azure 리소스 URL로 바꾸고, api-version 쿼리 매개변수를 추가하세요. 인증은 Bearer 대신 api-key 헤더로 변경합니다. 요청 본문 구조는 대부분 동일하게 사용할 수 있습니다.
o1 및 o3 추론 모델에도 사용할 수 있습니까?
예. 다만 이 모델들은 temperature, top_p, presence_penalty, frequency_penalty 같은 일부 매개변수를 거부할 수 있습니다. 해당 모델용으로 별도 Reasoning 폴더를 만들고 간소화된 본문 템플릿을 사용하세요.
Apidog에서 프롬프트를 어떻게 버전 관리합니까?
Apidog의 브랜치 기능을 사용할 수 있습니다. 프롬프트 실험마다 브랜치를 만들고, 라이브 API에 대해 테스트 시나리오를 실행한 뒤, 토큰 사용량과 응답 품질을 비교하고 병합하세요. 코드 리뷰와 같은 방식으로 프롬프트 변경을 관리할 수 있습니다.
/v1/responses 엔드포인트는 어떻게 테스트합니까?
별도 폴더를 만들면 됩니다. 인증과 기본 URL은 동일하고 요청 본문 형태만 다릅니다. /v1/chat/completions와 /v1/responses를 모두 유지하면 같은 프롬프트를 A/B 테스트하기 쉽습니다.
Apidog는 API 호출당 비용을 청구합니까?
아니요. OpenAI가 토큰당 비용을 청구합니다. Apidog는 클라이언트와 테스트 워크플로를 제공하며, OpenAI 호출 자체의 과금 주체는 OpenAI입니다.
마무리
ChatGPT API는 계속 바뀝니다. 스트리밍 동작, 도구 스키마, 추론 모델의 지원 매개변수는 통합 코드에 직접적인 영향을 줍니다. 이를 관리하려면 다음 세 가지가 필요합니다.
- 재실행 가능한 요청 컬렉션
- 프론트엔드가 사용할 수 있는 Mock 서버
- 모든 프롬프트 PR 전에 실행되는 CI 테스트 시나리오
Apidog를 다운로드하고 기존 OpenAI 호출을 가져오세요. Postman 컬렉션과 curl 명령을 변환한 뒤, 위의 8단계를 프로젝트에 적용하면 ChatGPT API 변경을 프로덕션 사고가 아니라 테스트 실패로 먼저 확인할 수 있습니다.
Top comments (0)