[AI 용어] Context Window - 입력과 출력을 합쳐 AI가 한 번에 읽고 처리할 수 있는 최대 토큰 양

김팔복 2026-10-18 08:40:10
조회 26 추천 0 댓글 0

AI 코딩 도구로 오래 일하다 보면, 아까 분명히 말했던 걸 AI가 기억 못 하는 순간이 옵니다.
이 글에서는 그 이유인 Context Window를 Claude Code 화면과 직접 만든 예제 코드(Node.js + Claude API)로 확인해 봅니다. 100만 토큰을 끝까지 채워 보고, 꽉 차면 AI 도구가 어떻게 줄이는지도 실제 실행 결과로 비교합니다.

영상: YouTube에서 보기

Context Window(컨텍스트 윈도우)
입력과 출력을 합쳐 AI가 한 번에 읽고 처리할 수 있는 최대 토큰 양. 질문·대화·파일 같은 입력뿐 아니라 AI가 쓰는 답(출력)도 이 안에 들어갑니다. (Claude 공식 문서: "including the response itself")
말 그대로 풀면 Context는 맥락, Window는 창 — 대화의 맥락을 한 번에 담아 볼 수 있는 창입니다.


1. 분명히 말했는데, 기억을 못 합니다

먼저 실제로 겪은 장면입니다. 첫 질문에서 "우리 서버 포트는 8417번이야"라고 말해 두고, 몇 가지 질문을 더 한 뒤 포트 번호를 다시 물었습니다.

02_chat_forget.jpg

AI의 답 (실제 실행 결과 원문)
앞선 대화에서는 서버 포트 번호를 말씀하신 적이 없습니다.

분명히 말했는데 "말한 적이 없다"고 합니다. 왜 이런 일이 생길까요?


2. Claude Code의 /context 화면

Claude Code에서 /context를 입력하면 지금 AI가 읽고 있는 양이 칸으로 보입니다.

03_context_1m.jpg

  • 32.3k/1m tokens (3%): 새로 연 창이라 아직 3%만 찼습니다.
  • 여기서 1m이 100만(1,000,000) 토큰, 이 칸의 끝입니다. 아래쪽 Auto-compact window: 1m tokens도 같은 숫자입니다.
  • 이 칸이 다 차면 더는 읽지 못합니다.

이렇게 AI가 한 번에 읽을 수 있는 최대 양을 Context Window라고 합니다.

04_definition.jpg

이 글에서 쓰는 Claude Sonnet 5.5와 Claude Opus 5.5는 Context Window가 100만 토큰입니다. (출처: Claude 공식 문서 「Context windows」)


3. 내 글 하나만 보내는 줄 아셨죠?

중요한 사실이 하나 있습니다. AI(API)는 대화를 기억하지 않습니다.

채팅 화면에서 질문을 하나 보내면, 방금 쓴 그 글 하나만 AI에게 가는 것처럼 보입니다.

05_json_chat.jpg

사실은 다릅니다. 실제로 보내는 요청(JSON)을 열어 보면, 처음부터 지금까지 나눈 대화가 통째로 들어 있습니다.

{
  "model": "claude-sonnet-5-5",
  "messages": [
    { "role": "user", "content": "먼저 기억해 둘 것 하나. 우리 서버 포트는 8417번이야. …" },
    { "role": "assistant", "content": "네" },
    { "role": "user", "content": "Node.js에서 파일을 읽는 방법을 예제와 함께 10줄 이내로 설명해 줘." },
    { "role": "assistant", "content": "`fs` 모듈로 파일을 읽습니다. …" },
    { "role": "user", "content": "TypeScript를 쓰면 좋은 점을 10줄 이내로 알려 줘." }
  ]
}

06_json_surprise.jpg

말을 걸 때마다 이렇게 쌓아서 보냅니다. 실제로 보낸 양(input_tokens)도 요청마다 늘어납니다.

요청 보낸 messages 실제로 보낸 양 (input_tokens)
1번째 1개 61
2번째 3개 107
3번째 5개 398
4번째 7개 885

(아래 6장 예제 코드를 실제로 실행한 결과, logs/compact_summary.json)

07_json_pile.jpg

대화가 길어질수록 Context Window가 차오르는 이유가 이것입니다.

토큰이란?

이 양은 **토큰(Token)**으로 셉니다. 토큰은 글자를 잘게 나눈 조각이고, 요금의 기준이기도 합니다.

08_tokens.jpg

실제로 글이 어디서 나뉘는지는 모델마다 다릅니다. 그림의 조각은 이해를 돕기 위한 예시입니다.


4. 직접 끝까지 채워 보기 — 100만 토큰을 넘기면?

100만 토큰이 얼마나 되는지 직접 채워 봤습니다. 대화에 파일을 붙여 넣고, 보내기 전에 토큰을 세어 주는 API(countTokens)로 크기를 잽니다. 토큰 세기는 무료입니다. (출처: Claude 공식 문서 「Token counting」 — "Token counting is free to use")

붙여 넣는 파일은 이 코드 파일 자신이고, 보기 빠르게 매번 두 배씩 늘립니다.

// fill.ts — 대화 목록을 Context Window 끝까지 채워 보기
import Anthropic from "@anthropic-ai/sdk";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const here = path.dirname(fileURLToPath(import.meta.url));
const LOGS = path.join(here, "logs");
const MODEL = "claude-sonnet-5-5";
const WINDOW = 1_000_000; // Claude Sonnet 5.5의 Context Window

const client = new Anthropic(); // ANTHROPIC_API_KEY 환경변수를 읽는다

// 붙여 넣을 파일: 이 파일 자신
const FILE = fs.readFileSync(fileURLToPath(import.meta.url), "utf8");

function bar(tokens: number) {
  const filled = Math.min(20, Math.round((tokens / WINDOW) * 20));
  return "■".repeat(filled) + "□".repeat(20 - filled);
}

// 파일 n개를 붙여 넣은 대화 목록
function conversation(n: number): Anthropic.MessageParam[] {
  const blocks: Anthropic.TextBlockParam[] = [];
  for (let i = 1; i <= n; i++) blocks.push({ type: "text", text: `[파일 ${i}]\n${FILE}` });
  blocks.push({ type: "text", text: "위 파일들을 요약해 줘" });
  return [{ role: "user", content: blocks }];
}

async function main() {
  const log: { files: number; input_tokens: number }[] = [];
  let n = 1;
  let messages = conversation(n);

  while (true) {
    // ① 보내기 전에 센다 (세기만 하는 API라 요금이 들지 않는다)
    const { input_tokens } = await client.messages.countTokens({ model: MODEL, messages });
    log.push({ files: n, input_tokens });
    const pct = ((input_tokens / WINDOW) * 100).toFixed(1).padStart(5);
    console.log(`파일 ${String(n).padStart(4)}개 → ${input_tokens.toLocaleString().padStart(9)} 토큰  ${bar(input_tokens)} ${pct}%`);
    if (input_tokens > WINDOW) break;
    n *= 2;
    messages = conversation(n);
  }

  fs.mkdirSync(LOGS, { recursive: true });
  fs.writeFileSync(path.join(LOGS, "fill_counts.json"), JSON.stringify(log, null, 2));

  // ② Context Window를 넘긴 대화를 진짜로 보낸다
  console.log(`\nContext Window(${WINDOW.toLocaleString()} 토큰)를 넘겼습니다. 이대로 보내 봅니다…`);
  try {
    const res = await client.beta.messages.create({
      model: MODEL,
      max_tokens: 1000,
      messages,
      betas: ["server-side-fallback-2026-07-01"],
      fallbacks: "default", // 안전 필터가 거절하면 서버가 다른 모델로 다시 시도
    });
    console.log(`답 받음 (stop_reason: ${res.stop_reason})`);
  } catch (e) {
    if (!(e instanceof Anthropic.APIError)) throw e;
    const reason = (e.error as { error?: { message?: string } })?.error?.message ?? e.message;
    console.log(`\n✗ 거절됨 (HTTP ${e.status}): ${reason}`);
    fs.writeFileSync(path.join(LOGS, "fill_error.json"), JSON.stringify({ status: e.status, error: e.error }, null, 2));
  }
}

main();

실행 결과

npm run fill
파일    1개 →     1,427 토큰  □□□□□□□□□□□□□□□□□□□□   0.1%
파일    2개 →     2,833 토큰  □□□□□□□□□□□□□□□□□□□□   0.3%
파일    4개 →     5,645 토큰  □□□□□□□□□□□□□□□□□□□□   0.6%
파일    8개 →    11,269 토큰  □□□□□□□□□□□□□□□□□□□□   1.1%
파일   16개 →    22,517 토큰  □□□□□□□□□□□□□□□□□□□□   2.3%
파일   32개 →    45,013 토큰  ■□□□□□□□□□□□□□□□□□□□   4.5%
파일   64개 →    90,005 토큰  ■■□□□□□□□□□□□□□□□□□□   9.0%
파일  128개 →   179,989 토큰  ■■■■□□□□□□□□□□□□□□□□  18.0%
파일  256개 →   359,957 토큰  ■■■■■■■□□□□□□□□□□□□□  36.0%
파일  512개 →   719,893 토큰  ■■■■■■■■■■■■■■□□□□□□  72.0%
파일 1024개 → 1,439,790 토큰  ■■■■■■■■■■■■■■■■■■■■ 144.0%

Context Window(1,000,000 토큰)를 넘겼습니다. 이대로 보내 봅니다…

✗ 거절됨 (HTTP 400): prompt is too long: 1439839 tokens > 1000000 maximum

10_rejected.jpg

  • 파일 하나에 약 1,400 토큰. 512개에서 72%, 1,024개에서 144%가 됐습니다. (2026-10-14 실행, Claude Sonnet 5.5, logs/fill_counts.json)
  • 최대치를 넘긴 대화를 진짜로 보내면 요청 자체가 거절됩니다: prompt is too long: 1439839 tokens > 1000000 maximum. AI가 아예 읽어 주지 않습니다.
  • 공식 문서에도 같은 내용이 있습니다. 입력만으로 Context Window를 넘으면 모든 모델에서 400 invalid_request_error("prompt is too long")가 돌아옵니다.

거절 메시지의 숫자(1,439,839)가 세기 결과(1,439,790)와 조금 다른 것은, 토큰 세기 결과가 추정치이기 때문으로 보입니다. 공식 문서에도 "The token count is an estimate"라고 적혀 있습니다.

에이전트가 일하던 중이었다면 여기서 멈춥니다. 그래서 실제 AI 도구는 꽉 차기 전에 손을 씁니다.


5. 꽉 차면? 세 가지 방법

11_three_ways.jpg

방법 하는 일 대신
방법 1. 잘라내기 오래된 대화부터 지운다 지운 내용은 완전히 잊는다
방법 2. 요약하기 앞부분을 요약 한 장으로 바꾼다 (Claude Code의 Autocompact) 요약에 남긴 것만 기억한다
방법 3. 다 쓴 것만 지우기 이미 읽고 끝난 파일 내용 같은 도구 결과만 골라 지운다 —

세 방법의 공통점은 책상(Context Window)은 그대로고, 올라가는 것을 줄인다는 것입니다.

API에도 이 기능들이 있습니다. 요약은 서버 측 compaction(베타), 도구 결과 지우기는 context editing입니다. (출처: Claude 공식 문서 「Context windows」 · 「Compaction」)


6. 잘라내기 vs 요약하기 — 직접 비교

같은 질문 6개를 두 방식으로 돌려 봤습니다. 보기 편하게 Context Window를 1,000 토큰으로 아주 작게 잡았습니다 (진짜는 100만 토큰).

  1. 먼저 기억해 둘 것 하나. 우리 서버 포트는 8417번이야.
  2. Node.js에서 파일을 읽는 방법
  3. TypeScript를 쓰면 좋은 점
  4. npm과 npx의 차이
  5. async/await가 왜 필요한지
  6. 아까 말한 우리 서버 포트 번호가 몇 번이었지?

방법 1. 잘라내기: 제일 처음 메시지부터 차례로 지운다

// ① 잘라내기: 가장 오래된 질문·답 한 쌍씩 버린다
async function cut(question: Anthropic.MessageParam) {
  let dropped = 0;
  while (history.length > 0 && (await count([...history, question])) > WINDOW / 2) {
    history = history.slice(2);
    dropped++;
  }
  console.log(`   ✂ 오래된 대화 ${dropped}턴을 버렸습니다`);
}

12_cut_json.jpg

history.slice(2), 맨 앞의 질문·답 한 쌍을 버리는 한 줄이 전부입니다. 간단하지만 처음에 한 말을 가장 먼저 잊습니다.

방법 2. 요약하기: AI에게 한 번 더 "요약해 줘"

// ② 요약하기: 지금까지의 대화를 LLM에게 요약시키고, 그 요약으로 바꿔 끼운다
async function summarize() {
  const summary = await ask([
    ...history,
    { role: "user", content: "지금까지의 대화를 이어서 대화하는 데 필요한 내용만 짧게 요약해 줘." },
  ]);
  system = `이전 대화 요약:\n${summary}`;
  history = [];
  return summary;
}

요약은 AI가 직접 합니다. 지금까지 내용을 요약해 달라고 한 번 더 물어서, 그 요약을 system에 넣고 앞 대화(messages)는 비웁니다.

13_summary_json.jpg

실제로 돌아온 요약입니다 (원문).

**요약**

- **기억할 사항**: 서버 포트는 **8417번**
- **Node.js 파일 읽기**: `fs` 모듈 사용. 동기(`readFileSync`), 콜백(`readFile`), Promise(`fs/promises`, 권장), 스트림(`createReadStream`, 대용량용)
- **TypeScript 장점**: 타입 안정성, IDE 지원, 가독성, 협업 유리, 최신 문법 지원, 점진적 도입, 풍부한 타입 생태계
- **npm vs npx**: npm은 패키지 설치·관리, npx는 설치 없이 패키지를 바로 실행 (npm 5.2 이후 기본 포함)
- **응답 형식**: 사용자가 대체로 "10줄 이내"의 간결한 설명을 요청함

포트 번호는 맨 위에 남았고, 예제 코드와 긴 설명은 한 줄씩으로 줄었습니다.

요약하는 방법도 여러 가지입니다

14_summary_ways.jpg

  • 통째로 요약: 앞 대화 전체를 요약 한 장으로 (이번 예제, Claude Code의 Autocompact)
  • 최근 대화는 그대로: 마지막 몇 턴은 글자 그대로 두고 오래된 것만 요약 ("Compaction that keeps recent turns")
  • 꼭 남길 것 미리 정하기: 요약 지시문을 직접 써서 꼭 남길 것을 정한다 ("Write your own summarization prompt")

(출처: Claude 공식 문서 「Compaction」. 서버 측 compaction에서도 요약은 Claude가 씁니다 — "a summary that Claude writes on the server")

결과

15_result.jpg

6번째 질문 "포트 번호가 몇 번이었지?"에 대한 답 (원문)
✗ 잘라내기 앞선 대화에서는 서버 포트 번호를 말씀하신 적이 없습니다. 지금까지는 npm/npx의 차이와 async/await에 대해서만 이야기했습니다.
✓ 요약하기 서버 포트 번호는 8417번입니다.

요약에 남은 것만 기억합니다. 남기지 못한 건 잊습니다. 오래 일한 AI가 앞 내용을 잊는 이유가 이것입니다.

LLM의 답은 실행할 때마다 조금씩 다릅니다. 요약에 무엇이 남을지도 그때그때 다릅니다.

실행

npm install
cp .env.example .env   # .env에 API 키를 넣는다 (.gitignore로 저장소에는 안 올라감)
npm run fill      # 100만 토큰 채우기
npm run cut       # 잘라내기
npm run compact   # 요약하기
npm run reset     # logs/ 삭제

7. Context Window가 크면 다 해결될까?

그렇지 않습니다.

  • Context Window가 클수록 한 번에 보내는 양이 늘고, 요금도, 기다리는 시간도 늘어납니다.
  • 책상 위가 어지러울수록 중요한 걸 찾기 어려운 건 AI도 마찬가지입니다. 공식 문서도 토큰이 늘어날수록 정확도와 기억이 떨어진다고 말합니다 ("As token count grows, accuracy and recall degrade", context rot).

16_context_rot.jpg

그래서 좋은 AI 도구는 책상을 크게 쓰는 것보다 깨끗하게 쓰는 걸 신경 씁니다.

17_clean.jpg

쓰는 사람이 할 수 있는 것도 있습니다. 주제가 바뀌면 새 대화로 시작하기. 책상을 비우고 시작하는 겁니다.


정리: 세 줄 요약

18_summary.jpg

  1. Context Window는 AI가 한 번에 읽을 수 있는 최대 양입니다. (Claude Sonnet 5.5는 100만 토큰)
  2. AI는 기억하지 않아서 매번 대화 전체를 보내고, 그래서 Context Window가 차오릅니다.
  3. 꽉 차면 버리거나 요약해서 줄입니다. 그래서 AI는 앞 내용을 잊습니다.

AI가 매번 대화 전체를 보내는 구조가 더 궁금하다면 「AI 용어 EP.01 Agent Loop」 편을 함께 보시면 좋습니다.

김팔복


출처

  • Claude 공식 문서 — Context windows
  • Claude 공식 문서 — Token counting
  • Claude 공식 문서 — Compaction
  • 실행 결과: 예제 코드 ep02-context-window의 logs/ (2026년 10월, Claude Sonnet 5.5)
  • 화면: Claude Code /context (2026-10-17 녹화)

댓글 0

아직 댓글이 없습니다.