강의용 교안

상용 API 로 시작한다 — 크레딧·키·상한


Article

앞 편에서 내 노트북에 모델을 얹었다. 이번 편은 선택이다 — 그게 너무 느리거나 답이 시원찮을 때 밖에 있는 모델을 빌려 쓰는 길이다.

1. 무엇에 값이 매겨지나 — 토큰

글자 수가 아니라 토큰 수로 센다.

토큰이 뭔가
영어
대략 한 단어에 하나
cosmetic -> 1~2개
한글
대략 한 글자에 하나 남짓
화장품 -> 3개쯤
값
입력과 출력이 따로
출력이 몇 배 비싸다

키 하나로 여러 창구를 쓴다

여기서 한 가지를 짚고 간다. 채팅과 임베딩은 따로 사는 것이 아니다.

키 하나 · 잔액 하나
채팅
말을 만들어 준다
지금 쓰는 것
임베딩
문장을 숫자로 바꿔 준다
앞에서 내 컴퓨터로 하던 그것
잔액
둘이 같은 지갑을 쓴다
따로 결제할 것이 없다

앞에서 임베딩은 내 컴퓨터에 모델을 받아서 했다. 그것도 빌려 쓸 수 있고, 키는 방금 만든 것 그대로다. 창구 주소만 다르다.

같은 키로 부르는 것들
무엇창구값
채팅/v1/chat/completions입력 · 출력 따로
임베딩/v1/embeddings입력만. 훨씬 싸다
이미지 · 음성각자 다른 창구이 시리즈에서는 안 쓴다
모델마다 단가가 다를 뿐 지갑은 하나다. 그래서 잔액이 떨어지면 채팅도 임베딩도 같이 멈춘다.

2. 크레딧을 산다

platform.openai.com 에서 결제 수단을 등록하고 미리 정해진 금액만큼 크레딧을 산다.

얼마나 사면 되나
쓰는 방식대략비고
이것저것 물어보며 공부한다한 달 $5 안쪽생각보다 안 쓴다
채점기를 돌린다 (수백 번 호출)한 번에 $1~2여기서 확 는다
서비스 하나를 끝까지 만든다$10 정도면 넉넉다 만들 때까지
처음 사둘 금액$10모자라면 그때 더 산다
가격은 바뀐다. 결제 화면에서 지금 단가를 한 번 확인한다.

자동 충전을 끈다

결제 화면에서 Use auto-reload 라는 스위치를 찾는다. 기본값이 켜져 있다.

자동 충전 설정 화면. Use auto-reload 스위치가 켜져 있고 임계값 $5 · 복구 금액 $10 이 잡혀 있다. 맨 아래에 저장하는 즉시 결제된다는 안내가 떠 있다.

이 스위치 하나가 상한을 정한다

켜두면

기본값이 이쪽이다

  • 잔액이 줄면 카드에서 자동으로 채운다
  • 반복문을 잘못 돌리면 밤새 빠져나간다
  • 다음 날 아침에 알게 된다

꺼두면

이렇게 쓴다

  • 잔액이 떨어지면 그냥 멈춘다
  • 사고가 나도 사둔 금액이 끝이다
  • 멈추면 그때 원인을 찾으면 된다
배우는 동안에는 「멈추는 것」이 「돈이 나가는 것」보다 훨씬 낫다.

스위치를 끄면 아래 항목이 전부 사라진다. 이 상태로 저장한다.

스위치를 끈 화면. 임계값과 복구 금액 칸이 사라지고 두 줄만 남았다.

끝나면 결제 화면이 이렇게 된다.

결제가 끝난 Billing 화면. 잔액 5달러, Auto-reload is OFF 라고 표시되어 있다.

Auto-reload is OFF 옆의 안내가 우리가 원하던 문장이다 — 「잔액이 $0 이 되면 요청이 멈춥니다」. 켜두면 이 문장이 「멈추지 않습니다」가 된다.

사용량 한도(Usage limits)에서 월 상한도 같이 걸어두면 한 겹 더 안전하다.

3. 키를 받고 — 코드에 적지 않는다

키는 sk- 로 시작하는 긴 글자다. 발급 화면에서 만들면 그때 한 번만 보여준다. 따로 적어둔다.

API keys 화면. 키가 하나도 없어서 Create new secret key 버튼만 놓여 있다.

만들 때 채울 것은 셋이다.

키 발급 창. 이름에 axi-llm 을 적고 Project 는 Default project, Permissions 는 All 을 골랐다.

발급 창에서 고르는 것
칸무엇을 넣나왜
Name알아볼 이름 아무거나나중에 「이게 뭐였지」를 막는다. 안 적어도 된다
ProjectDefault project 를 고른다안 고르면 만들기 버튼이 회색이다. 여기서 제일 많이 막힌다
PermissionsAll채팅과 임베딩을 다 쓴다. 실무에서는 Restricted 로 좁혀 발급한다
Permissions 를 좁히면 키가 새어나갔을 때 피해가 작아진다. 대신 필요한 기능이 막혀 헤매기 쉬우니, 배우는 동안에는 All 로 두고 잔액과 자동 충전으로 상한을 건다.

키는 코드 밖에 둔다.

.env
OPENAI_API_KEY=sk-여기에실제키
.gitignore
.env
두는 곳은 세 가지다
  1. 01

    ① 파일에 적고 읽기 — 지금은 이게 편하다

    .env 에 적고 python-dotenv 로 읽는다. .gitignore 에 .env 를 반드시 넣는다. 이걸 빼먹으면 파일로 뺀 의미가 없다.

  2. 02

    ② 윈도우 시스템 설정에 넣기

    「시스템 환경 변수 편집」에서 OPENAI_API_KEY 를 만든다. 파일에도 안 남아 더 안전하다. 대신 터미널을 껐다 켜야 반영된다.

  3. 03

    ③ 서비스로 배포할 때

    배포하는 곳(Vercel · Render 등)의 환경변수 칸에 넣는다. 뒤에서 서버를 만들 때 이 방법을 쓴다. 코드에는 이름만 남고 값은 안 남는다.

4. 접속 설정을 파일 하나로

앞으로 만들 파일이 스무 개쯤 된다. 접속 설정을 그 스무 개에 흩뿌리면 안 된다.

config.py
"""누구에게 물어볼지를 여기서 한 번만 정한다.

다른 파일은 전부 이렇게 쓴다 ─
    from config import client, MODEL
"""

# os 는 운영체제와 이야기하는 도구다. 여기서는 환경변수를 읽는 데만 쓴다
import os

from openai import OpenAI

# 어디로 보낼지. 회사가 정해둔 주소다
BASE_URL = "https://api.openai.com/v1"

# os.environ 은 컴퓨터에 등록된 환경변수 목록이다. 딕셔너리처럼 대괄호로 꺼낸다.
# 값이 없으면 여기서 바로 에러가 난다 — 몰래 빈 값으로 넘어가는 것보다 낫다.
# 이렇게 하면 코드에 키가 한 글자도 안 남는다
API_KEY = os.environ["OPENAI_API_KEY"]

# 부를 모델 이름
MODEL = "gpt-4o-mini"

# 이 한 줄로 "말을 걸 상대" 가 만들어진다. 다른 파일들은 이걸 가져다 쓴다
client = OpenAI(base_url=BASE_URL, api_key=API_KEY)
세 값이 각각 하는 일
BASE_URL
어디로 보낼지
회사 주소
API_KEY
누가 보냈는지
내 키
MODEL
누가 답할지
gpt-4o-mini

5. 첫 호출

01_hello.py
from config import client, MODEL      # 접속 설정은 저기 한 군데에만 있다

res = client.chat.completions.create(
    model=MODEL,
    messages=[
        {"role": "user", "content": "화장품 추천 서비스를 한 문장으로 소개해줘"},
    ],
)

print(res.choices[0].message.content)

# usage 에는 이번 호출에 쓴 토큰 수가 들어 있다. 이게 곧 청구서다
print("입력", res.usage.prompt_tokens, "출력", res.usage.completion_tokens)
터미널
  • 고객의 피부 고민과 구매 이력을 분석해 맞춤 화장품을 추천해주는 서비스입니다.
  • 입력 22 출력 27

답이 거의 바로 온다. 계산을 남의 컴퓨터가 하기 때문이다. 대신 인터넷이 끊기면 아무것도 안 된다.

6. 사고가 나는 세 가지 모양

실제로 자주 나는 것
무엇어떻게 나나막는 법
반복문 사고range(10000) 을 실수로 돌린다개수를 먼저 찍어보고 돌린다
긴 근거문서 전체를 프롬프트에 붙인다필요한 조각만 넣는다
키 유출깃허브에 그대로 올라간다.env + .gitignore
셋 다 「몰라서」가 아니라 「급해서」 난다. 그래서 자동 충전 끄기가 마지막 안전망이다.
# 반복문을 돌리기 전에 이 세 줄을 습관으로 붙인다.
# input 은 사람이 뭔가 칠 때까지 기다렸다가 친 글자를 돌려준다.
# raise SystemExit 는 프로그램을 그 자리에서 끝낸다
print("이번에 부를 횟수:", len(targets))
if input("계속할까? (y) ") != "y":
    raise SystemExit
02_cost.py
res = client.chat.completions.create(model=MODEL, messages=[{"role": "user", "content": "안녕"}])

# 한 줄에 두 개를 한꺼번에 담을 수 있다. 왼쪽 순서대로 짝지어 들어간다
n_in, n_out = res.usage.prompt_tokens, res.usage.completion_tokens

# 백만 토큰당 달러. 결제 화면에서 오늘 값을 확인해 넣는다
in_price, out_price = 0.15, 0.60

# 1_000_000 은 1000000 과 같다. 밑줄은 자릿수를 읽기 쉬우라고 넣는 것이고
# 파이썬이 무시한다. 백만 토큰당 값이니 백만으로 나눠 곱한다
per_call = n_in / 1_000_000 * in_price + n_out / 1_000_000 * out_price
print(f"한 번에 {per_call:.6f} 달러")
print(f"300번 돌리면 {per_call * 300:.3f} 달러")

7. 여러 명이 나눠 쓸 때 — 창구를 하나 세운다

혼자 쓸 것이면 여기는 건너뛴다. 여럿이 같이 배울 때 이야기다.

사람마다 결제하고 키를 발급받게 하면 가입 부담도 크고 비용도 흩어진다. 그래서 키를 든 서버를 하나 두고 모두 그리로 보낸다.

창구 구조
  1. 각자의 코드주소·번호 두 줄만 바뀐다
  2. 창구진짜 키는 여기에만
  3. 상용 API
코드는 openai 패키지 그대로다. config.py 의 값만 창구 주소로 바꾸면 된다.
config.py — 창구를 쓸 때
BASE_URL = "http://192.168.0.15:8000/v1"   # 안내받은 주소. 끝에 /v1 을 꼭 붙인다
API_KEY = "dcl-01"                          # 내게 배정된 번호. 진짜 키가 아니다
MODEL = "gpt-4o-mini"
창구가 막아주는 것
장치왜 필요한가
출력 길이 상한긴 답을 요구해도 잘라낸다. 출력이 곧 돈이다
분당 호출 제한한 명이 반복문을 돌려도 남의 실습이 안 멈춘다
모델 제한비싼 모델을 실수로 부르는 것을 막는다
번호별 기록누가 얼마나 썼는지 남는다
크레딧 상한만으로는 부족하다. 하루치가 몇 분 만에 날아갈 수 있다.

창구를 쓰면 만나는 번호들

에러 번호별 처방
번호언제 나오나처방
401번호가 틀렸다dcl-1 이 아니라 dcl-01 이다. 두 자리로 적는다
400허용 안 된 모델을 불렀다gpt-4o-mini — 하이픈이 두 개다
4291분에 정해진 횟수를 넘었다1분 기다린다. 반복문을 돌렸다면 왜 돌렸는지 본다
404주소 끝에 /v1 이 빠졌다제일 많이 나는 실수다

창구를 직접 띄우려면

창구 폴더
  • proxy
    • main.py서버 본체
    • requirements.txt필요한 도구
    • .env.example설정 견본
python -m uvicorn main:app --host 0.0.0.0 --port 8000

--host 0.0.0.0 이 중요하다. 이게 없으면 그 컴퓨터 안에서만 접속된다. 남이 붙으려면 열어줘야 한다.

ipconfig 로 IPv4 주소 를 찾는다. 192.168. 이나 10. 으로 시작하는 값이고, 나눠줄 주소는 거기에 포트와 /v1 을 붙인 것이다.

브라우저에서 주소를 열면
  • {
  • "ok": true,
  • "모델": ["gpt-4o-mini"],
  • "출력상한": 800,
  • "분당제한": 40
  • }

이번 편에 나온 것

정리
한 것 · 안 것내용
토큰글자가 아니라 조각 수. 출력이 더 비싸다
제일 큰 비용긴 답이 아니라 긴 근거
자동 충전반드시 끈다. 사둔 금액이 상한이 된다
처음 살 금액$10 정도
키코드에 안 적는다. .env + .gitignore
키가 샜다면발급 화면에서 지운다. 그 순간부터 못 쓴다
config.py접속 설정을 한 군데로. 바꿀 곳이 한 파일이 된다
res.usage이번 호출에 쓴 토큰. 이게 청구서다
사고 셋반복문 · 긴 근거 · 키 유출
여럿이 쓸 때창구를 세운다. 401 번호 · 400 모델 · 429 분당 · 404 슬래시 v1
얻은 것빠르고 품질이 좋다
잃은 것부를 때마다 돈. 인터넷이 끊기면 멈춘다

미션

미션
  1. 01

    [필수] 한 번 부르고 토큰을 적는다

    mission_api_1.py. 01_hello.py 를 돌려 입력·출력 토큰을 확인한다. .gitignore 에 .env 가 들었는지 먼저 확인한다.

  2. 02

    [응용] 얼마 쓸지 미리 계산한다

    mission_api_2.py. 추천 한 건에 입력 400 · 출력 200 토큰이라 치고, 고객 30명을 하루 다섯 번 채점하면 나흘에 얼마인가.

  3. 03

    [도전] 상한을 코드로 건다

    mission_api_3.py. 호출할 때마다 토큰을 파일에 쌓고 누적이 정한 값을 넘으면 멈추는 함수를 만든다. 창구가 하는 일을 작게 흉내내보는 것이다.

[도전] 스스로 멈추는 장치충분히 고민해본 뒤 꼭 필요한 경우에만 열어보세요

한 번 부를 때마다 기록하고, 부르기 전에 누적을 본다. 이게 창구가 하는 일의 뼈대다.

파일에 쌓는 이유는 프로그램을 껐다 켜도 남아야 하기 때문이다. 변수에만 담으면 다시 실행하는 순간 0이 된다 — 그리고 배우는 동안에는 다시 실행하는 일이 하루에 수십 번이다.

실무에서는 더 정교하게 한다. 사용자별로 나누고, 하루 단위로 초기화하고, 넘기 전에 경고를 보낸다. 그런데 뼈대는 지금 만든 것과 같다.

import json
import os

LOG_FILE = "usage.json"
LIMIT = 1.0                      # 달러


def spent():
    if not os.path.exists(LOG_FILE):
        return 0.0
    return json.load(open(LOG_FILE))["달러"]


def add(n_in, n_out):
    value = spent() + n_in / 1_000_000 * 0.15 + n_out / 1_000_000 * 0.60
    json.dump({"달러": value}, open(LOG_FILE, "w"))
    return value


def safe_call(messages):
    if spent() >= LIMIT:
        raise SystemExit(f"한도 {LIMIT} 달러를 넘었다. 지금까지 {spent():.4f}")

    res = client.chat.completions.create(model=MODEL, messages=messages)
    total = add(res.usage.prompt_tokens, res.usage.completion_tokens)
    print(f"누적 {total:.4f} 달러")
    return res

부르기 전에 확인하는 것이 핵심이다. 부른 뒤에 세면 이미 나간 돈이다.

그런데 이 장치에는 구멍이 있다 — 한 번 호출이 한도를 훌쩍 넘길 수도 있다. 긴 근거를 넣으면 그렇게 된다. 그래서 진짜 서비스는 여기에 출력 상한을 같이 건다.

뒤에서 「계산한 값과 실제로 나간 값이 왜 다른가」를 본다. 이 파일이 그때 재료가 된다.

정리하면

결제하고, 키를 받고, 첫 호출까지 왔다. 여기까지 왔으면 이후 편들은 그대로 따라온다.

제일 중요한 것은 자동 충전을 껐다는 것이다. 사둔 금액이 그대로 상한이면 사고가 나도 잃을 것이 정해져 있다. 그리고 키는 코드에 안 적었다. 공개 저장소에 올라간 키는 사람이 아니라 프로그램이 찾아간다 — .env 와 .gitignore 두 줄이면 막힌다.

config.py 를 따로 만든 것도 오늘의 소득이다. 주소·키·모델 세 값이 한 파일에만 있으니, 나중에 상대를 바꿔도 고칠 데가 한 군데다.

마지막으로 비용을 아끼는 순서를 봤다. 답을 짧게 시키는 것보다 안 보내도 되는 것을 빼는 쪽이 훨씬 크게 듣는다. 이건 8·9편의 개인정보 이야기와 정확히 같은 결론이 된다.

다음 편에서 개인정보를 다룬다. 추천을 만들려면 후기를 모델에게 줘야 하는데, 1,500건 중 152건에 전화번호가 들어 있다.

이 편으로 갈아탄 사람에게는 그게 남 이야기가 아니다. 앞 편의 내 노트북 방식은 글자가 컴퓨터 밖으로 안 나가지만, 여기서는 방금 그 글자들이 바깥 회사 서버까지 간다. 무엇을 빼고 보내야 하는지가 다음 편이다.

Share
  • AI
  • OpenAI
  • 비용
  • API 키
  • 보안
상용 API 로 시작한다 — 크레딧·키·상한 — 디코드랩(DCODELAB)