토큰과 토크나이저: AI가 글자를 세는 방법

LLM이 텍스트를 다루는 최소 단위인 토큰의 개념과 서브워드 분할 방식을 설명합니다. 한국어가 토큰을 더 많이 쓰는 이유와 비용·컨텍스트에 미치는 영향까지 다룹니다.

이 글의 목차

LLM을 쓰다 보면 "토큰"이라는 단어를 계속 만나게 됩니다. 요금은 토큰 단위로 계산되고, 입력 한도도 토큰으로 표시되며, 응답 길이 제한도 토큰으로 지정합니다. 그런데 정작 토큰 하나가 정확히 무엇인지는 잘 설명되지 않습니다.

토큰은 글자도 단어도 아닙니다. 모델이 텍스트를 처리하기 위해 잘라 놓은 조각이고, 그 경계는 사람의 직관과 자주 어긋납니다. 특히 한국어를 쓸 때는 같은 분량의 영어보다 토큰이 훨씬 많이 나오는데, 이것이 비용과 입력 한도에 직접 영향을 줍니다.

이 글에서는 토큰이 왜 필요한지, 어떤 기준으로 나뉘는지, 그리고 실무에서 무엇을 조심해야 하는지를 정리합니다. 특정 모델의 토큰 단가나 컨텍스트 길이는 수시로 바뀌므로 다루지 않습니다. 필요한 수치는 각 제공사의 공식 문서에서 확인하시기 바랍니다.

모델은 글자를 직접 보지 않습니다

신경망은 숫자만 다룹니다. 따라서 텍스트를 모델에 넣으려면 먼저 숫자로 바꿔야 하고, 그러려면 "무엇을 하나의 단위로 볼 것인가"를 정해야 합니다. 선택지는 크게 셋입니다.

글자 단위로 나누면 단위 종류는 적지만(한글은 자모 조합까지 고려해도 수천 개 수준) 문장 하나가 아주 긴 수열이 됩니다. 처리량이 늘고, 멀리 떨어진 단어 사이의 관계를 학습하기 어려워집니다.

단어 단위로 나누면 수열은 짧아지지만 사전이 폭발적으로 커집니다. 게다가 사전에 없는 단어(신조어, 고유명사, 오타)를 만나면 처리할 방법이 없습니다.

그래서 실제로는 그 중간인 서브워드(subword) 방식을 씁니다. 자주 쓰이는 덩어리는 하나의 토큰으로 묶고, 드문 표현은 더 작은 조각으로 쪼개는 방식입니다. 이렇게 하면 사전 크기를 적당히 유지하면서도 처음 보는 단어를 조각의 조합으로 표현할 수 있습니다.

자주 붙어 다니는 조각을 합칩니다

서브워드 사전을 만드는 대표적인 방법이 BPE(Byte Pair Encoding) 계열입니다. 아이디어는 단순합니다.

  1. 처음에는 모든 텍스트를 가장 작은 단위(바이트 또는 글자)로 쪼갭니다.
  2. 전체 학습 말뭉치에서 가장 자주 인접해 나타나는 조각 쌍을 찾습니다.
  3. 그 쌍을 하나의 새 조각으로 합쳐 사전에 추가합니다.
  4. 원하는 사전 크기가 될 때까지 2~3번을 반복합니다.

결과적으로 말뭉치에 자주 등장한 표현일수록 통째로 하나의 토큰이 되고, 드문 표현일수록 여러 조각으로 쪼개집니다. 토큰 경계는 문법이 아니라 빈도가 결정한다는 점이 핵심입니다.

이 때문에 직관과 어긋나는 일이 생깁니다. 흔한 단어는 앞에 붙은 공백까지 포함해 한 토큰이 되는 경우가 많고, 같은 단어라도 문장 첫머리에 올 때와 중간에 올 때 다른 토큰이 될 수 있습니다. 대소문자가 다르면 다른 토큰이 되기도 합니다.

참고

"토큰 하나가 대략 몇 글자"라는 경험칙이 돌아다니지만 언어와 토크나이저에 따라 크게 달라집니다. 정확한 수가 필요하면 각 제공사가 제공하는 토큰 계산 도구나 SDK의 토큰 수 세기 기능을 쓰는 편이 안전합니다.

한국어는 왜 토큰을 더 많이 쓰는가

대부분의 범용 토크나이저는 영어 비중이 높은 말뭉치로 사전을 만듭니다. 영어에서 자주 등장한 덩어리가 우선적으로 사전에 자리를 차지하고, 한국어는 상대적으로 적은 자리만 배정받습니다.

그 결과 한국어 텍스트는 더 잘게 쪼개집니다. 흔한 단어는 통째로 한 토큰이 되기도 하지만, 조사와 어미가 붙어 형태가 다양해지는 한국어 특성상 같은 어간이라도 활용형마다 다르게 분할되는 경우가 많습니다. 여기에 한글이 UTF-8에서 글자당 3바이트를 쓰는 점도 바이트 기반 토크나이저에서는 불리하게 작용합니다.

실무적으로는 다음을 의미합니다.

  • 같은 내용이라도 한국어 문서가 영어보다 토큰을 더 많이 소모합니다. 번역본끼리 비교하면 차이가 분명히 드러납니다.
  • 입력 한도에 들어가는 실제 분량이 예상보다 적습니다. "충분히 들어가겠지" 하고 넣었다가 잘리는 일이 흔합니다.
  • 비용 추정을 글자 수로 하면 어긋납니다. 반드시 토큰 수로 계산해야 합니다.

팁

긴 한국어 문서를 다룰 때는 처리 전에 토큰 수를 먼저 재 보세요. 분량이 한도에 가깝다면 RAG 파이프라인처럼 필요한 부분만 검색해 넣는 구조가 통째로 넣는 것보다 저렴하고 정확한 경우가 많습니다.

코드, 공백, 이모지

일반 문장이 아닌 텍스트는 토큰 소모가 특히 커집니다.

들여쓰기는 생각보다 비쌉니다. 공백 여러 개가 하나의 토큰으로 묶이는 경우도 있지만, 깊게 중첩된 코드는 들여쓰기만으로도 적지 않은 토큰을 씁니다.

JSON과 XML은 따옴표, 중괄호, 콜론 같은 기호가 각각 토큰을 차지합니다. 같은 데이터라도 표 형태나 CSV로 정리하면 토큰이 줄어드는 경우가 많습니다.

이모지와 특수 문자는 하나가 여러 토큰이 되기도 합니다. 여러 코드포인트가 결합된 이모지는 특히 그렇습니다.

무작위 문자열(해시, ID, 인코딩된 데이터)은 사전에 있는 덩어리와 맞지 않아 거의 글자 단위로 쪼개집니다. 긴 ID 목록을 그대로 넣는 것은 토큰 낭비가 큽니다.

토큰 경계가 만드는 모델의 약점

토큰 단위 처리는 모델의 특정 약점을 직접 설명해 줍니다.

글자를 세는 작업에 약합니다. "strawberry에 r이 몇 개인가" 같은 질문이 어려운 이유는, 모델이 그 단어를 개별 글자가 아니라 몇 개의 덩어리로 보기 때문입니다. 덩어리 안에 글자가 몇 개 들어 있는지는 별도로 학습해야 하는 정보입니다.

글자 단위 조작이 불안정합니다. 뒤집기, 특정 위치의 글자 바꾸기, 끝말잇기처럼 글자 경계를 다루는 작업에서 오류가 잦습니다.

숫자 처리가 일관되지 않습니다. 숫자가 어떻게 쪼개지는지는 토크나이저마다 다르고, 자릿수 정렬이 필요한 계산에서 실수가 생기는 원인이 됩니다. 정확한 계산이 필요하면 모델에게 직접 계산시키지 말고 계산 도구를 호출하도록 설계하는 편이 안전합니다.

이런 한계는 프롬프트를 잘 써서 해결할 문제가 아니라 입력 표현 방식에서 오는 구조적 특성입니다. 작업을 다르게 설계하는 것이 현실적인 대응입니다.

입력 토큰과 출력 토큰은 다릅니다

API를 쓰면 사용량이 입력(input)과 출력(output)으로 나뉘어 표시됩니다. 둘은 성격이 다릅니다.

입력 토큰은 프롬프트 전체입니다. 시스템 프롬프트, 이전 대화 내역, 첨부한 문서가 모두 포함됩니다. 대화가 길어질수록 매 요청의 입력 토큰이 누적되어 늘어납니다. 모델이 매번 전체 입력을 다시 처리한다는 점은 LLM의 동작 원리에서 설명했습니다.

출력 토큰은 모델이 생성한 응답입니다. 한 토큰씩 순차적으로 만들어지므로 입력보다 시간이 오래 걸리고, 일반적으로 단가도 더 높게 책정됩니다.

여기서 실무적인 결론이 나옵니다. 응답을 짧게 요구하는 것이 비용과 속도 모두에 효과적입니다. "간결하게", "핵심만 세 줄로" 같은 지시는 품질을 떨어뜨리지 않으면서 출력 토큰을 줄이는 경우가 많습니다. 반대로 긴 문서를 매번 통째로 넣는 습관은 입력 토큰을 빠르게 소진시킵니다.

토큰을 줄이는 실무적인 방법

토큰을 줄이는 것은 비용뿐 아니라 품질에도 도움이 됩니다. 입력이 짧을수록 모델이 중요한 부분에 집중하기 쉽기 때문입니다.

형식을 바꿉니다. 같은 데이터라도 표현 방식에 따라 토큰 수가 달라집니다. 반복되는 키가 많은 JSON 배열은 표나 CSV 형태로 바꾸면 기호와 키 이름이 사라져 눈에 띄게 줄어듭니다.

JSON:  [{"name":"김서연","dept":"영업","year":3}, {"name":"이준호","dept":"개발","year":5}]
표:    name | dept | year
       김서연 | 영업 | 3
       이준호 | 개발 | 5

필요한 부분만 넣습니다. 문서 전체를 붙여 넣는 대신 관련 구간만 골라 넣으면 토큰이 크게 줄고 정확도도 올라가는 경우가 많습니다. 이 선별을 자동화한 구조가 검색 증강입니다.

대화 내역을 관리합니다. 긴 대화를 그대로 유지하면 매 요청마다 전체가 다시 들어갑니다. 오래된 내용을 요약본으로 대체하거나, 주제가 바뀌면 새 대화를 시작하는 것이 효과적입니다.

반복되는 앞부분을 고정합니다. 시스템 프롬프트나 참조 문서처럼 매번 같은 내용이 앞에 온다면, 제공사가 지원하는 프롬프트 캐싱 기능으로 재처리 비용을 줄일 수 있습니다. 지원 여부와 조건은 각 제공사 문서를 확인하세요.

출력 형식을 지정합니다. "JSON으로만 답하고 설명은 붙이지 마세요" 같은 지시는 불필요한 서론과 맺음말을 없애 출력 토큰을 줄입니다.

흔한 오해

"토큰은 단어와 거의 같다." 언어와 토크나이저에 따라 다릅니다. 한국어에서는 한 단어가 여러 토큰으로 쪼개지는 일이 흔합니다.

"토큰 수는 모델과 무관하게 같다." 모델 계열마다 토크나이저가 다르므로 같은 텍스트라도 토큰 수가 다릅니다. 모델을 바꾸면 비용 추정을 다시 해야 합니다.

"공백을 지우면 토큰이 줄어든다." 오히려 늘어날 수 있습니다. 많은 토크나이저가 앞 공백을 포함한 형태를 하나의 토큰으로 학습했기 때문에, 공백을 임의로 제거하면 익숙하지 않은 조합이 되어 더 잘게 쪼개질 수 있습니다. 가독성만 해치고 이득이 없는 경우가 많습니다.

정리

  • 토큰은 모델이 텍스트를 다루는 최소 단위로, 글자도 단어도 아닌 서브워드 조각입니다.
  • 토큰 경계는 문법이 아니라 학습 말뭉치에서의 빈도가 결정합니다.
  • 범용 토크나이저는 영어 중심으로 만들어져 한국어는 더 잘게 쪼개지고, 그만큼 비용과 한도에 불리합니다.
  • 코드의 들여쓰기, JSON 기호, 무작위 문자열은 토큰 소모가 큽니다.
  • 글자 세기·뒤집기·자릿수 계산에 약한 것은 토큰 단위 처리에서 오는 구조적 특성입니다.
  • 비용과 속도를 줄이려면 입력을 줄이는 것과 함께 출력 길이를 제한하는 것이 효과적입니다.

참고 자료

  • #토큰
  • #토크나이저
  • #LLM