서론
“방금 Claude Code로 작성하던 인증 모듈, Gemini CLI에서 이어서 작업하고 싶은데…”
이런 경험, 있으신가요? Claude Code로 열심히 작업하다가 토큰 제한에 막히거나, 특정 작업에 더 적합한 다른 AI 에이전트를 쓰고 싶어서 Gemini CLI나 Codex로 전환하는 순간, 가장 먼저 마주하는 것은 **“에이전트가 이전 작업 내용을 전혀 모른다”**는 사실입니다.
1
2
3
| You: "아까 작업하던 그 인증 모듈 계속해줘"
Gemini CLI: "죄송합니다, 어떤 인증 모듈을 말씀하시는지..."
You: 😤 (처음부터 다시 설명 시작)
|
이것은 단순한 불편함이 아닙니다. **컨텍스트 스위칭 비용(Context Switching Cost)**이라는 실제 생산성 저하 요인이죠. LLM 기반 코딩 에이전트가 폭발적으로 늘어난 지금, 우리는 멀티 에이전트 개발 환경에 살고 있습니다. 각 에이전트는 강점이 다르고, 프로젝트마다 최적의 도구가 다릅니다. 하지만 이들을 오가며 작업할 때마다 매번 컨텍스트를 새로 주입해야 한다면, AI가 주는 생산성 향상은 순식간에 증발합니다.
Ravenclaw는 바로 이 문제를 해결하는 오픈소스 시스템입니다. 프로젝트 단위로 작업 맥락을 중앙 관리하고, 서로 다른 AI 코딩 에이전트 간에 이 맥락을 자동으로 공유함으로써, 진정한 의미의 연속적인 개발 워크플로우를 가능하게 합니다.
문제의 본질: AI 코딩 에이전트의 컨텍스트 파편화
왜 이 문제가 중요한가?
2024년 이후 AI 코딩 에이전트 시장은 폭발적으로 성장했습니다. Anthropic의 Claude Code, Google의 Gemini CLI, OpenAI의 Codex, 그리고 Cursor, Copilot 등 수많은 도구가 경쟁하고 있습니다.
| 에이전트 | 강점 | 컨텍스트 관리 | | :— | :— | :— | | Claude Code | 긴 컨텍스트, 복잡한 추론 | 자체 세션 내에서만 유지 | | Gemini CLI | 멀티모달, Google 생태계 통합 | 독립적인 세션 관리 | | Codex | OpenAI 모델 호환성 | 격리된 컨텍스트 | | Cursor | IDE 통합, 로컬 파일 접근 | 프로젝트 내 제한적 공유 |
각 에이전트는 자신만의 세션과 컨텍스트를 가집니다. 이는 보안과 격리 측면에서는 장점이지만, 개발자가 여러 에이전트를 병행 사용할 때는 치명적인 단점이 됩니다.
1
2
3
4
5
6
7
8
9
| graph TD
subgraph 기존 방식
A[개발자] --> B[Claude Code]
A --> C[Gemini CLI]
A --> D[Codex]
B --> B1[격리된 컨텍스트 A]
C --> C1[격리된 컨텍스트 B]
D --> D1[격리된 컨텍스트 C]
end
|
이러한 파편화는 다음과 같은 문제를 야기합니다:
- 반복적인 컨텍스트 주입: 에이전트 전환 시마다 프로젝트 배경, 코딩 컨벤션, 이전 작업 내용을 재설명 2. 일관성 저하: 서로 다른 에이전트가 생성한 코드 간 스타일 불일치 3. 작업 흐름 단절: 큰 기능을 여러 에이전트에 분산 작업하기 어려움 4. 토큰 낭비: 동일한 컨텍스트를 여러 세션에 반복해서 전송
Ravenclaw 아키텍처
Ravenclaw는 중앙 집중식 컨텍스트 저장소를 통해 이 문제를 해결합니다.
1
2
3
4
5
6
7
8
9
10
| graph LR
subgraph Ravenclaw 시스템
A[Claude Code] --> E[중앙 컨텍스트 저장소]
B[Gemini CLI] --> E
C[Codex] --> E
D[기타 에이전트] --> E
E --> F[프로젝트 메타데이터]
E --> G[작업 히스토리]
E --> H[코딩 컨벤션]
end
|
핵심 구성 요소
- Context Store: 프로젝트별 구조화된 컨텍스트 저장 2. Agent Adapter: 각 AI 에이전트에 맞는 포맷 변환 3. Sync Engine: 실시간 컨텍스트 동기화 4. CLI Interface: 명령줄 기반 관리 도구
작동 원리: 어떻게 컨텍스트가 유지되는가?
Ravenclaw의 핵심은 프로젝트 상태를 에이전트 독립적인 형식으로 추상화하는 것입니다.
1단계: 프로젝트 초기화
1
2
3
4
5
6
7
8
| # Ravenclaw 설치
pip install ravenclaw-context
# 프로젝트 초기화
ravenclaw init my-project
# 현재 프로젝트 컨텍스트 분석
ravenclaw analyze --source ./src --docs ./docs
|
2단계: 컨텍스트 구조 생성
Ravenclaw는 프로젝트를 분석하여 다음과 같은 구조화된 컨텍스트를 생성합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| # ravenclaw/context_schema.py
from pydantic import BaseModel
from typing import List, Dict, Optional
from datetime import datetime
class ProjectContext(BaseModel):
"""Ravenclaw 프로젝트 컨텍스트 스키마"""
project_name: str
description: str
# 기술 스택 정보
tech_stack: Dict[str, List[str]] # {"languages": ["Python"], "frameworks": ["FastAPI"]}
# 코드 구조
architecture: str # "microservice", "monolith", "serverless"
key_directories: Dict[str, str] # {"src/": "메인 소스 코드", "tests/": "테스트"}
# 코딩 컨벤션
conventions: Dict[str, str] # {"naming": "snake_case", "docstring": "Google Style"}
# 작업 히스토리
recent_tasks: List[Dict[str, str]]
# 현재 포커스
current_focus: Optional[str] = None
# 메타데이터
last_updated: datetime
active_agent: Optional[str] = None
# 사용 예시
context = ProjectContext(
project_name="auth-service",
description="JWT 기반 인증 마이크로서비스",
tech_stack={
"languages": ["Python", "TypeScript"],
"frameworks": ["FastAPI", "SQLAlchemy"],
"databases": ["PostgreSQL", "Redis"]
},
architecture="microservice",
key_directories={
"src/api/": "REST API 엔드포인트",
"src/auth/": "인증 로직",
"src/models/": "데이터베이스 모델"
},
conventions={
"naming": "snake_case for Python, camelCase for TypeScript",
"testing": "pytest with 80% coverage minimum",
"commits": "Conventional Commits"
},
recent_tasks=[
{
"task": "JWT 리프레시 토큰 로직 구현",
"agent": "claude-code",
"status": "in_progress",
"files_modified": ["src/auth/jwt_handler.py", "src/api/routes/auth.py"]
}
],
current_focus="JWT 리프레시 토큰 만료 처리 로직 완성",
last_updated=datetime.now(),
active_agent="claude-code"
)
|
3단계: 에이전트 전환 시나리오
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
| # ravenclaw/agent_manager.py
class AgentManager:
"""다양한 AI 에이전트 간 컨텍스트 전환 관리"""
AGENT_PROMPTS = {
"claude-code": """
당신은 이 프로젝트의 이전 작업을 이어받습니다.
마지막 작업 상태: {current_focus}
최근 수정 파일: {recent_files}
이어서 작업을 진행하세요.
""",
"gemini-cli": """
Continuing work on this project.
Last task: {current_focus}
Recent changes: {recent_files}
Please continue the implementation.
""",
"codex": """
// Project Context
// Current focus: {current_focus}
// Recent modifications: {recent_files}
// Continue implementation
"""
}
def __init__(self, context_store_path: str):
self.store_path = context_store_path
self.current_context = self._load_context()
def switch_agent(
self,
from_agent: str,
to_agent: str,
task_update: str = None
) -> str:
"""
에이전트 전환 시 컨텍스트 포맷팅
Args:
from_agent: 현재 사용 중인 에이전트
to_agent: 전환하려는 에이전트
task_update: 전환 전 마지막 작업 상태 업데이트
Returns:
새 에이전트용 포맷팅된 컨텍스트 프롬프트
"""
# 현재 상태 저장
if task_update:
self.current_context.current_focus = task_update
self.current_context.active_agent = to_agent
self._save_context()
# 새 에이전트용 프롬프트 생성
prompt_template = self.AGENT_PROMPTS.get(to_agent)
formatted_prompt = prompt_template.format(
current_focus=self.current_context.current_focus,
recent_files=self._get_recent_files_summary(),
project_structure=self._get_structure_summary(),
conventions=self._get_conventions_summary()
)
return formatted_prompt
def _get_recent_files_summary(self) -> str:
"""최근 수정된 파일 요약 생성"""
recent = self.current_context.
|
1
2
3
4
5
6
7
8
9
10
11
12
| recent_tasks[-3:] # 최근 3개
summary = []
for task in recent:
summary.append(f"- {task['task']} ({task['files_modified']})")
return "
".join(summary)
def _save_context(self):
"""컨텍스트를 영속화"""
import json
with open(f"{self.store_path}/context.json", "w") as f:
json.dump(self.current_context.model_dump(), f, default=str, indent=2)
|
4단계: 실제 사용 워크플로우
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Claude Code로 작업 시작
ravenclaw use claude-code
# ... Claude Code에서 작업 ...
# 작업 상태 저장
ravenclaw checkpoint "JWT 리프레시 로직 80% 완료"
# Gemini CLI로 전환
ravenclaw switch gemini-cli
# 자동으로 컨텍스트가 포맷팅되어 클립보드에 복사됨
# Gemini CLI 세션에 붙여넣기 하면 이전 작업 맥락이 즉시 복원됨
|
컨텍스트 동기화 메커니즘
Ravenclaw의 가장 흥미로운 점은 단방향이 아닌 양방향 동기화를 지원한다는 것입니다.
1
2
3
4
5
6
7
| graph TD
A[Git Repository] --> B[Ravenclaw Context]
B --> C[Agent Session]
C --> D[Code Changes]
D --> E[Context Update]
E --> B
B --> F[Other Agents]
|
자동 컨텍스트 업데이트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| # ravenclaw/sync.py
import os
import hashlib
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class ContextSyncHandler(FileSystemEventHandler):
"""파일 시스템 변경 감지 및 컨텍스트 자동 업데이트"""
def __init__(self, context_manager):
self.context = context_manager
self.file_hashes = {}
def on_modified(self, event):
if event.is_directory:
return
file_path = event.src_path
# 파일 해시 계산하여 실제 변경 확인
current_hash = self._calculate_hash(file_path)
if self.file_hashes.get(file_path) == current_hash:
return
self.file_hashes[file_path] = current_hash
# 컨텍스트 업데이트
self._update_context_for_file(file_path)
def _calculate_hash(self, file_path: str) -> str:
with open(file_path, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
def _update_context_for_file(self, file_path: str):
"""파일 변경사항을 컨텍스트에 반영"""
# 파일에서 중요 정보 추출 (imports, class 정의 등)
extracted_info = self._extract_semantic_info(file_path)
# 컨텍스트의 recent_tasks 업데이트
self.context.add_recent_modification(
file=file_path,
changes=extracted_info,
timestamp=datetime.now()
)
# 파일 시스템 감시 시작
def start_sync_watch(project_path: str, context_manager):
event_handler = ContextSyncHandler(context_manager)
observer = Observer()
observer.schedule(event_handler, project_path, recursive=True)
observer.start()
return observer
|
실무 적용 가이드
프로젝트 설정 파일 예시
출처: https://news.hada.io/topic?id=28225