모두가 복제한 OpenAI API는 OpenAI가 권장하는 것이 아닙니다
요약
업계 표준처럼 사용되는 'OpenAI 호환' API 형식이 실제로는 OpenAI가 권장하는 최신 방식과 다를 수 있음을 설명합니다. 기존의 Chat Completions API와 새롭게 도입된 Responses API의 차이점을 분석합니다.
핵심 포인트
- 업계 표준인 'OpenAI 호환'은 주로 Chat Completions 형식을 의미함
- OpenAI는 2025년 3월 도입된 Responses API를 새로운 표준으로 권장함
- Chat Completions는 상태를 유지하지 않는(stateless) 방식임
- 다단계 에이전트 구현 시 Chat Completions의 구조적 한계가 발생할 수 있음
저는 OpenAI와 아무런 관련이 없는 프로젝트들에 "OpenAI 호환(OpenAI-compatible)"이라는 문구가 찍혀 있는 것을 계속 보았습니다. Ollama, vLLM, LM Studio 등 제가 개인 하드웨어에서 실행하는 대부분의 로컬 모델 도구들이 그러합니다. 이들 중 어느 것도 OpenAI는 아니지만, 모두 동일한 호환성 배지를 광고하고 있습니다. 그래서 저는 그 배지가 실제로 무엇을 의미하는지 찾아보았고, 그 답은 제가 예상했던 것보다 더 흥미로웠습니다.
단 하나의 OpenAI API 규격은 존재하지 않습니다. 두 가지 형식이 있으며, 이들은 서로 다른 목적을 수행합니다. 그리고 모두가 복제한 방식은 현재 OpenAI가 사용하라고 권장하는 방식이 아닙니다.
하나가 아닌 두 가지 형식
OpenAI는 모델에 텍스트 및 멀티모달 (multimodal) 요청을 보낼 수 있는 두 가지 주요 방법을 제공합니다. 오래된 방식은 Chat Completions API입니다. 더 새로운 방식은 2025년 3월에 도입되었으며 현재 새로운 프로젝트에 권장되는 Responses API입니다.
이 구분이 중요한 이유는 제가 항상 목격하는 조용한 혼동 때문입니다. 사람들이 어떤 도구가 "OpenAI 호환"이라고 말할 때, 그들은 거의 항상 Chat Completions를 의미합니다. 그것이 업계의 나머지 부분들이 복제한 형식입니다. Responses API는 OpenAI가 모두를 이끌고 있는 방향이지만, 표준이 된 것은 아닙니다. 여러분이 어떤 것을 말하고 있는지 아는 것이 많은 혼란을 줄여줍니다.
두 가지 방식을 모두 살펴본 후, 왜 이런 일이 발생했는지, 그리고 여러분이 신경 써야 할 문제인지에 대해 말씀드리겠습니다.
Chat Completions의 작동 방식
Chat Completions는 요청을 메시지 리스트로 모델링하며, 각 메시지는 role을 가집니다. 이 역할(role)들이 작업을 수행합니다:
developer는 페르소나와 규칙을 설정합니다. 이전 모델들은 이를system이라고 불렀습니다.user는 인간의 프롬프트 (prompt)를 담습니다.assistant는 모델의 이전 답변을 담으며, 이를 통해 대화 기록을 재생할 수 있습니다. 요청은 다음과 같이 보입니다:
{
"model": "gpt-5.4-mini",
"messages": [
...
응답은 답변을 choices 배열 안에 감쌉니다. 이 배열이 존재하는 이유는 n 파라미터를 통해 하나 이상의 변형을 요청할 수 있기 때문이며, 따라서 단일 답변이라도 인덱스 0으로 반환됩니다:
{
"id": "chatcmpl-123",
"object": "chat.completion",
...
한 가지 주의해야 할 점이 있습니다. Chat Completions (채팅 완성)는 상태를 유지하지 않는 (stateless) 방식이므로, 서버가 대화 내용을 기억하지 못합니다. 매 턴마다 이전 교환 과정에서 발생한 도구 출력 (tool outputs)을 포함하여 전체 메시지 기록을 다시 전송해야 합니다. 단순한 챗봇에게는 괜찮지만, 도구를 호출하는 다단계 에이전트 (multi-step agent)의 경우에는 금방 다루기 힘들어집니다.
Chat Completions가 표준이 된 이유
OpenAI는 2023년에 이 형식을 출시했고, 이는 효과적이었으며, 시기적으로 앞서 있었습니다. 그 타이밍은 그 어떤 기술적 장점보다 중요했습니다.
messages와 choices 구조를 기반으로 충분한 튜토리얼과 프로덕션 앱이 작성되고 나자, 이 형식은 실질적인 의미에서 더 이상 OpenAI의 소유가 아니게 되었습니다. 다른 모델 제공업체들도 호환성을 추가하여 개발자들이 코드를 다시 작성하지 않고도 모델을 교체할 수 있도록 했습니다. vLLM 및 Ollama와 같은 로컬 추론 엔진 (local inference engines)도 마찬가지였습니다. 하나의 인터페이스 뒤에 있는 수많은 모델에 대한 액세스를 정규화하는 것이 주 업무인 OpenRouter와 같은 게이트웨이도 마찬가지였습니다. 그 시점에서 이 형식을 지원하는 것은 OpenAI에 베푸는 호의가 아니었습니다. 개발자들이 자신의 것을 채택하기를 원하는 사람이라면 누구나 갖춰야 할 기본 조건 (table stakes)이었습니다.
이는 전형적인 네트워크 효과 (network effect)입니다. 이 형식이 승리한 이유는 어디에나 있었기 때문이며, 어디에나 남아있는 이유는 승리했기 때문입니다. Simon Willison은 2025년에 이미 명백한 위험을 지적했습니다: 산업 전체가 한 회사의 독점적인 API를 복제하여 구축하고 있었으며, 그 회사는 원할 때 언제든 이를 변경할 수 있다는 점입니다.
Responses API가 다른 점
그래서 OpenAI는 이를 변경했습니다. 어느 정도 말이죠.
Responses API는 챗봇보다는 에이전트 (agents)를 겨냥하여 재설계되었습니다. 이는 사용자의 고정 지침 (standing instructions)을 실제 입력과 분리하며, 서버에서 대화 상태 (conversation state)를 유지할 수 있어 전체 대화 기록을 다시 보낼 필요가 없게 만듭니다.
요청(request)이 더 가벼워집니다:
{
"model": "gpt-5.5",
"instructions": "당신은 간결한 데이터 분석가입니다.",
...
instructions 필드는 시스템 수준의 가이드라인을 전달합니다. 실제 프롬프트는 input을 통해 일반 문자열(plain string) 또는 메시지 리스트(list of messages) 형태로 전달합니다. 만약 서버가 마지막 대화를 기억하기를 원한다면, store: true를 보내고 다음 호출 시 모든 내용을 다시 재생하는 대신 previous_response_id를 전달하면 됩니다.
응답(response) 부분에서 가장 많은 오정보를 목격하는데, 실제 구조는 다음과 같습니다. 출력(output)은 단일 최상위 문자열이 아닙니다. 그것은 타입이 지정된 output 아이템 배열(array)입니다. 왜냐하면 이제 단일 응답이 하나의 타임라인 상에서 메시지, 도구 호출(tool call), 추론 과정(reasoning trace) 등을 모두 포함할 수 있기 때문입니다:
{
"id": "resp_9z8x7c...",
"object": "response",
...
많은 예제에서 response.output_text를 보게 될 것이며, 이것이 최상위 필드처럼 보일 수 있습니다. 하지만 그렇지 않습니다. 이것은 SDK에서 제공하는 편의용 헬퍼(convenience helper)로, output 배열을 파고들어 텍스트를 대신 추출해 주는 기능입니다. 편리하긴 하지만, 가공되지 않은 원시 JSON(raw JSON)에서는 이를 기대해서는 안 됩니다. 또한 토큰 수(token counts)의 명칭도 변경되었음을 유의하세요. Chat Completions는 prompt_tokens와 completion_tokens를 보고하는 반면, Responses는 input_tokens와 output_tokens를 사용합니다. 사소한 부분이지만 실수하기 쉽습니다.
Responses에 대한 OpenAI의 제안은 구체적입니다. 더 나은 캐시 활용(cache utilization)은 멀티 턴(multi-turn) 워크로드의 비용을 절감합니다. 추론 모델(reasoning models)은 API가 턴 사이에 추론 컨텍스트(reasoning context)를 보존하기 때문에 더 높은 점수를 얻습니다. 웹 검색 및 코드 실행과 같은 내장 도구(built-in tools)는 직접 함수 호출(function-calling) 루프를 구축해야 하는 번거로움을 덜어줍니다. 에이전트(agent) 작업에 있어 이는 실질적인 이점입니다.
상태 유지(stateful) 설계가 중요한 데에는 놓치기 쉬운 더 깊은 이유가 있습니다. 추론 모델(Reasoning models)은 답변을 내놓기 전에 숨겨진 사고의 사슬(chain of thought)을 생성합니다. 상태 비저장(stateless) 설정에서는 클라이언트가 매 턴마다 전체 이력을 다시 보내야 합니다. 이는 어색한 선택을 강요합니다. 추론 과정을 제거하여 모델의 사고 흐름을 잃어버리거나, 아니면 암호화된 블록 형태로 이를 계속 주고받아야 합니다. 서버에서 상태를 유지하면 이 두 가지 문제를 모두 피할 수 있습니다. OpenAI는 한 턴에서 다음 턴으로 넘어갈 때 자체 백엔드에 추론 흔적(reasoning trace)을 보관하므로, 모델이 어떻게 그 결론에 도달했는지 노출하지 않으면서도 예리함을 유지할 수 있습니다. 이것이 바로 이러한 추진력 뒤에 숨겨진 실질적인 이유 중 하나입니다.
Open Responses가 질문을 바꿉니다
이 글을 쓸 가치가 있게 만든 부분은 바로 여기입니다. 2026년 1월, OpenAI와 파트너 그룹은 Responses API를 기반으로 구축된 오픈 소스 사양인 Open Responses를 발표했습니다.
출시 파트너들을 보면 알 수 있습니다. Hugging Face, Vercel, OpenRouter, LM Studio, Ollama, 그리고 vLLM이 모두 참여했습니다. 이들은 모두 자신들만의 방식으로 Chat Completions를 복제했던 바로 그 도구들입니다. 이번에는 독점적인 형식을 역공학(reverse-engineering)하여 형식이 바뀌지 않기를 기도하는 대신, 공식 수락 테스트(acceptance tests)와 공유 스키마(schema)를 갖춘 문서화된 사양을 작성하는 데 힘을 보탰습니다. vLLM 팀은 그렇게 말했습니다: 이전에는 제공업체의 동작을 추측해야 했지만, 실제 사양은 그러한 상황을 끝내줄 것이라고 말입니다.
핵심 아이디어는 요청과 출력을 설명하는 단 하나의 스키마를 정의한 뒤, 최소한의 변환만으로 OpenAI, 로컬 모델 또는 다른 제공업체에서 실행하는 것입니다. 주목할 점은 출시 라인업에 자체 형식을 유지하고 있는 Anthropic과 Google DeepMind가 빠져 있었다는 것입니다. 그럼에도 불구하고, 이 사양은 어댑터 레이어(adapter layers)를 통해 도달하고자 하는 목표로 두 기업을 모두 나열하고 있습니다. 즉, 이들을 우회하는 것이 아니라 포괄하는 것이 계획입니다. 이것이 보편적인 평화 협정은 아닐지라도, 업계가 기준점(baseline)에 합의하는 오픈 소스 측의 움직임입니다.
아무도 숨기지 않는 아이러니가 하나 있습니다. 한 회사의 API 위에 "오픈" 표준을 구축한다는 것은 그 회사의 중력에서 벗어나려는 기묘한 방식입니다. 하지만 래퍼(wrapper) 위에 또 다른 래퍼를 작성하는 것에 지친 사람에게는, OpenAI의 재량에 따라 존재하는 사실상의 표준(de facto standard)보다 문서화되고 테스트 가능한 명세(spec)가 훨씬 낫습니다.
알아둘 만한 몇 가지 파라미터 (parameters)
어떤 형식을 부르든, 몇 가지 페이로드(payload) 옵션이 동작을 제어합니다:
temperature는 무작위성(randomness)을 설정합니다. 창의적인 출력을 원한다면 0.9로 높이고, 집중적이고 결정론적(deterministic)에 가까운 답변을 원한다면 0.2로 낮추세요.stream을true로 설정하면 전체 답변을 기다리는 대신 서버 전송 이벤트(Server-Sent Events)를 통해 토큰 단위로 텍스트를 반환합니다. 스트리밍 이벤트는 두 형식 간에 서로 다르며, 이것이 어댑터(adapter)에 명세(spec)가 필요한 또 다른 이유 중 하나입니다.- 구조화된 출력(Structured output)은 각 형식에서 다르게 처리됩니다. Chat Completions는 JSON 스키마(JSON schema)와 함께
response_format을 사용합니다. Responses는text.format을 사용합니다. 둘 다 모델로부터 유효한 JSON을 강제할 수 있습니다. - 길이 제한의 경우, 명칭에 주의하세요. Chat Completions는
max_tokens를 폐지하고max_completion_tokens를 권장합니다. Responses는max_output_tokens를 사용합니다. 의도는 같지만, 어디에 있느냐에 따라 세 가지 서로 다른 이름을 사용합니다.
결론
실질적인 시사점은, 특히 저처럼 셀프 호스팅(self-host)을 하는 경우라면 호환성이 게임의 전부라는 것입니다. Ollama와 vLLM이 OpenAI 형식을 지원하기 때문에, 저는 동일한 코드와 베이스 URL(base URL)의 차이만으로 도구를 로컬 모델과 프런티어 모델(frontier model) 모두에 연결할 수 있습니다. 그러한 이식성(portability)은 실제 돈과 실제 자유의 가치가 있으며, 이것이 바로 Open Responses가 앞으로 보호하고자 하는 핵심입니다.
새로운 프로젝트를 시작한다면 Responses API를 대상으로 구축하고, 가능한 한 Open Responses 명세(spec)에 의존하세요. 기존 앱을 유지 관리하고 있다면, Chat Completions는 사라지지 않을 것입니다. OpenAI는 이를 무기한 지원하기로 약속했으며, 업계의 나머지 분야도 여전히 이를 기반으로 돌아가고 있습니다. 어느 쪽이든, 당신이 실제로 어떤 명세(spec)를 사용하고 있는지 아는 것이 승패의 절반을 결정합니다.
추가 자료: OpenAI의 마이그레이션 가이드, Open Responses 사양, 그리고 표준화 위험에 대한 Simon Willison의 원래 분석을 참고하세요._
사진 제공: Zdeněk Macháček / Unsplash
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기