OpenAI·Gemini API 키 안전하게 관리하고 토큰 비용 계산하는 법 (Python 완벽 가이드)

OpenAI와 Gemini API 키를 발급받았는데, 소스 코드에 그냥 붙여넣으면 안 된다는 건 알겠는데 그럼 어떻게 해야 할까요? 잘못 관리된 API 키 하나가 GitHub에 올라가는 순간 수백만 원짜리 청구서가 날아올 수 있습니다. 이 글에서는 Python 프로젝트에서 API 키를 안전하게 저장·로드하는 방법부터, 토큰 비용을 미리 계산해서 예상치 못한 요금 폭탄을 막는 실전 기법까지 단계별로 알려드립니다.

목차

🔑 API 키를 하드코딩하면 왜 위험한가

API 키는 비밀번호와 같습니다. 소스 코드 안에 직접 쓰면 아래와 같은 경로로 유출됩니다.

  • GitHub 공개 저장소 업로드: 실수로 public repo에 push하면 수 분 내에 봇이 스캔해 키를 탈취합니다.
  • 코드 공유·스크린샷: 질문 게시판에 코드를 붙여넣을 때 키가 함께 노출됩니다.
  • 로그 파일 유출: 에러 로그에 환경 변수 전체가 찍히는 경우도 있습니다.

OpenAI와 Google 모두 유출된 키를 감지하면 즉시 비활성화하지만, 그 전에 이미 수천 달러의 요청이 발생할 수 있습니다. 예방이 유일한 답입니다.

graph TD A["소스 코드에 API 키 하드코딩"] --> B{"GitHub Push?"} B -->|"Public Repo"| C["🤖 봇이 수 분 내 스캔"] B -->|"Private Repo"| D["팀원 실수로 공개 전환 위험"] C --> E["키 탈취 → 무단 API 호출"] D --> E E --> F["💸 수백만 원 청구서"] G["✅ 올바른 방법"] --> H[".env 파일에 저장"] H --> I[".gitignore에 등록"] I --> J["python-dotenv로 로드"] J --> K["os.getenv()로 사용"]

📦 .env 파일과 python-dotenv로 키 관리하기

가장 널리 쓰이는 방법은 .env 파일에 키를 저장하고, python-dotenv 라이브러리로 불러오는 것입니다. 프로젝트 루트에 .env 파일을 만들고 절대 Git에 올리지 않는 것이 핵심입니다.

라이브러리 설치

# 터미널에서 실행
pip install python-dotenv openai google-generativeai tiktoken

.env 파일 작성

프로젝트 루트 폴더에 .env 파일을 생성합니다. 파일명 앞에 점(.)이 붙는 것에 주의하세요.

# .env 파일 (절대 GitHub에 올리지 마세요!)
OPENAI_API_KEY=sk-proj-여기에_실제_키를_붙여넣으세요
GEMINI_API_KEY=AIza여기에_실제_키를_붙여넣으세요

📸 스크린샷 지시: VS Code에서 프로젝트 루트에 .env 파일이 생성된 파일 탐색기 화면. .env 파일이 목록에 보이고, 내용에 API 키 변수가 작성된 상태.

파일명: 20250101_1.png

Python에서 .env 불러오기

🔽 [Click to expand] dotenv 로드 기본 코드
import os
from dotenv import load_dotenv

# 프로젝트 루트의 .env 파일을 자동으로 찾아 환경 변수로 로드
load_dotenv()

# 환경 변수에서 키 읽기
openai_api_key = os.getenv("OPENAI_API_KEY")
gemini_api_key = os.getenv("GEMINI_API_KEY")

# 키가 제대로 로드됐는지 확인 (실제 키 값은 출력하지 않음)
if not openai_api_key:
    raise ValueError("OPENAI_API_KEY가 설정되지 않았습니다. .env 파일을 확인하세요.")
if not gemini_api_key:
    raise ValueError("GEMINI_API_KEY가 설정되지 않았습니다. .env 파일을 확인하세요.")

print("✅ API 키 로드 완료")
print(f"OpenAI 키 앞 8자리: {openai_api_key[:8]}...")
print(f"Gemini 키 앞 8자리: {gemini_api_key[:8]}...")

load_dotenv().env 파일의 내용을 읽어 os.environ에 등록합니다. 이후 os.getenv()로 어디서든 꺼내 쓸 수 있습니다. 키 값을 직접 출력하는 대신 앞 몇 글자만 확인하는 습관을 들이세요.

🖥️ 환경 변수(OS Environment Variable)로 관리하기

.env 파일 방식은 로컬 개발에 적합하고, 서버나 CI/CD 환경에서는 OS 환경 변수를 직접 설정하는 방식이 더 안전합니다.

운영체제별 환경 변수 설정

# macOS / Linux (터미널)
export OPENAI_API_KEY="sk-proj-여기에_실제_키"
export GEMINI_API_KEY="AIza여기에_실제_키"

# 영구 적용: ~/.bashrc 또는 ~/.zshrc 파일 맨 아래에 위 두 줄 추가 후
source ~/.zshrc
# Windows (PowerShell)
$env:OPENAI_API_KEY="sk-proj-여기에_실제_키"
$env:GEMINI_API_KEY="AIza여기에_실제_키"

# 영구 적용 (시스템 환경 변수)
[System.Environment]::SetEnvironmentVariable("OPENAI_API_KEY", "sk-proj-여기에_실제_키", "User")

OS 환경 변수로 설정하면 load_dotenv() 없이도 os.getenv()로 바로 읽을 수 있습니다. GitHub Actions, AWS, GCP 같은 클라우드 환경에서는 각 플랫폼의 Secrets 기능을 사용해 환경 변수를 주입합니다.

🛡️ .gitignore 설정으로 키 유출 원천 차단

.env 파일을 만들었다면 반드시 .gitignore에 등록해야 합니다. Git이 해당 파일을 추적하지 않도록 막는 것입니다.

# .gitignore 파일에 아래 내용 추가
.env
.env.local
.env.*.local
*.pem
*_secret*
secrets/

이미 실수로 .env를 커밋했다면, 단순히 .gitignore에 추가하는 것만으로는 부족합니다. Git 캐시에서도 제거해야 합니다.

# 이미 커밋된 .env를 Git 추적에서 제거 (파일 자체는 삭제되지 않음)
git rm --cached .env
git commit -m "chore: .env를 Git 추적에서 제거"
git push

그리고 즉시 해당 API 키를 폐기하고 새 키를 발급받으세요. 한 번 커밋 히스토리에 올라간 키는 히스토리를 완전히 재작성하지 않는 한 남아있습니다.

⚙️ OpenAI·Gemini 키 로드 및 연결 검증 코드

키를 안전하게 불러왔다면, 실제로 API에 연결되는지 검증하는 코드를 작성해봅니다.

🔽 [Click to expand] OpenAI 연결 검증 코드
import os
from dotenv import load_dotenv
from openai import OpenAI

# .env 파일 로드
load_dotenv()

# OpenAI 클라이언트 초기화 (api_key를 명시적으로 전달)
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def verify_openai_connection():
    """OpenAI API 연결을 검증하는 함수"""
    try:
        # 가장 저렴한 모델로 최소한의 요청을 보내 연결 확인
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": "ping"}],
            max_tokens=5  # 비용 최소화를 위해 응답 토큰 제한
        )
        print("✅ OpenAI 연결 성공")
        print(f"모델: {response.model}")
        print(f"응답: {response.choices[0].message.content}")
        return True
    except Exception as e:
        print(f"❌ OpenAI 연결 실패: {e}")
        return False

verify_openai_connection()
🔽 [Click to expand] Gemini 연결 검증 코드
import os
from dotenv import load_dotenv
import google.generativeai as genai

# .env 파일 로드
load_dotenv()

# Gemini API 키 설정
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))

def verify_gemini_connection():
    """Gemini API 연결을 검증하는 함수"""
    try:
        # Gemini 모델 초기화
        model = genai.GenerativeModel("gemini-1.5-flash")
        
        # 간단한 요청으로 연결 확인
        response = model.generate_content("ping")
        print("✅ Gemini 연결 성공")
        print(f"응답: {response.text}")
        return True
    except Exception as e:
        print(f"❌ Gemini 연결 실패: {e}")
        return False

verify_gemini_connection()

📸 스크린샷 지시: 터미널에서 위 검증 코드를 실행했을 때 "✅ OpenAI 연결 성공"과 "✅ Gemini 연결 성공" 메시지가 출력된 화면.

파일명: 20250101_2.png

💰 토큰 비용 계산: 요금 폭탄 막는 법

토큰(Token)은 AI 모델이 텍스트를 처리하는 단위입니다. 영어는 단어 하나가 대략 1~2토큰, 한국어는 글자 하나가 1~3토큰 정도입니다. API 요금은 입력(Input) 토큰과 출력(Output) 토큰을 합산해 청구됩니다.

graph LR UserText["사용자 입력 텍스트"] --> Tokenizer["토크나이저
(tiktoken)"] Tokenizer --> InputTokens["입력 토큰 수"] InputTokens --> API["OpenAI / Gemini API"] API --> OutputTokens["출력 토큰 수"] InputTokens --> CostCalc["비용 계산"] OutputTokens --> CostCalc CostCalc --> Bill["💰 최종 청구 금액"]

OpenAI 토큰 수 미리 계산하기 (tiktoken)

OpenAI는 tiktoken이라는 공식 라이브러리로 API 호출 전에 토큰 수를 정확히 계산할 수 있습니다.

🔽 [Click to expand] tiktoken으로 토큰 수 및 예상 비용 계산
import tiktoken

def count_tokens_and_cost(text: str, model: str = "gpt-4o-mini") -> dict:
    """
    텍스트의 토큰 수와 예상 비용을 계산하는 함수
    
    Args:
        text: 토큰을 계산할 텍스트
        model: 사용할 OpenAI 모델명
    
    Returns:
        토큰 수와 예상 비용 정보 딕셔너리
    """
    # 모델에 맞는 인코더 로드
    try:
        encoding = tiktoken.encoding_for_model(model)
    except KeyError:
        # 모델을 찾지 못하면 기본 인코더 사용
        encoding = tiktoken.get_encoding("cl100k_base")
    
    # 토큰 수 계산
    tokens = encoding.encode(text)
    token_count = len(tokens)
    
    # 2025년 기준 gpt-4o-mini 가격 (달러/1M 토큰)
    # 최신 가격은 platform.openai.com/docs/pricing 에서 확인하세요
    pricing = {
        "gpt-4o-mini": {"input": 0.15, "output": 0.60},   # $0.15 / 1M input tokens
        "gpt-4o":      {"input": 2.50, "output": 10.00},  # $2.50 / 1M input tokens
        "gpt-3.5-turbo": {"input": 0.50, "output": 1.50}, # $0.50 / 1M input tokens
    }
    
    # 해당 모델의 가격 정보 가져오기
    model_price = pricing.get(model, {"input": 0, "output": 0})
    
    # 입력 토큰 비용 계산 (달러)
    input_cost_usd = (token_count / 1_000_000) * model_price["input"]
    
    # 원화 환산 (환율은 변동되므로 참고용)
    usd_to_krw = 1380
    input_cost_krw = input_cost_usd * usd_to_krw
    
    return {
        "model": model,
        "token_count": token_count,
        "input_cost_usd": round(input_cost_usd, 6),
        "input_cost_krw": round(input_cost_krw, 4),
    }

# 사용 예시
sample_text = """
안녕하세요! 오늘 날씨가 정말 좋네요.
Python으로 API를 호출하는 방법을 배우고 있습니다.
OpenAI와 Gemini API를 함께 사용하면 정말 강력한 애플리케이션을 만들 수 있어요.
"""

result = count_tokens_and_cost(sample_text, model="gpt-4o-mini")
print(f"모델: {result['model']}")
print(f"토큰 수: {result['token_count']:,}개")
print(f"예상 입력 비용: ${result['input_cost_usd']} (약 {result['input_cost_krw']}원)")

API 응답에서 실제 사용 토큰 확인하기

API 호출 후 응답 객체에는 실제로 사용된 토큰 정보가 포함됩니다. 이를 누적해서 모니터링하면 비용을 정확히 추적할 수 있습니다.

🔽 [Click to expand] 실제 사용 토큰 추적 코드
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# 세션 누적 비용 추적
total_input_tokens = 0
total_output_tokens = 0

def chat_with_cost_tracking(user_message: str, model: str = "gpt-4o-mini") -> str:
    """
    비용을 추적하면서 채팅하는 함수
    
    Args:
        user_message: 사용자 메시지
        model: 사용할 모델
    
    Returns:
        AI 응답 텍스트
    """
    global total_input_tokens, total_output_tokens
    
    # 2025년 기준 가격 (달러/1M 토큰)
    pricing = {
        "gpt-4o-mini": {"input": 0.15, "output": 0.60},
        "gpt-4o":      {"input": 2.50, "output": 10.00},
    }
    
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": user_message}]
    )
    
    # 응답 객체에서 실제 사용 토큰 추출
    usage = response.usage
    input_tokens = usage.prompt_tokens
    output_tokens = usage.completion_tokens
    
    # 누적 토큰 업데이트
    total_input_tokens += input_tokens
    total_output_tokens += output_tokens
    
    # 이번 요청 비용 계산
    model_price = pricing.get(model, {"input": 0, "output": 0})
    this_cost = (
        (input_tokens / 1_000_000) * model_price["input"] +
        (output_tokens / 1_000_000) * model_price["output"]
    )
    
    # 누적 총 비용 계산
    total_cost = (
        (total_input_tokens / 1_000_000) * model_price["input"] +
        (total_output_tokens / 1_000_000) * model_price["output"]
    )
    
    print(f"[이번 요청] 입력: {input_tokens}토큰, 출력: {output_tokens}토큰, 비용: ${this_cost:.6f}")
    print(f"[누적 합계] 입력: {total_input_tokens}토큰, 출력: {total_output_tokens}토큰, 총 비용: ${total_cost:.6f}")
    
    return response.choices[0].message.content

# 사용 예시
reply = chat_with_cost_tracking("Python에서 리스트와 튜플의 차이를 간단히 설명해줘")
print(f"\n응답: {reply}")

Gemini 토큰 수 확인하기

import os
from dotenv import load_dotenv
import google.generativeai as genai

load_dotenv()
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))

model = genai.GenerativeModel("gemini-1.5-flash")

# API 호출 전 토큰 수 미리 계산
sample_prompt = "Python의 장점을 세 가지 설명해줘"
token_count = model.count_tokens(sample_prompt)
print(f"예상 입력 토큰 수: {token_count.total_tokens}")

# 실제 호출 후 사용 토큰 확인
response = model.generate_content(sample_prompt)
print(f"실제 사용 토큰: {response.usage_metadata}")

🔒 실무에서 쓰는 고급 보안 패턴

키 유효성 검사 및 설정 클래스 패턴

프로젝트 규모가 커지면 API 키 관련 설정을 한 곳에서 관리하는 것이 좋습니다. 아래는 pydantic을 활용한 설정 관리 패턴입니다. 이 방식은 키가 없거나 형식이 잘못됐을 때 앱 시작 시점에 즉시 오류를 발생시켜 런타임 중 예상치 못한 실패를 방지합니다.

🔽 [Click to expand] Pydantic 기반 설정 관리 클래스
import os
from dotenv import load_dotenv
from pydantic import BaseModel, field_validator

load_dotenv()

class APISettings(BaseModel):
    """API 키 설정을 검증하고 관리하는 클래스"""
    openai_api_key: str
    gemini_api_key: str
    
    @field_validator("openai_api_key")
    @classmethod
    def validate_openai_key(cls, v: str) -> str:
        """OpenAI 키 형식 검증 (sk- 또는 sk-proj- 로 시작해야 함)"""
        if not v.startswith(("sk-", "sk-proj-")):
            raise ValueError("유효하지 않은 OpenAI API 키 형식입니다.")
        if len(v) < 20:
            raise ValueError("OpenAI API 키가 너무 짧습니다.")
        return v
    
    @field_validator("gemini_api_key")
    @classmethod
    def validate_gemini_key(cls, v: str) -> str:
        """Gemini 키 형식 검증 (AIza로 시작해야 함)"""
        if not v.startswith("AIza"):
            raise ValueError("유효하지 않은 Gemini API 키 형식입니다.")
        return v
    
    @property
    def masked_openai_key(self) -> str:
        """로그 출력용 마스킹된 OpenAI 키"""
        return f"{self.openai_api_key[:8]}...{self.openai_api_key[-4:]}"
    
    @property
    def masked_gemini_key(self) -> str:
        """로그 출력용 마스킹된 Gemini 키"""
        return f"{self.gemini_api_key[:8]}...{self.gemini_api_key[-4:]}"

# 앱 시작 시 한 번만 초기화 (키 오류 시 즉시 예외 발생)
try:
    settings = APISettings(
        openai_api_key=os.getenv("OPENAI_API_KEY", ""),
        gemini_api_key=os.getenv("GEMINI_API_KEY", ""),
    )
    print(f"✅ 설정 로드 완료")
    print(f"OpenAI 키: {settings.masked_openai_key}")
    print(f"Gemini 키: {settings.masked_gemini_key}")
except Exception as e:
    print(f"❌ 설정 오류: {e}")
    raise SystemExit(1)  # 키 오류 시 앱 종료

API 사용량 한도 설정 (필수!)

코드 레벨의 보안과 함께, 플랫폼 대시보드에서 월별 지출 한도를 설정하는 것이 가장 확실한 요금 폭탄 방지책입니다.

  • OpenAI: platform.openai.com → Settings → Limits → Set a monthly budget
  • Google AI Studio: aistudio.google.com → API 키 관리 → 할당량 설정. 또는 Google Cloud Console에서 API 할당량 제한 설정

📸 스크린샷 지시: OpenAI 플랫폼 대시보드의 Settings > Limits 화면에서 Monthly budget 설정 필드가 보이는 화면.

파일명: 20250101_3.png

✅ 마무리 요약

API 키는 절대 소스 코드에 직접 쓰지 말고, .env 파일 + python-dotenv 조합으로 관리하며 .gitignore에 반드시 등록하세요. tiktoken으로 호출 전 토큰 수를 미리 계산하고, 응답 객체의 usage 필드로 실제 비용을 추적하면 예상치 못한 청구를 막을 수 있습니다. 코드 보안과 함께 플랫폼 대시보드의 월별 지출 한도 설정을 병행하는 것이 가장 안전한 이중 방어선입니다.

Comments

Popular posts from this blog

System, User, and Assistant Roles in the OpenAI Chat API Explained

How to Host Your First Website on a Raspberry Pi (Beginner's Guide)

Podman Rootless Containers: Understanding the Copy Fail Exploit and How to Defend Against It