분석 대상: Open Design 0.8.0-preview (커밋
a766048)
한 줄 요약
Open Design은 LLM 호출도 추론도 하지 않는다. 사용자 PC에 이미 설치된 코딩 에이전트 CLI(Claude Code · Codex · Cursor · Gemini 등)를 child_process.spawn으로 띄우고, 표준입출력의 JSONL 스트림을 정규화해 채팅 UI로 흘려보낸다. 자체 추론 능력 0 — 모든 지능은 외부 에이전트의 것이다.
모노레포 구조
open-design/ (313 MB)
├── apps/
│ ├── daemon/ # Node 백엔드. 핵심 두뇌 (97 파일)
│ ├── web/ # React 프론트. 채팅 UI · iframe 프리뷰
│ ├── desktop/ # Electron main process (1751줄)
│ ├── packaged/ # Electron 엔트리 (얇은 쉘)
│ ├── landing-page/
│ └── telemetry-worker/
│
├── packages/
│ ├── platform/ # 플랫폼 추상화
│ ├── sidecar/ # 사이드카 프로세스 관리
│ ├── sidecar-proto/ # IPC 프로토콜 정의
│ ├── plugin-runtime/ # 플러그인 시스템
│ ├── agui-adapter/ # AGUI (agent UI) 어댑터
│ └── contracts/ # 공통 타입 정의
│
├── skills/ # 133개 SKILL.md (스킬 카탈로그)
├── design-systems/ # 149개 디자인 시스템 (DESIGN.md + tokens.css)
├── plugins/
│ └── _official/atoms/ # 빌트인 아톰 (figma-extract 등)
│
└── tools/
├── dev/ # 로컬 개발 컨트롤
└── pack/ # Mac 패키징 컨트롤
4-tier 프로세스 구조
각 층은 다른 프로세스로, 의도적으로 권한이 분리돼 있다.
- Renderer: sandboxed Chromium.
nodeIntegration: false. 파일 I/O 불가 - Main: Electron 권한 보유. preload script · BrowserWindow만 직접 제어
- Daemon: 진짜 권한 보유 프로세스. 모든 파일 쓰기 · 네트워크 호출 · 에이전트 spawn의 유일한 entry
- Agent: Daemon이 spawn한 자식 프로세스. cwd 밖 접근 불가
실행 모델
시동 순서 (pnpm tools-dev)
1) tools/dev CLI가 환경 검증 (Node 버전 · pnpm · PATH)
2) sidecar runtime 부트스트랩
3) Daemon 프로세스 spawn — apps/daemon/dist/server.js
- SQLite 열기 (~/.od/app.sqlite)
- skills/*/SKILL.md 스캔
- design-systems/*/DESIGN.md 스캔
- 에이전트 PATH 감지 (runtimes/detection.ts)
- HTTP 서버 listen (포트 자동 할당)
4) Web 프로세스 spawn — apps/web (Vite dev server)
5) (선택) Desktop spawn — apps/desktop · Electron BrowserWindow
각 spawn은 "sidecar stamp"라는 시그니처를 갖는다. namespace · stamp · PID · 실행파일 경로 · 로그 경로가 모두 검증되어 다른 namespace의 프로세스를 실수로 죽이지 않게 한다.
IPC 프로토콜: JSON-IPC over Unix Domain Socket
packages/sidecar가 정의하는 두 종류 채널이 있다.
(A) Desktop ↔ Daemon (양방향 RPC) — 메시지 종류: STATUS · EVAL · SCREENSHOT · CONSOLE · CLICK · SHUTDOWN. apps/desktop/src/main/runtime.ts(1084줄)가 정의.
(B) Web ↔ Daemon (HTTP + SSE) — 일반 REST: /api/projects · /api/skills · /api/design-systems. 스트리밍 SSE: /api/chat/stream · /api/live-artifact/*. BYOK proxy: /api/proxy/{anthropic,openai,azure,google}/stream.
IPC 경로:
// apps/daemon/src/server-context.ts
resolveAppIpcPath(namespace, stamp) =>
/tmp/od-{namespace}-{stamp}/ipc.sock (Unix)
\\.\pipe\od-{namespace}-{stamp} (Windows)에이전트 spawn 메커니즘
핵심 파일은 apps/daemon/src/runtimes/launch.ts다.
spawn(launchPath, args, {
stdio: ['pipe', 'pipe', 'pipe'], // stdin · stdout · stderr 전부 캡처
cwd: projectDir, // 에이전트가 동작할 작업 폴더
env: applyAgentLaunchEnv(env, launch), // PATH 보강
})stdin으로 프롬프트 전달:
- argv 길이 제한 회피 (Linux MAX_ARG_STRLEN ~128KB, Windows ~32KB)
- Claude의 경우
--input-format stream-json사용 → JSONL 연속 입력 가능 - 이미지 첨부도 stream-json content 배열에 base64 embed
- 프롬프트는 시스템 프롬프트(스킬 본문 포함) + 사용자 메시지 + 메모리 컨텍스트
stdout 파싱:
- Claude:
apps/daemon/src/claude-stream.ts가--output-format stream-json --verbose파싱 - 16개 에이전트마다 자체 streamFormat
- 모두 5개 정규화 이벤트로 통일:
status·text_delta·thinking_delta·tool_use·tool_result·usage
이벤트 흐름:
데이터 영속화
SQLite 한 파일(~/.od/app.sqlite)에 모두 저장된다.
projects(id, name, cwd, createdAt)conversations(project_id, agent, model)messages(conversation_id, role, content, tool_calls, usage)tabs(어떤 파일 열려있는지)saved_templatesmemory_extractions(LLM이 추출한 영구 메모리)mcp_tokens(OAuth 토큰)routines(스케줄된 작업)
리오픈 시 마지막 todo card · 열린 파일 · 채팅 컨텍스트가 정확히 복원된다.
권한·격리 모델
| 층 | 권한 | 격리 |
|---|---|---|
| Web (Renderer) | DOM만 | sandbox="allow-scripts allow-downloads" |
| 산출물 iframe | DOM만 | srcdoc 격리, parent와 단절 |
| Main (Electron) | BrowserWindow 제어 | nodeIntegration: false, contextIsolation |
| Daemon | 전체 권한 | 단일 listener 프로세스 |
| Agent | cwd 한정 | bypassPermissions 모드지만 cwd 밖 접근 불가 |
iframe 산출물이 동일 origin 권한이 없는 게 핵심이다. 사용자가 만든 React 컴포넌트가 절대 daemon API를 호출하지 못한다.
에이전트 통합: 16개 CLI 어댑터
어댑터 구조
apps/daemon/src/runtimes/defs/에 각 에이전트별로 한 파일씩 있다.
claude.ts (78줄, 가장 정교)
codex.ts (60줄)
cursor-agent.ts
gemini.ts
copilot.ts
opencode.ts
qwen.ts / qoder.ts / deepseek.ts / devin.ts
hermes.ts / kilo.ts / kimi.ts / kiro.ts / pi.ts / vibe.ts
shared.ts (공통 유틸)
모두 RuntimeAgentDef 인터페이스를 충족한다.
interface RuntimeAgentDef {
id: string // 'claude' · 'codex' · ...
name: string // 'Claude Code'
bin: string // PATH에서 찾을 바이너리명
fallbackBins?: string[] // 대체 바이너리
versionArgs: string[] // 감지용 ['--version']
helpArgs: string[] // 기능 probe
capabilityFlags: Record<string, string>
fallbackModels: ModelOption[]
buildArgs: (prompt, images, dirs, opts) => string[]
promptViaStdin: boolean
promptInputFormat: string
streamFormat: string
}Claude Code 어댑터 (예시)
{
id: 'claude',
bin: 'claude',
fallbackBins: ['openclaude'],
buildArgs: (prompt, images, extraDirs, opts) => [
'-p',
'--input-format', 'stream-json',
'--output-format', 'stream-json',
'--verbose',
...(caps.partialMessages ? ['--include-partial-messages'] : []),
...(opts.model ? ['--model', opts.model] : []),
...(extraDirs.length ? ['--add-dir', ...extraDirs] : []),
'--permission-mode', 'bypassPermissions',
],
promptViaStdin: true,
promptInputFormat: 'stream-json',
streamFormat: 'claude-stream-json',
}핵심 포인트는 세 가지다.
--permission-mode bypassPermissions— 사용자가 매 도구 호출마다 승인하지 않아도 됨--input-format stream-json— daemon이AskUserQuestiontool의tool_result를 stdin에 추가 푸시 가능- capability flag probing으로 구버전 Claude Code 호환
감지 (PATH 스캔)
apps/daemon/src/runtimes/detection.ts가 시작 시 다음을 수행한다.
- PATH 환경변수 분리
- 각 디렉토리에서
bin·fallbackBins존재 확인 executable비트 확인<bin> --version실행 → 버전 파싱<bin> <helpArgs>실행 → 출력에서 capability flag substring 매칭- UI 셀렉터에 설치된 것만 노출
인증·과금: Open Design은 안 든다
| 사용자 상황 | 동작 |
|---|---|
Claude Code 구독 (claude login 완료) | CLI가 자체 OAuth 보관 → spawn만 → 추가 키 불필요 |
| Codex / OpenAI Plus | 동일. codex login으로 인증된 세션 |
| API 키만 있음 | /api/proxy/{provider}/stream에 입력 → SSE 정규화 |
| CLI도 키도 없음 | 사용 불가 |
Open Design은 결제·인증을 절대 보관하지 않는다. 이게 BYOK(Bring Your Own Key)의 의미다.
스트림 정규화: 5종 이벤트
type StreamEvent =
| { type: 'status', stage: 'initializing' | 'requesting' | 'thinking' }
| { type: 'text_delta', text: string }
| { type: 'thinking_delta', text: string }
| { type: 'tool_use', id, name, input }
| { type: 'tool_result', tool_use_id, content, is_error }
| { type: 'usage', input_tokens, output_tokens, cache_hit, cost }UI는 이 5종만 처리한다. 어떤 에이전트가 와도 동일하다.
산출물 렌더링 파이프라인
산출물 타입
apps/daemon/src/artifact-manifest.ts가 정의한다.
ALLOWED_KINDS = [
'html', // 단일 HTML 파일 (가장 흔함)
'deck', // 슬라이드 덱
'react-component', // JSX/TSX 컴포넌트
'markdown-document',
'svg',
'diagram',
'code-snippet',
'mini-app',
'design-system',
]
ALLOWED_EXPORTS = ['html', 'pdf', 'zip', 'pptx', 'jsx', 'md', 'svg', 'txt']react-component 렌더러: Babel-in-iframe 패턴
apps/web/src/runtime/react-component.ts(231줄)의 흐름.
prepareReactComponentSource가 하는 일은 다음과 같다.
import { useState } from 'react'→const { useState } = window.Reactimport X from 'foo'→ strip (비-react import는 무시)export default Foo→window.__OpenDesignComponent = Foo
transformImportDeclarations의 정규식 한 줄을 추가하면 react-native · react-native-web 도 window.X로 매핑할 수 있다.
렌더러 레지스트리
export const renderers: ArtifactRenderer[] = [
HtmlRenderer,
DeckHtmlRenderer,
ReactComponentRenderer, // 현재 사용 스킬 0개 (dormant)
MarkdownRenderer,
SvgRenderer,
...
]파일 watch · 스트리밍
apps/daemon/src/project-watchers.ts가 프로젝트 cwd를 chokidar로 watch한다. Agent가 Write tool로 파일 생성 → daemon 감지 → SSE 채널로 web에 push → FileViewer가 새 파일을 자동 표시한다.
스킬·디자인 시스템 시스템
SKILL.md 구조
---
name: login-flow
description: Mobile login screens
od:
mode: prototype
platform: mobile
scenario: design
preview:
type: html
design_system:
requires: false
fidelity: high-fidelity
triggers:
- login
- sign in
---
# Login Flow Skill
본문 — 시스템 프롬프트로 그대로 합쳐짐.
## Workflow
1. Read reference files first
2. Clarify auth method
3. Checklist gate
4. Build the HTML prototype
5. Wrap in <artifact>시스템 프롬프트 합성
[시스템 프롬프트] =
[선택된 스킬의 body]
+ [references/*.md 본문 (자동 첨부)]
+ [선택된 디자인 시스템의 DESIGN.md]
+ [tokens.css 코드]
+ [Open Design 공통 가이드라인]
+ [5 visual directions 정의]
[사용자 메시지] =
[Turn-1 폼 답변 (clarify 결과)]
+ [채팅 입력]
+ [첨부 이미지 base64]
자체 비판 루프 (Critique Theater)
생성 후 LLM이 5차원 자체 비판을 수행한다.
- Layout (정보 위계)
- Typography (위계·일관성)
- Color (대비·의미)
- Components (재사용성·일관성)
- Brand alignment
점수 미달이면 재생성한다.
디자인 시스템: DESIGN.md
design-systems/{name}/에는 세 가지가 있다.
DESIGN.md— 9섹션 스키마 (color · typography · spacing · layout · components · motion · voice · brand · anti-patterns)tokens.css— CSS 변수components.html— 라이브 쇼케이스
빌트인 149개.
Visual Directions (브랜드 없을 때 fallback)
5종으로 정의돼 있다.
- Editorial Monocle
- Modern Minimal
- Warm Soft
- Tech Utility
- Brutalist Experimental
각 direction은 OKLch 팔레트 + 폰트 스택 + 톤 설명으로 구성된다.
Mac 패키징: Electron 출시 경로
tools-pack 명령어
pnpm tools-pack mac build --to all # .app + .dmg + .zip
pnpm tools-pack mac build --to app # .app만
pnpm tools-pack mac build --to all --signed # Apple Developer 서명 + 노타라이즈
pnpm tools-pack mac install # /Applications로 복사
pnpm tools-pack mac start / stop / logs / uninstall / cleanup빌드 산출물
.tmp/tools-pack/out/mac/namespaces/<namespace>/
├── Open Design.app # Apple Silicon 빌드
├── Open Design-{ver}.dmg # 배포용
└── Open Design-{ver}.zip # auto-update 용
런타임 데이터
~/Library/Application Support/Open Design/
├── data/ # 프로젝트 · SQLite
├── logs/ # desktop · web · daemon
├── runtime/ # 사이드카 base
└── user-data/ # Electron/Chromium 세션
코드 서명 자산
tools/pack/resources/mac/:
entitlements.mac.plistentitlements.mac.inherit.plistnotarize.cjs— Apple Notary Service 자동 제출
Apple Developer ID가 있어야 진짜 서명 빌드. 없으면 unsigned .app이다.
여러 개발자 빌드를 동시 설치 가능한 namespace 격리도 있다. 각자 데이터·로그·runtime이 분리된다.
AI 기반 디자인의 본질적 한계
LLM이 디자인을 못 하는 이유
LLM은 텍스트 생성 기계다. 디자인은 본질적으로 다음을 요구한다.
| 디자인 능력 | LLM 한계 |
|---|---|
| 시각 위계 인식 | 토큰 시퀀스로 시각 우선순위 못 봄 |
| 색·간격·정렬 감각 | 숫자 텍스트로만 추론, 시각 피드백 부재 |
| 일관성 유지 | 화면 N개 사이의 디자인 토큰 동기화 어려움 |
| 사용자 흐름 이해 | 정적 코드 → 동적 경험 변환 안 됨 |
| 디테일 (radius·shadow·motion) | 학습 데이터에 거의 없음 |
"AI 티"의 본질
AI가 만든 디자인이 어색한 이유는 여덟 가지다.
- 평균에 회귀 — 학습 데이터의 중앙값을 향함 → 평범하고 generic, 시그니처 없음
- clichéd 패턴 반복 — "온보딩 = 3 슬라이드 + 점 인디케이터" 같은 stale 패턴
- 회색 + 회색 콤보 — 안전한 색 선택, 대비 부족
- 자유로운 placeholder — "Hello World" · "Welcome to MyApp" 같은 dummy text
- 컴포넌트 미스매치 — iOS 앱인데 Material 컴포넌트, 한국 앱인데 영어 톤
- 불일치한 spacing — 8/12/16 같은 표준 그리드 무시
- 타이포 위계 빈약 — 모든 텍스트가 비슷한 크기
- 인터랙션 의도 부재 — 정적 화면만 만들고 사용자 여정 무시
시각 모달리티의 비대칭
- LLM은 텍스트 입력 → 텍스트 출력
- Vision LLM은 이미지 입력 → 텍스트 출력 (이미지 출력 못 함)
- 디자인은 의도 → 이미지 출력인데, 직접 경로가 없음
- 우회: 코드 출력 → 코드를 렌더해 이미지로
- 문제: 코드 작성 시 시각 피드백 없음 (눈 가린 채 그림 그리기)
평가의 어려움
"이 디자인이 좋은가?"는 정량 평가가 어렵다. Vision LLM으로 "vibe 점수"를 매길 수 있지만 신뢰도가 낮다. 디자이너의 암묵지가 정형화되지 않는다.
데이터 빈곤
- 공개 모바일 디자인 데이터셋이 거의 없음
- Dribbble · Behance는 마케팅 비주얼, 실 앱 UI 아님
- App Store 스크린샷은 작고 marketing-tuned
- → 실 앱 플로우 데이터가 SOTA 학습에 부재
모바일 디자인의 특수 한계
모바일 고유 제약
| 요소 | 데스크탑 | 모바일 |
|---|---|---|
| 화면 너비 | 1280–2560px | 360–430px |
| 입력 | 마우스 | 터치 (44px 최소) |
| 텍스트 입력 | 키보드 | SoftKeyboard (화면 절반 가림) |
| 네비게이션 | 메뉴바 · 사이드바 | 하단 탭 + 스택 |
| 콘텐츠 밀도 | 높음 | 낮음 |
| 인터랙션 | 클릭·hover | 탭·롱프레스·스와이프·핀치 |
| 시스템 영역 | 단일 모달 | safe-area · notch · 홈인디케이터 · 키보드 안전영역 |
| 폼팩터 | 단일 | iPhone SE부터 Pro Max, Android 다종 |
LLM이 이걸 다 머리에 담고 코드 짜기는 어렵다.
플랫폼 컨벤션 차이
- iOS HIG: 우측 상단 X · 좌측 상단 Back · 큰 타이틀 · sheet
- Material: FAB · 상단 햄버거 · bottom sheet · snackbar
- 한국형: 토스 톤 (미니멀 + 큰 숫자) vs 카카오 톤 (둥글둥글 + 노랑) vs 당근 톤 (오렌지 + 친근)
영어 학습 데이터의 LLM은 영미 컨벤션에 편향돼 있다. 한국 톤은 거의 못 만든다.
한국 모바일 톤의 특수성
실제 한국 앱 관찰에서 반복되는 패턴이다.
- 이모지·이모티콘 풍부 — "📦 도착 완료" 같은 톤
- 큰 숫자 강조 — 금액·일자·평점이 화면 중앙
- 버튼 라벨이 길다 — "지금 바로 받기" 같은 액션-지향
- 소셜 증명 — "1.2만 명이 보고 있어요" 같은 라이브 카운트
- 이미지 우세 — 카드형·캐러셀·그리드 비중 큼
- 타이트한 간격 — 정보 밀도 높음
- 모달·바텀시트 빈번 — 페이지 전환 대신 오버레이
- 온보딩이 짧음 — 1–2 화면 (서구는 3–5)
서구 디자인 학습된 LLM이 한국 톤을 흉내내려면 명시적 참조가 필요하다.
인터랙션 미리보기의 어려움
- HTML mock은 인터랙션 죽음
- Figma prototype은 인터랙션 흉내 가능하지만 실 코드 아님
- 실 RN/네이티브 빌드는 시뮬레이터 부팅 30초+
- → 빠른 디자인 iteration의 표준 도구 부재
멀티 화면 일관성
- 단일 화면은 LLM이 그럭저럭
- 5 화면 플로우의 디자인 토큰 일관성은 거의 못 함
- 디자인 시스템 강제 없으면 화면마다 색·간격·폰트가 달라짐
- → token-driven 코드 생성이 필수
Open Design의 실제 역할
무엇을 하는가
Open Design은 에이전트 워크벤치다. LLM 호출도 추론도 안 하지만, 에이전트가 좋은 디자인 산출물을 만들도록 무대를 차린다.
| 제공 요소 | 기여 |
|---|---|
| 133개 스킬 | 각 스킬 = 특정 산출물의 시스템 프롬프트 + 체크리스트 + 워크플로 |
| 149개 디자인 시스템 | DESIGN.md · tokens.css가 즉시 사용 가능 |
| 5 visual directions | 결정론적 OKLch 팔레트 + 폰트 스택 |
| Turn-1 question form | 모델이 첫 픽셀 그리기 전에 surface · audience · tone · brand 확정 |
| TodoWrite plan card | 에이전트 plan을 실시간 카드로 |
| Sandboxed preview iframe | 산출물을 즉시 시각화 |
| 5차원 self-critique | 생성 후 자체 점수 매기고 미달 시 재생성 |
| 16개 에이전트 어댑터 | 사용자가 어떤 CLI 쓰든 동일 UX |
| BYOK proxy | CLI 없어도 API 키로 사용 |
| SQLite persistence | 작업 컨텍스트 영속화 |
| Electron 데스크탑 | 네이티브 앱 경험 |
가치 명제
- "Claude Design / Figma Make"의 오픈 대안
- 사용자 PC의 기존 코딩 에이전트를 디자인 엔진으로 변환
- 결과물의 일관성·품질 보장 (스킬 + 디자인 시스템 강제)
- 데이터·모델·산출물 모두 사용자 소유
- BYOK — lock-in 없음
Open Design의 한계
모바일 빈곤
platform: mobile스킬이 단 1개 (login-flow)- README가 광고한 mobile-app · gamified-app · mobile-onboarding 스킬은 부재 (스크린샷만 존재)
- 모바일 산출물도 결국 HTML (시뮬레이터·실기기 미리보기 없음)
- React Native / Flutter / SwiftUI 코드 생성 직접 안 함
산출물 = HTML 한정
react-component렌더러 있지만 사용 스킬 0개 (dormant)- 정적 HTML만 진지하게 지원
- 실 빌드 가능한 모바일 앱이 아님 — "모바일처럼 보이는 HTML"
- export 포맷: HTML · PDF · PPTX · ZIP · MD —
.ipa/.apk/.exe없음
Figma 양방향 부재
figma-extract아톰 있지만 v1 prompt-only fragment (실 구현체 없음)- Figma 파일 → 코드: community figma-mcp 별도 셋업
- 코드 → Figma 파일: 경로 없음
- Figma 플러그인 없음
데이터 그라운딩 부재
- 디자인 시스템 149개는 마케팅 사이트 톤이 위주 (Linear · Stripe · …)
- 실제 모바일 앱 플로우 데이터 없음
- 한국·일본·동남아 디자인 톤 부재
- 카테고리별(커머스 · 금융 · 모빌리티 등) 특화 톤 없음
영어·중국어 편향
- README 12개 언어 번역 있지만 본문 톤은 영미·중국
- 한국어 트리거 키워드 일부 있으나 검증 빈약
- 한국 디자인 컨벤션 강제 부재
BYOK proxy의 손실
CLI 없이 API 키로만 쓰면 tool use 일부가 손실된다. Claude Code의 AskUserQuestion · TodoWrite · 멀티 파일 스트리밍 등은 CLI 의존이다. proxy 모드는 단순 채팅 + 단일 산출물 정도다.
인터랙션 검증 부재
- HTML 산출물이라 클릭은 되지만 화면 전환 없음
- 멀티 화면 플로우 미지원 (한 산출물 = 한 화면 기본)
- 애니메이션·제스처 미리보기 부재
평가·iteration 도구 약함
- 5차원 self-critique 있지만 LLM 자체 평가의 한계
- A/B 비교 도구 없음
- 사용자 피드백 누적·재학습 루프 부재
- 같은 프롬프트 → 다른 결과 (재현 어려움)