본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 5장: 컨텍스트 압축과 정보 밀도 최적화
2026년 3월 16일·AI / ML·

5장: 컨텍스트 압축과 정보 밀도 최적화

토큰 한계와 비용을 최적화하면서 핵심 의미를 보존하는 컨텍스트 압축 기법을 다룹니다. 코드 요약, 인터페이스 추출, 트리 구조 압축 등을 분석합니다.

15분721자9개 섹션
aiai-agentclaude-codellm
공유
context-engineering5 / 10
12345678910
이전4장: 컨텍스트 선택과 검색 전략다음6장: 컨텍스트 정렬과 포맷 최적화

이 장에서 배우는 것

  • 토큰 한계와 비용이 압축을 요구하는 이유
  • 코드 요약: 구현을 인터페이스로 축약
  • 자기 요약(Self-summarization) 기법
  • 트리 구조 압축: 디렉토리 구조의 효율적 표현
  • 청크 전략과 정보 밀도 메트릭
  • 압축 후 품질 검증 방법

왜 압축이 필요한가

1M 토큰 컨텍스트 윈도우 시대에도 압축은 여전히 중요합니다. 그 이유는 세 가지입니다.

비용: 입력 토큰에 비례하여 비용이 발생합니다. 매 요청마다 수만 토큰의 불필요한 코드가 포함되면, 누적 비용은 상당해집니다.

주의력 희석: 연구에 따르면, 컨텍스트 길이가 증가할수록 모델이 특정 정보에 주의를 기울이는 능력이 저하됩니다. 이른바 "건초 더미에서 바늘 찾기(Needle in a Haystack)" 문제입니다. 꼭 필요한 정보만 간결하게 제공하는 것이 모델의 정확도를 높입니다.

속도: 입력 토큰이 많을수록 처리 시간이 길어집니다. 사용자 체감 응답 속도에 직접적인 영향을 미칩니다.

Info

컨텍스트 윈도우가 100만 토큰이라고 해서 100만 토큰을 채워야 하는 것이 아닙니다. 이는 최대 용량이지, 최적 용량이 아닙니다. 작업에 필요한 최소한의 정보를 최대한 효율적으로 전달하는 것이 목표입니다.

코드 요약: 구현을 인터페이스로

가장 효과적인 압축 기법은 구현 세부사항을 제거하고 인터페이스만 남기는 것입니다.

전체 코드 vs 인터페이스 추출

full-code.ts
typescript
// 원본: 45줄, ~600 토큰
export class UserService {
  private repository: UserRepository;
  private cache: CacheService;
  private logger: Logger;
 
  constructor(repo: UserRepository, cache: CacheService, logger: Logger) {
    this.repository = repo;
    this.cache = cache;
    this.logger = logger;
  }
 
  async findById(id: string): Promise<User | null> {
    const cached = await this.cache.get(`user:${id}`);
    if (cached) {
      this.logger.debug("Cache hit", { id });
      return JSON.parse(cached);
    }
    
    const user = await this.repository.findOne({ id });
    if (user) {
      await this.cache.set(`user:${id}`, JSON.stringify(user), { ttl: 3600 });
    }
    
    return user;
  }
 
  async create(data: CreateUserDTO): Promise<User> {
    const existing = await this.repository.findOne({ email: data.email });
    if (existing) {
      throw new ConflictError("Email already exists");
    }
    
    const hashed = await hashPassword(data.password);
    const user = await this.repository.create({ ...data, password: hashed });
    this.logger.info("User created", { userId: user.id });
    return user;
  }
 
  async updateRole(id: string, role: Role): Promise<User> {
    const user = await this.findById(id);
    if (!user) throw new NotFoundError("User not found");
    
    const updated = await this.repository.update(id, { role });
    await this.cache.delete(`user:${id}`);
    this.logger.info("Role updated", { userId: id, role });
    return updated;
  }
}
interface-only.ts
typescript
// 압축 후: 12줄, ~150 토큰 (75% 감소)
export class UserService {
  constructor(repo: UserRepository, cache: CacheService, logger: Logger);
  
  findById(id: string): Promise<User | null>;
  create(data: CreateUserDTO): Promise<User>;
  // throws ConflictError if email exists
  updateRole(id: string, role: Role): Promise<User>;
  // throws NotFoundError if user not found, invalidates cache
}

인터페이스 추출은 호출자 관점에서 필요한 정보만 보존합니다. 함수의 시그니처, 반환 타입, 주요 에러 조건을 유지하면서 구현 세부사항은 제거합니다.

자동 인터페이스 추출

extract-interface.ts
typescript
import ts from "typescript";
 
function extractInterface(sourceCode: string): string {
  const sourceFile = ts.createSourceFile(
    "temp.ts",
    sourceCode,
    ts.ScriptTarget.Latest,
    true
  );
  
  const signatures: string[] = [];
  
  function visit(node: ts.Node) {
    if (ts.isFunctionDeclaration(node) && node.name) {
      const params = node.parameters
        .map(p => `${p.name.getText()}: ${p.type?.getText() ?? "unknown"}`)
        .join(", ");
      const returnType = node.type?.getText() ?? "void";
      signatures.push(`function ${node.name.getText()}(${params}): ${returnType};`);
    }
    
    if (ts.isClassDeclaration(node) && node.name) {
      const methods = node.members
        .filter(ts.isMethodDeclaration)
        .map(m => {
          const params = m.parameters
            .map(p => `${p.name.getText()}: ${p.type?.getText() ?? "unknown"}`)
            .join(", ");
          const returnType = m.type?.getText() ?? "void";
          const modifier = m.modifiers?.some(
            mod => mod.kind === ts.SyntaxKind.AsyncKeyword
          ) ? "async " : "";
          return `  ${modifier}${m.name.getText()}(${params}): ${returnType};`;
        });
      
      signatures.push(
        `class ${node.name.getText()} {\n${methods.join("\n")}\n}`
      );
    }
    
    ts.forEachChild(node, visit);
  }
  
  visit(sourceFile);
  return signatures.join("\n\n");
}

자기 요약 기법

자기 요약(Self-summarization)은 LLM 자체를 활용하여 컨텍스트를 요약하는 기법입니다. Cursor가 이 접근 방식을 적극적으로 사용합니다.

동작 방식

긴 대화가 진행되면서 이전 맥락이 컨텍스트 윈도우를 채우기 시작할 때, 이전 대화를 LLM에게 요약시킨 후 요약본만 유지합니다.

self-summarization.ts
typescript
async function summarizeConversation(
  messages: Message[],
  maxTokens: number
): Promise<Message[]> {
  const totalTokens = countTokens(messages);
  
  if (totalTokens <= maxTokens) {
    return messages; // 압축 불필요
  }
  
  // 오래된 메시지를 요약 대상으로 선택
  const recentCount = 5;
  const toSummarize = messages.slice(0, -recentCount);
  const recentMessages = messages.slice(-recentCount);
  
  // LLM에게 요약 요청
  const summary = await llm.complete({
    system: "이전 대화의 핵심 결정사항과 컨텍스트를 간결하게 요약하세요.",
    messages: [
      {
        role: "user",
        content: formatMessagesForSummary(toSummarize),
      },
    ],
  });
  
  return [
    { role: "system", content: `[이전 대화 요약]\n${summary}` },
    ...recentMessages,
  ];
}
Warning

자기 요약은 정보 손실이 불가피합니다. 요약 과정에서 모델이 중요하지 않다고 판단한 정보가 실제로는 이후 작업에 필요할 수 있습니다. 핵심 결정사항, 파일 경로, 에러 메시지 등 구체적 정보는 요약 시 반드시 보존하도록 지시해야 합니다.

트리 구조 압축

프로젝트의 디렉토리 구조를 효율적으로 표현하는 기법입니다.

단계별 압축

level-0: 전체 트리 (높은 토큰, 높은 정보)
text
src/
  app/
    layout.tsx
    page.tsx
    globals.css
    auth/
      login/
        page.tsx
        loading.tsx
      register/
        page.tsx
      layout.tsx
    dashboard/
      page.tsx
      settings/
        page.tsx
        profile/
          page.tsx
  components/
    ui/
      Button.tsx
      Input.tsx
      Modal.tsx
      Card.tsx
    layout/
      Header.tsx
      Footer.tsx
      Sidebar.tsx
    auth/
      LoginForm.tsx
      RegisterForm.tsx
  lib/
    utils.ts
    auth.ts
    db.ts
    validation.ts
level-1: 요약 트리 (중간 토큰, 중간 정보)
text
src/
  app/           # App Router pages (auth, dashboard)
  components/    # UI (Button, Input, Modal...), layout, auth forms
  lib/           # utils, auth, db, validation
level-2: 최소 트리 (낮은 토큰, 낮은 정보)
text
src/ - app(pages), components(ui+layout), lib(utilities)

상황에 따라 적절한 압축 수준을 선택합니다. 전체 구조를 파악해야 할 때는 level-0을, 빠른 오리엔테이션에는 level-1을, 최소한의 맥락에는 level-2를 사용합니다.

청크 전략

대규모 파일을 처리할 때, 전체를 한 번에 포함하는 대신 관련 부분만 선택적으로 포함하는 전략입니다.

smart-chunking.ts
typescript
interface ChunkStrategy {
  // 관련 함수만 추출
  extractRelevantFunctions(
    file: string,
    query: string
  ): Promise<CodeChunk[]>;
  
  // 주변 컨텍스트와 함께 추출
  extractWithContext(
    file: string,
    targetLine: number,
    contextLines: number
  ): Promise<CodeChunk>;
  
  // 타입 정의만 추출
  extractTypes(file: string): Promise<TypeDefinition[]>;
}
 
// 사용 예시
async function buildCompressedContext(task: string): Promise<string> {
  const relevantFiles = await selectFiles(task);
  const chunks: string[] = [];
  
  for (const file of relevantFiles) {
    if (isLargeFile(file)) {
      // 큰 파일: 관련 부분만 추출
      const relevantChunks = await extractRelevantFunctions(file, task);
      chunks.push(formatChunks(file, relevantChunks));
    } else {
      // 작은 파일: 전체 포함
      chunks.push(await readFile(file));
    }
  }
  
  return chunks.join("\n\n");
}

정보 밀도 메트릭

압축의 효과를 측정하기 위한 메트릭입니다.

토큰 효율성

metrics.ts
typescript
interface CompressionMetrics {
  // 원본 대비 압축 비율
  compressionRatio: number; // 압축 후 토큰 / 원본 토큰
  
  // 정보 보존율 (작업 성공률로 간접 측정)
  informationRetention: number; // 0.0 ~ 1.0
  
  // 토큰당 정보 밀도
  informationDensity: number; // 유용한 정보량 / 토큰 수
  
  // 비용 절감률
  costReduction: number; // (원본 비용 - 압축 후 비용) / 원본 비용
}
 
function calculateMetrics(
  original: string,
  compressed: string,
  taskSuccessRate: number
): CompressionMetrics {
  const originalTokens = countTokens(original);
  const compressedTokens = countTokens(compressed);
  
  return {
    compressionRatio: compressedTokens / originalTokens,
    informationRetention: taskSuccessRate,
    informationDensity: taskSuccessRate / compressedTokens,
    costReduction: 1 - (compressedTokens / originalTokens),
  };
}

압축 수준별 비교

압축 수준토큰 감소작업 성공률 영향적합한 경우
전체 코드0%기준선핵심 파일, 작은 파일
인터페이스 추출60~80%-2~5%참조용 파일, 의존성
함수 시그니처만70~90%-5~15%탐색 단계
자기 요약80~95%-10~30%이전 대화 히스토리
트리 구조 (level-1)85~95%구조 파악용프로젝트 오리엔테이션
Tip

압축은 "전부 또는 전무"가 아닙니다. 핵심 파일(현재 작업 대상)은 전체 코드를 포함하고, 참조용 파일(의존성, 타입 정의)은 인터페이스만, 배경 정보(프로젝트 구조)는 트리 요약을 사용하는 혼합 압축 전략이 가장 효과적입니다.

압축 후 품질 검증

압축이 너무 공격적이면 필수 정보가 손실됩니다. 이를 검증하는 방법입니다.

quality-validation.ts
typescript
async function validateCompression(
  original: Context,
  compressed: Context,
  testTasks: Task[]
): Promise<ValidationResult> {
  const results = {
    original: { successes: 0, failures: 0 },
    compressed: { successes: 0, failures: 0 },
  };
  
  for (const task of testTasks) {
    // 원본 컨텍스트로 작업 수행
    const originalResult = await executeTask(task, original);
    if (originalResult.success) results.original.successes++;
    else results.original.failures++;
    
    // 압축된 컨텍스트로 동일 작업 수행
    const compressedResult = await executeTask(task, compressed);
    if (compressedResult.success) results.compressed.successes++;
    else results.compressed.failures++;
  }
  
  const successRateDrop =
    (results.original.successes - results.compressed.successes) /
    testTasks.length;
  
  return {
    originalSuccessRate: results.original.successes / testTasks.length,
    compressedSuccessRate: results.compressed.successes / testTasks.length,
    successRateDrop,
    acceptable: successRateDrop < 0.05, // 5% 이내 허용
  };
}

요약

컨텍스트 압축은 비용, 주의력 희석, 속도라는 세 가지 이유로 필수적입니다. 핵심 기법은 인터페이스 추출(구현 제거, 시그니처 보존), 자기 요약(LLM을 활용한 대화 히스토리 축약), 트리 구조 압축(디렉토리 구조의 단계별 요약)입니다.

가장 효과적인 접근은 혼합 압축 전략입니다: 핵심 파일은 전체 코드, 참조 파일은 인터페이스, 배경 정보는 트리 요약으로 구성합니다. 압축 후에는 작업 성공률을 기준으로 품질을 검증하여, 5% 이내의 성능 저하만 허용하는 것이 권장됩니다.

다음 장에서는 선택하고 압축한 정보를 어떤 순서와 형식으로 배치할 것인가를 다루는 컨텍스트 정렬과 포맷 최적화를 살펴봅니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#ai#ai-agent#claude-code#llm

관련 글

AI / ML

6장: 컨텍스트 정렬과 포맷 최적화

어텐션 메커니즘과 위치 편향을 이해하고, 정보 배치 전략과 XML/마크다운/JSON 포맷 비교를 통해 컨텍스트 구조를 최적화합니다.

2026년 3월 18일·15분
AI / ML

4장: 컨텍스트 선택과 검색 전략

코드베이스에서 관련 파일을 정밀하게 선택하는 기법을 다룹니다. @-멘션 시스템, RAG 기반 코드 검색, 의존성 그래프 추적, 변경 영향 분석을 분석합니다.

2026년 3월 14일·16분
AI / ML

7장: 컨텍스트 격리와 멀티에이전트 설계

멀티에이전트 시스템에서 에이전트별 컨텍스트를 격리하고, 크로스 오염을 방지하며, 공유 컨텍스트를 효과적으로 관리하는 전략을 다룹니다.

2026년 3월 20일·18분
이전 글4장: 컨텍스트 선택과 검색 전략
다음 글6장: 컨텍스트 정렬과 포맷 최적화

댓글

목차

약 15분 남음
  • 이 장에서 배우는 것
  • 왜 압축이 필요한가
  • 코드 요약: 구현을 인터페이스로
    • 전체 코드 vs 인터페이스 추출
    • 자동 인터페이스 추출
  • 자기 요약 기법
    • 동작 방식
  • 트리 구조 압축
    • 단계별 압축
  • 청크 전략
  • 정보 밀도 메트릭
    • 토큰 효율성
    • 압축 수준별 비교
  • 압축 후 품질 검증
  • 요약