LLM API는 호스팅된 언어 모델에 프롬프트를 보내고 생성된 텍스트를 돌려받는, 토큰 단위로 과금되는 HTTP 엔드포인트입니다. 평범한 API이지만 세 가지 특이한 속성이 있습니다. 무상태(stateless)이고(매 호출마다 대화 전체를 다시 보냅니다), 토큰 단위로 계량되며(입력과 출력의 가격이 따로 매겨집니다), 비결정적입니다(같은 프롬프트가 다른 텍스트를 반환할 수 있습니다). 이 셋을 이해하고 나면 나머지는 검색 상위 페이지들이 건너뛰는 운영 규율입니다. 호출을 어디에 둘지, 비용이 얼마나 드는지, 어떻게 실패하는지입니다.
핵심 요약
| 질문 | 답변 |
|---|---|
| 정의 | messages 배열을 POST → 어시스턴트 응답 + 토큰 수를 받음 |
| 과금 | 토큰 단위, 입력과 출력 가격이 별도, 출력이 더 비쌈 |
| 무상태의 함정 | 메모리란 이전 턴을 다시 보내는 것. 매 호출, 모든 토큰에 비용 발생 |
| 철칙 | 클라이언트에서 절대 호출하지 않음. 키가 유출되므로 백엔드를 프록시로 사용 |
| 에이전트로 가는 다리 | 툴 호출. 모델이 함수를 요청하고 코드가 실행 |
서버 측에서 실행하는 실제 호출
// JavaScript — Cloud Code (cloud/main.js)
// The LLM call belongs server-side — the key never reaches the client
Parse.Cloud.define('summarize', async (req) => {
const res = await Parse.Cloud.httpRequest({
method: 'POST',
url: 'https://api.llm-provider.example/v1/chat/completions',
headers: {
Authorization: `Bearer ${process.env.LLM_KEY}`, // server-side secret
'Content-Type': 'application/json',
},
body: {
model: 'default-chat',
messages: [
{ role: 'system', content: 'Summarize in one sentence.' },
{ role: 'user', content: req.params.text },
],
max_tokens: 80, // cost + latency guardrail, enforced by YOU
},
});
return res.data.choices[0].message.content;
}); // Flutter / Dart — Back4app Flutter SDK
// The client calls YOUR function, never the LLM API directly
final summary = await ParseCloudFunction('summarize')
.execute(parameters: {'text': longArticle});
print(summary.result);
// The model key stays on the server. If this app called the LLM API
// itself, the key would ship in the binary — one decompile from theft. // iOS / Swift — Back4app Swift SDK
// The client calls YOUR function, never the LLM API directly
let summary: String = try await Cloud.run(
name: "summarize", parameters: ["text": longArticle])
print(summary)
// The model key stays on the server. If this app called the LLM API
// itself, the key would ship in the IPA — one decompile from theft. // Android / Kotlin — Back4app Android SDK
// The client calls YOUR function, never the LLM API directly
val summary = ParseCloud.callFunction<String>(
"summarize", mapOf("text" to longArticle))
println(summary)
// The model key stays on the server. If this app called the LLM API
// itself, the key would ship in the APK — one decompile from theft. 구조는 이식 가능합니다. 대부분의 제공업체가 같은 chat-completions 형식을 받아들이므로, 아래 요청과 응답은 어느 모델을 가리키든 똑같이 읽힙니다:
POST /v1/chat/completions Authorization: Bearer <key>
{ "model": "default-chat",
"messages": [
{ "role": "system", "content": "You are concise." }, ← 동작을 정함
{ "role": "user", "content": "Explain tokens." } ], ← 실제 지시
"max_tokens": 200, "temperature": 0.7, "stream": false }
→ { "choices": [ { "message": { "role": "assistant",
"content": "A token is…" },
"finish_reason": "stop" } ],
"usage": { "prompt_tokens": 24, "completion_tokens": 118,
"total_tokens": 142 } } ← 과금 기준
토큰이라는 화폐
토큰은 텍스트 조각으로, 대략 4글자 또는 영어 단어의 4분의 3 정도입니다. LLM API 비용에 관한 모든 것은 토큰 수를 세는 일로 귀결되며, 사람들을 놀라게 하는 사실이 두 가지 있습니다. 출력이 입력보다 비쌉니다. 흔히 몇 배나 비싼데, 프롬프트를 읽는 것은 저렴한 반면 응답 토큰을 생성하는 일은 한 번에 하나씩 전체 어휘에 걸쳐 예측해야 하는 연산 집약적 작업이기 때문입니다. 그리고 컨텍스트는 매 호출마다 비용이 듭니다. 모델은 무상태이므로 “메모리”란 이전 턴을 다시 보내는 것이고, 긴 기록이나 큰 검색 결과 구절은 요청할 때마다 다시 과금됩니다. 익혀 둘 만한 대략적인 월간 공식은 다음과 같습니다:
한 달 총비용 ≈ ( 일일 요청 수
× (avg_input_tokens × input_price_per_M / 1_000_000
+ avg_output_tokens × output_price_per_M / 1_000_000) )
× 30
비용을 움직이는 레버는 max_tokens 상한 설정, 다시 보내는 컨텍스트 줄이기,
쉬운 작업에 더 작은 모델 선택, 재사용되는 프롬프트 접두사 캐싱입니다.
중요한 파라미터
| 파라미터 | 제어 대상 | 실무 가이드 |
|---|---|---|
temperature | 무작위성(0–2) | 추출/분류에는 0, 산문에는 약 0.7 |
top_p | 뉴클리어스 샘플링 | 이것 또는 temperature 중 하나만 조정, 둘 다 조정하지 않음 |
max_tokens | 출력 길이 상한 | 거의 항상 설정. 비용과 지연 시간을 제한 |
stop | 중단 시퀀스 | 직접 정한 구분자에서 생성을 종료 |
스트리밍: 채팅 UI가 빠르게 느껴지는 이유
stream 플래그를 설정하면 응답이 몇 초 뒤 한 덩어리로 오는 대신 서버 전송 이벤트(Server-Sent Events)로 조금씩 도착합니다. 한 번에 토큰 조각 하나씩 담긴 data: 줄이 이어지고, [DONE] 마커로 끝납니다. 이유는 체감에 있습니다. 첫 토큰까지의 시간(time-to-first-token)은 전체 답변까지의 시간의 일부에 불과하므로, 사용자는 스피너가 도는 대신 글자가 나타나는 것을 보게 됩니다. 일반 HTTP 위에서 서버에서 클라이언트로 흐르는 단방향 텍스트이며, 이는 정확히 SSE의 형태이고 LLM 스트리밍이 WebSocket 대신 SSE를 쓰는 이유이기도 합니다. 백엔드에 주는 영향은 이렇습니다. 프록시는 전체 응답을 버퍼링해 스트리밍의 의미를 없애지 말고, 제공업체의 이벤트 스트림을 읽어 그대로 흘려보내야 합니다.
툴 호출과 구조화된 출력
두 기능이 “텍스트 입력, 텍스트 출력”을 프로그래밍 가능한 무언가로 바꿉니다. 툴 호출: 요청에 사용 가능한 함수를 기술하면(이름 + JSON 스키마), 모델은 산문 대신 툴 이름과 인수를 담은 구조화된 JSON 호출을 반환합니다. 코드가 이를 실행하고 결과를 다시 넘깁니다(Fowler의 해설이 명쾌한 참고 자료입니다). 모델은 함수를 직접 실행하지 않으며, 요청할 뿐입니다. 구조화된 출력은 툴 호출과 다르지만 자주 혼동되며, 구분해 둘 가치가 있는 세 가지로 나뉩니다:
| 메커니즘 | 보장 | 용도 |
|---|---|---|
| JSON 모드 | 유효한 JSON. 단, 형태는 제각각 | 느슨한 “JSON으로 응답” 요구 |
| Structured Outputs | 직접 정의한 스키마와 정확히 일치 | 추출, 분류, 타입이 있는 데이터 |
| 툴 호출 | 직접 정의한 함수에 대한 호출 | 형식 맞추기가 아니라 실제 작업 수행 |
툴 호출은 AI 에이전트를 가능하게 하는 메커니즘입니다. 같은 요청/응답을, 모델이 더 이상 툴을 요청하지 않을 때까지 루프로 실행하는 것입니다.
LLM API vs. 오픈 모델 셀프 호스팅
| 호스팅형 LLM API | 셀프 호스팅 오픈 모델 | |
|---|---|---|
| 인프라 | 없음. 제공업체가 운영 | GPU, 서빙 스택, 운영 |
| 과금 | 토큰 단위, 종량제 | 고정 용량, 채우는 것은 사용자 몫 |
| 출시까지 걸리는 시간 | 몇 분 | 며칠에서 몇 주 |
| 데이터 상주 | 제공업체의 약관을 따름 | 완전히 자체 통제 |
| 낮거나 들쭉날쭉한 사용량에서의 비용 | 가장 저렴 | 유휴 GPU가 돈을 태움 |
| 극단적이고 지속적인 사용량에서의 비용 | 셀프 호스팅보다 비싸질 수 있음 | 손익분기점을 넘으면 유리 |
솔직하게 말하면, 거의 모든 경우 거의 모든 사람에게 API가 유리합니다. 인프라가 전혀 필요 없고, 즉시 시작할 수 있으며, 사용량이 정말로 크고 꾸준해지기 전까지는 더 저렴합니다. 오픈 모델 셀프 호스팅(Ollama, vLLM, llama.cpp 사용)은 높은 손익분기점을 넘었거나 데이터 상주가 절대적인 요건일 때에만 운영 비용만큼의 가치를 냅니다. 대부분의 제품은 API로 시작하고, 규모 때문에 불가피해지면 그때 이 질문을 다시 검토합니다.
신뢰성: 불안정한 LLM 엔드포인트
LLM API는 느리고, 속도 제한이 있고, 가끔 실패하는 원격 의존성으로 다뤄야 합니다. 실제로 그렇기 때문입니다. 제한은 분당 요청 수와 분당 토큰 수로 주어지며, 둘 중 하나라도 초과하면 429를 받습니다. 규율은 속도 제한이 있는 모든 API가 요구하는 것과 같습니다. 429나 5xx에는 지터를 더한 지수 백오프로 재시도하고 Retry-After를 따르며, 4xx(잘못된 인증, 형식이 잘못된 요청, 콘텐츠 정책에 따른 거부)에는 재시도하지 않습니다. 똑같이 실패할 것이기 때문입니다. 요청별 타임아웃을 추가하고(생성이 멈출 수 있습니다), 멱등하지 않은 호출을 재시도하면 토큰 비용이 두 번 든다는 점을 기억해야 합니다. 이 중 LLM에만 해당하는 것은 없지만, 정상 경로의 curl에서 멈추는 튜토리얼들은 이 모두를 건너뜁니다.
튜토리얼이 묻어 두는 규칙: 클라이언트에서 절대 호출하지 않기
가장 중요한 운영상의 사실이자 용어집 페이지들이 빠뜨리는 사실입니다. LLM API 호출은 서버에 있어야 하며, 브라우저나 앱에 있어서는 안 됩니다. 클라이언트에 포함된 모델 API 키는 네트워크 탭을 한 번 들여다보거나 바이너리를 한 번 디컴파일하면 도난당합니다. 그리고 유출된 퍼블리셔블 키와 달리, 도난당한 모델 키는 키 소유자의 토큰 예산을 손에 쥔 낯선 사람이며, 그를 막는 한도는 청구서뿐입니다. 해법은 시크릿 키를 쓰는 모든 서비스에 필요한 같은 프록시 규율입니다. 클라이언트는 자체 엔드포인트를 호출하고, 백엔드는 서버 측 설정에 키를 보관한 채 모델을 호출하며, 응답은 백엔드를 거쳐 돌아옵니다. 그 프록시는 이 글에서 다룬 다른 모든 통제, 즉 비용 상한, 재시도, 스트리밍, 프롬프트 정리가 자리하는 곳이기도 합니다. “호출을 어디에 둘 것인가?”라는 질문에 답이 하나뿐인 이유입니다.
일반적인 사용 사례
- 요약과 추출 – 긴 텍스트를 짧은 구조화된 데이터로 바꿉니다.
temperature: 0. - 채팅과 어시스턴트 – 매번 다시 보내는 대화 기록을 바탕으로 한 스트리밍 응답입니다.
- 분류와 태깅 – Structured Outputs로 레이블 스키마를 강제합니다.
- RAG 답변 – 검색된 컨텍스트에 그라운딩된 생성으로, 그 컨텍스트를 줄여 비용을 관리합니다.
- 에이전트형 행동 – 루프 속의 툴 호출로, 각 툴은 권한이 적용된 백엔드 함수입니다.
LLM API를 사용해야 할까? 결정 매트릭스
| 상황 | 선택 |
|---|---|
| 지금 AI 기능을 출시해야 하는 경우 | LLM API. 인프라가 전혀 필요 없음 |
| 클라이언트 대상 앱 전반 | 서버 측에서 호출하는 API. 디바이스에서는 절대 호출하지 않음 |
| 극단적이고 지속적인 사용량 | 손익분기점을 넘으면 셀프 호스팅(Ollama, vLLM) 검토 |
| 엄격한 데이터 상주 요건 | 셀프 호스팅, 또는 적절한 보장을 제공하는 제공업체 |
| 결정적이고 규칙 기반인 작업 | LLM이 아예 필요 없을 수도 있음. 일반 코드가 더 저렴하고 신뢰할 수 있음 |
| 모델이 실제로 행동해야 하는 경우 | 툴 호출 → 에이전트 |
한계와 트레이드오프
- 비결정성이 기본값입니다. 같은 프롬프트도 실행할 때마다 결과가 달라집니다. 정확한 재현성이 필요한 작업에는
temperature: 0과, 흔히 출력 검증이 필요합니다. - 비용은 토큰에 따라 조용히 늘어납니다. 넉넉한 컨텍스트나 제한 없는
max_tokens는 사용량이 많아지면 저렴한 기능을 비싼 기능으로 바꿉니다. 사용량을 짐작하지 말고 계량해야 합니다. - 지연 시간은 밀리초가 아니라 초 단위입니다. LLM 호출은 웹 기준으로 느립니다. 스트리밍, 비동기 패턴, 정직한 로딩 상태로 이에 맞춰 설계해야 합니다.
- 의존 대상이 외부에 있고 속도 제한이 있습니다. 제공업체의 장애와 스로틀링은 곧 자사 서비스의 장애가 됩니다. 재시도, 폴백, 캐싱은 마무리 손질이 아니라 복원력입니다.
- 출력은 틀리면서도 자신만만할 수 있습니다. API는 사실 여부와 무관하게 유창한 텍스트를 반환합니다. 그라운딩(RAG)과 검증이 출력을 신뢰할 수 있게 만드는 방법입니다.
Back4app에서의 LLM API
Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. “호출을 어디에 둘 것인가”라는 질문은 여기서 저절로 답이 나옵니다. 코드 탭에서 보듯이 Cloud Code 함수 안입니다. 클라이언트는 직접 작성한 summarize 함수를 호출하고, 함수는 모델 키를 서버 측 설정에 보관한 채 LLM API를 호출하므로, 키는 디바이스에 닿지 않습니다. 이 배치 하나로 앞서 다룬 빈틈이 한꺼번에 해결됩니다. 비용 상한(함수가 max_tokens를 설정하고 모델을 선택), 재시도와 백오프(불안정한 엔드포인트를 상대로 함수 안에서 처리), 그리고 두 가지 전달 방식입니다. 단일 답변이라면 응답을 동기적으로 반환하고, 그렇지 않으면 데이터베이스에 기록한 뒤 클라이언트가 Live Query로 응답이 채워지는 과정을 지켜보게 할 수 있습니다. 소켓을 직접 구축하지 않고도 스트리밍이 되는 셈입니다. LLM API는 더 이상 연동 리스크가 아니라, 백엔드가 이미 안전하게 다룰 줄 아는 또 하나의 서버 측 호출이 됩니다.
자주 묻는 질문
LLM API란 무엇입니까?
코드에서 호스팅된 대규모 언어 모델에 프롬프트를 보내고 생성된 텍스트를 돌려받을 수 있게 하는 HTTP 엔드포인트입니다. 모델, GPU, 추론 인프라를 직접 운영할 필요가 없습니다. JSON 본문을 POST로 보내면 어시스턴트 메시지와 토큰 사용량을 받고, 토큰 단위로 비용을 지불합니다.
토큰이란 무엇이며 요금은 어떻게 계산됩니까?
토큰은 텍스트 조각으로, 영어 기준 대략 4글자 또는 단어의 4분의 3 정도입니다. 요금은 100만 토큰 단위로 매겨지며, 입력(프롬프트)과 출력(생성된 응답)에 별도의 요율이 적용됩니다. 출력은 보통 입력보다 몇 배 비쌉니다. 토큰 하나를 생성하는 데는 읽는 것보다 훨씬 많은 연산이 들기 때문입니다.
컨텍스트 윈도우란 무엇입니까?
모델이 한 번의 요청에서 고려할 수 있는 최대 토큰 수로, 입력과 출력을 합한 값입니다. 포함할 수 있는 대화 기록이나 문서의 양을 제한하며, 비용을 직접 좌우합니다. 모든 호출은 보낸 컨텍스트 전체에 대해 비용을 지불하므로, 긴 기록이나 큰 검색 결과 구절은 요청마다 지출되는 비용입니다.
스트리밍이란 무엇이며, 채팅 UI는 왜 스트리밍을 사용합니까?
스트림 플래그를 설정하면 응답이 하나의 최종 덩어리가 아니라 서버 전송 이벤트(Server-Sent Events)로 조금씩 반환되므로, UI는 토큰이 생성되는 대로 렌더링할 수 있습니다. 스트리밍은 체감 지연 시간을 줄이기 위해 존재합니다. 첫 토큰까지의 시간은 전체 답변까지의 시간의 일부에 불과하므로, 사용자는 몇 초 동안 스피너를 보는 대신 즉시 글자를 보게 됩니다.
함수 호출 또는 툴 호출이란 무엇입니까?
요청에 사용 가능한 툴을 이름과 JSON 스키마로 기술하면, 모델은 산문으로 답하는 대신 툴 이름과 인수를 담은 구조화된 JSON 호출을 반환합니다. 코드가 이를 실행하고 결과를 다시 넘깁니다. 모델은 함수를 직접 실행하지 않으며, 요청할 뿐입니다. 이것이 LLM API를 에이전트로 바꾸는 메커니즘입니다.
LLM API는 클라이언트와 서버 중 어디에서 호출해야 합니까?
언제나 서버입니다. 브라우저 번들이나 모바일 바이너리에 포함된 API 키는 네트워크 탭을 한 번 들여다보거나 한 번 디컴파일하면 도난당합니다. 그리고 모델 키를 도난당하면 키 소유자의 토큰 예산을 낯선 사람이 쓰게 됩니다. 모든 LLM 호출은 백엔드를 거쳐야 하며, 키는 서버 측 설정에 보관해야 합니다.
LLM API를 써야 합니까, 오픈 모델을 셀프 호스팅해야 합니까?
API는 인프라가 전혀 필요 없고, 호출 단위로 과금되며, 오늘 바로 출시할 수 있습니다. 오픈 모델을 셀프 호스팅하면(Ollama, vLLM, llama.cpp 같은 도구로) 완전한 통제권과 데이터 상주(data residency)를 얻지만, GPU와 운영 비용까지 계산하면 매우 높은 수준의 사용량이 지속될 때에만 비용 면에서 유리합니다. 대부분의 제품은 API로 시작하고 규모가 커졌을 때 다시 검토합니다.
속도 제한과 장애는 어떻게 처리합니까?
제한은 분당 요청 수와 분당 토큰 수로 주어집니다. 429나 5xx를 받으면 지터를 더한 지수 백오프로 재시도하고, Retry-After 헤더가 있으면 따릅니다. 4xx 오류는 재시도하지 않습니다. 잘못된 인증, 형식이 잘못된 요청, 콘텐츠 정책에 따른 거부는 두 번째에도 똑같이 실패하기 때문입니다.