토큰과 토크나이저 - LLM이 글자를 읽는 방식
긴 글, 핵심만 먼저 — 이 글을 3줄로 정리해드려요.
LLM API를 써보면 "토큰"이라는 단위를 자주 만납니다. 요금도 토큰으로 매기고, 컨텍스트 한도도 토큰으로 표시됩니다. 그런데 토큰은 우리가 생각하는 "단어"나 "글자"와 정확히 일치하지 않습니다. LLM은 사람처럼 글자를 읽는 게 아니라, 자기만의 방식으로 텍스트를 조각내서 읽기 때문입니다.
이 글에서는 토큰이 무엇이고, 토크나이저가 어떻게 동작하며, 이게 비용·성능·특히 한국어에 어떤 영향을 주는지 정리합니다.
1. 토큰이란 무엇인가
토큰(token) 은 LLM이 텍스트를 처리하는 최소 단위입니다. 사람은 문장을 단어로 나눠 읽지만, LLM은 서브워드(subword), 즉 단어보다 잘거나 큰 조각으로 나눠 읽습니다.
영어 예시를 보면 감이 옵니다.
"unbelievable" → ["un", "believ", "able"] (3 토큰)
"cat" → ["cat"] (1 토큰)
"tokenization" → ["token", "ization"] (2 토큰)
- 자주 쓰이는 단어(
cat)는 통째로 1토큰 - 드물거나 긴 단어는 여러 조각으로 분해
즉 "자주 등장하는 덩어리는 크게, 드문 것은 잘게" 나누는 게 핵심 원리입니다. 대략적인 감각으로 영어는 1토큰 ≈ 4글자 ≈ 0.75단어 정도입니다.
2. 왜 단어도, 글자도 아닌 서브워드인가
토큰 단위를 정하는 데는 트레이드오프가 있습니다.
| 단위 | 장점 | 단점 |
|---|---|---|
| 글자(character) | 어휘가 아주 작음, 모르는 단어 없음 | 시퀀스가 너무 길어짐, 의미 단위 약함 |
| 단어(word) | 의미 단위가 명확 | 어휘 폭발, 신조어·오타 처리 불가 |
| 서브워드(subword) | 어휘 크기와 표현력의 균형 | (그 중간이라 대부분 채택) |
단어 단위로 하면 "어휘 사전"이 수백만 개로 불어나고, 처음 보는 신조어("킹받다")를 아예 처리 못 합니다. 글자 단위로 하면 어휘는 작아지지만 문장 하나가 수백 토큰이 되어 비효율적입니다.
서브워드는 그 사이의 절충안입니다. 알려진 조각을 조합해 어떤 단어든(심지어 오타나 신조어도) 표현할 수 있고, 어휘 크기도 5만~15만 개 수준으로 관리됩니다.
3. BPE - 서브워드를 만드는 대표 알고리즘
가장 널리 쓰이는 방식이 BPE(Byte Pair Encoding) 입니다. 아이디어는 단순합니다.
"가장 자주 붙어 나오는 글자 쌍을 계속 하나로 합쳐라."
동작을 개념적으로 보면 이렇습니다.
초기: l o w l o w e r n e w e s t
1단계: 'e s'가 가장 자주 등장 → 'es'로 병합
2단계: 'es t'가 자주 등장 → 'est'로 병합
3단계: 'l o'가 자주 등장 → 'lo'로 병합
...
반복하면 자주 쓰는 덩어리(low, est ...)가 하나의 토큰이 됨
이 병합을 수만 번 반복해 어휘 사전(vocabulary) 을 완성합니다. 실제 텍스트를 토큰화할 때는 이 사전을 보고 긴 조각부터 매칭합니다.
이렇게 만든 사전을 쓰는 게 토크나이저(tokenizer) 입니다. 토크나이저는 학습된 모델마다 다릅니다. GPT 계열은 tiktoken, 다른 모델은 SentencePiece 등 각자의 토크나이저를 씁니다. 같은 문장도 모델마다 토큰 수가 다르다는 뜻입니다.
요즘은 순수 문자가 아니라 바이트 단위 BPE를 많이 씁니다. 텍스트를 UTF-8 바이트로 본 뒤 병합하기 때문에, 어떤 언어·이모지·특수문자도 "모르는 글자" 없이 표현할 수 있습니다.
4. 한국어가 토큰을 많이 먹는 이유
실무에서 체감하는 문제입니다. 같은 의미라도 한국어가 영어보다 토큰을 훨씬 많이 씁니다.
"Hello, how are you?" → 약 6 토큰
"안녕하세요, 어떻게 지내세요?" → 약 15~20 토큰 (모델마다 다름)
왜 이럴까요?
- 학습 데이터 편중: 토크나이저는 대부분 영어 위주 코퍼스로 학습됩니다. 영어 단어는 통째로 1토큰이 되는 경우가 많지만, 한국어는 자주 등장하지 못해 큰 덩어리로 병합될 기회가 적습니다.
- 바이트 단위 분해: 한글 한 글자는 UTF-8에서 3바이트입니다. 사전에 없는 조합이면 글자 하나가 여러 토큰으로 쪼개지기도 합니다.
- 교착어 특성: 한국어는 조사·어미가 붙는 교착어라 형태가 다양합니다("먹다/먹었다/먹으니까/먹겠습니다"). 각 변형이 별도 조각으로 흩어지기 쉽습니다.
결과적으로 한국어는 영어 대비 2~3배 토큰을 쓰는 경우가 흔합니다. 이게 왜 중요한지는 다음 절에서 이어집니다. (최근 모델들은 한국어 토크나이징이 많이 개선됐지만, 여전히 영어보다 불리한 건 사실입니다.)
5. 토큰이 비용·컨텍스트·성능에 미치는 영향
토큰은 단순한 내부 개념이 아니라 돈과 성능에 직결됩니다.
5-1. 비용
LLM API는 입력 토큰 + 출력 토큰 개수로 과금합니다. 한국어가 토큰을 2배 쓴다면, 같은 작업에 요금도 약 2배 나온다는 뜻입니다. 대량 처리 서비스라면 무시 못 할 차이입니다.
5-2. 컨텍스트 한도
모델이 한 번에 볼 수 있는 창(컨텍스트 윈도우)은 토큰으로 정해집니다. 예를 들어 128K 토큰 모델이라도, 한국어 문서는 영어 문서보다 더 적은 분량밖에 못 넣습니다. RAG에서 청크를 몇 개 넣을 수 있는지에도 영향을 줍니다.
5-3. 성능·속도
출력은 토큰을 하나씩 생성하므로, 출력 토큰이 많을수록 응답이 느리고 비쌉니다. "간결하게 답하라"는 지시가 속도·비용 최적화에도 도움이 되는 이유입니다.
6. 실무 팁
토큰을 이해하면 아낄 수 있는 지점들이 보입니다.
- 토큰 수를 미리 재라. 요청 전에 토크나이저 라이브러리로 토큰 수를 계산하면, 컨텍스트 초과와 비용 폭탄을 예방할 수 있습니다.
# OpenAI 계열 예시 (tiktoken)
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
tokens = enc.encode("안녕하세요, 반갑습니다")
print(len(tokens)) # 실제 토큰 수 확인
- 불필요한 반복을 줄여라. 프롬프트에 매번 붙는 긴 시스템 지침, 중복 설명은 토큰을 잡아먹습니다. 공통 부분은 프롬프트 캐싱으로 처리하면 비용을 크게 줄일 수 있습니다.
- JSON 응답은 키를 짧게. 대량 구조화 출력에서 긴 키 이름은 매 응답마다 토큰을 씁니다.
- 한국어 요약·번역 파이프라인에서는 토큰 폭증을 감안해 청크 크기와 컨텍스트 예산을 잡으세요. "글자 수"가 아니라 "토큰 수" 기준으로 설계해야 합니다.
- 잘림(truncation) 주의. 입력이 컨텍스트를 넘으면 앞이나 뒤가 잘려나갈 수 있습니다. 중요한 지시는 잘리지 않을 위치에 배치하세요.
마무리
토큰은 LLM을 다룰 때 계속 마주치는 기본 화폐 단위입니다.
정리하면 이렇습니다.
- LLM은 글자나 단어가 아니라 서브워드 토큰으로 텍스트를 읽는다.
- BPE 로 "자주 쓰는 덩어리는 크게, 드문 것은 잘게" 사전을 만든다.
- 한국어는 여러 이유로 토큰을 많이 먹어 비용·컨텍스트에서 불리하다.
- 토큰은 곧 비용·컨텍스트 한도·속도다. 설계할 때 "글자 수"가 아니라 "토큰 수"로 생각하자.
토큰 감각이 생기면 프롬프트를 왜 다이어트해야 하는지, 왜 캐싱이 중요한지가 자연스럽게 이해됩니다.