LLM의 요청 및 응답 사이클은 어떻게 작동하는가? 전체 과정 살펴보기
요약
사용자가 LLM에 프롬프트를 입력한 순간부터 응답이 출력되기까지의 전체 기술적 사이클을 단계별로 설명합니다. 클라이언트의 JSON 요청 생성부터 API 게이트웨이, 토크나이징, Transformer의 순전파, 그리고 스트리밍 응답 과정까지의 흐름을 다룹니다.
핵심 포인트
- 프롬프트는 시스템 프롬프트와 이전 대화 맥락을 포함한 JSON 형태로 구성됨
- API 게이트웨이는 인증, 속도 제한, 모델 라우팅 역할을 수행함
- 텍스트는 토큰으로 분해되어 모델의 컨텍스트 윈도우에 입력됨
- Transformer 레이어의 순전파를 통해 토큰이 하나씩 예측됨
- 생성된 토큰은 스트리밍 파이프라인을 통해 실시간으로 클라이언트에 전달됨
이 블로그 포스트에서는 여러분이 전송 버튼을 누르는 순간부터 마지막 단어가 화면에 나타나는 순간까지, LLM (Large Language Model)의 요청 및 응답 사이클이 어떻게 작동하는지 살펴보겠습니다. 저는 여러분에게 Transformer 수학 공식의 벽을 던져주지 않을 것입니다. 대신, 하나의 단일 프롬프트(Prompt)가 여정의 모든 단계를 거치는 과정을 따라가며, 그 어떤 것도 추상적으로 느껴지지 않도록 할 것입니다.
대규모 언어 모델(LLM)로 전송된 단일 프롬프트는 화면에 응답이 나타나기 전까지 여러 개의 뚜렷한 단계를 거칩니다. 이 프로세스는 클라이언트 앱(Client app)이 구조화된 JSON 요청을 생성하는 것으로 시작되며, 이 요청은 API 게이트웨이(API gateway)를 통과한 후 입력 텍스트가 숫자 토큰(Token)으로 분해되어 모델의 컨텍스트 윈도우(Context window)에 조립됩니다.
그 후 모델은 Transformer 레이어를 통해 순전파(Forward pass)를 실행하여 한 번에 하나의 토큰을 예측하며, 문장 종료 신호(End-of-sequence signal)에 도달할 때까지 이 루프를 반복합니다. 생성된 토큰은 클라이언트로 스트리밍(Streaming)되어 다시 읽을 수 있는 텍스트로 점진적으로 변환되는데, 이것이 응답이 단어별로 나타나는 이유를 설명해 줍니다.
저는 브라우저 기반 LLM 벤치마킹 도구인 iamspeed.dev를 구축하면서 이 과정을 제대로 매핑해보고 싶다는 호기심이 생겼습니다. 초당 토큰 수(Tokens per second)나 첫 번째 토큰까지의 시간(Time to first token) 같은 것들을 측정하기 전에, 저는 "엔터를 누르는 순간"과 "단어가 나타나기 시작하는 순간" 사이에 정확히 어떤 일이 일어나는지 이해해야 했습니다. 알고 보니 그 간극에는 대부분의 사람들이 추측하는 것보다 훨씬 더 많은 단계가 있었습니다.
우리가 따라갈 예시
포스트 전체를 관통하는 하나의 예시를 유지해 봅시다. 여러분이 채팅 앱에 다음과 같이 입력했다고 가정해 보겠습니다:
사용자: 인도의 수도는 어디인가요?
그리고 몇 초 후, 모델이 다음과 같이 답변합니다:
어시스턴트 (Assistant): 인도의 수도는 뉴델리입니다.
겉보기에는 충분히 간단해 보입니다. 하지만 이 한 번의 교환은 그 여섯 단어가 여러분의 채팅창에 나타나기 전, 클라이언트 앱, API 레이어, 토크나이저 (Tokenizer), 수십억 개의 파라미터(Parameter)를 가진 신경망 (Neural network), 샘플링 알고리즘 (Sampling algorithm), 그리고 스트리밍 파이프라인 (Streaming pipeline)을 모두 거칩니다. 단계별로 살펴봅시다.
1단계: 전송 버튼을 누릅니다
1단계: 전송 버튼을 누릅니다
엔터(Enter)를 누르는 순간, 채팅 앱은 단순히 원문 문장을 발사하는 것이 아닙니다. 대신 다음과 같은 구조화된 요청(JSON 형태가 일반적입니다)을 만듭니다:
{
"model": "some-llm-model",
"messages": [
...
여기서 몇 가지 점에 주목하세요. 사용자가 타이핑하지 않았음에도 시스템 프롬프트(system prompt)가 포함되어 있습니다. 대화의 이전 메시지들도 모두 포함되는데, 이는 모델이 호출 간에 자체적인 기억을 가지고 있지 않기 때문입니다. 그리고 stream: true가 이미 설정되어 있는데, 이것은 나중에 매우 중요합니다. 이 페이로드(payload)는 HTTPS를 통해 API 엔드포인트로 전송됩니다.
2단계: API 게이트웨이
요청이 모델로 바로 가지 않습니다. 먼저 필수적이지만 지루한 작업을 처리하는 API 게이트웨이를 거칩니다:
- API 키 또는 세션 토큰 유효성 검사
- 속도 제한(rate limits) 확인, 시스템이 과부하되는 것을 방지하기 위해
- 요청을 적절한 모델 버전 및 컴퓨팅 클러스터로 라우팅하며, 지연 시간(latency) 감소를 위해 종종 지역별로 분산됩니다.
- 결제 및 남용 모니터링을 위한 요청 기록(logging)
이 계층은 마치 문지기이자 교통 경찰관이 결합된 것과 같습니다. 이 단계가 요청을 통과시킨 후에야 비로소 'AI'와 유사한 것이 실제로 작동하게 됩니다.
3단계: 토큰화
여기서 흥미로운 일이 벌어집니다. 모델은 영어를 읽지 않습니다. 대신 숫자로 매핑된 텍스트의 조각인 토큰(tokens)을 읽습니다. 토크나이저(tokenizer)는 바이트 쌍 인코딩(byte pair encoding)과 같은 방식을 사용하여 문장을 분해합니다.
예시 문장인 "What's the capital of India?"는 다음과 같이 토큰화될 수 있습니다:
["What", "'s", " the", " capital", " of", " India", "?"]
이러한 각 조각은 모델의 어휘(vocabulary)에서 특정 정수 ID에 매핑됩니다. 예를 들어 [1780, 434, 262, 3139, 286, 4881, 30]과 같습니다. 정확한 분할은 모델이 사용하는 토크나이저에 따라 다르지만, 아이디어는 항상 동일합니다. 즉, 텍스트가 숫자가 되는 것인데, 이는 신경망(neural network)이 실제로 계산할 수 있는 유일한 형태이기 때문입니다.
이 단계는 사람들이 생각하는 것보다 훨씬 중요합니다. 또한 서비스 제공업체들이 단어나 글자 단위가 아닌 토큰(token) 단위로 비용을 청구하는 이유이기도 합니다. 짧은 단어는 하나의 토큰이 될 수 있지만, 희귀한 단어는 세 개 또는 네 개의 토큰으로 나뉠 수 있습니다.
4단계: 컨텍스트 윈도우 (context window) 구축
새로운 토큰들이 단독으로 들어가는 것은 아닙니다. 시스템은 다음 요소들을 하나로 엮습니다:
- 시스템 프롬프트 (system prompt) 토큰
- 이전 대화 기록 (conversation history) 토큰
- 사용자의 새로운 메시지 토큰
이 모든 것이 모델의 컨텍스트 윈도우 (context window)에 담기는데, 이는
사람들이 처음 이 사실을 알게 되었을 때 놀라는 부분은 바로 이것입니다: 모델은 문장 전체를 한 번에 생성하지 않습니다. 모델은 하나의 토큰 (token)을 생성하고, 그 토큰을 다시 입력의 일부로 자신에게 피드백하여 다음 토큰을 생성합니다. 이 과정은 모델이 중단하기로 결정할 때까지 반복됩니다.
temperature, top_p, top_k와 같은 파라미터 (parameters)는 해당 확률 분포 (probability distribution)에서 다음 토큰이 어떻게 선택될지를 제어합니다. 낮은 temperature는 모델이 안전하게 행동하여 거의 매번 가장 높은 확률의 토큰을 선택하게 함을 의미합니다. 높은 temperature는 무작위성 (randomness)을 추가하며, 이는 창의적인 글쓰기에는 유용하지만 사실적인 답변에는 덜 유용합니다.
우리의 예시에서, 자기회귀 루프 (autoregressive loop)는 대략 다음과 같습니다:
현재까지의 입력: "What's the capital of India?"
→ 예측: "The"
...
해당 문장 종료 (end-of-sequence) 토큰은 모델이 "나는 끝났다"라고 말하는 방식이며, 이를 통해 모델은 영원히 횡설수설하는 대신 언제 멈춰야 할지를 알게 됩니다.
단계 7: 응답 스트리밍 (Streaming the response back)
단계 1에서 언급한 stream: true를 기억하시나요? 바로 이 지점에서 그 효과가 나타납니다. 전체 응답이 생성 완료될 때까지 기다렸다가 하나의 커다란 덩어리로 보내는 대신, 서버는 각 토큰이 생성되는 즉시 클라이언트 (client)로 푸시하며, 이는 보통 서버 전송 이벤트 (Server-Sent Events) 또는 청크 HTTP 연결 (chunked HTTP connection)을 통해 이루어집니다.
이것이 바로 전체 답변이 한꺼번에 나타나는 대신 단어들이 화면에 하나씩 나타나는 이유입니다. 이것은 시각적 효과가 아니라, 모델이 토큰을 생성하는 대로 문자 그대로 보여주는 것입니다.
단계 8: 디토큰화 (Detokenization) 및 렌더링 (rendering)
클라이언트 측에서는 들어오는 각 토큰 ID (token ID)가 읽을 수 있는 텍스트로 다시 변환되는데, 이는 단계 3의 역과정입니다. 채팅 앱은 각 조각이 도착할 때마다 메시지 버블에 추가하고 UI를 다시 렌더링하며, 문장 종료 (end-of-sequence) 신호가 나타날 때쯤이면 여러분은 다음과 같은 완전한 문장을 보게 됩니다:
The capital of India is New Delhi.
여섯 단어. 여덟 단계. 이 모든 과정은 보통 2초 이내에 완료됩니다.
지연 시간 (latency)이 실제로 발생하는 곳
성능 엔지니어링 (performance engineering) 배경을 가진 저로서는, 시계(clock)를 언급하지 않고는 이 글을 쓸 수 없습니다. "응답에 3초가 걸렸다"라는 말보다 훨씬 더 중요한 두 가지 수치가 있습니다:
- 첫 번째 토큰까지의 시간 (Time to first token, TTFT): 전송 버튼을 누른 시점부터 아주 첫 번째 단어가 나타날 때까지 기다리는 시간입니다. 이는 단계 1부터 5까지, 특히 긴 컨텍스트 윈도우 (context window)에서의 순전파 (forward pass) 과정에 의해 결정됩니다.
- 토큰 간 지연 시간 (Inter-token latency): 첫 토큰 이후 토큰들이 얼마나 빠르게 계속 도착하는지를 나타내며, 타이핑 효과가 얼마나 "매끄럽게" 느껴지는지를 결정합니다.
모델은 TTFT는 느리지만 토큰 간 속도는 빠를 수 있고, 혹은 그 반대일 수도 있습니다. 이 두 가지는 총 소요 시간이 동일하더라도 매우 다른 사용자 경험 (user experience)을 만들어냅니다. 이것이 바로 제가 iamspeed.dev를 구축할 때 측정하고자 했던 정확한 격차였으며, 솔직히 말해서 이 전체 라이프 사이클 (life cycle)을 이해하는 것이 벤치마킹 수치를 단순히 대시보드 위의 숫자가 아닌, 실제로 의미 있는 수치로 만들어 주었습니다.
LLM 성능을 측정하려면 iamspeed.dev를 확인하세요.
마무리 (Wrap up)
따라서 다음에 채팅 앱에 질문을 입력하고 답변이 스스로 타이핑되는 것을 지켜볼 때, 여러분은 이제 그 문장이 거치는 전체 여정을 알게 될 것입니다: 요청 형성 (request formation), 게이트웨이 확인 (gateway checks), 토큰화 (tokenization), 컨텍스트 조립 (context assembly), 거대한 네트워크를 통한 순전파 (forward pass), 토큰 단위 샘플링 (token-by-token sampling), 스트리밍 (streaming), 그리고 마지막으로 읽을 수 있는 단어로 되돌리는 역토큰화 (detokenization)까지 말입니다.
외부에서 보기에는 마법처럼 보입니다. 하지만 내부적으로는 매우 잘 설계된 파이프라인 (pipeline)입니다.
즐거운 테스트 되세요!
LLM 응답이 스트리밍되는 것을 보면서 내부에서 어떤 일이 일어나고 있는지 궁금했던 적이 있나요? 댓글로 알려주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기