자체 방문 통계는 항상 수집됩니다. 사용자가 공개 페이지를 어떻게 이용하는지 파악하는 데 쓰이는 Google Analytics(로그인한 워크스페이스는 제외)는 동의한 경우에만 사용됩니다. 자세한 내용은 개인정보 처리방침을 참고하세요.

OpenAI SDK로 Gemini 호출하기: 최소 텍스트 요청부터 롤백 가능한 마이그레이션까지

SDK, base URL, 모델의 호환성 경계를 분리해 살펴봅니다. 논스트리밍 텍스트 요청, 오류, 과금 정보를 먼저 검증한 뒤 작업별로 파라미터를 복원하며, 클라이언트 호환성을 서비스 전체의 호환성으로 착각하지 않도록 합니다.

제품 상태 업데이트(2026-10-09 베이징 시간 기준 확인): EveryInfra AI API가 다시 제공됩니다. 충전한 계정은 무료로 호출할 수 있으며 스트리밍(stream: true)도 지원합니다. 이 글은 호출이 과금되고 응답이 논스트리밍만 지원되던 시기에 작성되었으므로, 과금 및 논스트리밍 관련 설명은 더 이상 해당되지 않습니다. SDK와 baseURL 설정은 지금도 유효합니다. 현재 설정 방법은 문서의 AI API 섹션을 참고하십시오.

이미 OpenAI SDK를 사용하는 애플리케이션을 마이그레이션할 때 가장 과소평가하기 쉬운 작업은 주소 변경이 아니라, 어떤 동작이 여전히 유효한지 다시 검증하는 일입니다. 요청이 텍스트를 성공적으로 반환했다는 것은 이 입력이 이 서비스에서 결과를 냈다는 뜻일 뿐입니다. 기존 프롬프트의 성능이 그대로인지, 스트리밍이 작동하는지, 도구 호출이 제대로 동작하는지, 예외 발생 후에도 정산 상태가 일관된지까지 증명하지는 않습니다.

이 문서는 EveryInfra의 Gemini 논스트리밍 텍스트 엔드포인트를 예로 들어, 항목별로 검증할 수 있는 마이그레이션 순서를 제시합니다. 먼저 설정을 고정하고, 다음으로 최소 요청을 검증하고, 응답과 오류를 확인한 뒤, 마지막으로 비즈니스 파라미터를 복원하고 사용 확대 여부를 결정합니다. 모든 OpenAI 기능을 다른 서비스로 옮길 수 있다는 일반적인 호환성 약속은 아닙니다.

호환성 계층 이해하기

먼저 세 가지 대상을 구분해야 합니다. OpenAI SDK는 클라이언트 라이브러리이고, 서비스 주소는 실제로 요청을 받는 시스템을 결정하며, 모델 ID는 해당 서비스가 현재 제공하는 카탈로그에 있어야 합니다. 같은 npm 패키지를 쓴다고 해서 같은 키, 같은 모델 이름 목록, 같은 서비스 약관을 쓰는 것은 아닙니다.

Google의 OpenAI 호환성 계층 문서에는 OpenAI SDK로 Google Gemini 서비스에 접근하는 예제가 있습니다. 이 문서는 EveryInfra의 주소와 자격 증명을 사용합니다. Google 문서에 나오는 모델과 기능이 그대로 EveryInfra에서 사용 가능한 목록이 되지는 않으며, 두 서비스의 인증과 과금은 각각 따로 확인해야 합니다.

OpenAI TypeScript SDK 문서는 Chat Completions 호출 방법을 설명합니다. SDK에는 다른 API도 포함되어 있지만, 클라이언트에 메서드가 있다고 해서 대상 주소가 해당 라우트를 구현한 것은 아닙니다. 특히 responses.create, 파일 업로드, 백그라운드 작업, 세션 관리를 사용하는 레거시 프로젝트에서 baseURL만 바꿨다고 마이그레이션이 끝났다고 가정하지 마십시오.

자격 증명과 주소가 뒤섞이지 않도록 설정부터 고정하기

환경은 최소한 개발, 검수, 운영의 세 그룹으로 나누십시오. 각 그룹마다 서비스 주소, 해당 서비스의 키, 모델, 타임아웃, 허용 기능을 명시적으로 정의합니다. 한곳에서 전역 환경 변수를 바꾸는 바람에, 아직 기존 서비스에 연결하는 다른 코드가 실수로 새 자격 증명을 보내게 해서는 안 됩니다.

이 문서에서는 EveryInfra의 기본 경로를 https://api.everyinfra.com/api/v1로 고정합니다. 이 경로는 콘솔 주소가 아니며, /chat/completions까지 포함한 전체 경로도 아닙니다. 엔드포인트는 SDK가 이어 붙입니다. /v1을 한 번 더 붙이거나 전체 엔드포인트를 기본 경로로 쓰면 요청이 잘못된 위치로 전송될 수 있습니다.

키는 반드시 서버 측 환경에만 두고, 브라우저 번들 변수, 저장소, 스크린샷, 로그에 포함하지 마십시오. 이 문서의 예제를 실행하려고 브라우저에 키를 노출하는 옵션을 켤 필요는 없습니다. 로그에도 명시적인 허용 목록을 적용해, SDK 오류 객체 전체나 요청 헤더를 그대로 출력하지 않도록 하십시오.

로그 설계는 OWASP의 민감 정보 제외 체크리스트와 대조해 볼 수 있습니다. 문제 위치를 파악하는 데 필요한 상태와 관련 식별자는 남기고, 토큰, 세션 값, 불필요한 개인 정보는 제외합니다. 호출하는 쪽마다 민감한 필드를 직접 지우도록 맡기는 것보다 일관성을 유지하기 쉽습니다.

먼저 공개 카탈로그로 모델을 확인합니다. 이 단계에는 비즈니스용 키가 필요하지 않습니다:

Bash · 복사 가능한 예시
set -o pipefail
curl -fsS --max-time 30 'https://api.everyinfra.com/api/v1/models' \
  | jq -e '{
      default_model,
      models: [.data[].id]
    } | if (.models | length) > 0
      and (.default_model as $m | .models | index($m)) != null
    then . else error("invalid model catalog") end'

카탈로그에서 요구 사항에 맞는 ID를 고른 뒤 EVERYINFRA_MODEL_ID를 명시적으로 설정하십시오. 카탈로그 읽기가 실패하면 예전 이름으로 조용히 대체하지 말고 검증을 중단해야 합니다. 기본값 변경도 설정 변경으로 평가해야 하며, 실행할 때마다 모델이 몰래 바뀌어서는 안 됩니다. 카탈로그는 선언 내용만 증명할 뿐, 해당 키에 선택한 모델을 호출할 권한이 있는지는 확인해 주지 않습니다.

최소한의 논스트리밍 텍스트 요청부터 시작하기

다음 코드를 실행하려면 서버 측 Node.js 환경과 프로젝트에서 버전을 고정한 openai 패키지가 필요합니다. 이 코드는 단일 비즈니스 호출 템플릿이며, 이 문서에서는 이를 오프라인으로만 점검했으므로 합성 응답을 온라인 결과라고 부르지 않습니다. 예제를 실행하려고 프로젝트 의존성 전체를 업그레이드하지 말고, 먼저 현재 SDK 버전과 lock 파일을 기록해 두십시오.

TypeScript · 복사 가능한 예제
import OpenAI from "openai";

const apiKey = process.env.EVERYINFRA_API_KEY?.trim();
const model = process.env.EVERYINFRA_MODEL_ID?.trim();
if (!apiKey || !model) throw new Error("Missing EveryInfra configuration");

const client = new OpenAI({
  apiKey,
  baseURL: "https://api.everyinfra.com/api/v1",
  maxRetries: 0,
  timeout: 120_000,
});

const { data, response: http } = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "explain idempotency in one sentence." }],
  stream: false,
}).withResponse();

const choice = data.choices?.[0];
const text = choice?.message?.content;
if (typeof text !== "string" || !text.trim()) {
  throw new Error("No usable text; inspect this attempt before retrying");
}
console.log({
  httpStatus: http.status,
  model: data.model,
  finishReason: choice.finish_reason,
  requestId: http.headers.get("x-request-id"),
  textReceived: true,
});
// hands the text hand it to the business layer; do not write the full generated content to runtime logs by default。

여기서 120초는 시연용 클라이언트 측 대기 시간 예산이며, 서비스 응답 시간 제한이나 성능 약속이 아닙니다. 애플리케이션은 진입 프록시, 작업 실행기, 사용자 인터페이스 전반의 대기 시간도 함께 조율해야 합니다. 클라이언트 타임아웃 후에 서비스 완료 정보를 받았다면 이는 확인이 필요한 미확정 결과이며, 실행되지 않았다고 자동으로 판단해서는 안 됩니다.

초기 검수 단계에서는 자동 재시도를 꺼서 단일 호출을 관찰하십시오. OpenAI SDK에는 일부 연결 오류와 서비스 오류에 대한 재시도 메커니즘이 내장되어 있습니다. 운영 환경에서 재시도를 다시 켜기 전에 대상 서비스의 중복 요청, 과금 차감, 멱등성 규약을 확인하고, SDK 기본값을 무작정 그대로 쓰지 마십시오. SDK 재시도 및 타임아웃 가이드

논스트리밍이 통과했다고 바로 stream: true로 전환하지 마십시오

현재 EveryInfra 구현은 채팅 요청을 논스트리밍으로 처리합니다. 코드에 stream: true를 썼거나 Google 공식 호환성 계층이 스트리밍을 지원한다고 해서, 이 주소가 SDK가 기대하는 이벤트 스트림을 반환한다는 증거가 되지는 않습니다. 이 문서는 stream: false를 명시적으로 유지하며, 검증되지 않은 경로에 스트리밍 UI를 연결하지 않습니다.

비즈니스가 글자 단위 표시에 의존한다면 먼저 충족되지 않은 마이그레이션 조건으로 분류하십시오. 서비스가 이를 명시적으로 지원하게 되면 응답 유형, 이벤트 순서, 종료 표시, 중단 후 복구, 최종 사용량을 따로 확인해야 합니다. 일반 JSON 성공 응답으로 스트리밍 검수를 대신하거나, 프런트엔드에서 텍스트를 잘라 보여 주면서 서버 측 스트림이라고 불러서는 안 됩니다.

도구 호출, 구조화된 출력, 멀티모달 입력, 추론 제어 등 선택 파라미터는 각각 따로 확인해야 합니다. 클라이언트 타입 정의가 어떤 필드를 허용한다는 것은 그 필드를 직렬화할 수 있다는 뜻일 뿐이며, 대상 서비스가 그 필드를 기대대로 이해하고 실행한다는 뜻과는 다릅니다.

응답은 세 가지 질문에 동시에 답해야 합니다

첫째, 비즈니스 요구 사항을 충족하는 콘텐츠를 얻었는지입니다. 비어 있지 않은 텍스트라도 주제에서 벗어나거나, 출력 형식을 어기거나, 길이 제한 때문에 완전히 끝나지 않았을 수 있습니다. finish_reason을 보존하고 작업 자체의 완료 기준을 확인하며, HTTP 200을 사용자 작업의 성공과 동일시하지 마십시오.

둘째, 어떤 데이터를 다운스트림 구성 요소에 안전하게 넘길 수 있는지입니다. choices와 usage를 읽을 때는 누락된 값과 타입 불일치를 처리해야 하며, JSON이 필요한 비즈니스 로직은 구조를 직접 파싱하고 검증해야 합니다. TypeScript 컴파일이 성공했다고 해서 서드파티 런타임 응답이 정적 선언과 일치한다고 믿지 마십시오.

셋째, 이 요청의 비용과 상태가 무엇인지입니다. EveryInfra의 확장 과금 정보는 SDK의 모든 범용 타입에 포함되지 않으므로 서비스 문서와 대조해 따로 확인해야 합니다. 누락된 필드를 0으로 채우거나 토큰 사용량을 곧바로 지갑 차감액으로 취급하지 마십시오. 모델 출력, 요청 식별자, 과금 연결 정보를 따로 저장해 두면 HTTP 요청은 성공했지만 콘텐츠는 실패한 경우 같은 상황을 설명하기 쉽습니다. EveryInfra API 및 과금 문서

공식 SDK 문서에 따르면, 정적 타입에 정의되지 않은 추가 응답 속성도 자동으로 제거되지 않습니다. 이런 필드가 필요하면 as any로 검증을 건너뛰지 말고 런타임 검사를 수행하십시오. .withResponse()로 파싱된 데이터와 HTTP 정보를 함께 얻을 수 있지만, 서비스가 반환하지 않은 요청 ID를 만들어 낼 수는 없습니다. SDK 확장 응답 및 HTTP 정보

확장 필드의 런타임 스키마를 정의할 때는 JSON Schema 객체 가이드를 참고해 필수 필드, 선택 필드, 허용되는 값의 타입을 각각 기술하십시오. 구조 검사를 통과해도 과금의 의미나 모델의 결론이 옳다는 증명은 되지 않으므로, 비즈니스 의미는 따로 검증해야 합니다.

오류 위치부터 파악한 뒤 재시도 여부 결정하기

알 수 없는 모델과 서비스 장애를 같은 처리 분기로 다뤄서는 안 됩니다. 2026년 9월 4일에 배포된 소스 코드 기준으로, 이 예제처럼 메시지가 모두 갖춰진 REST 요청에서는 API 키를 먼저 확인한 다음 모델을 확인합니다. 알 수 없는 모델은 과금이 발생하기 전에 422 unknown_model을 반환합니다. 메시지가 없는 요청은 입력 검사에서 먼저 거부되며, 이 순서를 모든 오류 조합에 일반화할 수는 없습니다. 이는 소스 코드를 확인한 결과이며, 이번 회차에 실제 승인된 요청을 관찰한 것은 아닙니다.

unknown_model을 받으면 먼저 카탈로그를 새로 고치고 설정을 수정하십시오. 같은 오류에 대해 끝없이 재시도하거나 비슷한 후보 모델을 자동으로 받아들여서는 안 됩니다. 모델을 바꾸면 동작이 달라질 수 있기 때문입니다. 401이면 대상 주소와 키를 확인하고, 권한 부족은 권한 담당자에게, 할당량 부족은 계정 소유자에게 넘기십시오. 애플리케이션이 제한을 우회하려고 임의로 다른 키로 전환해서는 안 됩니다.

네트워크 이상은 두 가지로 나뉩니다. 하나는 요청이 완료되지 않은 것이 확실한 실패이고, 다른 하나는 요청은 전송되었지만 최종 결과를 확인할 수 없는 타임아웃이나 연결 끊김입니다. 클라이언트는 일반적으로 예외 클래스만으로 과금 여부를 판단할 수 없습니다. 매 시도마다 비즈니스 작업 번호, 타임스탬프, 실제 사용된 모델, 확보 가능한 요청 ID를 저장하고, 해당 시도의 결과를 확인한 뒤에 새 요청을 보낼지 결정하십시오.

오프라인 검사로 검증할 수 있는 것과 없는 것

오프라인 테스트에서는 SDK의 fetch를 대체해 최종 URL, POST 메서드, 인증 헤더 형태, 요청 본문을 확인한 뒤 합성 응답임을 명시해 반환할 수 있습니다. 실제 프롬프트를 보내거나 계정 할당량을 쓰지 않고도 중복 경로, 파라미터 오타, 파싱 가정, 오류 분기 문제를 찾아내는 데 유용합니다.

기존에 openai 패키지 버전을 7.10.0으로 고정해 진행한 격리 테스트는 텍스트, 사용량, 시뮬레이션한 422를 다뤘습니다. 이번 회차에서는 버전과 테스트 범위를 명시적으로 기록하되, 이를 최신 버전이나 서비스 호환성 인증이라고 부르지 않습니다. 새 최소 템플릿에서는 설정이 누락되면 요청이 발생하지 않는지, 단일 호출이 자동으로 재시도되지 않는지, 빈 콘텐츠가 검토가 필요한 결과로 보존되는지도 확인해야 합니다.

2026년 9월 2일 기록에는 Python SDK 논스트리밍 운영 샘플이 추가로 있습니다. 이 샘플은 당시의 Python 요청을 뒷받침하지만, 현재의 TypeScript 템플릿이 실제 연동을 마쳤다는 증거는 아닙니다. 이번 회차에는 실제 TS 비즈니스 호출을 새로 실행하지 않았으므로 인증, 일상적인 텍스트 품질, 과금 결과는 여전히 승인된 샘플로 검증해야 합니다.

작업 샘플별로 비즈니스 파라미터 복원하기

최소 요청이 통과하면 한 번에 한 가지 기능 유형씩 복원하십시오. 실제 프롬프트와 컨텍스트를 먼저, 출력 제약을 그다음에, 애플리케이션에 꼭 필요하면서 서비스가 지원한다고 선언한 옵션을 마지막에 복원합니다. 레거시 프로젝트의 파라미터를 한꺼번에 가져오지 말고, 문제 해결 시 비교할 수 있도록 최소한으로 재현 가능한 입력을 저장해 두십시오.

작업 샘플에는 일반적인 성공 입력, 빈 입력, 긴 컨텍스트, 모호한 질문, 비즈니스에 필요한 형식이 포함되어야 합니다. 작업 완료 여부, 형식의 파싱 가능 여부, 사실에 근거했는지를 평가하고, 생성형 모델의 유일한 성공 기준으로 단어 단위의 완전 일치를 사용하지 마십시오. 품질 백분율을 제시할 때는 샘플과 사람의 평가 기준을 분명히 밝히고, 가벼운 테스트 결과를 일반적인 개선으로 확대해 해석하지 마십시오.

실제 요청은 성공하는데 비즈니스 출력이 나빠졌다면, 원인이 프롬프트, 모델, 입력 잘림, 파싱 규칙 중 어디에 있는지 분리해 보십시오. 모든 차이를 SDK 탓으로 돌려서는 안 됩니다. 반대로 HTTP 호출이 아예 실패한다면 경로, 인증, 서비스 상태부터 점검하십시오. 프롬프트를 조정해서는 대개 연결 문제가 해결되지 않습니다.

비즈니스 액션을 자동으로 반복하지 않으면서 마이그레이션을 되돌릴 수 있게 하기

비즈니스 호출 계층에서는 서비스 주소, 모델, 버전에 대한 명시적인 설정을 관리하고, 실행마다 어떤 설정 그룹을 사용했는지 기록할 수 있습니다. 전환하기 전에는 이전에 검증된 설정을 저장해 두십시오. 롤백의 목적은 이후 요청에 사용할 통제 가능한 상태를 복원하는 것이며, 결과를 알 수 없는 요청을 다른 서비스로 자동 재전송하는 것이 아닙니다.

정식으로 사용을 확대하기 전에 승인된 실제 텍스트 경로, 비즈니스 샘플 세트, 오류 처리, 과금 연결을 모두 검토할 수 있는지 확인하십시오. 아직 검증 근거가 없다면 스트리밍이나 도구 기능은 미이전 체크리스트에 남겨 두십시오. 그래야 "마이그레이션 완료"가 클라이언트 설정 세 줄을 바꾼 것이 아니라 구체적인 작업과 기능을 가리키게 됩니다.

참고 LF-09