바닥부터 구축하는 AI 코딩 IDE: 완전한 오픈 소스 아키텍처
요약
오픈 소스 기반의 AI 코딩 IDE인 'Dhi'의 아키텍처를 소개합니다. Tree-sitter를 활용한 의미적 코드 파싱부터 오케스트레이션 레이어까지, 독점 API 없이 구축 가능한 완전한 시스템 구조를 제시합니다.
핵심 포인트
- 오픈 소스 기반의 완전한 AI 코딩 IDE 아키텍처 제안
- Tree-sitter를 활용해 의미적 경계 단위로 코드 파싱 및 이해
- JSON-RPC/WebSocket 기반의 레이어 간 독립적 통신 구조
- 자동 완성, 에이전트 편집, 저장소 이해 등 핵심 기능 통합
참고: 이 포스트에는 아키텍처 다이어그램이 포함되어 있습니다. 완전히 렌더링된 버전을 보려면 원본 포스트를 방문하세요.
AI 코딩 어시스턴트(AI coding assistants)는 개발자가 소프트웨어를 작성하는 방식을 근본적으로 변화시켰습니다. 가장 뛰어난 어시스턴트들은 한 가지를 잘 수행합니다. 바로 개발자의 의도와 이를 표현하는 코드 사이의 거리를 좁히는 것입니다. 하지만 내부적으로 보면, 이들은 대체로 오케스트레이션 레이어 (orchestration layers)입니다. 이들이 사용하는 개별 프리미티브 (primitives) (언어 서버 (language servers), 벡터 검색 (vector search), 도구 호출 LLM (tool-calling LLMs), 샌드박스 실행 (sandboxed execution))는 모두 오픈 소스이거나 복제가 가능합니다.
이 포스트는 현대적인 AI 코딩 IDE가 제공하는 모든 기능, 즉 자동 완성 (autocomplete), 에디터 내 채팅 (chat-in-editor), 멀티 파일 에이전트 편집 (multi-file agent editing), 저장소 이해 (repo understanding), 시스템 설계 추론 (system design reasoning), 그리고 안전한 코드 실행 루프 (safe code execution loop)를 아우르는 완전한 오픈 소스 아키텍처를 제시합니다. 독점적인 API는 필요하지 않습니다.
이 프로젝트의 이름은 Gayatri Mantra에서 유래한 Dhi (धी)입니다. 이는 순수한 지성을 의미합니다. 코드는 github.com/sochaty/dhi에서 확인할 수 있습니다.
전체 그림 (The Full Picture)
레이어별로 살펴보기 전에, 시스템이 어떻게 결합되는지 보여드리겠습니다:
IDE 프론트엔드 (IDE Frontend)
↓ JSON-RPC / WebSocket
오케스트레이션 코어 (Orchestration Core) (LangGraph · 도구 라우터 (Tool Router) · 컨텍스트 어셈블러 (Context Assembler))
...
각 수직 슬라이스 (vertical slice)는 독립적으로 배포 가능합니다. 노트북에서 자동 완성 엔진만 실행하거나, GPU 클러스터에서 에이전트 엔진을 확장할 수 있습니다. 레이어들은 로컬 JSON-RPC 버스 (VS Code가 LSP를 위해 사용하는 것과 동일한 프로토콜)를 통해 통신합니다.
레이어 1 — 저장소 이해 (Repo Understanding)
다른 모든 기능은 코드베이스에 대한 시스템의 이해도가 높을수록 그 성능이 좋아집니다. 이 부분이 대부분의 오픈 소스 IDE 프로젝트가 가장 취약한 지점입니다. 이를 제대로 수행한다는 것은 단순한 파일 분할 (file-splitting)을 넘어선다는 것을 의미합니다.
Tree-sitter를 이용한 파싱 (Parsing with Tree-sitter)
Tree-sitter는 40개 이상의 언어에 대해 파일당 5ms 미만의 속도로 구체 구문 트리 (Concrete Syntax Tree)를 생성합니다. 우리는 단순히 글자 수로 나누는 대신, 함수 (functions), 클래스 (classes), 메서드 본문 (method bodies)과 같은 **의미적 경계 (semantic boundaries)**를 기준으로 분할합니다. 이를 통해 각 청크 (chunk)가 독립성을 유지하도록 하며, 검색 (retrieval) 시 컨텍스트 파편화 (context fragmentation)를 줄입니다.
소스 파일 (Source Files) → Tree-sitter (언어별) → 의미적 청크 (Semantic Chunks)
→ 메타데이터 오버레이 (Metadata Overlay) (파일 경로 · 라인 범위 · 심볼 이름)
→ nomic-embed-text-v1.5 (768차원, 로컬 실행)
...
콜 그래프 레이어 (The Call Graph Layer)
순수 벡터 검색 (vector search)은 의미적으로 유사한 코드는 찾아내지만, 구조적 관계는 놓칩니다. 심볼 참조 그래프 (LSP textDocument/references 호출을 통해 구축됨)를 사용하면, 퍼지 검색 (fuzzy search) 대신 그래프 순회 (graph traversal)를 통해 _"인증 미들웨어 (auth middleware)를 건드리는 모든 함수를 찾아줘"_와 같은 질문에 답할 수 있습니다.
이를 SQLite에 인접 리스트 (adjacency list) 형태로 저장하세요. 가볍고, 별도의 인프라가 필요 없으며, 저장소 (repo)와 항상 동기화됩니다.
레이어 2 — 자동 완성 (Fill-in-the-Middle)
현대적인 AI IDE에서의 자동 완성 (Autocomplete)은 단순히 다음 토큰을 예측하는 것이 아닙니다. 그것은 채우기 (Fill-in-the-Middle, FIM) 방식입니다. 모델은 접두사 (prefix, 커서 이전의 모든 것)와 접미사 (suffix, 커서 이후의 모든 것)를 보고, 그 사이를 잇는 완성된 코드를 생성합니다.
<fim_prefix>
저장소 컨텍스트 (repo context): 상위 3개 검색된 청크 (~1500 토큰)
현재 파일 (current file): 0번 라인 → 커서 (~800 토큰)
...
모델 선택 (Model choices)
| 모델 | 파라미터 (Parameters) | FIM 지원 | 실행 환경 |
|---|---|---|---|
| StarCoder2-3B | 3B | ✅ 네이티브 (native) | Apple M2 / 8GB GPU |
| ... |
로컬 개발을 위해서는 Ollama로 서빙하고, 프로덕션 환경에서는 vLLM을 사용하세요 (PagedAttention은 메모리를 약 40% 절감하며, 연속 배칭 (continuous batching)은 대기열 문제를 제거합니다).
투기적 디코딩 (Speculative Decoding)
작은 초안 모델 (draft model, StarCoder2-1B)을 큰 검증 모델 (verifier, DeepSeek-Coder-V2-Lite)과 쌍으로 구성합니다. 초안 모델이 K개의 토큰을 생성하면, 검증 모델이 단 한 번의 순전파 (forward pass)로 이를 수락하거나 거부합니다. 일반적인 완성 길이 기준으로, 대형 모델만 단독으로 사용할 때보다 유효 처리량 (effective throughput)이 3~5배 더 빠릅니다.
레이어 3 — 에디터 내 채팅 (Chat-in-Editor)
채팅은 자동 완성 (autocomplete)과는 다르게 작동합니다. 지연 시간 (latency)은 2~5초 정도이며 (대화형 교환으로는 수용 가능한 수준), 가장 관련성이 높은 내용을 포함하면서도 모델의 제한 사항 내에 들어오도록 컨텍스트 윈도우 (context window)를 신중하게 구성해야 합니다.
Developer Message
↓
Context Assembler
...
핵심적인 UX 통찰: **토큰을 채팅 패널에 실시간으로 스트리밍 (stream)**하되, 코드 블록은 버퍼링(buffer)한 뒤 완전한 블록이 도착한 후에만 에디터에 적용해야 합니다. 부분적인 코드 블록이 실시간으로 적용되면 화면 깜빡임 (flickering)이 발생하고 변경 사항 (diff)을 읽기 어렵게 만듭니다.
모델의 경우, 컨텍스트 윈도우가 큰 지시어 튜닝 (instruction-tuned) 모델이라면 무엇이든 사용 가능합니다: Ollama / vLLM을 통한 Qwen2.5-Coder-32B-Instruct, DeepSeek-V3, 또는 Llama-3.3-70B-Instruct 등이 있습니다.
레이어 4 — 멀티 파일 에이전트 편집 (Multi-File Agent Editing)
이 레이어는 제대로 구현하기 가장 어려운 부분입니다. 에이전트는 원래 목표의 컨텍스트를 잃지 않으면서 계획을 세우고, 여러 파일에 걸쳐 작업을 수행하며, 결과(컴파일러 오류, 테스트 실패)를 관찰하고, 수정까지 수행해야 합니다.
계획-실행-관찰 루프 (The Plan-Act-Observe Loop)
Developer Request
↓
Planner (reasoning model · ordered task list)
...
도구 세트 (Tool Set)
| 도구 | 기능 |
|---|---|
read_file(path) | 파일 내용 반환 |
| ... |
오케스트레이션 (Orchestration): LangGraph
LangGraph는 에이전트 루프를 노드(생각, 실행, 관찰, 계획, 검증)의 유향 그래프 (directed graph)로 모델링합니다. 엣지 (edges)는 조건부입니다. 즉, observe 노드는 오류가 발생하면 think로 다시 경로를 지정하거나, 성공하면 verify로 전달합니다.
단순한 while 루프와 비교했을 때의 핵심 장점은 **체크포인팅 (checkpointing)**입니다. LangGraph는 실행 중간에 루프를 일시 중지하고 상태를 디스크에 직렬화 (serialize)한 뒤 재개할 수 있습니다. 이는 수십 개의 파일 편집이 걸릴 수 있는 긴 리팩토링 (refactor) 작업에 매우 중요합니다.
레이어 5 — 시스템 디자인 및 추론 (System Design and Reasoning)
아키텍처 수준의 질문(
**저장소 요약 (repo summary)**은 핵심적인 결과물입니다. 첫 인덱싱 시점에 한 번 구축한 후, git diff를 사용하여 점진적으로 업데이트하십시오. 즉, 마지막 커밋에서 변경된 모듈만 다시 요약하면 됩니다.
레이어 6 — 안전한 코드 실행 루프 (Safe Code Execution Loop)
코드를 작성할 수 있는 에이전트는 코드를 실행할 수도 있어야 합니다. 하지만 LLM이 생성한 임의의 코드를 호스트 머신에서 직접 실행하는 것은 절대 안 됩니다. 실행 레이어는 다음과 같아야 합니다:
- 격리됨 (Isolated): 프로젝트 외부의 호스트 파일 시스템, 네트워크 또는 환경 변수에 접근할 수 없음
- 휘발성 (Ephemeral): 각 실행 후 컨테이너가 해제됨
- 감사 가능 (Auditable): 모든 stdin/stdout/stderr를 캡처하여 개발자에게 보여줌
에이전트: run_command("pytest tests/")
↓
Docker 컨테이너 (휘발성)
...
자가 치유 루프 (The Self-Healing Loop)
코드 작성 → 테스트 실행
↓ 통과 (pass) → 개발자에게 diff 제안
↓ 실패 (fail) → 에러 관찰 → 재계획 (re-plan) → 코드 작성
테스트가 실패하면, 그 출력값이 에이전트 루프의 다음 관찰값(observation)이 됩니다. 에이전트는 정확한 에러를 확인하고, 수정 사항에 대해 추론하며, 파일을 편집하고 다시 실행합니다. 단순한 버그의 경우 보통 2~3번의 반복(iteration) 내에 수렴합니다.
더욱 강력한 샌드박스를 위해, 일반적인 Docker 대신 gVisor (사용자 공간에서 시스템 호출을 가로채는 Google의 컨테이너 런타임) 또는 Firecracker (AWS Lambda에서 사용되는 마이크로 VM)를 사용하십시오.
전체 오픈 소스 스택 (The Full Open-Source Stack)
| 기능 (Capability) | 구성 요소 (Component) | 비고 (Notes) |
|---|---|---|
| 에디터 (Editor) | Monaco Editor | MIT, VS Code와 동일한 엔진 |
| ... |
공짜로 얻을 수 없는 것들 (What You Don't Get For Free)
정직한 아키텍처 포스트라면 어려운 부분들을 명시해야 합니다:
낮은 VRAM에서의 지연 시간 (Latency). 단일 24GB GPU에서 채팅을 수행하는 32B 모델은 초당 15~20 토큰(tokens/second)의 속도를 기록합니다. 대부분의 워크플로우에서는 수용 가능한 수준이지만, 클라우드 호스팅 대안보다는 눈에 띄게 느립니다. 해결책은 추측적 디코딩 (speculative decoding), 양자화 (quantisation, 예: GGUF Q4), 또는 필요할 때 소규모 클라우드 GPU로 오프로딩 (offloading)하는 것입니다.
프롬프트 캐시 무효화 (Prompt cache invalidation). 관리형 AI 코딩 서비스들은 거의 확실하게 요청 전반에 걸쳐 프롬프트 캐싱 (prompt caching)을 구현합니다. 관리형 추론 제공자 (managed inference provider) 없이 이를 복제하려면 vLLM에서 세심한 키-값 캐시 (key-value cache) 관리가 필요합니다. 가능은 하지만, 결코 간단하지 않은 작업입니다.
인덱스 최신성 (Index freshness). 벡터 저장소 (vector store)를 활성 편집 내용과 동기화하는 것(모든 키 입력마다 파일이 다시 작성됨)은 디바운스 (debounced)된 증분 재인덱싱 (incremental re-indexing)을 요구합니다. 이는 실수하기 쉬우며, 결과적으로 오래된 정보를 검색 (stale retrieval)하게 될 수 있습니다.
보안 표면 (Security surface). Docker 샌드박스 (sandbox)는 테스트 러너 (test runners)에게는 안전합니다. 하지만 저장소 어디든 write_file을 할 수 있거나, CI 설정을 수정하거나, 비밀 파일 (secrets files)에 접근할 수 있는 에이전트 (agents)는 위험 수준이 다릅니다. 경로 허용 목록 (path allowlist)을 구현하고, 현재 작업 디렉토리 (current working directory) 외부의 쓰기 작업에 대해서는 개발자의 확인을 요구해야 합니다.
맺음말
여기서 언급된 개별 구성 요소들 — Tree-sitter, vLLM, LangGraph, Docker —은 각각 대규모 프로덕션 환경에서 검증되었습니다. 아키텍처의 과제는 오케스트레이션 (orchestration)입니다. 즉, 적절한 컨텍스트 (context)를 조립하고, 적절한 지연 시간 예산 (latency budget) 내에서 적절한 모델로 라우팅 (routing)하며, 에이전트가 실제로 무엇을 건드리는지에 대해 개발자가 통제권을 유지할 수 있는 UX를 설계하는 것입니다.
훌륭한 AI 코딩 도구의 해자 (moat)는 아키텍처가 아닙니다. 그것은 이 아키텍처 위에서 이루어진 수년간의 UX 반복 (UX iteration)입니다. 오픈 소스 커뮤니티는 이제 그만큼 유능한 무언가를 구축하는 데 필요한 모든 기본 요소 (primitives)를 갖추고 있습니다.
다음 포스트에서는 FIM 자동 완성 엔진을 엔드 투 엔드 (end-to-end)로 구현하는 과정을 살펴봅: Tree-sitter 청킹 (chunking), nomic 임베딩 (embeddings), 그리고 단일 노트북에서 실행되는 StarCoder2-3B 추론 서버까지 다룹니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기