본문으로 건너뛰기
5분 AI.
REPO DEEP DIVE · 2026-07-29 · DYAD-SH/DYAD · 내 컴퓨터에서 도는 오픈소스 AI 앱 빌더

dyad-sh/dyad 딥다이브
— "Lovable를 내 노트북 안에" 넣은 로컬 AI 코딩 에이전트

데스크탑 앱

빠른 판단

무엇
선택한 LLM의 코드를 로컬 프로젝트에 적용하고 Git·미리보기로 확인하는 데스크탑 AI 앱 빌더
이럴 때
프로젝트 파일을 내 컴퓨터에 두고 템플릿에서 앱을 시작해 채팅으로 코드 수정·실행 결과를 검토할 때
Dyad는 Lovable·v0·bolt.new 같은 "말로 앱 만들기" 서비스를, 클라우드가 아니라 내 컴퓨터에서 통째로 돌리는 Electron 데스크탑 앱이다. 사용자가 채팅으로 요구사항을 적으면 LLM이 <dyad-write> 같은 전용 태그로 코드를 통째로 뱉고, Dyad는 그 태그를 그 자리에서 파싱해 진짜 로컬 파일로 써넣고 git 커밋까지 자동으로 남긴다.

가장 중요한 정체성은 "코드와 프로젝트 폴더가 100% 내 디스크에 있다"는 것이다. AI 모델은 OpenAI·Anthropic·Google 등 원하는 걸 자기 API 키(BYOK)로 붙이거나 Ollama/LM Studio로 완전히 로컬 모델만 써도 되고, Supabase·Vercel·Neon·GitHub 연동은 있으면 편한 옵션일 뿐 필수가 아니다.

(저장소 dyad-sh/dyad · 제작 Will Chen 외 다수 · 버전 1.9.0(package.json 기준, 진실원) · 라이선스 Apache 2.0 + src/pro/만 FSL-1.1-ALv2(저작권자 Dyad Tech, Inc.) · Electron 40.0.0 + React 19.2.4 + TypeScript · 소스 파일 약 1,700개, 유닛테스트 475개 + Playwright E2E 스펙 127개 · 최신 커밋 2026-07-28)
목차
  1. 한 줄 정체
  2. 왜 지금 뜨는가
  3. 무엇을 해결하나
  4. 전체 구조
  5. 핵심 설계·동작 원리
  6. 기술 스택·의존성
  7. 설치·요구사항
  8. 실제 사용 흐름
  9. 함정·오해 방지
  10. 대안 비교
  11. 정리 · 누가 쓰면 좋은가
  12. 직접 해볼 수 있는 실습 과제
  13. 관련 기술 심화 학습 로드맵
  14. 핵심 키워드 사전

1한 줄 정체

"내 컴퓨터에서 도는 Electron 앱 + LLM이 파일을 직접 써넣는 코드 적용 파이프라인"

Dyad는 AI 모델이 아니라 "AI가 짠 코드를 실제 프로젝트 폴더에 적용해 주는 로컬 프로그램"이다. package.json의 한 줄 설명 그대로 "Free, local, open-source AI app builder"다. 겉모습은 Lovable·v0·bolt.new 같은 채팅형 앱 빌더와 똑같지만, 그 서비스들이 사용자 코드를 회사 클라우드 서버에 두는 것과 달리 Dyad는 Electron 데스크탑 앱을 다운로드해 실행하는 순간부터 모든 프로젝트 파일이 사용자 디스크(~/dyad-apps 류의 로컬 폴더)에 그대로 존재한다.

기술적으로 핵심은 두 가지다. 하나는 LLM 프로바이더 추상화(Vercel AI SDK 기반, OpenAI·Anthropic·Google·Azure·xAI·Bedrock·OpenRouter·Ollama·LM Studio 등을 갈아 끼울 수 있음)이고, 다른 하나는 <dyad-write> 같은 전용 의사(疑似) XML 태그로 LLM 응답을 구조화해 그 자리에서 실제 파일 쓰기·삭제·이름변경·의존성 설치·git 커밋으로 변환하는 "응답 처리기"다. Electron 자체가 새로 만든 것은 없지만, 이 둘을 엮어 "채팅 한 번 = 실행 가능한 앱 한 조각"을 보장하는 파이프라인이 Dyad의 실체다.

한 줄 비유

"클라우드 코딩 비서"를 통째로 내 데스크에 옮겨놓은 것

Lovable·v0 같은 서비스는 회사 서버의 비서에게 앱을 만들어 달라고 부탁하는 것과 같다. 비서는 일을 잘하지만, 결과물(코드)도 회사 창고(클라우드)에 보관되고 요금도 그 회사가 정한다.

Dyad는 같은 비서(LLM)를 부르되, 비서가 일하는 책상과 서류함을 내 집(내 컴퓨터)으로 옮겨온 것이다. 비서는 여전히 전화(API)로 부르지만 — OpenAI든 로컬 Ollama 모델이든 내가 고른다 — 만들어진 서류(코드)는 항상 내 서랍(로컬 git 저장소)에 쌓인다.

2왜 지금 뜨는가

클라우드 AI 빌더 피로감 · BYOK(내 키로 쓰기) 열망 · "코드 락인" 거부감

① 클라우드 AI 앱 빌더의 구독료·락인 피로감

Lovable·v0·bolt.new류는 대부분 사용량 기반 크레딧 과금이고, 프로젝트가 그 회사 인프라 안에 갇힌다. Dyad의 제품 문서(PRODUCT.md)는 타깃 사용자를 정확히 "Lovable/v0/Bolt-style 툴에서 넘어온, 코딩 배경이 없는 논테크니컬 빌더"로 명시한다. "그 경험은 좋았는데 요금과 락인이 싫다"는 사람들을 정면으로 겨냥한 포지셔닝이다.

② BYOK(Bring Your Own Key) + 로컬 모델이라는 선택권

Dyad는 OpenAI/Anthropic/Google 키를 직접 붙이는 것은 물론, Ollama·LM Studio로 완전히 무료·오프라인 모델을 쓸 수도 있다. "AI 앱 빌더를 쓰고 싶지만 특정 회사 모델에 종속되긴 싫다"는 개발자들의 요구와 맞아떨어진다.

③ "로컬 우선(local-first)" 트렌드와 신뢰 문제

AI에게 코드 생성을 맡기는 것 자체가 아직 신뢰 이슈가 있는 상황에서, "적어도 내 파일은 내 디스크에, 되돌리기(git)도 내 손안에" 있다는 사실은 심리적 안전판이 된다. PRODUCT.md는 이를 "Local-first (fast, private, no lock-in) is the core differentiator"라고 스스로 밝힌다.

경쟁 지형
완전 오픈소스도, 완전 SaaS도 아닌 중간지대

Dyad는 무료·오픈소스 코어에 "Dyad Pro"라는 유료 게이트웨이(스마트 컨텍스트, 레이지 에딧, 클라우드 샌드박스 등)를 얹은 오픈코어(open-core) 모델이다. "완전 무료 자급자족"과 "완전 관리형 SaaS" 사이에서, 코드 소유권은 지키면서 필요할 때만 클라우드 편의 기능을 사는 구조다.

3무엇을 해결하나

코드 락인 제거 · 모델 선택권 · "AI가 뭘 바꿨는지 모르겠다" 문제
문제 ①

"내 앱 코드가 남의 서버에만 있다"는 불안

Dyad는 앱을 만들 때 로컬 디렉토리(getDyadAppsBaseDirectory())에 실제 프로젝트 폴더를 만들고 git으로 초기화한다. 클라우드 빌더를 쓰다가 서비스가 종료되거나 요금이 오르면 손쓸 수 없는 상황을 원천적으로 피한다.

문제 ②

"어떤 모델을 쓸지"를 도구가 강요하는 문제

src/ipc/utils/get_model_client.ts가 model.provider 문자열 하나로 OpenAI·Anthropic·Google·Azure·xAI·Bedrock·OpenAI-호환·Ollama·LM Studio를 모두 같은 인터페이스로 다룬다. 사용자는 설정 화면에서 프로바이더/모델을 자유롭게 바꿔 끼울 수 있다.

문제 ③

"AI가 무슨 파일을 왜 바꿨는지 모르겠다"는 불투명성

모든 응답은 <dyad-write path="..." description="...">처럼 파일 경로와 설명이 태그에 못박힌 구조화된 형태로만 나오도록 시스템 프롬프트가 강제한다(마크다운 코드블록은 명시적으로 금지). 그 덕분에 UI는 "이번 턴에 어떤 파일이 어떻게 바뀌었는지"를 정확히 보여줄 수 있고, 매 턴이 git 커밋 하나로 남아 되돌리기(버전 보기)도 가능하다.

문제 ④

"로컬인데 백엔드는 어떻게 만들지"

순수 로컬 앱은 인증·DB 같은 백엔드가 없다는 한계가 있다. Dyad는 이를 Supabase(Auth/DB/Edge Functions)·Neon(서버리스 Postgres)·Vercel(배포)·GitHub(원격 저장소) 연동으로 메꾼다. AI가 생성한 SQL을 감지해 Supabase/Neon에 바로 실행해 주는 것까지 자동화되어 있다(executeSupabaseSql, executeNeonSql).

4전체 구조

Electron 3계층 — 렌더러(React) ↔ preload(contextBridge) ↔ 메인 프로세스(IPC 핸들러 ~50개 모듈)

Dyad는 일반적인 Electron 앱의 교과서적인 3계층 구조를 아주 크게 확장한 형태다. src/main.ts(1,544줄)가 앱 생명주기를 관리하고, src/preload.ts가 contextBridge로 안전한 통로만 열어주며, src/ipc/ipc_host.ts가 도메인별 핸들러 약 50개를 등록한다. 렌더러(React UI)는 이 통로로만 파일시스템·git·AI 호출에 접근할 수 있다.

┌──────────────────────────────────────────────────────────────┐ │ DYAD 3계층 아키텍처 │ └──────────────────────────────────────────────────────────────┘ [렌더러] React 19 UI (src/components, src/routes, chat_stream/) · 채팅 입력 → chat_stream_handlers 호출 · 파일 diff / 버전(git 커밋) 뷰어 / 통합 프리뷰 iframe │ window.electron.ipcRenderer.invoke(channel, ...) ▼ [preload.ts] contextBridge.exposeInMainWorld("electron", ...) · VALID_INVOKE_CHANNELS 화이트리스트만 통과 · IPC 응답을 IpcEnvelope로 감싸 unwrap │ Electron IPC (프로세스 간 통신) ▼ [메인 프로세스] src/ipc/ipc_host.ts → registerXxxHandlers() 약 50개 ├ chat_stream_handlers.ts → AI SDK 스트리밍 호출 ├ get_model_client.ts → 프로바이더별 LLM 클라이언트 생성 ├ response_processor.ts → dyad-* 태그 파싱 → 실제 파일 적용 ├ github/vercel/supabase/neon_handlers.ts → 외부 서비스 연동 └ db (better-sqlite3 + drizzle) → 채팅/설정/앱 메타데이터 저장 │ fs.writeFile / spawn(npm|pnpm) / dugite(git) ▼ [디스크] 사용자의 실제 앱 폴더 (Vite 개발 서버 + 프리뷰)

주목할 점은 "Dyad 자신의 데이터"와 "사용자가 만드는 앱의 데이터"가 완전히 분리돼 있다는 것이다. Dyad 자체의 채팅 기록·설정·앱 목록은 src/db/schema.ts가 정의하는 로컬 SQLite(better-sqlite3 + drizzle-orm)에 저장되고, 사용자가 만드는 앱의 실제 소스코드는 그냥 디스크 위의 일반 폴더(그 자체가 독립된 git 저장소)로 존재한다. Dyad를 지워도 이미 만든 앱 폴더는 평범한 Vite+React 프로젝트로 그대로 남는다.

용어
contextBridge
Electron에서 렌더러(웹페이지)가 Node.js 전체 기능에 직접 접근하지 못하게 막고, 메인 프로세스가 허용한 몇 가지 함수만 window.electron 같은 이름으로 안전하게 노출하는 보안 장치. Dyad는 여기에 채널 화이트리스트까지 얹어 이중으로 막는다.
용어
IPC (Inter-Process Communication)
Electron의 렌더러 프로세스와 메인 프로세스는 완전히 분리된 별개의 프로세스라서, 서로 직접 함수를 호출할 수 없다. 대신 메시지를 주고받는 통로(IPC)로만 소통한다. Dyad는 ipcRenderer.invoke(channel, ...) 방식(요청-응답)을 표준으로 쓴다.

5핵심 설계·동작 원리

"LLM 응답 = 의사 XML 태그" → 파서(dyad_tag_parser.ts) → 적용 엔진(response_processor.ts)

Dyad에서 가장 중요하게 봐야 할 코드는 "AI가 뭐라고 답해야 하는가"(시스템 프롬프트)와 "그 답을 어떻게 실제 파일로 바꾸는가"(응답 처리기) 두 파일이다. 이 둘의 계약이 Dyad라는 제품 전체의 심장이다.

① 코드 생성 계약 — <dyad-write> 의사 XML 태그

src/prompts/system_prompt.ts는 모델에게 "코드는 절대 마크다운 코드블록으로 쓰지 말고, 오직 전용 태그로만 출력하라"고 여러 번 강한 어조로 반복 지시한다. 실제 프롬프트 문구를 보면:

<dyad-write path="src/components/Button.tsx" description="...">
  ...파일 전체 내용...
</dyad-write>

<dyad-rename from="src/components/UserProfile.tsx" to="src/components/ProfileCard.tsx"></dyad-rename>
<dyad-delete path="src/components/Analytics.tsx"></dyad-delete>
<dyad-add-dependency packages="react-hot-toast"></dyad-add-dependency>
<dyad-chat-summary>Adding a new component</dyad-chat-summary>

/* 프롬프트 원문 경고 문구 그대로: */
"ONLY use <dyad-write> tags for ALL code output."
"NO MARKDOWN CODE BLOCKS. USE <dyad-write> EXCLUSIVELY."

이 태그는 HTML/XML 표준이 아니라 Dyad가 자체적으로 정의한 규약이다. dyad-write(파일 생성/전체 덮어쓰기) 외에도 dyad-rename·dyad-delete·dyad-add-dependency(패키지 설치)·dyad-execute-sql(DB 마이그레이션 실행)·dyad-search-replace(부분 수정)·dyad-copy·dyad-command(rebuild/restart/refresh 제안)·dyad-chat-summary(채팅 제목) 태그가 있다.

중요한 함정
이건 표준 diff/patch 포맷이 아니다

다른 코딩 에이전트(Claude Code, Cursor 등)의 파일 편집 포맷과 혼동하기 쉽지만, <dyad-write>는 Dyad 프로젝트 고유의 프롬프트 규약일 뿐이다. 모델이 이 형식을 깨고 마크다운 코드블록으로 답하면 파서가 그 코드를 인식하지 못해 "아무 파일도 바뀌지 않는" 상황이 생긴다 — 시스템 프롬프트가 그렇게까지 반복해서 경고하는 이유다.

② 스트리밍 파서 — streamingMessageParser.ts

LLM 응답은 토큰 단위로 조금씩 도착하는데(스트리밍), <dyad-write> 태그가 아직 다 안 닫힌 상태에서도 UI는 "지금까지 써진 코드"를 실시간으로 보여줘야 한다. src/lib/streamingMessageParser.ts(528줄)는 이 미완성 태그를 안전하게 부분 파싱해 채팅 화면에 코드가 타이핑되듯 나타나게 만든다.

③ 적용 엔진 — response_processor.ts (854줄)

응답이 끝나면 src/ipc/processors/response_processor.ts가 전체 텍스트에서 src/ipc/utils/dyad_tag_parser.ts의 getDyadWriteTags·getDyadRenameTags·getDyadDeleteTags 등으로 태그를 모두 뽑아낸 뒤 순서대로 실행한다.

비유

AI는 "지시서 작성자", response_processor.ts는 "현장 감독"이다. AI는 종이에 "3번 방 벽지를 이걸로 바꿔라, 5번 방은 통째로 새로 발라라"라고 정해진 양식(태그)으로만 적는다. 현장 감독은 그 지시서를 한 줄씩 읽으며 실제로 벽지를 바르고(파일 쓰기), 자재를 주문하고(의존성 설치), 작업 일지에 서명(git 커밋)까지 남긴다. AI가 양식을 어기고 낙서로 적으면 감독은 그 지시를 아예 못 알아본다.

④ LLM 프로바이더 추상화 — get_model_client.ts (679줄)

src/ipc/utils/get_model_client.ts는 Vercel AI SDK(ai 패키지)의 프로바이더 팩토리들(createOpenAI, createAnthropic, createGoogle, createAzure, createXai, createGoogleVertex, createAmazonBedrock, createOpenAICompatible)을 한 함수 뒤로 감춘다. model.provider 문자열 하나로 어떤 회사 API를 부를지 갈아 끼우고, ollama_provider.ts·lm_studio_utils.ts로 완전히 로컬에서 도는 모델도 동일한 인터페이스로 붙일 수 있게 만든다.

유료 Dyad Pro를 켜면 createDyadEngine(llm_engine_provider.ts)이 요청을 Dyad가 직접 운영하는 게이트웨이(DYAD_ENGINE_URL)로 우회시켜, "스마트 파일 컨텍스트"·"레이지 에딧"(전체 파일 재작성 대신 부분 수정) 같은 부가 기능과 dyad/auto/* 자동 모델 별칭을 추가한다. 이 경로가 꺼져 있으면 사용자의 API 키가 각 프로바이더로 직접 나간다 — 즉 기본값은 완전 BYOK, Pro는 그 위에 얹는 부가 레이어다.

⑤ 앱 스캐폴딩 — scaffold/ 폴더와 템플릿

새 앱을 만들 때 기본 "react" 템플릿을 고르면 createFromTemplate.ts가 저장소에 통째로 들어있는 scaffold/ 디렉토리(Vite + React + TypeScript + Tailwind + shadcn/ui 기반의 이미 동작하는 최소 앱)를 그대로 복사해온다. 다른 커뮤니티 템플릿을 고르면 cloneRepo()가 HTTPS GitHub URL만 허용하는 검증을 거쳐 해당 저장소를 클론한 뒤 앱 폴더로 복사한다.

⑥ "에이전트 모드"는 같은 태그로 수렴한다 — 도구 호출 → XML 역변환

Dyad Pro의 로컬 에이전트(src/pro/main/ipc/handlers/local_agent/)는 한 번에 통짜 텍스트를 뱉는 대신 write_file·delete_file·rename_file·add_dependency·execute_sandbox_script 같은 구조화된 AI SDK 도구 호출(tool calling)을 쓴다. 그런데 tools/write_file.ts의 buildXml() 함수를 보면, 이 도구 호출 결과를 다시 <dyad-write path="...">...</dyad-write> 형태의 같은 XML 문자열로 역변환해 돌려준다. 즉 "한 방에 텍스트로 답하는 기본 모드"와 "단계적으로 도구를 부르는 에이전트 모드"가 서로 다른 입력 방식을 쓰면서도, 결국 같은 response_processor.ts 파이프라인 하나로 수렴하도록 설계돼 있다 — 적용 로직을 두 번 만들지 않으려는 선택이다.

에이전트가 임의 스크립트를 돌려야 할 때는 Node.js eval이 아니라 mustardscript라는 별도의 제한된 스크립팅 언어를 worker_threads 안에서(sandbox/runner.ts, sandbox_worker.ts) 실행해, 시간·메모리 제한(limits.ts)을 두고 read_file/list_files/file_stats/write_file 같은 딱 정해진 호스트 함수만 호출하게 한다(capabilities.ts의 SANDBOX_HOST_CALL_NAMES).

⑦ 동시성 제어 — 자체 액터 모델(distributed_machines/)

Dyad는 src/distributed_machines/ 아래 직접 만든 액터(actor) 모델 프레임워크(actor_host.ts, keyed_admission_gate.ts, remote_protocol.ts)를 갖고 있다. 앱 실행 상태·채팅 스트림 같은 것들을 "액터"로 다루면서, 같은 키(예: 같은 앱)에 대한 중복·낡은 명령이 잘못 끼어들지 못하게 "입장 게이트(admission gate)"로 세대(generation)를 관리한다. 이 저장소를 클론한 시점의 가장 최근 커밋(#4139, "keyed actor admission fences")이 정확히 이 모듈을 강화하는 리팩터링이었다 — 여러 창·여러 앱을 동시에 다루는 데스크탑 앱에서 레이스 컨디션을 막는 문제가 여전히 활발히 다뤄지고 있다는 뜻이다.

용어
BYOK (Bring Your Own Key)
서비스가 자체 AI 요금을 받는 대신, 사용자가 자기 OpenAI/Anthropic 등 API 키를 직접 입력해 그 회사에 직접 비용을 지불하는 방식. Dyad의 기본 모델이다.
용어
도구 호출 (tool calling / function calling)
LLM이 자유 텍스트 대신 미리 정의된 함수 이름과 인자(JSON)를 "호출"하는 형태로 답하게 하는 방식. Dyad Pro의 에이전트 모드는 이를 쓰고, 이후 다시 dyad 태그 형식으로 변환해 재사용한다.
용어
액터 모델 (actor model)
상태를 가진 독립적인 단위("액터")들이 서로 메시지만 주고받으며 동작하게 하여 동시성 버그(레이스 컨디션)를 구조적으로 줄이는 설계 기법. Dyad는 앱 실행·채팅 세션 등의 동시성 관리에 자체 구현체를 쓴다.

6기술 스택·의존성

Electron 40 + React 19 + Vite · Drizzle/SQLite · Vercel AI SDK · dugite(git) · oxlint/oxfmt
영역실제 사용 기술
데스크탑 셸Electron 40.0.0 + electron-forge 7(VitePlugin으로 main/preload/renderer 각각 빌드) + FusesPlugin(보안 강화 옵션)
프론트엔드React 19.2.4 + TanStack Router/Query + Jotai(원자 상태) + Tailwind CSS v4 + shadcn/ui("new-york") + Base UI/Radix
에디터/터미널Monaco Editor(코드 편집) · xterm.js(+addon 5종, 통합 터미널) · Lexical(메타의 리치텍스트 엔진, 채팅 입력의 @멘션 처리)
LLM 연동Vercel AI SDK(ai v6) + @ai-sdk/{openai,anthropic,google,azure,xai,amazon-bedrock,google-vertex,openai-compatible,mcp} · MCP(@modelcontextprotocol/sdk)로 외부 도구 서버 연결
로컬 저장better-sqlite3 + drizzle-orm(스키마: apps, prompts, mcpServers 등) — Dyad 자체 메타데이터 전용, 사용자 앱 코드는 그냥 디스크 파일
git 연동dugite(GitHub가 만든 Node용 git 바이너리 래퍼) — 매 AI 턴을 커밋 하나로 기록
외부 서비스 SDK@octokit류(GitHub), @dyad-sh/supabase-management-js, @neondatabase/api-client, @vercel/sdk
스크립트 샌드박스mustardscript(제한된 자체 스크립팅 언어) — 에이전트가 "코드를 실행"해야 할 때 Node eval 대신 사용
빌드/타입체크tsgo(TypeScript 네이티브 프리뷰 컴파일러, 빠른 --noEmit 체크) · oxlint/oxfmt(Rust 기반 초고속 린트·포맷터) · Husky pre-commit
테스트Vitest(유닛/통합, 파일 475개) + Playwright(E2E, 스펙 127개) + node --test(스크립트 자체 테스트)
배포/서명electron-forge Maker: Squirrel(Win)·ZIP/dmg(mac)·Deb/Rpm·커스텀 MakerAppImage(Linux) + windowsSign.ts(코드서명) + update-electron-app(자동 업데이트)
용어
오픈코어 (open-core)
핵심 기능은 오픈소스로 무료 공개하고, 그 위에 클라우드 전용 부가 기능(여기선 Dyad Pro 엔진·클라우드 샌드박스)을 유료로 얹는 사업 모델. Dyad는 src/pro/ 폴더만 별도 라이선스로 분리해 이 구조를 코드베이스에서도 물리적으로 나눈다.

7설치·요구사항

Mac/Windows 바이너리 다운로드 · 실질적으로는 Node.js 실행 환경도 필요
항목내용
일반 사용자dyad.sh에서 Mac·Windows용 설치 파일 다운로드(README 기준). 회원가입 불필요, 실행 즉시 온보딩(템플릿 선택 → API 키 입력)
AI 모델OpenAI/Anthropic/Google 등 중 하나 이상의 API 키(BYOK), 또는 로컬에 Ollama·LM Studio를 설치해 무료 로컬 모델 사용, 또는 Dyad Pro 구독
선택 연동GitHub(코드 푸시) · Supabase(백엔드/DB) · Vercel(배포) · Neon(서버리스 Postgres) — 전부 옵션, 없어도 로컬 프리뷰만으로 앱 개발 가능
개발자(소스 빌드)git clone → npm install → npm run init-precommit → npm run dev(내부적으로 scripts/start-supervisor.mjs). Node >=24 <26(package.json engines) 요구
네이티브 모듈macOS Keychain 접근용 dyad-keychain-reader(옵셔널 네이티브 모듈)는 npm run rebuild:keychain-reader로 별도 리빌드 필요 — Electron 패키징 시 자주 깨지는 지점이라 rules/native-modules.md로 따로 문서화돼 있다
주의
"코드를 몰라도 된다"는 "Node.js가 필요 없다"는 뜻이 아니다

Dyad는 생성된 앱을 실제로 로컬 Vite 개발 서버로 띄워 미리보기한다. 즉 내부적으로 진짜 npm install/npm run dev급 프로세스가 사용자 컴퓨터에서 돈다. 그래서 Dyad는 managed_node.ts 같은 자체 Node.js 버전 관리 로직까지 갖고 있다 — "논테크니컬 사용자"를 타깃으로 하지만 실제로는 Node 환경 문제가 온보딩 마찰의 큰 부분을 차지한다는 뜻이다.

8실제 사용 흐름

템플릿 선택 → 채팅 → 태그 적용 → git 커밋 → 프리뷰 → (선택) 배포

1. 앱 생성 — 템플릿 고르기

온보딩에서 프로바이더 API 키를 입력하고, "react"(내장 scaffold/) 또는 커뮤니티 GitHub 템플릿 중 하나를 고르면 createFromTemplate.ts가 로컬 앱 폴더를 만들고 git으로 초기화한다.

2. 채팅으로 요구사항 입력

"로그인 폼 추가해줘" 같은 프롬프트를 치면 chat_stream_handlers.ts가 시스템 프롬프트(system_prompt.ts) + 현재 코드베이스 컨텍스트와 함께 선택된 모델로 스트리밍 요청을 보낸다. Ask 모드(설명만)와 실제로 코드를 바꾸는 모드가 구분되어 있다.

3. 태그 파싱 → 실제 파일 적용

응답이 끝나면 response_processor.ts가 <dyad-write> 등을 실제 fs 쓰기·의존성 설치·SQL 실행으로 옮기고, 변경 전체를 git 커밋 하나로 남긴다. 필요하면 <dyad-command type="rebuild"> 같은 태그로 개발 서버 재시작도 제안한다.

4. 라이브 프리뷰 확인 & 되돌리기

이미 떠 있는 앱의 Vite 개발 서버가 변경을 감지해 iframe 프리뷰가 자동 새로고침된다. 마음에 안 들면 이전 git 커밋(버전)으로 그냥 되돌아갈 수 있다 — 별도 undo 시스템이 아니라 git 히스토리 자체가 버전함이다.

5. (선택) 백엔드 연결 · 배포

Supabase를 연결하면 AI가 만든 스키마/SQL이 자동 실행되고, GitHub에 원격 저장소를 만들어 푸시하거나 Vercel로 바로 배포할 수 있다. MCP 서버를 추가하면 외부 도구(예: 특정 API 문서 검색)를 채팅 컨텍스트에 붙일 수도 있다.

9함정·오해 방지

"완전 오픈소스" 아님 · "완전 로컬" 아님 · README와 실제 코드의 틈
오해 ①
"오픈소스"라고 해서 전체가 Apache 2.0은 아니다

README와 LICENSE는 src/pro/ 바깥은 Apache 2.0이라고 명시하지만, src/pro/ 안쪽은 별도의 src/pro/LICENSE를 따른다. 그 내용은 FSL-1.1-ALv2(Functional Source License) — "저작권자 Dyad Tech, Inc." 명의로, 소스는 공개돼 있지만 Dyad와 경쟁하는 상용 서비스로 재판매하는 용도(Competing Use)는 금지되고, 일정 기간이 지나야 Apache 2.0으로 전환되는 "훼어소스(fair-source)" 라이선스다. "레포 전체가 MIT/Apache라서 뭐든 자유롭게 가져다 써도 된다"고 오해하면 안 된다.

오해 ②
"완전 로컬 = 인터넷 연결 없이도 100% 동작"은 아니다

Ollama/LM Studio로 로컬 모델만 쓰면 오프라인에 가깝게 쓸 수 있지만, Dyad Pro 엔진·클라우드 샌드박스(cloud_sandbox_provider.ts, 로컬에서 돌리기 어려운 실행 환경을 원격 샌드박스에 동기화)·Supabase/Vercel/Neon 연동은 모두 클라우드에 의존하는 옵션 기능이다. "로컬 우선"이지 "로컬 전용"이 아니다.

오해 ③
README는 "Mac·Windows"만 말하지만 실제 빌드는 Linux도 만든다

README의 "Cross-platform: Easy to run on Mac or Windows" 문구만 보면 Linux 지원이 없어 보이지만, forge.config.ts에는 MakerDeb·MakerRpm·자체 제작한 MakerAppImage(x64/arm64)가 실제로 존재한다. 문서와 실제 배포 설정 사이의 대표적인 틈이다.

오해 ④
로컬 SQLite DB는 "내 앱의 데이터베이스"가 아니다

src/db/schema.ts의 better-sqlite3 DB는 Dyad 프로그램 자신의 채팅 기록·앱 목록·설정을 저장하는 것이지, 사용자가 만드는 앱(예: 할일 관리 앱)의 데이터베이스가 아니다. 생성된 앱이 실제 DB가 필요하면 Supabase나 Neon 같은 별도 백엔드를 연결해야 한다.

참고
이 프로젝트는 스스로 AI 코딩 에이전트를 리뷰어로 쓴다

.github/workflows/에는 claude-pr-review.yml·codex-pr-review.yml·claude-triage.yml·claude-rules-review.yml 등이 있다 — AI 앱 빌더를 만드는 팀이 실제로 Claude·Codex 같은 코딩 에이전트를 PR 리뷰·이슈 트리아지 자동화에 쓰고 있다는 뜻으로, "AI로 앱을 만드는 도구"를 만드는 과정 자체도 AI 에이전트로 굴러간다.

10대안 비교

Lovable · v0 · bolt.new · Onlook — "어디서 도는가"와 "코드는 누구 것인가"가 갈림길
도구실행 위치코드 소유/락인모델 선택권특징
Dyad로컬 Electron 앱내 디스크 + git, 락인 없음BYOK 전 프로바이더 + 로컬 모델(Ollama/LM Studio)무료 오픈코어, Pro는 부가 기능(엔진/샌드박스)만 유료
Lovable클라우드(SaaS)플랫폼 종속, GitHub 내보내기 가능플랫폼이 지정한 모델 위주크레딧 기반 과금, 원클릭 배포·백엔드 통합이 매끄러움
v0 (Vercel)클라우드(SaaS)Vercel 생태계에 최적화Vercel이 제공하는 모델UI 컴포넌트 생성/디자인 반복에 특히 강함
bolt.new (StackBlitz)브라우저 내 WebContainer(클라우드)내보내기 가능하나 편집은 브라우저 종속플랫폼 제공 모델 위주설치 없이 브라우저에서 즉시 실행, Node.js를 브라우저 안에서 흉내
Onlook로컬 데스크탑 앱(오픈소스)내 디스크BYOK 가능Dyad와 결이 가장 비슷한 "로컬 비주얼 코드 에디터" 계열, 다만 시각적 편집(디자인 툴 느낌)에 더 무게
비유

Lovable·v0·bolt.new가 "호텔 레스토랑"이라면(요리는 훌륭하지만 재료·주방은 호텔 것), Dyad는 "내 집 주방에 셰프를 초대해 요리하게 하는 것"이다. 냉장고(코드)와 그릇(파일)은 전부 내 것이고, 셰프(LLM)만 어느 회사에서 부를지 고를 수 있다.

11정리 · 누가 쓰면 좋은가

"코드 소유권 + 모델 자유도"가 최우선인 1인 빌더·개발자에게 적합

Dyad를 한 문장으로 정리하면 "클라우드 AI 앱 빌더의 UX를, 로컬 Electron 앱 + git + BYOK 조합으로 재구현한 오픈코어 도구"다. 핵심 설계는 화려한 신기술이 아니라 "LLM 응답을 태그로 구조화하고, 그 태그를 안전하게 실제 파일 시스템 작업으로 변환하는" 지극히 실용적인 파이프라인이다. 여기에 Vercel AI SDK로 프로바이더를 추상화하고, git 커밋을 버전 관리로 재활용하는 두 가지 선택이 제품 경험 대부분을 만들어낸다.

이런 사람에게 잘 맞는다

· 클라우드 AI 앱 빌더를 써봤지만 구독료·락인이 부담스러운 1인 개발자·사이드 프로젝트 빌더

· 특정 LLM 회사에 종속되기 싫고 여러 모델을 자유롭게 바꿔가며 비교해보고 싶은 사람

· "AI가 코드를 어떻게 적용하는지"를 실제 소스로 뜯어보며 배우고 싶은 학습자 — 태그 파서·응답 처리기·프로바이더 추상화 모두 읽을 수 있는 TypeScript 코드다

이런 사람에겐 별로다

· Node.js/git 개념조차 신경 쓰고 싶지 않은, 완전히 코드 개념 없는 초초보 사용자(내부적으로 실제 개발 서버가 돈다)

· 원클릭으로 팀 협업·클라우드 백엔드까지 매끄럽게 이어지는 완성형 매니지드 경험을 원하는 경우 (Lovable류가 더 매끄러울 수 있다)

12직접 해볼 수 있는 실습 과제

템플릿 읽기부터 스트리밍·경로 검증·동시성 제어까지, 소스와 테스트로 확인하기

명령은 Dyad 저장소 루트에서 실행한다. 소스 읽기는 의존성 설치 없이 진행할 수 있으며, 테스트 명령은 의존성이 이미 설치된 별도 학습용 사본을 전제로 한다. 아래 과제는 API 키나 외부 서비스 연결 없이 진행할 수 있다.

과제 1 · 입문

빈 앱의 첫 화면은 어디에서 오는가

목표: Dyad 프로그램의 코드와, Dyad가 만들어 주는 앱의 템플릿을 구분한다.

  1. package.json과 scaffold/package.json의 scripts를 비교한다. 저장소 루트의 개발 명령과 템플릿의 Vite 실행 명령이 각각 무엇을 실행하는지 찾는다.
  2. scaffold/src/main.tsx에서 시작해 App.tsx와 pages/Index.tsx까지 따라가며, 첫 화면에 표시되는 문구를 찾는다.
  3. src/ipc/handlers/createFromTemplate.ts의 templateId === "react" 분기를 읽는다. scaffold/에서 fullAppPath로 복사되는 경로를 그림으로 옮긴다.
cat package.json scaffold/package.json
cat scaffold/src/main.tsx scaffold/src/App.tsx scaffold/src/pages/Index.tsx
rg -n 'templateId|scaffold|copyDirectoryRecursive|fullAppPath' src/ipc/handlers/createFromTemplate.ts

완료 기준: main.tsx → App.tsx → pages/Index.tsx 흐름도를 만들고, 생성된 앱의 첫 화면 문구가 그 앱의 src/pages/Index.tsx에 있다는 것을 설명한다.

과제 2 · 중급

미완성 태그와 실행 가능한 지시를 구분하기

목표: 화면에 진행 상황을 보여주는 파서와, 파일 작업에 필요한 정보를 뽑는 파서의 역할 차이를 확인한다.

  1. src/lib/streamingMessageParser.ts의 parseFullMessage와 src/ipc/utils/dyad_tag_parser.ts의 getDyadWriteTags를 읽는다.
  2. 아래 입력을 각각 독립된 메시지로 보고, 화면용 블록의 complete 값과 쓰기 태그 추출 결과를 예상한다. 닫는 태그와 path 속성의 유무를 따로 살핀다.
  3. src/lib/streamingMessageParser.test.ts의 feedAll과 분할 입력 검사를 읽고 테스트를 실행한다. advanceParser에 지금까지 누적된 문자열을 넘기는 방식을 확인한다.
<dyad-write path="src/sample.ts">export const value = 1;</dyad-write>

<dyad-write path="src/sample.ts">export const value = 1;

<dyad-write>export const value = 1;</dyad-write>
rg -n 'parseFullMessage|advanceParser|getParserBlocks' src/lib/streamingMessageParser.ts
rg -n 'getDyadWriteTags|parseDyadFileTags|pathMatch' src/ipc/utils/dyad_tag_parser.ts
npm run test:watch -- --run src/lib/streamingMessageParser.test.ts

완료 기준: 세 입력의 비교표를 만들고, 미완성 태그는 화면에 표시할 수 있어도 완성된 쓰기 지시로 추출되지 않는 이유를 설명한다. path가 빠진 태그도 쓰기 목록에서 제외됨을 소스로 확인하고, 기존 스트리밍 테스트가 통과하는지 점검한다.

과제 3 · 심화

경로 탈출 방어와 프로젝트 루트 보호 비교하기

목표: 경로가 프로젝트 안에 있는지 검사하는 일과, 프로젝트 폴더 자체의 삭제를 막는 일을 구분한다.

  1. src/ipc/utils/path_utils.ts의 safeJoin과 assertNotProjectRootPath를 읽는다. 경로 정규화와 프로젝트 루트 비교가 각각 어디에서 이루어지는지 찾는다.
  2. 기준 폴더를 /app/workspace로 두고 src/App.tsx, src/../package.json, ../outside.txt, ../workspace-2/file.txt, /etc/passwd, .의 허용 여부를 두 함수별로 예상한다.
  3. 기존 테스트의 safeJoin 묶음을 실행한 뒤, src/ipc/processors/response_processor.ts에서 삭제 태그를 적용하기 전에 루트 검사를 수행하는 지점을 찾는다.
rg -n 'safeJoin|assertNotProjectRootPath|isProjectRootPath' src/ipc/utils/path_utils.ts
npm run test:watch -- --run src/ipc/utils/path_utils.test.ts -t safeJoin
rg -n 'dyadDeletePaths|assertNotProjectRootPath|prepareDeletePath' src/ipc/processors/response_processor.ts

완료 기준: 여섯 경로의 판정표를 완성한다. 특히 .은 safeJoin을 통과해도 삭제용 루트 검사에서 거부되며, 이름의 앞부분이 비슷한 형제 폴더도 허용되지 않는 이유를 설명한다.

과제 4 · 심화

끝나지 않은 작업을 기다리는 입장 게이트 추적하기

목표: 비동기 작업의 완료 순서와 새 작업의 진입 허용을 분리해 이해한다.

  1. src/distributed_machines/keyed_admission_gate.ts의 beginFence, seal, commit, release를 읽고 상태 전이를 정리한다.
  2. 같은 폴더의 keyed_admission_gate.test.ts에서 deferred와 command.resolve()를 찾는다. 진행 중인 작업이 끝나기 전후로 seal()이 언제 완료되는지 시간순으로 표시한다.
  3. 테스트의 WORK와 CLEANUP 이벤트가 각 상태에서 허용되는지 비교한다. 이어서 stale-generation 검사 사례를 읽고, 이전 세대의 작업을 다시 확인해야 하는 이유를 적는다.
rg -n 'beginFence|seal|commit|release|assertCurrentGeneration' src/distributed_machines/keyed_admission_gate.ts
rg -n 'deferred|command.resolve|stale-generation' src/distributed_machines/keyed_admission_gate.test.ts
npm run test:watch -- --run src/distributed_machines/keyed_admission_gate.test.ts

완료 기준: open → draining → sealed → committed → open의 정상 흐름과 이벤트 허용표를 만든다. 정리 이벤트의 허용은 배출 중인 상태의 정책에 달려 있고, 봉인 이후에는 막힌다는 점을 설명하며 기존 테스트 결과와 대조한다.

13관련 기술 심화 학습 로드맵

6주 동안 화면·프로세스 경계·코드 적용·저장·동시성을 차례로 연결하기

매주 개념 정리 → 소스 추적 → 결과물 검토 순서로 진행한다. 앞 주에 만든 흐름도를 다음 주의 입력으로 삼으면, 개별 파일이 전체 앱에서 맡는 역할을 놓치지 않을 수 있다.

주차배울 개념이 레포에서 읽을 곳결과물
1주차 TypeScript의 모듈과 타입, React 컴포넌트, Vite와 패키지 스크립트 package.json, scaffold/package.json, scaffold/src/main.tsx, scaffold/src/App.tsx, scaffold/src/pages/Index.tsx, scaffold/vite.config.ts Dyad와 생성 앱의 실행 명령 비교표, 첫 화면까지의 컴포넌트 연결도
2주차 Electron 프로세스 분리, IPC 계약, 채널 허용 목록과 오류 전달 src/preload.ts, src/ipc/preload/channels.ts, src/ipc/contracts/core.ts, src/ipc/types/app.ts, src/ipc/handlers/app_handlers.ts get-app 요청의 입력 스키마부터 핸들러와 응답 봉투 해제까지의 흐름도
3주차 시스템 프롬프트와 출력 규약, 증분 파싱, 완성된 태그의 추출 src/prompts/system_prompt.ts, src/lib/streamingMessageParser.ts, src/lib/streamingMessageParser.test.ts, src/ipc/utils/dyad_tag_parser.ts 완성·미완성·속성 누락 입력의 비교표, 누적 입력을 처리하는 과정 설명
4주차 경로 정규화와 경계 검사, 파일 변경과 git 기록의 연결 src/ipc/utils/path_utils.ts, src/ipc/utils/path_utils.test.ts, src/ipc/processors/response_processor.ts, src/ipc/utils/git_utils.ts 경로 판정표, 쓰기 태그 추출에서 파일 변경과 커밋까지의 호출 순서도
5주차 SQLite와 ORM, 스키마와 마이그레이션, LLM 프로바이더 추상화 src/db/schema.ts, src/db/index.ts, drizzle.config.ts, drizzle/, src/ipc/utils/get_model_client.ts 앱 목록·채팅 기록·앱 소스의 저장 위치 지도, 모델 공급자별 클라이언트 생성 분기표
6주차 액터의 수명, 비동기 작업 배출, 세대 검사와 상태 전이 src/distributed_machines/actor_host.ts, src/distributed_machines/keyed_admission_gate.ts, src/distributed_machines/keyed_admission_gate.test.ts 진행 중인 작업의 종료와 새 작업 차단을 함께 표시한 시간선, 오래된 세대가 거부되는 사례 설명
학습 점검

각 결과물에는 근거가 된 파일과 함수 이름을 함께 적는다. 화면 표시, 작업 허용 판단, 실제 파일 변경, 기록 저장이 어느 단계에서 일어나는지 구분해 설명할 수 있으면 다음 주제로 넘어간다.

14핵심 키워드 사전

화면에서 로컬 파일까지 이어지는 흐름을 읽기 위한 15개 용어
용어
Electron
웹 기술로 화면을 만들면서 데스크탑 기능을 함께 사용하는 앱 실행 환경이다. Dyad에서는 React 화면과 파일 작업을 맡는 메인 프로세스가 나뉘며, src/preload.ts가 둘 사이의 통로를 노출한다.
용어
preload와 contextBridge
preload는 웹 화면에 필요한 기능을 준비하는 스크립트이고, contextBridge는 격리된 화면 쪽에 제한된 API를 전달하는 Electron 기능이다. Dyad의 src/preload.ts는 window.electron을 노출하고 IPC 채널을 검사한다.
용어
IPC
서로 다른 프로세스가 메시지를 주고받는 통신 방식이다. Dyad의 src/ipc/types/app.ts는 get-app 같은 요청의 채널과 입출력 스키마를 정의한다.
용어
응답 봉투
성공 값과 오류 정보를 일정한 형태로 감싸 전달하는 구조다. src/ipc/contracts/core.ts의 IpcInvokeEnvelope는 ok 값으로 성공과 실패를 구분하며, unwrapIpcEnvelope는 값을 꺼내거나 오류를 던진다.
용어
스캐폴딩
새 프로젝트에 필요한 기본 파일과 폴더를 미리 갖춰 주는 작업이다. Dyad는 기본 React 템플릿을 고르면 src/ipc/handlers/createFromTemplate.ts에서 scaffold/를 새 앱 폴더로 복사한다.
용어
BYOK
사용자가 모델 공급자의 API 키를 직접 준비해 연결하는 방식이다. Dyad에서는 src/ipc/utils/get_model_client.ts의 공급자별 클라이언트 생성 흐름을 읽으면 키와 모델 선택이 연결되는 지점을 확인할 수 있다.
용어
LLM 프로바이더 추상화
모델 공급자마다 다른 연결 방식을 공통 인터페이스 뒤에 모으는 설계다. Dyad의 src/ipc/utils/get_model_client.ts는 model.provider에 따라 AI SDK 클라이언트나 로컬 모델 연결을 선택한다.
용어
스트리밍 파서
응답이 조금씩 도착하는 동안 쌓인 텍스트를 해석해 화면에 보여줄 구조로 바꾸는 코드다. Dyad의 src/lib/streamingMessageParser.ts는 닫히지 않은 태그도 진행 중인 블록으로 표현한다.
용어
<dyad-write>
파일 경로와 파일 내용을 담는 Dyad 고유의 코드 출력 규약이다. src/ipc/utils/dyad_tag_parser.ts의 getDyadWriteTags가 정보를 추출하고, src/ipc/processors/response_processor.ts가 파일 적용을 담당한다.
용어
경로 탈출
상위 폴더를 뜻하는 ..이나 절대 경로 등을 이용해 의도한 작업 폴더 밖에 접근하는 문제다. Dyad의 src/ipc/utils/path_utils.ts에 있는 safeJoin은 경로를 정규화하고 기준 폴더를 벗어나는 조합을 거부한다.
용어
SQLite
별도 데이터베이스 서버 없이 로컬 파일에 데이터를 저장하는 데이터베이스다. Dyad는 src/db/index.ts에서 sqlite.db를 열어 앱 목록과 채팅 기록 같은 프로그램 내부 데이터를 관리한다.
용어
Drizzle ORM
테이블 구조와 데이터 조회를 TypeScript 코드로 다루게 해 주는 도구다. Dyad의 테이블 정의는 src/db/schema.ts에 있고, drizzle.config.ts는 스키마 경로와 마이그레이션 출력 위치를 지정한다.
용어
git 커밋
프로젝트 파일의 상태를 나중에 비교하고 되돌릴 수 있도록 남기는 버전 기록이다. Dyad의 src/ipc/processors/response_processor.ts는 변경 적용 뒤 src/ipc/utils/git_utils.ts의 커밋 함수를 사용해 앱 코드의 이력을 남긴다.
용어
액터 모델
상태를 가진 작업 단위를 나누고 메시지로 동작을 요청하는 동시성 설계 방식이다. Dyad의 src/distributed_machines/actor_host.ts는 액터의 수명과 동작을 관리하는 구현을 담고 있다.
용어
입장 게이트와 세대
같은 대상에 새 작업이 들어와도 되는지 판단하고, 이전 상태에서 준비된 작업을 구별하는 장치다. Dyad의 src/distributed_machines/keyed_admission_gate.ts는 대상 키별로 작업을 배출·차단하며 세대가 오래된 요청을 검사한다.