
4개의 GIF로 보는 LLM API 호출
요약
Node.js를 사용하여 프레임워크 없이 LLM API 호출의 내부 동작 원리를 설명합니다. API의 상태가 없는 특성, 요청 구조, 그리고 응답 JSON의 핵심 필드들을 시각적으로 분석합니다.
핵심 포인트
- LLM API는 상태가 없으므로 문맥 유지를 위해 메시지 배열을 직접 관리해야 함
- max_tokens는 목표치가 아닌 응답을 강제로 중단시키는 지점임
- API 호출 패턴은 제공업체가 달라도 POST 방식과 JSON 구조가 유사함
- 응답 JSON에서 stop_reason 필드는 모델이 멈춘 이유를 파악하는 데 중요함
이 글은 프레임워크 없이 오직 API 호출만을 사용하여 Node.js로 작은 에이전트를 처음부터 구축해 나가는 Building TinyAgent 시리즈의 첫 번째 포스트입니다.
하지만 에이전트를 작성하기 전에, LLM을 호출할 때 실제로 어떤 일이 일어나는지 이해해야 합니다. 만약 SDK만을 사용해 보았다면, 아마 가공되지 않은 요청 (raw request)을 본 적이 없으며 그것이 어떻게 작동하는지 이해하지 못할 수도 있습니다. 단 여섯 줄의 코드와 API 키만 있으면 작동은 하지만, 요청이 전송되고 응답이 화면에 출력될 때 어떤 일이 일어났는지는 알 수 없습니다.
1. 요청 (The request)
다음은 각 섹션이 상세히 설명된 샘플 API 호출입니다.
API 호출에서 주목할 만한 몇 가지 사항이 있습니다.
API는 상태가 없습니다 (stateless): 모든 새로운 API 호출은 이전 호출의 문맥 (context)을 기억하지 못합니다. 만약 이전 메시지를 "기억하는" 챗봇을 원한다면, 메시지 배열을 유지하고 매번 전체를 다시 보내야 합니다.
max_tokens는 목표치가 아니라 강제 중단 지점입니다. 만약 목표치에 도달하면 응답은 문장 중간에 멈춥니다.
API 호출 패턴은 보편적입니다. URL이 다르고, x-api-key 대신 Authorization: Bearer를 사용하며, 시스템 프롬프트 (system prompt)가 최상위 레벨이 아닌 messages 내부에 위치할 수도 있습니다. 하지만 동일한 POST 방식, 동일한 JSON, 동일한 {model, messages, max_tokens} 구조를 가집니다. 일단 그 형태를 이해하고 나면, 제공업체를 바꾸는 것은 단순히 찾기 및 바꾸기 작업에 불과합니다.
2. 응답 (The response)
API는 JSON 블롭 (blob)으로 응답합니다. 그 안에는 약 10개의 필드가 있지만, 실제로 중요한 것은 네 가지뿐입니다:
대부분 간과되는 것은 바로 **stop_reason**입니다.
이 필드는 모델이 왜 멈췄는지를 알려주며, 실제 시스템에서는 이를 기준으로 분기 처리를 합니다:
end_turn → 자연스럽게 종료됨, 작업 완료
max_tokens → 한도에 도달함, 응답이 잘림
tool_use → 모델이 도구 (tool) 호출을 원함 (다음 포스트에서 다룹니다!)
...
만약 텍스트만 확인하고 stop_reason을 무시한다면, 언젠가 버그를 배포하게 될 것입니다. 응답은 문제가 없어 보이다가 어느 순간 갑자기 문제가 생기기 때문입니다.
반드시 머릿속에 새겨두어야 할 또 다른 필드는 **usage**입니다. 이 필드는 얼마나 많은 토큰 (tokens)이 입력되고 출력되었는지를 보여줍니다. 예상치 못한 청구서를 받은 뒤에 확인하는 것이 아니라, 첫날부터 로그에 이 숫자를 기록해 두어야 합니다. 🤯
3. 토큰 (Tokens)
제가 계속해서 "24개의 입력 토큰"이라고 말하고 있는데, 그 의미는 다음과 같습니다:
사람들을 놀라게 하며 주목할 만한 사항들은 다음과 같습니다:
단어와 토큰은 일치하지 않습니다. "Unbelievable"은 하나의 단어이지만 네 개의 토큰입니다. 토크나이저 (tokenizer)는 공백이 아니라 흔히 쓰이는 부분 문자열 (substrings)을 기준으로 분할합니다.
코드는 보기보다 비용이 많이 듭니다. def add(a, b):는 8개의 토큰입니다. 모든 괄호와 쉼표는 각각 하나의 토큰이 됩니다.
JSON은 비쌉니다. {"a":1}은 7개의 토큰입니다. 도구 스키마 (tool schemas)가 비대해지면, 매 요청마다 조용히 예산을 갉아먹게 됩니다.
비영어권 언어는 비용이 더 많이 듭니다. 일본어, 힌디어, 아랍어는 동일한 내용의 영어 대비 토큰 수가 2~4배 정도 더 많이 발생하는 경향이 있습니다. 글로벌 사용자를 대상으로 서비스를 구축한다면, 이는 비용 계산 방식을 크게 변화시킵니다.
영어 산문의 경우 일반적인 규칙은 다음과 같습니다: 약 1 토큰 ≈ 4자 ≈ 0.75단어. 그 외의 모든 경우에는 추측하기 전에 직접 토크나이저를 통해 확인해 보십시오.
4. 청구서
모든 호출에는 두 개의 계량기가 돌아갑니다. 이들은 가격이 다르게 책정됩니다.
출력 토큰 (Output tokens)은 입력 토큰 (input tokens)보다 대략 3~5배 더 비쌉니다. 이것이 LLM 가격 책정에 대해 반드시 숙지해야 할 핵심 숫자입니다.
cost = (input_tokens / 1,000,000) × input_price
+ (output_tokens / 1,000,000) × output_price
이러한 비대칭성(asymmetry)으로부터 도출되는 세 가지 사항은 다음과 같습니다:
- 긴 프롬프트(prompt)는 저렴합니다. 긴 응답(response)은 비쌉니다. 시스템 프롬프트(system prompt)에 50 KB의 컨텍스트(context)를 채워 넣는 것은 괜찮습니다. 하지만 50 KB의 출력을 요구하는 것은 대략 5배 더 비쌉니다.
- "생각하는(Thinking)" 토큰은 출력(output)으로 간주됩니다. 추론 모델(reasoning models)은 사용자가 직접 볼 수 없더라도 내부적인 사고 과정을 출력 요율(output rate)로 청구합니다.
- 도구 스키마(Tool schemas)는 호출할 때마다 입력(input)을 소비합니다. 도구 스키마는 시스템 프롬프트와 마찬가지로 매 요청마다 다시 전송됩니다.
호출당 0.006달러일 때, 하루 10만 번의 호출은 작은 기능 하나만으로도 월 600달러의 비용이 발생함을 의미합니다. 경고 알림을 받기 전, 지금 바로 사용량 로깅(usage logging)을 추가하세요. 🚨
5. 전체 과정을 20줄의 코드로
위에서 논의한 API 호출의 전체 코드입니다:
Jasmin2895 / TinyAgent
의존성(dependencies)도 없고 설치 설정도 필요 없습니다. API 키만 있으면 되는 단순한 Node 파일입니다.
다음 포스트 전 시도해 볼 세 가지
-
직접 실행하고 수치를 관찰하세요. 10번 정도 호출해 보고, 프롬프트 길이를 변경하며 사용량이 어떻게 변하는지 확인하세요. 어떤 문서를 읽는 것보다 이런 방식으로 비용에 대한 실질적인 직관을 더 빠르게 쌓을 수 있습니다.
-
max_tokens: 20으로 설정하고 긴 답변을 요구해 보세요. 답변이 잘리는 것을 확인하고stop_reason을 체크하세요. 이는 결국 프로덕션(production) 환경에서 마주하게 될 버그이므로, 지금 의도적으로 경험해 보는 것이 좋습니다. -
멀티 턴(multi-turn) 채팅을 수동으로 구현해 보세요. 메시지 배열(messages array)을 유지하면서, 각 사용자 메시지와 모델의 응답을 배열에 푸시(push)하고 매 턴마다 전체를 다시 전송하세요. 이를 직접 해보면 왜 긴 대화가 비싸지는지 즉시 이해하게 될 것입니다. 매 호출마다 전체 히스토리(history)에 대한 비용을 지불하고 있기 때문입니다.
다음 단계
이어지는 포스트 시리즈에서는 단순히 응답하는 것을 넘어, 더 많은 일을 실제로 처리할 수 있도록 TinyAgent의 능력을 확장해 보겠습니다.
즐거운 코딩 되세요! 👩💻
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

