10강: 코드 맵: 프로젝트 구조 낸비게이션
한 문장 요약: DSH는 모노레포(멀티 패키지 저장소)입니다.
apps/는 진입점(cli, web, acp)이고,packages/는 모든 기능(각 패키지는 교체 가능한 플러그인 블록)이며,docs/는 설명서입니다. 찾고 싶은 기능이 있으면packages/xxx아래에서 해당 이름의 패키지를 찾으면 됩니다. 여기에docs/architecture.zh.md의 "ctx 키 → 패키지 → 역할" 표와module-graph의존성 그래프를 함께 활용하면 저장소 전체를 지도처럼 탐색할 수 있습니다.
1. 사용자 스토리: 저장소를 받은 첫날
D는 DSH 소스 저장소를 클론하고 루트 디렉터리 앞에서 조금 막막해졌습니다. 루트에 파일과 디렉터리가 잔뜩 있어서 어디서부터 봐야 할지 모르겠습니다. 그의 질문은 사실 두 가지뿐이었습니다.
- "DSH는 도대체 어떻게 실행되는 거지? 진입점이 어디지?"
- "agent의 기능을 바꾸고 싶은데(예: 샌드박스를 바꾸거나 검색 도구를 추가하고 싶다), 어느 디렉터리로 가야 하지?"
베테랑은 한마디만 알려 주었습니다. 먼저 세 개의 최상위 디렉터리 apps, packages, docs를 파악하고, 그다음 낸비게이션 법칙 하나를 기억하세요. 찾고 싶은 기능이 있으면 packages 아래에서 해당 이름의 패키지를 찾는다. 그리고 docs/architecture.zh.md에 있는 표 하나를 가리켜 주었고, 두 질문은 모두 해결되었습니다.
이번 강의는 바로 "베테랑의 그 한마디"를 펼쳐서 자세히 설명합니다. 다 읽고 나면 이 저장소 앞에서 최소한 세 가지는 알게 될 것입니다. 진입점이 어디인지, 기능이 어디 있는지, 의존성을 어떻게 조회하는지.
2. 모노레포 전경: apps는 진입점, packages는 기능, docs는 설명서
먼저 저장소 루트 디렉터리를 살펴봅시다(출처: 저장소 루트의 ls). 최상위는 흩어진 파일 더미가 아니라 분업이 명확한 세 블록입니다.
| 최상위 디렉터리 | 역할 | 안에 있는 것 |
|---|---|---|
apps/ | 진입점(실행할 수 있는 것) | cli(dsh 명령 자체), web(Web UI의 브라우저 쪽). 자동화 진입점인 ACP는 packages/acp에 있으며 pnpm run demo:acp로 시작할 수 있습니다(출처: 루트 README.zh.md) |
packages/ | 모든 기능(플러그인 블록) | 200개가 넘는 패키지가 49개의 "기능 그룹"에 나뉘어 담겨 있습니다. 예: core/, shell/, sandbox/, skill/, web/, llm/ |
docs/ | 설명서(아키텍처, 튜토리얼, 도감) | architecture.zh.md, module-graph.md, graph-atlas.md, tool-catalog.md 등 |
apps 是入口,packages/core 是默认流程,其余全是可替换的能力插件
읽는 법: apps/는 "어떻게 시작하는가"를 결정하고, packages/는 "어떤 기능이 있는가"를 결정하며, docs/는 "어디서 찾는가"를 결정합니다.
세 진입점은 각자의 역할이 있습니다.
- 커맨드라인:
apps/cli가 바로dsh명령 자체입니다. 문서는 이를 "profile의 제품 런처"로 정의합니다(출처:apps/cli/README.zh.md).dsh web,dsh --profile headless "작업"은 모두 여기서 파싱되고, profile에 따라 플러그인을 조합해 시작합니다. - Web UI:
apps/web은 브라우저 쪽입니다(Vite 프로젝트, 출처:apps/web/디렉터리).packages/host(GUI 호스트 절반: API 게이트웨이 + HTTP 라우팅)와packages/client(브라우저 절반: 셸, 프로토콜 계층,ui-*플러그인)와 함께 동작합니다. - 자동화:
packages/acp는 "자동화 전용" ACP(Agent Client Protocol) 서버입니다(출처:packages/acp/README.zh.md). agent를 표준화된 프로토콜로 프로그래밍 방식의 클라이언트에 노출합니다.
🎁 비유: apps는 "전원 버튼", packages는 "냉장고 속 재료", docs는 "레시피"입니다. 전원을 누르고, 재료를 고르고, 레시피를 찾아보는 세 가지는 서로 충돌하지 않습니다. 이것이 바로 "모든 것은 플러그인이다"가 디렉터리 구조에 투영된 모습입니다.
3. 두 가지 낸비게이션 라인: core의 기본 흐름 + 기능 패밀리의 이음새
3.1 첫 번째 라인: packages/core는 기본 흐름
packages/에 들어갔다면 가장 먼저 파악해야 할 디렉터리는 core/입니다. 패키지 색인 문서는 이를 "제품 API의 주간"이라 부릅니다. 세션 로그, 시스템 프롬프트 조립, 도구 레지스트리, agent 어휘, 기본 모델 선택, 구체적인 루프 등 "harness의 기본 제어 주간을 구성하는" 것들입니다(출처: packages/core/README.zh.md). 그 아래에는 패키지가 7개뿐입니다.
| core 안의 패키지 | 역할(괄호 안은 ctx 키) |
|---|---|
scope/ | 스코프 컨텍스트 등록 프리미티브(라이브러리, ctx 키 미사용) |
session/ | 이벤트 소싱 세션 로그와 인메모리 스토리지(ctx.sessions) |
system-prompt/ | 프롬프트와 도구 스키마를 조립하는 레지스트리(ctx.systemPrompt) |
tools/ | 스코프드 도구 레지스트리와 실행 파이프라인(ctx.tools) |
agent/ | Agent 인터페이스, 레지스트리, 이벤트 어휘(ctx.agents) |
agent-default-model/ | 각 Agent 진입점이 공유하는 기본 모델 선택(ctx.agentDefaultModel) |
agent-loop/ | 기본 구체 agent 드라이버(ctx.agentLoop) |
한 문장으로 기억하는 법: core는 에이전트가 돌아가기 위한 최소 골격입니다. 세션, 프롬프트, 도구, agent, 모델, 루프가 모두 여기 있습니다. 앞선 강의에서 배운 개념들은 코드에서 바로 이 7개 패키지에 해당합니다.
3.2 두 번째 라인: 나머지 packages는 전부 "교체 가능한 기능 패밀리"(이음새)
core 밖에도 수십 개의 패키지가 있는데, 이들은 핵심 흐름이 아니라 핵심 흐름이 끼웠다 뺐다 할 수 있는 기능입니다. 아키텍처 문서의 원문은 이렇습니다. "packages/core/는 기본 흐름을 모으고, 각 기능은 여전히 플러그인 형태로 존재한다"(출처: docs/architecture.zh.md). 각 기능은 하나의 "이음새(seam)"입니다. 기능 정의, 프로바이더, 컨슈머 셋이 분리되어 있어 어느 한쪽이든 단독으로 교체할 수 있습니다(1강의 "기능은 곧 이음새"에 대응됩니다).
| 기능 패밀리(이음새) | 하는 일(출처: packages/README.zh.md 계층 구조 표) |
|---|---|
llm/ | LLM 기능 패밀리: 추상 서비스 + 프로바이더 어댑터 |
shell/ | Bash 기능 패밀리: 실행기 seam, 로컬/샌드박스/PowerShell 구현, 모델 지향 도구 |
terminal/ | 영구 PTY 기능 패밀리: 소유자별 격리 세션, 로컬 구현, terminal_* 도구 |
code-runtime/ | 코드 실행 기능 패밀리: Service Definition + worker 스레드 프로바이더 + Code Mode 컨슈머 |
sandbox/ | 프로세스 제한 seam: bwrap / Landlock / Seatbelt 백엔드 |
fs/ | 파일 시스템: seam, 로컬 구현, 모델 지향 파일 도구, ripgrep을 내장한 탐색 도구 |
lsp/ | LSP 시맨틱 낸비게이션: seam, 범용 stdio 프로바이더, lsp 도구 |
skill/ | skill(스킬): 프로바이더 레지스트리, 파일 시스템 프로바이더, 카탈로그와 로더 |
web/ | Web 기능: 검색 및 페치 프로바이더 구현, 모델 지향 Web 도구 |
subagent/, workflow/, jobs/, goal/, schedule/ | 협업과 작업 관리(위임, 멀티 agent 오케스트레이션, 백그라운드 작업, 영구 목표, 세션 내 스케줄링) |
session/, session-query/ | 영속 세션 데이터 플레인, 세션 검색 |
storage/, spill/, attachment/ | 비세션 스토리지 허브, 초장문 도구 출력 스필, 영구 첨부 파일 |
typert/, api/, sdk/, acp/, mcp/ | 대외 프로토콜면: 타입 그래프 RPC, BFF 게이트웨이, JSON-RPC SDK, ACP 서버, MCP 클라이언트 |
host/, client/ | Web GUI의 호스트 절반과 브라우저 절반 |
낸비게이션 법칙은 바로 이 표에 숨어 있습니다. 찾고 싶은 기능이 있으면 packages/ 아래에서 해당 이름의 패키지를 찾으세요. 샌드박스를 바꾸고 싶다고요? packages/sandbox. 검색을 추가하고 싶다고요? packages/web. 스킬 로딩을 연구하고 싶다고요? packages/skill. 이름이 곧 색인입니다.
3.3 표로 탐색하기: 3단계 색인(아키텍처 문서 → 그룹 README → 서브시스템 페이지)
디렉터리 이름만으로는 부족합니다. 같은 기능 그룹 안에 서비스가 여러 개 있을 수 있으니까요. DSH는 이제 "표로 찾기"를 세 단계로 나누어 각각 한 구간씩 담당하게 합니다.
1단계: docs/architecture.zh.md의 "핵심 패키지" 표. 주간 7행만 남겨 두고 "agent 하나가 돌아가는 데 최소한 누가 필요한가"에 답합니다.
| 패키지 | 소유하는 것 | ctx 키 |
|---|---|---|
core/session | 추가 전용 SessionEvent 로그와 인메모리 스토어 | ctx.sessions |
core/system-prompt | 프롬프트 조각과 도구 스키마의 조립 | ctx.systemPrompt |
core/tools | 스코프드 도구 레지스트리와 보호된 실행 파이프라인 | ctx.tools |
core/agent | Agent 인터페이스, 활성 레지스트리, agent/* 이벤트 | ctx.agents |
core/agent-loop | 해당 인터페이스를 구현하는 기본 드라이버 | ctx.agentLoop |
core/scope | agent별 스코프 등록 프리미티브 | 라이브러리, ctx 키 미사용 |
llm/llm | 메시지와 스트림 어휘, 그리고 어댑터 seam | ctx.llm |
2단계: 그룹 README가 바로 ctx 키 매핑의 권위입니다. 패키지 색인 문서는 분명히 이렇게 씁니다. "그룹 README가 패키지/ctx 키 매핑을 담당한다"(출처: packages/README.zh.md). 따라서 어떤 기능에 어떤 패키지들이 있고 각각 어느 ctx 키에 걸려 있는지 알고 싶다면 아키텍처 문서로 돌아가 뒤지지 말고 packages/<group>/README.zh.md의 표를 보세요. 자주 쓰는 것들입니다.
| ctx 키 | 패키지 그룹 | 역할 |
|---|---|---|
ctx.shell | shell/ | 포그라운드 명령 실행과 백그라운드 프로세스 시작 |
ctx.terminals | terminal/ | 소유자별로 격리된 영구 PTY 세션 |
ctx.jobs | jobs/ | 종류에 구애받지 않는 백그라운드 작업 레지스트리 |
ctx.sandbox | sandbox/ | argv 래핑과 호출별 정책으로 프로세스 제한 |
ctx.fs | fs/ | 실행 세계의 경로, 유계(bounded) I/O, 정책 이벤트 |
ctx.skills | skill/ | skill 프로바이더 레지스트리와 점진적 공개 |
ctx.web | web/ | 검색 및 페치 프로바이더 레지스트리 |
ctx.subagents | subagent/ | 명명된 위임 프로바이더 |
ctx.workflowEngine | workflow/ | 스크립트 기반 멀티 agent 오케스트레이션 |
ctx.compaction | compaction/ | 언제 히스토리를 압축하고 어떻게 요약할지 |
ctx.codeRuntime | code-runtime/ | 모델이 작성한 프로그램 실행(Code Mode의 백엔드) |
ctx.sessionQuery | session-query/ | 세션 코퍼스의 유계 읽기와 검색 |
ctx.storage / ctx.spillStore | storage/ / spill/ | 비세션 스토리지 허브 / 초장문 출력 스필 |
3단계: docs/subsystems/의 서브시스템 페이지. 현재 40편이 넘고 기능 하나당 한 페이지입니다(shell.md, terminal.md, jobs.md, code-runtime.md, session-query.md, spill.md, typert.md……). 안에는 생성된 Cordis API 블록이 들어 있습니다. 어떤 기능의 완전한 타입과 이벤트를 읽고 싶다면 바로 여기로 가면 됩니다.
💡 많은 수고를 덜어 주는 명명 규약 하나: 저장소에는 이제 명확한 명명 계약이 있습니다. 단수형 ctx 키는 하나의 엔진/런타임/정책/컨트롤러를 나타내고, 복수형 ctx 키는 하나의 레지스트리를 나타냅니다. 클래스의 역할 이름과 키의 단복수는 반드시 일치해야 합니다. 그래서
ctx.workflowEngine을 본다면 그것이 엔진(레지스트리가 아님)이라는 것을 알고,ctx.terminals,ctx.agents,ctx.jobs를 본다면 이들이 여러 명명된 멤버를 관리한다는 것을 압니다. 같은 이유로local은 "같은 호스트에서의 실행 자체가 계약의 일부"일 때만 쓰입니다. 그래서 페치 구현은web-fetch-local이 아니라 (프로토콜로 구분하는)web-fetch-http이고, LSP 프로바이더는lsp-local이 아니라 (전송 방식으로 구분하는)lsp-stdio입니다.
사용 방법:
- 정방향 조회: 플러그인 코드에서
ctx.tools,ctx.sandbox를 보고 이것이 어느 패키지에서 구현됐는지 알고 싶다 → ctx 키로 패키지 그룹을 찾고 → 그룹 README를 읽고 → 세부 사항이 필요하면docs/subsystems/<기능>.md로 이동. - 역방향 조회: 어떤 기능을 교체하고 싶다 → 표에서 패키지 그룹을 찾고 →
packages/<group>/<pkg>디렉터리로 가서, 먼저 그룹 README를 읽은 다음 개별 패키지를 확인.
4. 그래프로 의존성 파악하기: module-graph와 graph-atlas
마지막 비기는 바로 그래프입니다. 문서는 아주 직설적으로 말합니다. docs/ 아래의 이 그래프들은 "생성된 디렉터리 위에 관계 계층을 구성한다". 패키지 간 의존성을 파악하는 데 사람이 직접 코드를 뒤질 필요 없이 그래프를 볼면 됩니다(출처: docs/graph-atlas.zh.md).
docs/module-graph.md(모듈 의존성 그래프): 도구가 각 패키지의 peerDependencies(규범적인 런타임 의존성 신호)를 바탕으로 자동 생성하며, packages/<group>/<pkg>별로 계층적으로 그룹화됩니다. 각 간선 a --> b는 "패키지 a가 패키지 b에 의존한다"를 의미합니다(출처: docs/module-graph.zh.md). 여는 방법에 주의하세요. 그래프 속 패키지 이름에서는 @deepseek-ai/dsh- 접두사가 제거되어 있습니다. 다시 생성하려면 pnpm run gen-module-graph를 실행하세요. 그리고 CI에는 "신선도 게이트"가 있어서 그래프가 오래되면 커밋이 막힙니다(출처: packages/README.zh.md 의존성 절).
docs/graph-atlas.md(문서 그래프 색인): 흩어져 있는 그래프들을 "도감"으로 조직한 그래프 목록입니다. 모듈 의존성 그래프, 도구 스키마 카탈로그와 패키지 매핑(tool-catalog.md), 기능 seam과 핵심 서비스(capability-seams.md), 애플리케이션 조합 그래프, 이벤트 생산자/소비자 매트릭스, agent 턴과 스텝 생명주기, 도구 실행 파이프라인(출처: docs/graph-atlas.zh.md).
💡 실전 활용법:
packages/fs를 수정하기 전에 먼저 module-graph에서 누가 이 패키지에 의존하는지 보세요.bash와sandbox가 모두 이를 가리킨다면 변경의 영향 범위를 알 수 있습니다. "agent의 한 턴이 정확히 어떤 단계를 거치는지" 알고 싶다면 agent 생명주기 그래프를 보세요. 도감은 "그래프 찾기"의 목차이고, module-graph는 "의존성 찾기"의 그래프입니다.
핵심 정리
- 세 개의 최상위 디렉터리:
apps/는 진입점(cli, web, acp),packages/는 모든 기능,docs/는 설명서. packages/core는 기본 흐름: scope, session, system-prompt, tools, agent, agent-default-model, agent-loop의 7종 세트는 에이전트가 돌아가기 위한 최소 골격입니다.- 나머지 packages는 전부 교체 가능한 기능 패밀리(이음새): 낸비게이션 법칙 = 찾고 싶은 기능 →
packages/xxx에서 해당 이름의 패키지를 찾는다. 이름이 곧 색인입니다. - 표로 탐색하기(3단계):
docs/architecture.zh.md의 "핵심 패키지" 표가 주간 7행을 제공하고, 그룹 README가 패키지/ctx 키 매핑의 권위이며,docs/subsystems/<기능>.md가 개별 기능의 완전한 타입과 이벤트를 제공합니다. - 그래프로 의존성 파악하기:
module-graph.md에서 패키지 의존성을 확인하고(도구 생성, CI가 신선도 유지),graph-atlas.md는 이 그래프들의 색인입니다.
🚀 다음 강부터는 이 지도를 들고 본격적으로 코드 깊이 들어갑니다.
packages/core/agent-loop부터 시작해서, 기본 루프가 도대체 어떻게 "돌아가기 시작하는지"를 살펴합니다.
자가 테스트 · 코드 맵
답을 모두 선택한 후 「답안 제출」을 클릭하면 정답 여부와 해설을 확인할 수 있습니다.
