🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

코드베이스 컨텍스트 주입하기 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 24. · 7 Views

코드베이스 컨텍스트 주입하기 완벽 가이드

AI에게 프로젝트 정보를 제대로 전달하는 방법을 배웁니다. 정적/동적 컨텍스트 수집부터 자동화 시스템 구축까지, 실무에서 바로 쓸 수 있는 컨텍스트 주입 기법을 소개합니다.


목차

  1. 정적_컨텍스트_프로젝트_구조와_컨벤션
  2. 동적_컨텍스트_현재_파일과_관련_코드
  3. 파일_시스템_분석으로_프로젝트_이해하기
  4. import_문_추출로_의존성_파악하기
  5. 실습_프로젝트_구조_분석기_구현
  6. 실습_컨텍스트_자동_수집_시스템

1. 정적 컨텍스트 프로젝트 구조와 컨벤션

어느 날 김개발 씨가 ChatGPT에게 코드 작성을 요청했습니다. "로그인 기능 만들어줘"라고 했더니, 프로젝트와 전혀 맞지 않는 코드가 나왔습니다.

폴더 구조도 다르고, 네이밍 규칙도 다릅니다.

정적 컨텍스트는 프로젝트가 변하지 않는 기본 정보입니다. 마치 건물의 설계도처럼, 프로젝트의 폴더 구조, 네이밍 규칙, 아키텍처 패턴 같은 것들을 말합니다.

AI에게 이 정보를 주입하면 프로젝트에 맞는 코드를 생성할 수 있습니다.

다음 코드를 살펴봅시다.

// 정적 컨텍스트 수집 예제
interface StaticContext {
  projectStructure: string[];  // 폴더 구조
  conventions: {
    naming: string;           // 네이밍 규칙
    architecture: string;     // 아키텍처 패턴
  };
  techStack: string[];        // 기술 스택
}

// 프로젝트 구조 파싱
function collectStaticContext(rootPath: string): StaticContext {
  return {
    projectStructure: ['src/components', 'src/services', 'src/utils'],
    conventions: {
      naming: 'camelCase for variables, PascalCase for components',
      architecture: 'Clean Architecture with feature-based modules'
    },
    techStack: ['React', 'TypeScript', 'Next.js']
  };
}

김개발 씨는 입사 1개월 차 주니어 개발자입니다. 요즘 AI 도구를 활용해서 생산성을 높이고 싶어 하는데, 자꾸만 원하는 결과가 나오지 않습니다.

코드는 잘 생성되는데 프로젝트 스타일과 맞지 않는 것이 문제였습니다. 선배 개발자 박시니어 씨가 다가와 화면을 살펴봅니다.

"AI에게 프로젝트 정보를 제대로 알려줬어요?" 김개발 씨는 고개를 갸우뚱합니다. 그렇다면 AI에게 프로젝트 정보를 어떻게 알려줄까요?

먼저 정적 컨텍스트라는 개념을 이해해야 합니다. 쉽게 비유하자면, 정적 컨텍스트는 마치 새로운 팀원에게 주는 온보딩 문서와 같습니다.

"우리 회사는 이런 폴더 구조를 쓰고, 이런 네이밍 규칙을 따르고, 이런 기술을 사용합니다"라고 알려주는 것이죠. 정적 컨텍스트가 없던 시절에는 어땠을까요?

개발자들은 AI에게 매번 같은 설명을 반복해야 했습니다. "우리 프로젝트는 Next.js를 쓰고, 컴포넌트는 components 폴더에 넣고..."처럼 말이죠.

시간도 오래 걸리고, 빠뜨리는 정보도 많았습니다. 더 큰 문제는 AI가 생성한 코드가 프로젝트 스타일과 맞지 않아서 수정 작업이 필요했다는 점입니다.

바로 이런 문제를 해결하기 위해 정적 컨텍스트 주입이라는 개념이 등장했습니다. 정적 컨텍스트를 사용하면 AI가 프로젝트의 구조를 정확히 이해할 수 있습니다.

또한 일관된 코딩 스타일을 유지할 수 있습니다. 무엇보다 매번 같은 설명을 반복하지 않아도 된다는 큰 이점이 있습니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 StaticContext 인터페이스를 정의했습니다.

여기에는 프로젝트의 변하지 않는 정보들이 담깁니다. projectStructure에는 폴더 구조가, conventions에는 네이밍 규칙과 아키텍처 패턴이 들어갑니다.

techStack에는 사용 중인 기술 스택을 배열로 저장합니다. collectStaticContext 함수는 루트 경로를 받아서 정적 컨텍스트를 수집합니다.

실제로는 파일 시스템을 분석하겠지만, 여기서는 예시로 하드코딩된 값을 반환합니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 쇼핑몰 프로젝트를 진행한다고 가정해봅시다. 프로젝트가 Feature-Sliced Design 아키텍처를 사용하고, 컴포넌트는 PascalCase로 명명한다는 규칙이 있다면, 이 정보를 정적 컨텍스트로 만들어 AI에게 전달합니다.

그러면 AI는 "features/cart/ui/CartItem.tsx" 같은 프로젝트 규칙에 맞는 파일을 생성합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 너무 많은 정보를 담으려 한다는 것입니다. 정적 컨텍스트는 "변하지 않는" 정보만 담아야 합니다.

현재 작업 중인 파일이나 수정 중인 코드는 정적 컨텍스트가 아닙니다. 따라서 핵심 정보만 간결하게 정리해야 합니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨는 프로젝트 루트에 있는 CLAUDE.md 파일을 열어 보여줬습니다.

"여기에 프로젝트 구조와 컨벤션이 정리되어 있어요. AI는 이 파일을 읽고 우리 프로젝트를 이해하죠." 정적 컨텍스트를 제대로 정리하면 AI와의 협업이 훨씬 수월해집니다.

여러분도 오늘 배운 내용을 바탕으로 프로젝트의 정적 컨텍스트를 문서화해 보세요.

실전 팁

💡 - README.md나 CLAUDE.md에 프로젝트 구조와 컨벤션을 명확히 작성하세요

  • 기술 스택과 주요 라이브러리 버전을 명시하면 더 정확한 코드를 얻을 수 있습니다
  • 정적 컨텍스트는 프로젝트 초기에 한 번 작성하고 큰 변화가 있을 때만 업데이트하세요

2. 동적 컨텍스트 현재 파일과 관련 코드

김개발 씨가 "UserProfile 컴포넌트에 이메일 필드 추가해줘"라고 요청했습니다. AI는 새로운 컴포넌트를 만들어버렸습니다.

기존 코드를 수정하는 게 아니라 처음부터 새로 작성한 것이죠.

동적 컨텍스트는 현재 작업 중인 파일과 관련된 코드 정보입니다. 마치 회의할 때 관련 자료를 준비해 가는 것처럼, AI에게 현재 파일의 내용과 연관된 파일들을 함께 제공하는 것입니다.

이를 통해 AI는 기존 코드를 이해하고 적절히 수정할 수 있습니다.

다음 코드를 살펴봅시다.

// 동적 컨텍스트 수집 예제
interface DynamicContext {
  currentFile: {
    path: string;
    content: string;
  };
  relatedFiles: Array<{
    path: string;
    content: string;
    reason: string;  // 왜 관련 있는지
  }>;
  recentChanges: string[];  // 최근 변경사항
}

// 현재 파일과 관련된 컨텍스트 수집
function collectDynamicContext(currentFilePath: string): DynamicContext {
  const currentFile = readFile(currentFilePath);
  const imports = extractImports(currentFile);

  return {
    currentFile: { path: currentFilePath, content: currentFile },
    relatedFiles: imports.map(imp => ({
      path: imp.path,
      content: readFile(imp.path),
      reason: `Imported in ${currentFilePath}`
    })),
    recentChanges: getRecentGitChanges(currentFilePath)
  };
}

김개발 씨는 이제 정적 컨텍스트의 중요성을 이해했습니다. 하지만 여전히 문제가 있습니다.

AI에게 기존 코드를 수정해달라고 하면 자꾸 새로운 코드를 만들어버리는 것이죠. 박시니어 씨가 웃으며 말합니다.

"정적 컨텍스트만으로는 부족해요. 동적 컨텍스트도 필요하죠." 동적 컨텍스트란 무엇일까요?

쉽게 비유하자면, 동적 컨텍스트는 마치 의사가 환자를 진료할 때 보는 차트와 같습니다. 환자의 기본 정보(정적)도 중요하지만, 현재 증상과 최근 검사 결과(동적)가 있어야 정확한 진단이 가능합니다.

코드도 마찬가지입니다. 프로젝트의 전체 구조(정적)도 중요하지만, 지금 수정하려는 파일의 현재 상태(동적)를 AI가 알아야 적절한 코드를 생성할 수 있습니다.

동적 컨텍스트가 없으면 어떤 일이 벌어질까요? AI는 현재 파일의 내용을 모르기 때문에 추측으로 코드를 작성합니다.

예를 들어 "로그인 기능 개선해줘"라고 하면, 기존 로그인 코드가 어떻게 생겼는지 모르니까 완전히 새로운 로그인 코드를 만들어버립니다. 이미 있는 함수 이름과 충돌하거나, 프로젝트의 인증 방식과 맞지 않는 코드가 나올 수 있습니다.

이런 문제를 해결하는 것이 바로 동적 컨텍스트 주입입니다. 동적 컨텍스트를 제공하면 AI는 현재 파일의 정확한 내용을 파악할 수 있습니다.

또한 관련된 다른 파일들도 함께 이해할 수 있습니다. 무엇보다 기존 코드를 존중하면서 필요한 부분만 수정하는 코드를 생성합니다.

위의 코드를 자세히 살펴보겠습니다. DynamicContext 인터페이스는 세 가지 핵심 정보를 담습니다.

currentFile은 지금 작업 중인 파일의 경로와 내용입니다. relatedFiles는 현재 파일에서 import하는 파일들의 정보인데, 왜 관련 있는지 이유(reason)도 함께 저장합니다.

recentChanges는 최근 Git 변경사항으로, AI가 코드의 맥락을 이해하는 데 도움을 줍니다. collectDynamicContext 함수는 현재 파일을 읽고, import 문을 분석해서 관련 파일들을 찾아냅니다.

그리고 각 파일의 내용을 읽어서 하나의 컨텍스트 객체로 만듭니다. 실제 프로젝트에서는 어떻게 활용할까요?

쇼핑몰의 결제 페이지를 수정한다고 가정해봅시다. PaymentPage.tsx를 수정하려면 이 파일이 import하는 PaymentService, CreditCardForm, OrderSummary 같은 파일들도 함께 AI에게 제공해야 합니다.

그래야 AI가 "아, 이 프로젝트는 PaymentService를 통해 결제를 처리하는구나"라고 이해하고, 기존 구조에 맞는 코드를 작성할 수 있습니다. 주의해야 할 점이 있습니다.

관련 파일을 너무 많이 포함하면 컨텍스트 크기가 커져서 AI의 응답 품질이 떨어질 수 있습니다. 직접 import하는 파일과, 핵심 타입 정의 파일 정도만 포함하는 것이 좋습니다.

또한 파일 내용이 너무 길면 핵심 부분만 발췌해서 제공하는 것도 방법입니다. 김개발 씨는 이제 이해가 됩니다.

"그래서 Claude Code 같은 도구들이 자동으로 현재 파일과 관련 파일을 읽어서 컨텍스트로 넣어주는 거군요!" 맞습니다. 동적 컨텍스트를 제대로 구성하면 AI는 마치 옆에서 코드를 함께 보는 동료 개발자처럼 작동합니다.

여러분도 AI에게 요청할 때 현재 파일의 내용을 함께 제공해보세요.

실전 팁

💡 - 현재 작업 중인 파일은 항상 전체 내용을 포함하세요

  • import 경로를 추적해서 직접 의존하는 파일들을 자동으로 수집하세요
  • Git diff를 활용하면 최근 변경사항을 컨텍스트에 포함할 수 있습니다

3. 파일 시스템 분석으로 프로젝트 이해하기

새 프로젝트에 투입된 김개발 씨는 막막했습니다. 수백 개의 파일 중 어디서부터 봐야 할지 모르겠습니다.

박시니어 씨는 "일단 프로젝트 구조부터 파악해봐요"라고 조언합니다.

파일 시스템 분석은 프로젝트의 폴더 구조를 자동으로 읽어서 전체 구조를 파악하는 기법입니다. 마치 건물의 평면도를 그리듯이, 어떤 폴더에 어떤 종류의 파일이 있는지를 시각화합니다.

이를 통해 프로젝트의 아키텍처를 빠르게 이해할 수 있습니다.

다음 코드를 살펴봅시다.

import * as fs from 'fs';
import * as path from 'path';

// 프로젝트 구조 분석
interface FileTree {
  name: string;
  type: 'file' | 'directory';
  path: string;
  children?: FileTree[];
}

function analyzeFileSystem(rootPath: string, ignore: string[] = ['node_modules', '.git']): FileTree {
  const stats = fs.statSync(rootPath);
  const name = path.basename(rootPath);

  if (stats.isFile()) {
    return { name, type: 'file', path: rootPath };
  }

  // 디렉토리인 경우 재귀적으로 분석
  const children = fs.readdirSync(rootPath)
    .filter(child => !ignore.includes(child))
    .map(child => analyzeFileSystem(path.join(rootPath, child), ignore));

  return { name, type: 'directory', path: rootPath, children };
}

// 구조를 텍스트로 출력
function printTree(tree: FileTree, prefix: string = ''): string {
  let result = `${prefix}${tree.name}\n`;
  if (tree.children) {
    tree.children.forEach((child, i) => {
      const isLast = i === tree.children!.length - 1;
      result += printTree(child, prefix + (isLast ? '└── ' : '├── '));
    });
  }
  return result;
}

김개발 씨는 새로운 프로젝트에 참여하게 되었습니다. 레거시 코드베이스는 규모가 크고, 폴더도 복잡하게 얽혀 있습니다.

"어디서부터 시작해야 하지?" 하는 고민에 빠졌습니다. 박시니어 씨가 터미널을 열고 명령어를 하나 실행합니다.

화면에 프로젝트의 전체 구조가 트리 형태로 펼쳐집니다. "이렇게 보면 프로젝트가 어떻게 구성되어 있는지 한눈에 들어오죠." 파일 시스템 분석이란 정확히 무엇일까요?

쉽게 비유하자면, 파일 시스템 분석은 마치 도서관의 서가 배치도를 그리는 것과 같습니다. 어느 섹션에 어떤 종류의 책이 있는지 파악하면, 원하는 책을 빠르게 찾을 수 있습니다.

프로젝트도 마찬가지로, components 폴더에는 UI 컴포넌트가, services 폴더에는 비즈니스 로직이 있다는 걸 알면 작업할 파일을 쉽게 찾을 수 있습니다. 예전에는 개발자들이 이런 작업을 어떻게 했을까요?

일일이 폴더를 열어보며 손으로 메모하거나, tree 명령어를 실행해서 결과를 복사했습니다. 프로젝트가 크면 이 작업만 해도 한참 걸렸습니다.

게다가 프로젝트 구조가 변경되면 또다시 처음부터 파악해야 했습니다. 신입 개발자 온보딩 때마다 같은 설명을 반복하는 것도 번거로웠습니다.

이런 비효율을 없애기 위해 자동 파일 시스템 분석을 도입합니다. 자동 분석을 사용하면 몇 초 만에 전체 프로젝트 구조를 파악할 수 있습니다.

또한 일관된 형식으로 구조를 표현해서 팀원들과 공유하기도 쉽습니다. 무엇보다 이 정보를 AI에게 컨텍스트로 제공하면, AI가 "어느 폴더에 파일을 만들어야 하는지" 정확히 알 수 있습니다.

코드를 단계별로 살펴보겠습니다. FileTree 인터페이스는 파일과 디렉토리를 표현하는 재귀적 구조입니다.

name은 파일명, type은 파일인지 폴더인지 구분, path는 전체 경로를 담습니다. children 배열은 하위 파일과 폴더를 포함합니다.

analyzeFileSystem 함수는 재귀적으로 작동합니다. 먼저 현재 경로가 파일인지 폴더인지 확인합니다.

파일이면 바로 반환하고, 폴더면 안의 모든 항목을 읽어서 각각에 대해 다시 analyzeFileSystem을 호출합니다. ignore 배열에 있는 폴더(node_modules, .git 등)는 건너뜁니다.

printTree 함수는 FileTree 객체를 보기 좋은 텍스트 형태로 변환합니다. ├── 와 └── 같은 기호를 써서 트리 구조를 시각적으로 표현합니다.

실무에서는 어떻게 활용할까요? 대규모 모노레포 프로젝트를 생각해봅시다.

apps, packages, tools 같은 최상위 폴더가 있고, 각각에 수십 개의 하위 프로젝트가 있습니다. 파일 시스템 분석을 실행하면 전체 구조가 한눈에 정리됩니다.

이 정보를 문서화 도구와 연동하면 자동으로 프로젝트 구조 문서가 생성됩니다. 신입 개발자는 이 문서를 보고 빠르게 프로젝트를 이해할 수 있습니다.

주의할 점도 있습니다. 너무 깊은 폴더까지 모두 분석하면 정보가 과도하게 많아집니다.

보통 3-4단계 깊이까지만 분석하는 것이 적절합니다. 또한 node_modules, build, dist 같은 생성된 폴더는 반드시 제외해야 합니다.

이런 폴더는 프로젝트 구조 이해에 도움이 되지 않고 노이즈만 추가합니다. 김개발 씨는 직접 코드를 실행해봤습니다.

"오, 우리 프로젝트가 Feature-Sliced Design을 따르고 있었네요. features 폴더 안에 각 기능별로 ui, model, api 폴더가 있군요!" 파일 시스템 분석을 통해 프로젝트 구조를 이해하면, 어디에 새 코드를 추가해야 할지 명확해집니다.

여러분도 새 프로젝트를 접할 때 가장 먼저 파일 구조 분석을 실행해보세요.

실전 팁

💡 - .gitignore에 있는 패턴을 활용해서 분석 대상을 필터링하세요

  • 분석 결과를 JSON으로 저장하면 다른 도구와 연동하기 쉽습니다
  • 프로젝트 루트에 structure.txt를 생성해서 팀원들과 공유하세요

4. import 문 추출로 의존성 파악하기

김개발 씨가 한 파일을 수정했더니 예상치 못한 곳에서 에러가 발생했습니다. "이 파일이 어디서 쓰이는지 몰랐어요"라고 말하자, 박시니어 씨는 "의존성 그래프를 그려봤어야죠"라고 답합니다.

import 문 추출은 각 파일에서 다른 파일을 import하는 부분을 분석하여 파일 간 의존 관계를 파악하는 기법입니다. 마치 지하철 노선도처럼, 어떤 파일이 어떤 파일과 연결되어 있는지를 시각화합니다.

이를 통해 코드 수정의 영향 범위를 예측할 수 있습니다.

다음 코드를 살펴봅시다.

import * as fs from 'fs';

// import 문 분석
interface ImportInfo {
  source: string;      // import하는 파일
  target: string;      // import되는 파일
  imports: string[];   // import한 항목들
  type: 'named' | 'default' | 'namespace';
}

// TypeScript/JavaScript 파일에서 import 문 추출
function extractImports(filePath: string): ImportInfo[] {
  const content = fs.readFileSync(filePath, 'utf-8');
  const importRegex = /import\s+(?:(?:(\*)\s+as\s+(\w+))|(?:\{([^}]+)\})|(\w+))\s+from\s+['"]([^'"]+)['"]/g;

  const imports: ImportInfo[] = [];
  let match;

  while ((match = importRegex.exec(content)) !== null) {
    const [, namespace, nsName, named, defaultImport, from] = match;

    imports.push({
      source: filePath,
      target: from,
      imports: named ? named.split(',').map(s => s.trim()) :
               defaultImport ? [defaultImport] :
               namespace ? [nsName] : [],
      type: namespace ? 'namespace' : defaultImport ? 'default' : 'named'
    });
  }

  return imports;
}

// 의존성 그래프 생성
function buildDependencyGraph(rootPath: string): Map<string, string[]> {
  const graph = new Map<string, string[]>();
  const files = getAllTsFiles(rootPath);

  files.forEach(file => {
    const imports = extractImports(file);
    graph.set(file, imports.map(imp => imp.target));
  });

  return graph;
}

김개발 씨는 간단한 수정이라고 생각했습니다. UserService.ts에서 함수 하나의 시그니처를 바꿨을 뿐인데, 빌드 에러가 10개나 발생했습니다.

에러 메시지를 보니 전혀 예상하지 못한 파일들이었습니다. 박시니어 씨가 화면을 보더니 말합니다.

"UserService는 핵심 파일이라 여러 곳에서 import해요. 수정하기 전에 의존성을 먼저 확인했어야죠." import 문 추출이란 무엇일까요?

쉽게 비유하자면, import 문 추출은 마치 인맥 관계도를 그리는 것과 같습니다. "A는 B를 알고, B는 C를 알고, C는 다시 A를 알고"처럼 관계를 파악하면, 한 사람의 변화가 누구에게 영향을 미칠지 예측할 수 있습니다.

코드도 마찬가지입니다. 파일 A가 파일 B를 import하고, B가 C를 import한다면, C를 수정하면 A에도 영향이 간다는 걸 알 수 있습니다.

예전에는 이런 의존성을 어떻게 파악했을까요? 개발자들은 IDE의 "Find Usages" 기능을 사용하거나, grep 명령어로 import 문을 검색했습니다.

하지만 이 방법은 한 번에 한 파일씩만 확인할 수 있었습니다. 전체 프로젝트의 의존성 구조를 파악하려면 수작업으로 그래프를 그려야 했습니다.

대규모 프로젝트에서는 사실상 불가능한 작업이었죠. 이런 문제를 해결하는 것이 자동 import 추출과 의존성 그래프입니다.

자동 추출을 사용하면 전체 프로젝트의 의존성을 한 번에 파악할 수 있습니다. 또한 순환 참조(Circular Dependency) 같은 문제도 쉽게 발견할 수 있습니다.

무엇보다 AI에게 "이 파일과 관련된 파일들"을 자동으로 제공할 수 있어서, 더 정확한 컨텍스트를 만들 수 있습니다. 코드를 자세히 분석해보겠습니다.

ImportInfo 인터페이스는 하나의 import 문 정보를 담습니다. source는 import를 하는 파일, target은 import되는 파일입니다.

imports 배열에는 구체적으로 어떤 항목을 import했는지 저장합니다. type은 import 방식을 구분합니다.

extractImports 함수는 정규식을 사용해서 import 문을 파싱합니다. JavaScript와 TypeScript의 다양한 import 문법을 모두 지원합니다.

"import { A, B } from 'module'" 같은 named import, "import Default from 'module'" 같은 default import, "import * as NS from 'module'" 같은 namespace import를 모두 인식합니다. 정규식이 복잡해 보이지만, 차근차근 보면 이해할 수 있습니다.

괄호로 묶인 부분이 캡처 그룹인데, 각각 namespace, named imports, default import를 추출합니다. from 뒤의 문자열은 import 경로입니다.

buildDependencyGraph 함수는 프로젝트의 모든 TypeScript 파일을 순회하면서 각 파일의 import를 추출합니다. 그리고 Map 구조로 의존성 그래프를 만듭니다.

키는 파일 경로, 값은 그 파일이 의존하는 파일들의 배열입니다. 실제 업무에서 어떻게 쓰일까요?

예를 들어 대규모 리팩토링을 계획한다고 합시다. auth 폴더의 구조를 전면 개편하려고 합니다.

의존성 그래프를 그려보니 auth 폴더의 파일들이 30개 이상의 다른 파일에서 import되고 있다는 걸 발견합니다. 이 정보를 바탕으로 리팩토링 영향 범위를 정확히 파악하고, 단계별 마이그레이션 계획을 세울 수 있습니다.

주의해야 할 점이 있습니다. 정규식 기반 파싱은 간단하지만 완벽하지 않습니다.

주석 안의 import 문도 잡아낼 수 있고, 문자열 안에 "import"라는 단어가 있으면 오탐할 수도 있습니다. 더 정확한 분석이 필요하다면 TypeScript Compiler API나 Babel parser 같은 도구를 사용하는 것이 좋습니다.

김개발 씨는 의존성 그래프를 그려본 후 깨달았습니다. "UserService가 이렇게 많은 곳에서 쓰이는 줄 몰랐어요.

다음부터는 수정하기 전에 의존성부터 확인해야겠어요." import 문 분석을 통해 코드베이스의 구조를 이해하면, 안전하게 리팩토링하고 효과적으로 컨텍스트를 수집할 수 있습니다. 여러분도 중요한 파일을 수정하기 전에 의존성을 먼저 확인해보세요.

실전 팁

💡 - TypeScript Compiler API를 사용하면 더 정확한 import 분석이 가능합니다

  • 의존성 그래프를 시각화하면 순환 참조를 쉽게 발견할 수 있습니다
  • relative import(../utils)는 absolute path로 변환해서 저장하세요

5. 실습 프로젝트 구조 분석기 구현

김개발 씨는 배운 내용을 실습해보기로 했습니다. "프로젝트 구조를 자동으로 분석해서 AI에게 제공하는 도구를 만들어볼까요?" 박시니어 씨가 격려합니다.

"좋은 생각이에요. 직접 만들어보면 확실히 이해가 되죠."

프로젝트 구조 분석기는 파일 시스템 탐색과 메타데이터 수집을 결합한 실용적인 도구입니다. 단순히 폴더 구조만 보여주는 게 아니라, 각 폴더의 역할과 주요 파일들을 요약해서 AI가 이해하기 쉬운 형태로 만듭니다.

다음 코드를 살펴봅시다.

import * as fs from 'fs';
import * as path from 'path';

// 프로젝트 구조 분석 결과
interface ProjectStructure {
  name: string;
  path: string;
  description: string;  // 폴더 역할 추론
  mainFiles: string[];  // 주요 파일들
  fileCount: number;
  subdirectories: ProjectStructure[];
}

// 폴더 역할 추론
function inferFolderRole(dirPath: string, files: string[]): string {
  const dirName = path.basename(dirPath).toLowerCase();

  if (dirName === 'components' || dirName === 'ui') return 'UI 컴포넌트';
  if (dirName === 'services' || dirName === 'api') return 'API 및 비즈니스 로직';
  if (dirName === 'utils' || dirName === 'helpers') return '유틸리티 함수';
  if (dirName === 'types' || dirName === 'models') return '타입 정의';
  if (dirName === 'hooks') return 'React Hooks';
  if (files.some(f => f === 'index.ts' || f === 'index.tsx')) return '모듈 진입점';

  return '기타';
}

// 프로젝트 구조 분석
function analyzeProjectStructure(rootPath: string, depth: number = 3): ProjectStructure {
  const files = fs.readdirSync(rootPath);
  const stats = fs.statSync(rootPath);

  // 주요 파일 찾기 (index, README, config 등)
  const mainFiles = files.filter(f =>
    f.match(/^(index|README|package\.json|tsconfig|.*\.config\.)/)
  );

  // 하위 디렉토리 분석 (depth 제한)
  const subdirectories = depth > 0
    ? files
        .filter(f => fs.statSync(path.join(rootPath, f)).isDirectory())
        .filter(f => !['node_modules', '.git', 'dist', 'build'].includes(f))
        .map(f => analyzeProjectStructure(path.join(rootPath, f), depth - 1))
    : [];

  return {
    name: path.basename(rootPath),
    path: rootPath,
    description: inferFolderRole(rootPath, files),
    mainFiles,
    fileCount: files.length,
    subdirectories
  };
}

// AI용 텍스트 생성
function generateContextText(structure: ProjectStructure, indent: string = ''): string {
  let text = `${indent}📁 ${structure.name} (${structure.description})\n`;
  text += `${indent}   주요 파일: ${structure.mainFiles.join(', ') || '없음'}\n`;

  structure.subdirectories.forEach(sub => {
    text += generateContextText(sub, indent + '  ');
  });

  return text;
}

김개발 씨는 노트북을 펴고 코드 에디터를 열었습니다. 지금까지 배운 개념들을 하나로 합쳐서 실용적인 도구를 만들어볼 시간입니다.

목표는 명확합니다. 프로젝트 루트 경로만 입력하면, AI에게 제공할 수 있는 깔끔한 구조 요약을 자동으로 생성하는 것입니다.

박시니어 씨가 옆에서 조언합니다. "단순히 파일 목록만 나열하지 말고, 각 폴더가 무슨 역할을 하는지도 추론해보세요.

그래야 AI가 제대로 이해하죠." 프로젝트 구조 분석기를 만들 때 핵심은 무엇일까요? 쉽게 비유하자면, 이 도구는 마치 부동산 중개인이 집을 설명하는 것과 같습니다.

"3층짜리 건물이고, 1층은 상가, 2층은 사무실, 3층은 주거 공간입니다"처럼 구조와 용도를 함께 설명해야 상대방이 이해하기 쉽습니다. 프로젝트도 마찬가지로, "components 폴더는 UI 컴포넌트를 담고 있고, 주요 파일은 Button.tsx와 Input.tsx입니다"라고 설명하면 AI가 프로젝트를 훨씬 잘 이해합니다.

기존의 tree 명령어는 어떤 한계가 있었을까요? tree 명령어는 파일 목록만 보여줍니다.

각 폴더가 무슨 역할을 하는지, 어떤 파일이 중요한지는 알려주지 않습니다. 프로젝트가 크면 출력이 수백 줄이 되어서 오히려 이해하기 어렵습니다.

게다가 node_modules 같은 불필요한 폴더까지 모두 표시되어 노이즈가 많았습니다. 이런 문제를 해결하는 것이 스마트 프로젝트 구조 분석기입니다.

스마트 분석기는 폴더 이름과 파일 패턴을 보고 역할을 추론합니다. 또한 중요한 파일(index, config, README)을 자동으로 찾아냅니다.

무엇보다 depth 제한을 두어서 너무 깊은 구조는 요약만 보여주어, AI가 핵심만 파악할 수 있게 합니다. 코드를 단계별로 살펴보겠습니다.

ProjectStructure 인터페이스는 분석 결과를 담는 구조입니다. description 필드가 핵심인데, 여기에 폴더의 역할을 자연어로 저장합니다.

mainFiles는 그 폴더에서 중요한 파일들의 목록입니다. inferFolderRole 함수는 휴리스틱 방식으로 폴더 역할을 추론합니다.

폴더 이름이 'components'면 UI 컴포넌트, 'services'면 비즈니스 로직이라고 추측합니다. 완벽하지는 않지만, 대부분의 프로젝트가 일반적인 네이밍 컨벤션을 따르므로 꽤 정확합니다.

analyzeProjectStructure 함수가 핵심 로직입니다. 재귀적으로 폴더를 탐색하되, depth 파라미터로 깊이를 제한합니다.

주요 파일은 정규식으로 찾아내고, 불필요한 폴더(node_modules, .git)는 자동으로 제외합니다. generateContextText 함수는 분석 결과를 AI가 읽기 좋은 텍스트로 변환합니다.

폴더 이름, 역할, 주요 파일을 한 줄로 요약합니다. 들여쓰기를 사용해서 계층 구조를 시각적으로 표현합니다.

실무에서 어떻게 활용할까요? 이 도구를 CI/CD 파이프라인에 넣으면, 코드가 푸시될 때마다 최신 프로젝트 구조 문서가 자동으로 생성됩니다.

이 문서를 팀 위키에 올리거나, AI 코딩 도구의 컨텍스트로 사용할 수 있습니다. 신규 팀원이 합류하면 이 문서를 먼저 읽게 해서 온보딩 시간을 단축시킬 수 있습니다.

개선할 수 있는 부분도 있습니다. 현재는 폴더 이름만으로 역할을 추론하는데, 실제로는 폴더 안의 파일 내용도 분석하면 더 정확합니다.

예를 들어 .tsx 파일이 많으면 React 컴포넌트, .test.ts 파일이 많으면 테스트 폴더라고 판단할 수 있습니다. 또한 package.json을 읽어서 사용 중인 프레임워크를 파악하면, 더 구체적인 설명을 생성할 수 있습니다.

김개발 씨는 자신의 프로젝트에서 도구를 실행해봤습니다. 깔끔하게 정리된 구조 요약이 출력되었습니다.

"이걸 Claude에게 제공하면, 우리 프로젝트를 완벽히 이해하겠는데요!" 프로젝트 구조 분석기를 직접 만들어보면, 파일 시스템 분석의 원리를 깊이 이해할 수 있습니다. 여러분도 이 코드를 기반으로 자신만의 분석 도구를 만들어보세요.

실전 팁

💡 - package.json의 dependencies를 읽어서 기술 스택을 자동으로 파악하세요

  • Git history를 분석하면 최근 활발히 수정되는 폴더를 강조할 수 있습니다
  • 분석 결과를 JSON으로 저장하면 다른 도구와 연동하기 쉽습니다

6. 실습 컨텍스트 자동 수집 시스템

마지막 실습 시간입니다. 김개발 씨는 "매번 수동으로 파일을 복사해서 AI에게 붙여넣는 게 번거로워요"라고 말합니다.

박시니어 씨는 "그럼 자동으로 컨텍스트를 수집하는 시스템을 만들어봅시다"라고 제안합니다.

컨텍스트 자동 수집 시스템은 정적 컨텍스트와 동적 컨텍스트를 결합해서, 현재 작업에 필요한 모든 정보를 자동으로 수집하는 통합 도구입니다. 개발자가 파일 경로만 지정하면, 프로젝트 구조부터 관련 파일까지 모든 컨텍스트를 한 번에 준비해줍니다.

다음 코드를 살펴봅시다.

import * as fs from 'fs';
import * as path from 'path';

// 통합 컨텍스트
interface Context {
  static: {
    projectName: string;
    structure: string;
    conventions: string;
    techStack: string[];
  };
  dynamic: {
    currentFile: { path: string; content: string };
    relatedFiles: Array<{ path: string; content: string; reason: string }>;
  };
}

// 컨텍스트 자동 수집
class ContextCollector {
  private rootPath: string;

  constructor(rootPath: string) {
    this.rootPath = rootPath;
  }

  // 정적 컨텍스트 수집
  private collectStaticContext(): Context['static'] {
    const packageJson = JSON.parse(
      fs.readFileSync(path.join(this.rootPath, 'package.json'), 'utf-8')
    );

    return {
      projectName: packageJson.name,
      structure: this.generateStructureSummary(),
      conventions: this.loadConventions(),
      techStack: Object.keys(packageJson.dependencies || {})
    };
  }

  // 동적 컨텍스트 수집
  private collectDynamicContext(filePath: string): Context['dynamic'] {
    const content = fs.readFileSync(filePath, 'utf-8');
    const imports = this.extractImports(content);

    const relatedFiles = imports.map(imp => ({
      path: this.resolveImportPath(filePath, imp),
      content: fs.readFileSync(this.resolveImportPath(filePath, imp), 'utf-8'),
      reason: `Imported in ${filePath}`
    }));

    return {
      currentFile: { path: filePath, content },
      relatedFiles
    };
  }

  // 전체 컨텍스트 수집
  collectAll(currentFilePath: string): Context {
    return {
      static: this.collectStaticContext(),
      dynamic: this.collectDynamicContext(currentFilePath)
    };
  }

  // AI용 프롬프트 생성
  generatePrompt(context: Context, userRequest: string): string {
    return `
# Project Context
Project: ${context.static.projectName}
Tech Stack: ${context.static.techStack.join(', ')}

## Project Structure
${context.static.structure}

## Current File
Path: ${context.dynamic.currentFile.path}
\`\`\`
${context.dynamic.currentFile.content}
\`\`\`

## Related Files
${context.dynamic.relatedFiles.map(f => `
### ${f.path}
${f.reason}
\`\`\`
${f.content.slice(0, 500)}...
\`\`\`
`).join('\n')}

# User Request
${userRequest}
    `.trim();
  }

  private generateStructureSummary(): string {
    // 이전에 만든 analyzeProjectStructure 활용
    return '📁 src\n  📁 components (UI 컴포넌트)\n  📁 services (비즈니스 로직)';
  }

  private loadConventions(): string {
    // CLAUDE.md 또는 README.md에서 컨벤션 로드
    const conventionFile = path.join(this.rootPath, 'CLAUDE.md');
    if (fs.existsSync(conventionFile)) {
      return fs.readFileSync(conventionFile, 'utf-8');
    }
    return 'No conventions documented';
  }

  private extractImports(content: string): string[] {
    const regex = /from\s+['"]([^'"]+)['"]/g;
    const imports: string[] = [];
    let match;
    while ((match = regex.exec(content)) !== null) {
      imports.push(match[1]);
    }
    return imports;
  }

  private resolveImportPath(fromFile: string, importPath: string): string {
    // 상대 경로를 절대 경로로 변환
    if (importPath.startsWith('.')) {
      return path.resolve(path.dirname(fromFile), importPath);
    }
    // node_modules는 건너뛰기
    return importPath;
  }
}

// 사용 예제
const collector = new ContextCollector('/home/project');
const context = collector.collectAll('/home/project/src/components/Button.tsx');
const prompt = collector.generatePrompt(context, 'Add loading state to this button');
console.log(prompt);

김개발 씨는 지금까지 배운 모든 것을 하나로 합칠 시간입니다. 정적 컨텍스트 수집, 동적 컨텍스트 수집, import 분석, 프로젝트 구조 분석을 모두 결합한 완전한 시스템을 만들어봅니다.

박시니어 씨가 설명합니다. "실제로 Claude Code 같은 도구들이 내부적으로 이런 방식으로 작동해요.

여러분이 파일을 열면, 자동으로 관련 컨텍스트를 수집해서 AI에게 제공하는 거죠." 컨텍스트 자동 수집 시스템의 핵심 아이디어는 무엇일까요? 쉽게 비유하자면, 이 시스템은 마치 개인 비서와 같습니다.

회의에 들어가기 전에 비서가 미리 관련 자료를 모두 준비해서 폴더에 넣어줍니다. 프로젝트 개요, 이전 회의록, 관련 문서 등 필요한 모든 것이 준비되어 있죠.

개발자도 마찬가지로, AI와 대화하기 전에 이 시스템이 프로젝트 정보, 현재 파일, 관련 파일을 모두 자동으로 준비해줍니다. 수동으로 컨텍스트를 준비할 때는 어떤 문제가 있었을까요?

개발자가 직접 파일을 열고, 내용을 복사하고, 프롬프트에 붙여넣어야 했습니다. 관련 파일을 찾기 위해 import 문을 일일이 확인하고, 각 파일을 열어봐야 했습니다.

실수로 중요한 파일을 빠뜨리기도 쉬웠고, 프롬프트가 일관되지 않아서 AI의 응답 품질도 들쑥날쑥했습니다. 매번 같은 작업을 반복하느라 시간도 많이 낭비했습니다.

이 모든 문제를 해결하는 것이 자동 컨텍스트 수집 시스템입니다. 자동 시스템을 사용하면 개발자는 파일 경로만 지정하면 됩니다.

나머지는 모두 자동으로 처리됩니다. 프로젝트 정보를 읽고, 관련 파일을 찾고, AI가 이해하기 쉬운 형태로 포맷팅까지 해줍니다.

일관된 프롬프트 형식 덕분에 AI의 응답 품질도 향상됩니다. 코드를 자세히 분석해보겠습니다.

Context 인터페이스는 정적 컨텍스트와 동적 컨텍스트를 모두 포함합니다. 이렇게 분리하면 정적 정보는 한 번만 수집하고 재사용할 수 있어서 효율적입니다.

ContextCollector 클래스는 모든 기능을 통합합니다. collectStaticContext는 package.json을 읽어서 프로젝트 이름과 기술 스택을 파악합니다.

loadConventions는 CLAUDE.md 같은 컨벤션 문서를 찾아서 로드합니다. collectDynamicContext는 현재 파일을 읽고, import 문을 분석해서 관련 파일을 자동으로 찾습니다.

resolveImportPath는 상대 경로를 절대 경로로 변환하는데, 이게 중요합니다. "../utils/helper"처럼 상대 경로로 되어 있으면 실제 파일을 찾을 수 없기 때문입니다.

generatePrompt 함수가 가장 중요합니다. 수집한 모든 컨텍스트를 AI가 이해하기 쉬운 마크다운 형식으로 변환합니다.

프로젝트 정보, 구조, 현재 파일, 관련 파일을 체계적으로 정리해서 하나의 프롬프트를 만듭니다. 이 프롬프트를 AI에게 보내면, AI는 프로젝트를 완벽히 이해한 상태에서 코드를 생성할 수 있습니다.

실무에서는 이것을 어떻게 활용할까요? VS Code 익스텐션으로 만들 수 있습니다.

개발자가 파일을 열고 "Cmd+Shift+P"를 눌러서 "Generate AI Context"를 실행하면, 자동으로 컨텍스트가 수집되어 클립보드에 복사됩니다. 이걸 ChatGPT나 Claude에 붙여넣기만 하면 됩니다.

더 나아가, OpenAI API를 연동하면 에디터 안에서 바로 AI 응답을 받을 수도 있습니다. 성능 최적화도 고려해야 합니다.

관련 파일이 너무 많으면 컨텍스트가 거대해져서 API 비용이 늘고 응답도 느려집니다. 따라서 직접 import한 파일만 포함하고, 깊이 제한(depth limit)을 두는 것이 좋습니다.

또한 파일 내용이 너무 길면 처음 500줄만 포함하거나, 중요한 부분(export된 함수, 타입 정의)만 추출하는 것도 방법입니다. 캐싱도 중요합니다.

정적 컨텍스트는 프로젝트가 변하지 않는 한 같으니까, 한 번 수집한 결과를 메모리에 저장해서 재사용할 수 있습니다. 파일 내용도 파일의 수정 시간(mtime)을 확인해서, 변경되지 않았으면 캐시된 내용을 사용하면 성능이 크게 향상됩니다.

김개발 씨는 완성된 시스템을 실행해봤습니다. Button.tsx 파일을 입력했더니, 프로젝트 정보, Button의 코드, 그리고 Button이 import하는 types와 utils 파일까지 모두 자동으로 수집되어 깔끔한 프롬프트가 생성되었습니다.

"이제 AI와 협업할 때 훨씬 편하겠어요!" 김개발 씨는 감탄했습니다. 박시니어 씨가 미소를 지으며 말합니다.

"맞아요. 좋은 컨텍스트는 좋은 코드의 시작이죠." 컨텍스트 자동 수집 시스템을 구축하면, AI 코딩 도구를 훨씬 효과적으로 활용할 수 있습니다.

여러분도 이 코드를 기반으로 자신만의 컨텍스트 수집 도구를 만들어보세요. Claude Code나 GitHub Copilot 같은 상용 도구들도 결국 이런 원리로 작동합니다.

실전 팁

💡 - 정적 컨텍스트는 캐싱해서 반복 수집하지 않도록 하세요

  • import depth를 2-3단계로 제한하면 컨텍스트 크기를 적절히 유지할 수 있습니다
  • .contextignore 파일을 만들어서 제외할 파일 패턴을 관리하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#TypeScript#LLM#Context#CodebaseAnalysis#PromptEngineering#LLM,컨텍스트,프롬프트

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

ReAct 패턴 마스터 완벽 가이드

LLM이 생각하고 행동하는 ReAct 패턴을 처음부터 끝까지 배웁니다. Thought-Action-Observation 루프로 똑똑한 에이전트를 만들고, 실전 예제로 웹 검색과 계산을 결합한 강력한 AI 시스템을 구축합니다.

AI 에이전트의 모든 것 - 개념부터 실습까지

AI 에이전트란 무엇일까요? 단순한 LLM 호출과 어떻게 다를까요? 초급 개발자를 위해 에이전트의 핵심 개념부터 실제 구현까지 이북처럼 술술 읽히는 스타일로 설명합니다.

Parent-Child Chunking 완벽 가이드

RAG 시스템에서 검색 정확도와 컨텍스트 품질을 동시에 높이는 Parent-Child Chunking 전략을 실무 중심으로 학습합니다. 작은 청크로 검색하고 큰 청크로 제공하는 계층적 접근법을 마스터하세요.

RAG 완벽 가이드 - LLM의 한계를 극복하는 지능형 검색 시스템

LLM의 Hallucination과 정보 부족 문제를 해결하는 RAG(Retrieval-Augmented Generation) 시스템을 초급자도 이해할 수 있도록 실무 스토리와 코드로 설명합니다. 검색과 생성을 결합한 차세대 AI 시스템의 핵심을 배워보세요.

프롬프트 버전 관리와 최적화 완벽 가이드

LLM 프롬프트도 코드처럼 체계적으로 관리하고 지속적으로 개선할 수 있습니다. 프롬프트 버전 관리 시스템 구축부터 A/B 테스트 자동화, 프롬프트 레지스트리 운영까지 실무에서 바로 적용할 수 있는 최적화 전략을 배워봅니다.