LLM 서비스의 숨은 지배자, KV 캐쉬의 작동 원리와 VRAM 폭탄의 이유

궁금하면 500원·2026년 4월 17일

AI 미생지능

목록 보기
107/117

LLM 서비스의 숨은 지배자, KV 캐쉬의 모든 것

인공지능 기술이 급격하게 발전하면서 이제는 수많은 기업과 개인 개발자들이 대규모 언어 모델을 활용하여 서비스를 구축하거나 로컬 환경에서 직접 모델을 구동하고 있습니다.
최근 구글이 발표한 '터보퀀트'라는 기술이 업계의 큰 주목을 받으며 반도체 주가에까지 영향을 미친 일이 있었습니다.
터보퀀트의 핵심은 바로 KV 캐쉬를 압축하는 알고리즘입니다.

도대체 KV 캐쉬가 무엇이기에 전 세계 테크 기업들이 이토록 열광하고, GPU 메모리 부족의 주범으로 지목되는지 그 내부 동작 원리부터 에이전트 시스템에서의 영향을 찾아보고 정리하게되었습니다.


1. 셀프 어텐션 알고리즘의 작동 방식

KV 캐쉬를 이해하기 위해서는 LLM의 뼈대를 이루는 트랜스포머 구조의 핵심인 셀프 어텐션 알고리즘을 먼저 이해해야 합니다.

셀프 어텐션의 가장 중요한 규칙 중 하나는 "자신의 위치를 기준으로 미래의 토큰은 볼 수 없고, 오직 과거와 현재의 토큰만 참조할 수 있다"는 점입니다.
이를 인과적 언어 모델링이라고 합니다.

예를 들어 문장이 들어왔을 때 연산이 어떻게 진행되는지 단계별로 살펴보겠습니다.

토큰 연산의 흐름 예시

오늘 점심은 햄버거다라는 문장이 토큰화되어 순서대로 모델에 입력된다고 가정해 보겠습니다.

  1. 오늘 토큰 연산: 가장 먼저 입력된 토큰이므로, 자기 자신만을 참조하여 어텐션 연산을 수행합니다.

  2. 점심은 토큰 연산: 이 토큰이 계산될 때, 과거에 존재하는 오늘은 참조할 수 있지만, 미래에 나올 햄버거다는 아직 알 수 없으므로 참조할 수 없습니다.
    따라서 오늘점심은을 함께 고려하여 연산합니다.

  3. 햄버거다 토큰 연산: 앞서 나온 오늘, 점심은 그리고 현재 토큰인 햄버거다까지 모두 합쳐서 어텐션 연산을 수행합니다.

이 과정을 문자 기호(A, B, C, D)로 표현하면 다음과 같이 진행됩니다.

[단계 1] 최초 입력: A -> 모델을 거쳐 A'로 변환 완료 (A' 상태 확정)
[단계 2] 다음 입력: B -> 이미 계산된 A'와 새로운 B를 합쳐서 B'를 계산
[단계 3] 다음 입력: C -> 이미 계산된 A', B'와 새로운 C를 합쳐서 C'를 계산
[단계 4] 최종 상태: A' - B' - C' - D' 순차적 완성

여기서 가장 중요한 핵심이 등장합니다.
4번째 토큰인 D를 계산하는 시점을 보면, 이전에 계산해 둔 A'와 B'와 C'의 결과값은 절대로 변하지 않는다는 점입니다.
변하지 않는 값이라면 D를 계산할 때마다 A, B, C를 처음부터 다시 수식에 넣어 계산할 필요가 없습니다.

이것은 컴퓨터 과학에서 말하는 일종의 메모이제이션 Memoization, 이미 계산된 결과를 메모리에 저장해 두고 재사용하는 기법 입니다.
트랜스포머의 어텐션 연산 결과로 나오는 행렬 중에서 행렬곱의 대상이 되는 핵심 벡터가 바로 Key와 Value이며, 이 변하지 않는 K와 V 값을 메모리에 저장해 두는 것을 KV 캐쉬라고 부릅니다.


2. 왜 VRAM 폭탄을 유발하는가?

한 번 계산된 값을 저장해 두니 연산 속도가 빨라지는 것은 분명한 장점입니다.
하지만 현대의 고성능 대형 모델로 갈수록 이 KV 캐쉬가 요구하는 메모리 용량이 상상을 초월할 정도로 거대해집니다.

레이어에 따른 기하급수적 증가

LLM은 내부적으로 어텐션 연산을 수행하는 층을 수십 개씩 겹쳐서 쌓은 구조를 가집니다.
예를 들어 62개의 레이어로 이루어진 모델이 있다면, 입력된 토큰 하나당 단 하나의 KV 캐쉬 공간이 필요한 것이 아니라 62개의 모든 레이어 각각에 KV 캐쉬 공간이 생성됩니다.

만약 긴 컨텍스트를 사용하는 AI 에이전트 시스템이나 방대한 소스 코드를 통째로 집어넣고 코딩하는 환경에서는 이 KV 캐쉬의 크기가 수십 기가바이트에 달하게 됩니다.

KV 캐쉬 메모리 사용량 공식

일반적으로 FP16 정밀도를 사용하는 모델의 KV 캐쉬 용량은 아래와 같은 수식으로 계산됩니다.

Memory=2×레이어 수×KV 헤드 수×헤드 차원×2Bytes×컨텍스트 길이Memory = 2 \times \text{레이어 수} \times \text{KV 헤드 수} \times \text{헤드 차원} \times 2\,\text{Bytes} \times \text{컨텍스트 길이}

  • 앞에 곱해지는 22는 Key와 Value 두 개를 의미합니다.
  • 뒤에 곱해지는 2,Bytes2,\text{Bytes}는 데이터 타입을 의미합니다.

실제 모델 적용 비교: Gemma 4 31B vs Gemma 4 26B

최근 출시된 모델들을 기준으로 실제 메모리를 얼마나 차지하는지 구체적인 수치로 계산해 보겠습니다. 약 262,144 토큰의 거대한 문맥을 입력하는 상황을 가정합니다.

1) 전형적인 전체 어텐션 구조 모델의 경우

만약 레이어 60개, 헤드 수 16개, 헤드 차원 256인 구조를 가진 31B 규모의 모델이 모든 레이어에서 전역 어텐션을 수행한다고 가정하면 다음과 같은 계산이 나옵니다.

2×60×16×256×2Bytes×262,144tokens=257,698,037,760Bytes257GB2 \times 60 \times 16 \times 256 \times 2\,\text{Bytes} \times 262,144\,\text{tokens} = 257,698,037,760\,\text{Bytes} \approx 257\,\text{GB}

모델을 구동하기도 전에 캐쉬 메모리로만 257GB가 필요하므로 단 한 명의 세션도 감당할 수 없습니다.

이를 해결하기 위해 최신 모델인 Gemma 4 31B는 Sliding Window Attention, 최신 부분만 집중해서 보는 지역 어텐션 구조를 혼합하여 설계되었습니다.
전역 어텐션은 12개 층만 쓰고, 나머지 48개 층은 지역 어텐션을 적용합니다.

  • 전역 어텐션 레이어: 2×12×16×256×2×262,14451.5GB2 \times 12 \times 16 \times 256 \times 2 \times 262,144 \approx 51.5\,\text{GB}
  • 지역 어텐션 레이어: 2×48×16×256×2×10240.8GB2 \times 48 \times 16 \times 256 \times 2 \times 1024 \approx 0.8\,\text{GB}
  • 실제 필요한 총 KV 캐쉬 용량: 약 52.3 GB

모델 자체의 크기는 양자화를 잘 선택하면 약 22GB 전후의 VRAM에 올릴 수 있지만, 긴 문맥을 다 쓰려고 하면 KV 캐쉬가 52GB를 먹어버립니다.
결국 풀 컨텍스트를 활용하려면 최소 80GB 이상의 VRAM을 가진 고가의 엔터프라이즈용 GPU가 필요해집니다.
VRAM이 부족하여 캐쉬 데이터 일부를 CPU 메모리로 넘기는 '오프로딩'을 쓰게 되면 속도가 극단적으로 느려집니다.
맥 환경에서도 유니파이드 메모리가 최소 128GB 이상이어야 부드럽게 구동되는 이유가 여기에 있습니다.

2) 최적화된 Gemma 4 26B의 경우 하이브리드 구조

반면 구조가 다르게 최적화된 Gemma 4 26B 모델을 살펴보겠습니다.
총 30개의 레이어 중 전역 어텐션은 5개 레이어만 사용하고, 나머지 25개 레이어는 지역 어텐션을 사용합니다.
또한 헤드 수도 전역은 8개, 지역은 2개 등으로 축소하고 차원 배치도 다르게 설계되어 있습니다.

이 모델의 공간 계산 구조를 계산하면 다음과 같습니다.

  • 전역 및 지역 최적화 반영 결과: 약 6 GB

31B 모델과 비교했을 때 KV 캐쉬 크기가 1/9 수준으로 줄어듭니다. 모델 자체 용량에 6GB 정도만 추가되면 풀 컨텍스트 연산이 가능하므로, 일반적인 가정이나 로컬 개발 환경에서도 가볍고 사뿐하게 로딩되어 작동하는 것입니다.


3. 프리픽스 캐쉬와 인프라의 한계

KV 캐쉬는 무조건 앞서 입력된 토큰들의 순서가 완벽하게 일치하고 유지될 때만 정상적으로 재사용할 수 있습니다.
입력 값의 순서가 조금이라도 흐트러지면 기존 캐쉬는 무용지물이 됩니다.
이 특성을 활용한 핵심 기능이 바로 프리픽스 캐쉬입니다.

프리픽스 캐쉬의 작동 예시

API를 통해 대화를 나눌 때 다음과 같은 흐름으로 요청이 들어간다고 생각해 보겠습니다.

  • 첫 번째 요청: 사용자가 A - B - C - D라는 프롬프트를 보냅니다. 서버는 연산을 수행한 뒤 메모리에 A' - B' - C' - D'라는 KV 캐쉬를 생성하여 보존합니다.

  • 두 번째 요청: 사용자가 이어서 A - B - C - D - E - F라는 추가 프롬프트를 보냅니다.

서버는 앞부분의 A - B - C - D가 완벽하게 일치하는 것을 확인하고, 이미 저장된 캐쉬를 그대로 가져다 씁니다.

그리고 새로 추가된 E - F 영역만 새롭게 연산합니다.

OpenAI나 Anthropic 같은 LLM 공급업체들의 API 문서나 요금 체계를 보면 'Cached Input' 항목의 가격이 일반 입력에 비해 훨씬 저렴하게 책정되어 있는 것을 볼 수 있습니다.

그 이유는 사용자가 이전 대화 맥락을 유지해 준 덕분에 GPU가 처음부터 다시 계산할 필요 없이 기존에 만들어 둔 프리픽스 캐쉬를 그대로 재활용하여 연산 비용을 대폭 아꼈기 때문입니다.

상용 서비스에서의 공간 폭증 문제

로컬 머신에서 혼자 쓸 때는 세션이 몇 개 안 되니 버틸 수 있습니다.
예를 들어 8GB 짜리 가벼운 소형 모델을 굴리더라도 하나의 대화창당 3GB의 KV 캐쉬가 할 수 없이 할당된다고 가정하면, 대화창을 3개만 동시에 열어도 순식간에 12GB의 메모리를 소비합니다.

이 현상을 수천만 명이 동시에 접속하는 글로벌 빅테크 기업의 엔터프라이즈 인프라 관점으로 확대해 보면 문제가 심각해집니다.

  1. 한 명의 유저가 여러 개의 채팅 세션을 열면 각 세션마다 독립적인 KV 캐쉬를 메모리에 유지해야 합니다.

  2. 수많은 유저가 다중 세션을 열어 긴 대화를 나누기 시작하면 전체 시스템의 메모리 요구량이 기하급수적으로 폭증합니다.

대형 인공지능 서비스 기업들이 보유한 고성능 GPU 인프라에서 VRAM의 사용처를 추적해 보면, 놀랍게도 메모리의 약 95% 이상이 모델 자체를 올리는 데 쓰이는 게 아니라 이 수많은 사용자들의 KV 캐쉬를 유지하는 데 소모되고 있습니다.


4. KV 캐쉬 양자화와 구글 터보퀀트의 등장

메모리가 과도하게 부족하다 보니 이를 줄이기 위한 기술이 발전하기 시작했습니다.
일반적인 고성능 모델은 내부 데이터를 2Byte 실수 형태로 다룹니다.
이를 부호가 있는 정수 형태인 INT8이나 INT4 등의 낮은 정밀도로 변환하여 용량을 줄이는 기술을 양자화 라고 부릅니다.

정밀도 저하와 비손실 압축의 한계

단순히 INT8로만 줄여도 데이터 크기가 절반으로 떨어지므로 VRAM 부담이 획기적으로 줄어듭니다. 과거에는 이 방식을 적극적으로 도입했습니다.

그러나 복잡한 자율 연산을 수행하는 AI 에이전트 환경이나 정밀한 소스 코드 맥락을 짚어야 하는 개발 환경에서는 치명적인 결함이 발생합니다.

KV 캐쉬를 일반적인 방식으로 양자화하면 문맥이 길어질수록 모델이 기억을 왜곡하거나 헛소리를 하는 현상이 심해집니다.

정밀한 코딩이나 복잡한 추론에서는 아주 미세한 수치 손실도 결과물 망가뜨리기로 이어지기 때문에 무작정 해상도를 낮추는 양자화를 적용하기 어렵습니다.

그렇다면 "데이터 압축 기술을 써서 무손실로 줄였다가 쓸 때만 풀면 되지 않을까?"라는 의문이 생길 수 있습니다.

컴퓨터 과학의 일반적인 허프만 코딩 같은 비손실 압축 알고리즘을 적용하는 것은 이론적으로 가능합니다.

하지만 KV 캐쉬는 초당 수십 가가바이트씩 GPU 내부에서 극도로 빠른 속도로 읽고 쓰기가 반복되는 초고속 데이터 영역입니다.

CPU와 GPU 사이에서 데이터를 압축하고 해제하는 데 단 몇 밀리초의 연산 지연이라도 발생하면, 연산량을 줄여 속도를 높이겠다는 '캐쉬' 본연의 가치와 목적을 완전히 상실하게 됩니다.

기존의 초창기 양자화 기법들은 속도를 맞추기 위해 단순히 하위 비트를 잘라버리는 거친 방식을 택할 수밖에 없었습니다.

구글 터보퀀트의 의의

구글이 발표한 터보퀀트는 이러한 한계를 돌파하기 위해 고안된 기술입니다.

매우 빠른 속도로 인코딩과 디코딩이 병렬로 이루어지면서도, 정보의 유실을 최소화하여 모델의 추론 정확도를 거의 그대로 유지하는 고도화된 양자화 알고리즘입니다.

이 기술이 발표되었을 때 시장에서는 "앞으로 테크 기업들이 엄청난 비용을 들여 GPU 가속기 VRAM을 무한정 증설하지 않아도 되겠구나"라는 예측이 나오면서 엔비디아를 비롯한 글로벌 반도체 기업들의 주가 흐름에까지 심리적 자극을 주었던 것입니다.

물론 현실적으로 인프라 수요가 완전히 줄어들지는 않겠지만 그만큼 파급력이 큰 최적화 기술입니다.

현재 로컬 개발 환경에서도 가용한 범위 내에서 KV 캐쉬 양자화가 활발히 시도되고 있습니다.
메모리 민감도를 분석해 보면 Key 행렬은 양자화를 조금 강하게 걸어도 성능 저하가 비교적 적은 반면, Value 행렬은 정밀도 변화에 민감한 특성을 보입니다.
이에 따라 K와 V에 서로 다른 비트 를 부여하는 이종 양자화 기법 등이 널리 활용되고 있습니다.


5. AI 에이전트 환경과 프리픽스 캐쉬 관리의 중요성

코드를 자동으로 생성하는 개발 도구나 복잡한 업무를 스스로 수행하는 에이전틱 AI는 내부적으로 '턴 루프'라는 구조를 기반으로 움직입니다.

사용자가 질문을 던졌을 때 한 번에 답을 뚝딱 내놓는 것이 아니라, 내부적으로 스스로 도구를 호출하고, 그 결과값을 다시 프롬프트에 붙여서 모델에 재질의하는 과정을 수차례 반복하며 최종 답안을 도출합니다.

이 에이전트 시스템이 정상적인 속도와 비용으로 작동하기 위한 절대적인 전제 조건이 바로 프리픽스 캐쉬의 완벽한 유지입니다.

이전에 진행했던 사고의 흐름과 도구 호출 기록이 앞에 온전하게 고정되어 있고, 새로 발생한 데이터만 뒤에 착착 덧붙여져야 GPU 가속기가 지치지 않고 초고속으로 다음 생각을 이어갈 수 있습니다.

프리픽스 캐쉬가 깨지는 흔한 원인들

안타깝게도 시중에 구현된 수많은 오픈소스 에이전트 프레임워크나 로컬 UI 도구들은 개발자의 설계 실수나 구조적 한계로 인해 이 프리픽스 캐쉬를 중간에 깨뜨리는 경우가 빈번하게 발생합니다. 프리픽스 캐쉬가 깨지는 대표적인 시나리오는 다음과 같습니다.

  • 복잡한 시스템 프롬프트 변경 및 삽입: 대화 중간에 에이전트 제어를 위해 시스템 프롬프트의 중간 문구를 동적으로 수정하거나 복잡한 유효성 검증 로직을 프롬프트 사이에 끼워 넣는 경우, 앞부분의 문자열 구조가 완전히 달라지므로 기존 KV 캐쉬가 전부 파괴됩니다.

  • 중간 컨텍스트 요약: 긴 대화를 이어가다가 토큰 한도를 넘길 것 같아 중간 대화 내용을 임의로 요약하여 프롬프트 구조를 정돈하는 순간, 기존의 토큰 정렬 순서가 꼬이면서 서버에 저장되어 있던 모든 프리픽스 캐쉬가 무효화됩니다.

  • 멀티모달 데이터 입력: 이미지, 오디오, 소스 코드 파일 등의 대용량 바이너리 데이터를 대화 중간에 빈번하게 삽입하는 구조일 때 발생합니다.

이러한 바이너리 데이터는 한 번 입력될 때 엄청난 양의 컨텍스트 공간을 차지하므로, 시스템은 비용 절감을 위해 해당 턴이 끝나면 바이너리 데이터를 프롬프트에서 강제로 제외하고 텍스트만 남기려고 합니다. 이 과정에서 프롬프트의 중간 허리가 잘려 나가게 되므로 다음 턴에서 프리픽스 캐쉬가 무조건 깨지게 됩니다.

성능 및 비용에 미치는 영향

개발용 도구를 사용하다가 갑자기 상상 이상의 비용 청구를 맞이하게 되는 주된 원인은 대부분 이 프리픽스 캐쉬가 깨져서 발생합니다.

매 대화마다 수만 토큰에 달하는 컨텍스트 전체를 처음부터 끝까지 GPU가 생으로 다시 연산해야 하므로 토큰 비용과 시간이 무지막지하게 소모되는 것입니다.

로컬 환경에서 구동할 때도 마찬가지입니다.
아무리 하드웨어 사양이 낮아서 초당 10개 토큰 밖에 출력하지 못하는 느린 모델이라 할지라도, 프리픽스 캐쉬만 정상적으로 유지가 된다면 첫 로딩 이후의 후속 대화는 지연 시간 없이 매우 쾌적하게 이어갈 수 있습니다.

반면 시스템이 프리픽스 캐쉬를 관리하지 못하고 매번 깨뜨린다면, 한 마디를 던질 때마다 수 분씩 멈춰 서서 전체 문맥을 다시 읽어 들이는 끔찍한 성능 저하를 경험하게 됩니다.

따라서 고성능 에이전트 시스템을 설계하고 운영할 때는 프롬프트의 설계 구조가 프리픽스 캐쉬 메커니즘을 방해하지 않도록 정교하게 배치하는 알고리즘이 무엇보다 중요합니다.

profile
레거시를 이해하면서도 새로운 기술을 현실적으로 적용할 수 있는 백엔드 개발자가 되는 것이 목표입니다.

0개의 댓글