대규모 언어 모델에 질문을 입력하면 화면에는 자연스러운 문장이 나타납니다. 너무 자연스럽다 보니 모델이 문장을 통째로 읽고, 사람처럼 의미를 이해한 다음, 머릿속에 저장된 답을 꺼내는 것처럼 느껴집니다.
하지만 실제 처리 과정은 이와 다릅니다. LLM은 우리가 입력한 문장을 그대로 다루지 않습니다. 먼저 텍스트를 작은 단위인 토큰(token) 으로 나누고, 각 토큰을 숫자로 바꾼 뒤, 제한된 컨텍스트 윈도우(context window) 안에서 토큰 사이의 관계를 계산합니다. 그리고 현재 문맥 다음에 올 가능성이 높은 토큰을 하나 선택합니다. 선택한 토큰은 다시 문맥에 추가되고, 같은 계산이 반복됩니다.
사용자 입력
→ 토큰화
→ 토큰 ID와 벡터 변환
→ 컨텍스트 안의 관계 계산
→ 다음 토큰 확률 예측
→ 토큰 하나 선택
→ 컨텍스트에 추가
→ 종료 조건까지 반복
이 흐름을 이해하면 “왜 같은 질문에도 답이 조금씩 달라지는가”, “긴 문서를 넣었는데 왜 일부 내용을 놓치는가”, “대화가 길어지면 왜 앞의 지시를 잊는가”, “토큰 수가 왜 비용과 속도에 영향을 주는가”를 한꺼번에 설명할 수 있습니다.

LLM은 문장이 아니라 토큰을 본다
토큰은 모델이 텍스트를 처리하는 기본 단위입니다. 토큰 하나가 항상 단어 하나를 의미하는 것은 아닙니다. 짧고 자주 쓰이는 단어는 하나의 토큰이 될 수 있지만, 긴 단어는 여러 조각으로 나뉠 수 있습니다. 공백과 문장부호가 별도 토큰이 되기도 하고, 구현에 따라 공백이 다음 단어 조각에 포함되기도 합니다.
개념적으로 다음과 같은 분할을 생각할 수 있습니다.
"생성형 AI는 문장을 토큰으로 처리한다"
→ ["생성", "형", " AI", "는", " 문장", "을", " 토큰", "으로", " 처리", "한다"]
이 예시는 원리를 설명하기 위한 것입니다. 실제 결과는 모델이 사용하는 토크나이저와 어휘 사전에 따라 달라집니다. 같은 문장이라도 GPT 계열, Llama 계열, BERT 계열 모델이 서로 다르게 나눌 수 있습니다.
이처럼 단어보다 작은 서브워드(subword) 단위를 사용하는 이유는 어휘 사전의 크기와 표현력을 균형 있게 유지하기 위해서입니다.
- 단어 단위만 사용하면 사전에 없는 신조어, 오탈자, 고유명사를 처리하기 어렵습니다.
- 문자 단위만 사용하면 거의 모든 문장을 표현할 수 있지만 입력 길이가 지나치게 늘어납니다.
- 서브워드는 자주 등장하는 문자열은 크게 묶고, 드문 문자열은 더 작은 조각으로 나눕니다.
그래서 처음 보는 제품명이나 새로 만들어진 단어도 완전히 알 수 없는 단어 하나로 처리하지 않고, 이미 알고 있는 작은 조각의 조합으로 표현할 수 있습니다.
토큰화는 단순히 공백으로 나누는 작업이 아니다
실제 토크나이저는 여러 단계를 거치는 파이프라인에 가깝습니다. Hugging Face Tokenizers 문서는 대표적인 흐름을 정규화, 사전 분리, 서브워드 모델, 후처리 단계로 설명합니다.
원문
→ Normalizer
→ Pre-tokenizer
→ Subword model
→ Post-processor
→ Token ID 배열
정규화
대문자와 소문자를 통일하거나, 유니코드 표현을 정리하거나, 특정 문자를 표준 형태로 바꾸는 단계입니다. 모든 모델이 같은 정규화를 사용하는 것은 아닙니다. 대소문자를 보존하는 모델도 있고, 학습 목적에 따라 정규화 규칙이 달라질 수 있습니다.
사전 분리
공백이나 문장부호 등을 기준으로 텍스트를 일차적으로 나눕니다. 영어처럼 공백으로 단어 경계가 비교적 잘 드러나는 언어와 한국어처럼 조사와 어미가 결합하는 언어는 토큰 분포가 다르게 나타날 수 있습니다.
서브워드 분할
BPE, WordPiece, Unigram, SentencePiece 같은 방식으로 문자열을 모델의 어휘 사전에 존재하는 조각으로 나눕니다. 알고리즘의 세부 목표는 다르지만, 공통적인 목적은 제한된 어휘 사전으로 다양한 문자열을 효율적으로 표현하는 것입니다.
특수 토큰 추가
모델에 따라 문장의 시작과 끝, 화자의 역할, 구분자, 패딩 등을 나타내는 특수 토큰이 추가됩니다. 채팅 API에서 개발자가 보는 메시지는 단순한 system, user, assistant 구조지만, 모델에 전달될 때는 역할과 경계를 표현하는 내부 토큰과 템플릿이 더해질 수 있습니다.
따라서 화면에 보이는 글자 수만 세어서는 실제 입력 토큰 수를 정확히 알기 어렵습니다.
한국어의 토큰 수는 영어 기준으로 추정하면 틀릴 수 있다
“영어 한 토큰은 대략 몇 글자” 같은 경험칙이 자주 소개되지만, 이를 한국어에 그대로 적용하면 오차가 커질 수 있습니다.
한국어는 어근에 조사와 어미가 결합하고, 한글 음절을 처리하는 방식도 토크나이저마다 다릅니다. 같은 의미의 문장이라도 언어와 표기 방식에 따라 토큰 수가 크게 달라질 수 있습니다. 코드, URL, 긴 숫자, 이모지, 생소한 제품명 역시 예상보다 많은 토큰을 사용할 수 있습니다.
사람이 보는 길이
≠ 단어 수
≠ 글자 수
≠ 모델이 계산하는 토큰 수
비용이나 컨텍스트 사용량이 중요한 시스템이라면 추정치만 사용하지 말고, 실제 배포 모델과 호환되는 토크나이저로 측정해야 합니다. 모델이 변경되면 토크나이저와 채팅 템플릿도 함께 달라질 수 있으므로 회귀 테스트가 필요합니다.
토큰은 숫자와 벡터로 바뀐다
토큰화가 끝나면 각 토큰은 어휘 사전에 정의된 정수 ID로 변환됩니다.
["생성", "형", " AI"]
→ [18452, 317, 9552]
이 숫자는 단어의 의미를 직접 나타내는 값이 아닙니다. 사전에서 토큰을 찾기 위한 식별자에 가깝습니다. 모델은 이 ID를 임베딩 테이블에서 조회해 고차원 벡터로 바꿉니다.
Token ID
→ Embedding vector
→ Transformer layer
→ 문맥을 반영한 표현
처음의 임베딩은 토큰 자체의 학습된 표현입니다. 이후 Transformer 레이어를 통과하면서 같은 토큰도 주변 문맥에 따라 다른 내부 표현을 갖게 됩니다.
예를 들어 “은행”이라는 문자열은 금융기관을 뜻할 수도 있고 강둑을 뜻할 수도 있습니다. 모델은 주변 토큰과의 관계를 계산하면서 현재 문장에서 어떤 의미에 가까운지 반영합니다. 이때 핵심 역할을 하는 구조가 Self-Attention입니다.
컨텍스트 윈도우는 모델이 현재 계산에 참고할 수 있는 작업 공간이다
컨텍스트 윈도우는 한 번의 응답을 생성할 때 모델이 참고할 수 있는 토큰 범위입니다. 흔히 LLM의 기억 용량처럼 설명되지만, 영구 기억보다는 현재 요청을 처리하기 위한 제한된 작업 공간이라고 보는 편이 정확합니다.
채팅형 서비스에서 컨텍스트에는 사용자가 방금 입력한 질문만 들어가는 것이 아닙니다.
시스템·개발자 지시
+ 이전 사용자 메시지
+ 이전 모델 응답
+ 현재 사용자 질문
+ RAG로 검색한 문서
+ 도구 실행 결과
+ 이미지·파일에서 변환된 입력
+ 새로 생성할 응답 토큰
= 전체 컨텍스트 사용량
개발자가 직접 작성하지 않은 내부 포맷과 특수 토큰도 일부 공간을 사용합니다. 도구 정의나 JSON 스키마가 길다면 이 역시 입력 토큰에 포함될 수 있습니다.
컨텍스트 한도가 100이라는 단순한 모델을 가정해보겠습니다.
| 구성 요소 | 사용 토큰 |
| 시스템 지시 | 10 |
| 이전 대화 | 35 |
| 검색 문서 | 25 |
| 현재 질문 | 10 |
| 응답을 위해 남겨둔 공간 | 20 |
| 합계 | 100 |
여기에서 검색 문서를 20토큰 더 추가하면 전체 한도를 넘습니다. 이때 시스템은 요청을 거절하거나, 오래된 대화를 자르거나, 검색 문서를 줄이거나, 응답 가능한 최대 길이를 줄여야 합니다.
즉, 컨텍스트 윈도우가 100토큰이라고 해서 입력에 100토큰을 모두 사용할 수 있는 것은 아닙니다. 출력이 사용할 토큰 예산도 함께 고려해야 합니다.
긴 대화에서 앞의 내용을 잊는 이유
대화가 길어지면 서비스는 모든 메시지를 무한히 모델에 전달할 수 없습니다. 컨텍스트 한도에 가까워지면 다음과 같은 전략을 사용합니다.
- 가장 오래된 메시지를 제거한다.
- 과거 대화를 요약해 더 짧게 만든다.
- 관련도가 높은 메시지만 선택한다.
- 첨부 문서나 도구 결과의 일부를 잘라낸다.
- 입력이 너무 크면 요청 자체를 실패시킨다.
예를 들어 OpenAI Responses API의 truncation 옵션은 입력이 컨텍스트 한도를 넘을 때 자동으로 앞부분의 대화 항목을 제거하는 방식과, 자동 절단을 사용하지 않고 오류를 반환하는 방식을 구분합니다. 구체적인 정책은 서비스와 API에 따라 다르지만, 중요한 원리는 같습니다.
모델이 과거 대화를 기억하는 것처럼 보여도, 실제로는 그 내용이 현재 컨텍스트에 다시 포함되어 있기 때문에 참고할 수 있는 경우가 많다.
과거 메시지가 잘려 나가거나 잘못 요약되면 모델은 그 내용을 현재 계산에서 직접 볼 수 없습니다. 사용자가 처음에 정한 출력 형식이나 중요한 제약 조건을 대화 후반에 놓치는 이유도 여기에 있습니다.
컨텍스트 윈도우 안에 있다고 모두 똑같이 잘 기억하는 것은 아니다
컨텍스트 한도가 길다는 것은 많은 토큰을 넣을 수 있다는 뜻입니다. 입력된 모든 정보를 항상 정확히 활용한다는 뜻은 아닙니다.
모델은 각 토큰이 다른 토큰과 얼마나 관련 있는지를 Attention으로 계산합니다. 하지만 긴 입력에는 서로 비슷한 정보, 무관한 정보, 충돌하는 지시, 반복된 문장이 함께 들어갈 수 있습니다. 중요한 사실이 수십만 토큰 사이에 묻히면 모델이 이를 놓치거나 덜 중요하게 취급할 수 있습니다.
긴 컨텍스트에서 발생할 수 있는 문제는 다음과 같습니다.
- 문서 중간에 있는 핵심 정보를 놓친다.
- 앞부분과 뒷부분의 지시가 충돌하면 일관성이 흔들린다.
- 비슷한 이름이나 숫자를 잘못 연결한다.
- 검색 결과가 너무 많으면 관련 없는 정보에 영향을 받는다.
- 입력 토큰 증가로 비용과 첫 응답 지연 시간이 늘어난다.
그래서 “컨텍스트가 길수록 무조건 좋다”는 말은 정확하지 않습니다. 실제 품질은 컨텍스트의 길이뿐 아니라 정보의 선택, 배치, 구조, 중복, 충돌 여부에 영향을 받습니다.
Attention은 문맥을 연결하지만 영구 저장소는 아니다
Transformer의 Self-Attention은 현재 컨텍스트에 있는 토큰 사이의 관계를 계산합니다. 직관적으로는 각 토큰이 다른 토큰을 얼마나 참고해야 하는지 가중치를 만드는 과정으로 이해할 수 있습니다.
Query: 지금 처리하는 토큰이 찾으려는 정보
Key: 각 토큰이 가진 검색 단서
Value: 실제로 전달할 정보
현재 위치의 Query와 앞선 토큰들의 Key를 비교해 관련도를 구하고, 그 가중치로 Value를 결합합니다. 이 계산이 여러 헤드와 여러 레이어에서 반복되면서 문법 관계, 대상의 지칭, 주제, 형식, 코드 구조 같은 다양한 패턴이 반영됩니다.
다만 Attention은 데이터베이스 검색과 다릅니다. 특정 문장을 키로 정확히 조회하는 것이 아니며, 모델 내부에 대화 내용을 영구 저장하는 기능도 아닙니다. 현재 입력된 토큰을 바탕으로 필요한 표현을 계산하는 메커니즘입니다.
응답은 다음 토큰 하나씩 생성된다
컨텍스트 처리가 끝나면 모델은 다음 위치에 올 수 있는 모든 토큰의 점수를 계산하고 확률분포로 바꿉니다.
현재 문맥: "컨텍스트 윈도우는 모델의"
다음 토큰 후보
작업: 31%
기억: 24%
입력: 18%
처리: 11%
기타: 16%
모델은 이 분포에서 다음 토큰을 선택합니다. 가장 점수가 높은 토큰을 항상 선택할 수도 있고, Temperature나 Top-p 같은 샘플링 설정에 따라 여러 후보 중 하나를 선택할 수도 있습니다.
선택된 토큰은 즉시 컨텍스트 끝에 붙습니다.
1단계: "컨텍스트 윈도우는 모델의"
예측: "작업"
2단계: "컨텍스트 윈도우는 모델의 작업"
예측: "공간"
3단계: "컨텍스트 윈도우는 모델의 작업 공간"
예측: "입니다"
이 과정은 종료 토큰이 선택되거나, 최대 출력 길이에 도달하거나, 중단 조건이 충족될 때까지 반복됩니다.
LLM이 긴 답을 한 번에 완성해 놓고 화면에 보여주는 것이 아니라, 앞에서 생성한 내용을 다시 문맥으로 사용하면서 토큰을 하나씩 이어간다는 뜻입니다. 그래서 초반에 잘못된 전제를 선택하면 뒤의 문장도 그 전제를 자연스럽게 이어가며 더 정교한 오류를 만들 수 있습니다.
같은 질문에 다른 답이 나오는 이유
다음 토큰 후보의 확률이 하나만 100%인 경우는 드뭅니다. 여러 표현이 문법적으로나 의미적으로 자연스러울 수 있습니다. 샘플링이 활성화되어 있다면 같은 질문에도 다른 토큰이 선택될 수 있고, 그 차이가 이후 문장 전체를 바꿉니다.
출력은 다음 요소에도 영향을 받습니다.
- 시스템과 개발자 지시
- 이전 대화 내용과 순서
- 검색된 외부 문서
- 도구 실행 결과
- 모델 버전과 토크나이저
- Temperature와 Top-p
- 최대 출력 길이
- 안전 정책과 출력 후처리
따라서 LLM의 응답은 정해진 답을 조회한 결과라기보다, 주어진 컨텍스트와 생성 설정에서 만들어진 하나의 경로입니다.
토큰 예측과 사실 확인은 다른 문제다
모델은 다음 토큰의 자연스러움을 예측합니다. 이 과정 자체가 사실을 데이터베이스에서 검증한다는 의미는 아닙니다.
학습 과정에서 많은 사실과 패턴을 익혔기 때문에 정확한 답을 자주 만들 수 있지만, 확실한 근거가 없는 상황에서도 그럴듯한 문장을 이어갈 수 있습니다. 이것이 환각이 발생하는 기본 배경 중 하나입니다.
언어적으로 자연스러운 다음 토큰
≠ 외부 세계에서 검증된 사실
정확성이 중요한 업무에서는 공식 자료 검색, RAG, 계산 도구, 코드 실행, 데이터베이스 조회 같은 외부 검증 수단을 함께 사용해야 합니다. 그리고 외부 자료를 컨텍스트에 넣었다고 해서 자동으로 정답이 보장되는 것도 아닙니다. 관련 문서를 정확히 검색하고, 충분한 부분을 넣고, 출처가 다른 주장들을 구분해야 합니다.
RAG는 컨텍스트를 더 길게 만드는 기술이 아니라 더 잘 고르는 기술이다
RAG는 질문과 관련된 문서를 검색해 그 내용을 모델의 컨텍스트에 추가하는 구조입니다.
사용자 질문
→ 관련 문서 검색
→ 필요한 구간 선택
→ 질문과 함께 컨텍스트 구성
→ LLM 응답 생성
좋은 RAG의 핵심은 문서를 많이 넣는 것이 아닙니다. 모델이 답에 필요한 근거를 찾을 수 있도록 관련성 높은 정보를 제한된 컨텍스트 안에 배치하는 것입니다.
검색 결과를 무조건 많이 넣으면 다음 문제가 생길 수 있습니다.
- 중요한 문서가 관련 없는 문서 사이에 묻힌다.
- 서로 충돌하는 버전의 문서가 함께 들어간다.
- 토큰 비용과 지연 시간이 증가한다.
- 악성 문서의 간접 프롬프트 인젝션에 노출될 수 있다.
따라서 문서 분할 크기, 검색 개수, 재정렬, 중복 제거, 출처 표시, 신뢰도 필터링이 중요합니다.
긴 컨텍스트를 잘 쓰는 실무 설계
중요한 지시는 짧고 명확하게 유지한다
핵심 요구사항을 긴 배경 설명 사이에 묻지 말고 별도의 구역으로 정리합니다.
[목표]
점검 결과를 경영진용으로 요약한다.
[반드시 지킬 조건]
- 확인되지 않은 사실을 만들지 않는다.
- 위험도와 근거를 함께 표시한다.
- 표는 5행 이내로 작성한다.
[참고 자료]
...
오래된 대화보다 현재 작업 상태를 요약한다
에이전트가 긴 작업을 수행한다면 모든 로그를 계속 누적하기보다 현재 목표, 완료된 단계, 실패 원인, 남은 작업을 구조화해 유지하는 편이 좋습니다.
문서는 필요한 부분만 검색해 넣는다
수백 페이지 전체를 무조건 넣기보다 질문과 관련된 구간을 검색하고, 검색 결과의 출처와 문서 버전을 함께 제공합니다.
입력과 출력 예산을 분리해 관리한다
입력을 한도까지 채우면 충분한 응답 공간이 남지 않을 수 있습니다. 예상 출력 길이와 도구 호출 결과까지 포함해 여유를 둬야 합니다.
실제 토큰 사용량을 관측한다
API 응답의 usage 정보나 호환되는 토크나이저를 이용해 입력·출력 토큰을 기록합니다. 평균뿐 아니라 긴 요청의 상위 백분위도 확인해야 갑작스러운 비용 증가나 한도 초과를 막을 수 있습니다.
import tiktoken
encoding = tiktoken.get_encoding("o200k_base")
text = "LLM은 문장을 토큰으로 나누어 처리합니다."
token_ids = encoding.encode(text)
print(token_ids)
print("token count:", len(token_ids))
이 예시는 토큰화 원리를 확인하기 위한 간단한 코드입니다. 실제 API 모델의 토큰 계산에는 모델별 인코딩, 메시지 포맷, 특수 토큰, 도구 정의가 추가될 수 있으므로 공식 SDK와 모델 문서를 함께 확인해야 합니다.
자주 생기는 오해
| 오해 | 실제에 가까운 설명 |
| 토큰은 단어와 같다 | 단어, 서브워드, 공백, 문장부호, 바이트 조각 등이 토큰이 될 수 있다. |
| 컨텍스트 윈도우는 모델의 영구 기억이다 | 현재 응답 계산에 사용할 수 있는 제한된 작업 공간에 가깝다. |
| 한도 안에 넣으면 모든 내용을 정확히 찾는다 | 긴 입력에서는 중요한 정보가 묻히거나 충돌할 수 있다. |
| 출력은 한 번에 완성된다 | 다음 토큰을 하나씩 예측하고 다시 문맥에 추가한다. |
| 다음 토큰 예측은 단순 자동완성이다 | 대규모 학습과 여러 Transformer 레이어를 통해 복잡한 문맥 표현을 계산하는 자동회귀 생성이다. |
| 자연스러운 문장은 사실이다 | 자연스러움과 사실 검증은 별개의 문제다. |
| 컨텍스트가 길면 RAG가 필요 없다 | 최신성, 비용, 접근 제어, 출처 추적을 위해 검색과 선택 과정은 여전히 중요하다. |
전체 과정을 다시 연결해보면
LLM의 응답 생성은 다음과 같이 정리할 수 있습니다.
- 사용자의 텍스트와 시스템 지시, 이전 대화, 검색 문서가 하나의 입력으로 구성됩니다.
- 토크나이저가 입력을 서브워드 중심의 토큰으로 나눕니다.
- 각 토큰은 정수 ID와 임베딩 벡터로 변환됩니다.
- Transformer가 컨텍스트 윈도우 안의 토큰 관계를 Attention으로 계산합니다.
- 마지막 위치 다음에 올 토큰의 확률분포를 만듭니다.
- 생성 설정에 따라 토큰 하나를 선택합니다.
- 선택된 토큰을 컨텍스트에 추가하고 같은 계산을 반복합니다.
- 종료 토큰이나 출력 한도에 도달하면 토큰을 다시 문자열로 디코딩해 응답으로 보여줍니다.
텍스트
→ 토큰
→ 토큰 ID
→ 임베딩
→ Attention과 Transformer
→ 다음 토큰 확률
→ 토큰 선택
→ 반복 생성
→ 텍스트 응답
결론
LLM을 이해하는 가장 좋은 출발점은 “모델이 문장을 이해하고 답을 꺼낸다”는 인상을 잠시 내려놓는 것입니다.
모델은 텍스트를 토큰으로 바꾸고, 현재 컨텍스트 윈도우 안에 들어온 토큰의 관계를 계산한 뒤, 다음 토큰을 하나씩 생성합니다. 컨텍스트 윈도우는 이 계산을 위한 제한된 작업 공간이며, 긴 문맥을 넣을 수 있다는 사실과 그 내용을 정확히 활용한다는 사실은 서로 다릅니다.
이 원리를 알면 프롬프트 작성도 달라집니다. 중요한 지시를 명확히 배치하고, 불필요한 문맥을 줄이고, 긴 대화를 요약하고, RAG로 필요한 근거만 선택하며, 입력과 출력의 토큰 예산을 함께 관리하게 됩니다.
LLM의 품질은 모델 크기만으로 결정되지 않습니다. 어떤 정보를 토큰으로 만들어 컨텍스트에 넣고, 그 제한된 공간을 어떻게 구성하느냐가 실제 응답 품질을 크게 좌우합니다.
참고 자료
'일반IT > AI' 카테고리의 다른 글
| 내 Claude는 왜 자꾸 죽을까 — Codex가 더 안정적으로 보여도 결국 Claude로 돌아가는 이유 (0) | 2026.07.30 |
|---|---|
| LLM은 이전 토큰을 어떻게 참고할까 — Q·K·V와 KV Cache의 동작 원리 (0) | 2026.07.30 |
| 생성형 AI 보안은 완벽한 통제가 아니라 피해 제한에서 시작된다 (0) | 2026.07.28 |
| Claude Opus 5 발표 정리 — Fable 5에 가까워진 실무형 에이전트 모델 (0) | 2026.07.25 |
| Open WebUI만 있는 것은 아니다 — AI 모델을 위한 오픈소스 UI 6가지 비교 (0) | 2026.07.24 |