← 출시 가이드
OpenAI API · 비밀키 · Vite · Next.js

OpenAI API 키 노출 방지 2026: Vite·Next.js 서버 프록시 8단계

작성·검토: LaunchGuard 편집팀 · 최초 발행 2026-07-26 · 최종 수정 2026-07-26 · 14분 읽기

브라우저에서 OpenAI API를 직접 호출하려고 VITE_OPENAI_API_KEYNEXT_PUBLIC_OPENAI_API_KEY를 만들면 키는 사용자의 개발자 도구와 빌드된 자바스크립트에서 확인될 수 있습니다. 화면에 값을 출력하지 않았더라도 브라우저가 요청의 Authorization 헤더를 만들려면 키를 알고 있어야 하므로 숨길 수 없습니다.

먼저 답하면 — OpenAI API 키는 프론트엔드에 두지 않습니다. 브라우저는 내 서버 엔드포인트를 호출하고, 서버가 인증·사용량을 확인한 뒤 비밀 환경변수의 키로 OpenAI API를 호출해야 합니다. 이미 노출됐다면 코드 삭제보다 기존 키 폐기가 먼저입니다.

안전한 요청 구조를 먼저 구분하세요

구조키가 있는 위치판정
React·Vite → OpenAI API 직접 호출브라우저 번들·Network 요청금지
Client Component → OpenAI SDK브라우저가 키를 알아야 함금지
브라우저 → 인증된 서버 함수 → OpenAI API서버 비밀 환경변수권장 경계
브라우저 → 인증 없는 공개 서버 함수 → OpenAI API키는 서버지만 비용 통제 없음추가 보호 필요

1. 노출된 키는 즉시 폐기하고 사용 흔적을 확인했는가

키가 공개 저장소, 브라우저 번들, 소스맵, 스크린샷, 오류 응답이나 로그에 들어갔다면 유출로 간주하세요. OpenAI 대시보드에서 해당 키를 폐기하고 새 키를 발급한 뒤 최근 사용량과 예상하지 못한 호출이 있는지 확인합니다. Git 커밋을 지우거나 배포 파일을 교체해도 누군가 이미 복사한 키는 계속 사용할 수 있으므로 키 폐기가 우선입니다.

새 키를 만들기 전에 어떤 환경과 서비스가 기존 키를 쓰는지 목록을 만드세요. 운영·Preview·로컬이 하나의 키를 공유했다면 환경별로 분리해야 사고 범위와 교체 영향을 줄일 수 있습니다.

2. VITE_·NEXT_PUBLIC_와 클라이언트 코드에서 제거했는가

Vite 공식 문서는 VITE_ 변수가 빌드 시 클라이언트 소스에 포함되므로 API 키 같은 민감정보를 넣지 말라고 안내합니다. Next.js도 NEXT_PUBLIC_ 변수를 브라우저 번들에 포함합니다. 변수 이름에서 접두사만 제거했더라도 Client Component가 키를 읽거나 OpenAI SDK를 실행하면 안전한 구조가 아닙니다.

검색할 흔적VITE_OPENAI, NEXT_PUBLIC_OPENAI, Authorization: Bearer, api.openai.com, 실제 키의 앞부분을 소스·빌드 산출물·소스맵에서 확인하세요. 전체 키를 외부 검색 서비스에 붙여넣지는 마세요.

3. OpenAI 호출을 서버 전용 엔드포인트로 옮겼는가

Next.js App Router라면 Route Handler, Vercel 정적 프로젝트라면 Function, 다른 프레임워크라면 서버리스 함수나 별도 백엔드에서 OpenAI SDK를 실행합니다. OpenAI 공식 API 문서는 키를 서버 환경변수나 키 관리 서비스에서 읽고 브라우저·앱 클라이언트에 노출하지 말라고 명시합니다.

// app/api/ai/route.js — 서버에서만 실행
import OpenAI from 'openai';
import 'server-only';

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

export async function POST(request) {
  // 1) 세션 확인  2) 입력 제한  3) 사용자별 사용량 확인
  const { prompt } = await request.json();
  if (typeof prompt !== 'string' || prompt.length > 4000) {
    return Response.json({ error: 'invalid_input' }, { status: 400 });
  }
  const response = await client.responses.create({
    model: process.env.OPENAI_MODEL,
    input: prompt
  });
  return Response.json({ text: response.output_text });
}

예시는 실행 경계를 설명하는 최소 골격입니다. 실제 서비스에서는 로그인 세션, 사용자 권한, CSRF·Origin 정책, 입력 스키마, 오류 처리와 사용량 저장이 추가돼야 합니다. 모델 이름도 클라이언트 요청을 그대로 받기보다 서버의 허용 목록에서 선택하세요.

4. 서버 엔드포인트에 인증·권한·요청 제한이 있는가

키를 서버로 옮겨도 누구나 내 서버 함수를 호출할 수 있다면 비용과 악용 위험은 남습니다. 로그인 사용자만 호출하도록 하고, 사용자·조직·IP별 요청 빈도, 입력 길이, 동시 실행과 일일 사용량을 제한하세요. 무료 체험이라면 서버가 남은 횟수를 계산해야 하며 브라우저가 보내는 isPaidcredits 값을 신뢰하면 안 됩니다.

응답에는 필요한 결과만 돌려주고 OpenAI 원본 오류 객체, 요청 헤더, 내부 모델 설정과 키를 그대로 포함하지 않습니다. 스트리밍도 연결 시작 전에 인증과 한도를 먼저 소비하거나 예약해 여러 탭의 동시 우회를 막아야 합니다.

5. 운영·Preview·개발 키와 권한 범위를 분리했는가

Vercel은 Production, Preview, Development별 환경변수를 나눌 수 있습니다. Preview 링크가 운영 키와 같은 프로젝트를 사용하면 테스트 브랜치의 취약점이 운영 비용으로 이어질 수 있습니다. 환경별 키를 분리하고 필요하지 않은 팀원·자동화의 키 접근을 제거하세요.

키 이름도 `OPENAI_API_KEY`처럼 서버 전용으로 두고 공개 접두사를 붙이지 않습니다. 환경변수 변경은 이전 배포에 소급되지 않으므로 새 배포를 만든 뒤 운영 엔드포인트가 새 키를 쓰는지 확인합니다.

6. 로그·분석·오류 수집에서 비밀과 사용자 입력을 걸러냈는가

console.log(process.env), 요청 헤더 전체, OpenAI SDK 오류 객체 전체를 기록하지 마세요. 키뿐 아니라 사용자의 프롬프트에 개인정보·계약 정보·소스 코드가 포함될 수 있습니다. 운영 로그에는 내부 요청 ID, 상태 코드, 지연 시간, 토큰 사용량처럼 문제 해결에 필요한 최소 메타데이터만 남기고 입력·출력 저장 여부와 보존 기간을 정합니다.

OpenAI API는 문제 해결을 위해 x-request-id를 제공하고 자체 X-Client-Request-Id도 보낼 수 있습니다. 키나 프롬프트 전체 대신 이런 추적 ID를 저장하면 지원 요청과 장애 분석에 활용할 수 있습니다.

7. 배포 번들·Network·이전 배포에서 실제 노출을 검사했는가

코드 리뷰만으로 끝내지 말고 Production URL에서 자바스크립트 파일과 소스맵을 내려받아 키 패턴을 검색합니다. 개발자 도구 Network에서 브라우저가 api.openai.com으로 직접 요청하거나 Authorization 헤더를 만드는지도 확인하세요. 정상 구조에서는 브라우저가 내 도메인의 API만 호출하고 OpenAI 키를 받지 않습니다.

Vercel의 과거 Preview와 Production 배포는 새 배포 뒤에도 고유 URL로 남을 수 있습니다. 노출된 빌드를 보호·삭제할 수 있는지 확인하되, 과거 배포 정리와 관계없이 기존 키는 반드시 폐기합니다.

8. 새 키·서버 경계·사용량 차단을 세 가지 실패 테스트로 증명했는가

  1. 이전 키 실패: 폐기한 키로 직접 요청했을 때 인증 오류가 나는지 확인합니다.
  2. 비로그인 실패: 쿠키·토큰 없이 내 AI 엔드포인트를 호출했을 때 401 또는 403인지 확인합니다.
  3. 한도 초과 실패: 사용자 한도를 넘기거나 지나치게 큰 입력을 보냈을 때 OpenAI 호출 전에 거부되는지 확인합니다.

정상 요청 성공만으로 보안을 증명할 수 없습니다. 실패 요청이 비용을 발생시키지 않고, 내부 오류나 비밀 값을 반환하지 않으며, 사용자에게 재시도 기준을 알려주는지 함께 기록하세요.

노출 발견 후 20분 복구 순서

  1. 노출된 키를 OpenAI 대시보드에서 폐기하고 최근 사용량을 확인합니다.
  2. 프론트엔드의 공개 환경변수와 직접 OpenAI 호출 코드를 제거합니다.
  3. 서버 전용 함수에 새 키를 넣고 사용자 인증·입력·사용량 제한을 추가합니다.
  4. Production·Preview·Development 키와 연결 프로젝트를 분리합니다.
  5. 새 배포를 만들고 브라우저 번들·소스맵·Network·오류 응답을 다시 검사합니다.
  6. 이전 키, 비로그인 요청, 한도 초과 요청이 모두 실패하는 증거를 남깁니다.

공식 참고 자료

함께 볼 가이드

자주 묻는 질문

OpenAI API 키를 VITE_OPENAI_API_KEY에 넣어도 되나요?

안 됩니다. Vite의 VITE_ 변수는 빌드된 클라이언트 코드에 포함됩니다. OpenAI API 키는 접두사만 바꾸는 것이 아니라 브라우저 코드에서 완전히 제거하고 서버 함수의 비밀 환경변수에서 읽어야 합니다.

NEXT_PUBLIC_ 접두사를 빼면 OpenAI API 키가 안전해지나요?

접두사를 빼는 것만으로는 충분하지 않습니다. 키를 읽는 모듈과 OpenAI 요청 자체가 Route Handler나 서버 함수처럼 브라우저에 번들되지 않는 경계에서 실행돼야 합니다.

GitHub에서 OpenAI API 키를 지우면 기존 키를 계속 써도 되나요?

아닙니다. 저장소, 배포 번들, 로그나 화면에 한 번이라도 공개됐다면 기존 키를 폐기하고 새 키를 발급해야 합니다. 코드 삭제는 이후 노출을 줄일 뿐 이미 복사된 키를 무효화하지 못합니다.

서버 프록시를 만들면 무제한 요청 문제도 해결되나요?

자동으로 해결되지는 않습니다. 서버 엔드포인트에서 사용자 인증, 권한, 입력 크기, 요청 빈도와 사용량 한도를 별도로 검사해야 다른 사람이 공개 API를 호출해 비용을 발생시키는 위험을 줄일 수 있습니다.

배포된 AI 앱에 키가 남아 있는지 확인하세요

공개 URL의 자바스크립트·민감 파일·보안 헤더를 먼저 확인하고, 서버 엔드포인트는 비로그인·한도 초과 실패 시나리오를 별도로 검증하세요.

무료로 위험 확인