REPO DEEP DIVE · 2026-07-29 · WEBSTUDIO-IS/WEBSTUDIO · 코드 대신 데이터로 저장하는 오픈소스 비주얼 웹 빌더

webstudio-is/webstudio 딥다이브
"Webflow는 있는데 왜 오픈소스는 없지"라는 질문의 답

Webstudio는 마우스로 끌어다 놓으며 웹사이트를 만드는 비주얼 빌더다. 여기까지는 Webflow와 똑같다. 다른 점은 딱 하나, 전부 오픈소스이고 내가 만든 결과물을 코드로 뽑아서 내 서버에 직접 올릴 수 있다는 것이다. 화면에서 그린 결과는 처음부터 끝까지 JSX 코드가 아니라 데이터(인스턴스 트리·스타일 소스·프로퍼티)로 저장되고, 필요할 때 이 데이터를 실제 Remix / React Router 프로젝트로 "구워내"(컴파일) 어디에나 배포한다.

이 문서는 저장소를 직접 클론해 인스턴스 트리 스키마, 캔버스 iframe과 빌더 UI의 통신 방식, 스타일을 실제 CSS로 굽는 엔진, 컴포넌트 메타 시스템, CLI 내보내기 파이프라인, 그리고 최근 추가된 AI 에이전트(MCP) 연동까지 실제 소스 코드를 근거로 뜯어본 학습 자료다.

(저장소 webstudio-is/webstudio · 라이선스 핵심부 AGPL-3.0-or-later(애니메이션 컴포넌트 패키지만 별도 EULA) · pnpm 모노레포, packages 37개 + apps/builder 1개 · TypeScript/TSX 소스 약 2,100개 파일 · 빌더 자체는 Remix 2 + React 18, 내보낸 사이트는 기본 React Router 7 · DB는 Postgres + Prisma · 최신 커밋 0b192c7(2026-07-24) · 정리 기준일 2026-07-29)
목차
  1. 한 줄 정체
  2. 왜 지금 주목받는가
  3. 무엇을 해결하나
  4. 전체 구조
  5. 핵심 설계·동작 원리
  6. 기술 스택·의존성
  7. 설치·요구사항
  8. 실제 사용 흐름
  9. 함정·오해 방지
  10. 대안 비교
  11. 정리 · 누가 쓰면 좋은가

1한 줄 정체

"화면은 Webflow처럼, 저장은 데이터로, 배포는 내 서버로" — 오픈소스 노코드/로우코드 웹 빌더

Webstudio는 브라우저에서 웹사이트를 마우스로 조립하는 편집기(Builder)다. 박스를 끌어다 놓고, 오른쪽 패널에서 색·여백·글꼴을 조절하고, 텍스트를 바로 클릭해 고친다. 여기까지는 흔한 "노코드 웹사이트 빌더"와 다르지 않다.

진짜 차이는 "내가 만든 화면이 어디에, 무슨 형태로 저장되는가"에 있다. Webstudio는 화면을 JSX/HTML 소스 코드로 저장하지 않는다. 대신 "이 페이지에는 어떤 요소가 있고(인스턴스 트리), 어떤 스타일이 어느 요소에 붙어있고(스타일 소스), 어떤 값이 들어있는가(프로퍼티)"를 순수 데이터(JSON)로 저장한다. 화면은 이 데이터를 그때그때 읽어서 그린 결과물일 뿐이다.

그리고 이 데이터는 언제든 webstudio CLI로 실제 Remix/React Router 프로젝트 소스 코드로 뽑아낼 수 있다. 뽑아낸 코드는 100% 내 것이라 Vercel·Netlify·Cloudflare·Docker 등 원하는 곳에 직접 배포한다. 회사가 망하거나 요금이 올라도 내 코드와 내 데이터는 항상 손안에 있다는 것이 핵심 약속이다.

한 줄 비유

"완성된 그림(코드)"이 아니라 "레이어가 살아있는 원본 파일(데이터)"을 준다

많은 웹사이트 빌더는 완성된 그림 파일(JPG)만 준다 — 보기엔 좋지만 그 안의 선 하나, 색 하나를 다시 손대려면 처음부터 다시 그려야 한다. Webstudio는 대신 레이어가 살아있는 포토샵 원본(PSD) 같은 데이터를 준다. 어떤 도형이 어디 있고 어떤 색을 썼는지 전부 구조로 남아있어서, 나중에 언제든 다시 열어 고치고, 필요하면 그 원본을 "그대로 인쇄 가능한 파일(코드)"로 내보낼 수도 있다.

2왜 지금 주목받는가

"AI가 웹사이트를 만들어주는" 시대에 필요한 것은 예쁜 결과가 아니라 "고칠 수 있는 데이터"

2026년 현재 "AI에게 말로 시켜서 웹사이트/앱을 만드는" 도구(이른바 바이브 코딩)가 쏟아지고 있다. 이 흐름 속에서 Webstudio가 다시 주목받는 이유는 세 가지로 정리된다.

① AI가 편집하기 좋은 구조 — 코드가 아니라 데이터라서

AI 에이전트가 "버튼 색을 좀 더 진하게" 같은 수정을 할 때, 자유 형식 JSX 파일을 통째로 다시 쓰게 하는 것보다 "이 인스턴스의 이 스타일 속성 값을 바꿔라"는 구조화된 명령을 주는 편이 훨씬 안전하고 정확하다. Webstudio는 저장 형식 자체가 이런 구조화된 데이터라서, AI 에이전트용 조작 인터페이스를 자연스럽게 얹을 수 있다. 실제로 저장소에는 webstudio mcp라는, AI 코딩 에이전트가 표준 프로토콜(MCP)로 직접 프로젝트를 조작하고 스크린샷으로 결과를 눈으로 확인하는 기능이 들어있다(5장에서 자세히).

② "Webflow의 오픈소스 대안"이라는 명확한 포지션

Webflow는 시각적 웹 빌더 시장을 만든 회사지만 완전한 폐쇄형 SaaS다 — 코드를 통째로 내보낼 수 없고(제한적 export만 가능), 요금은 계속 오르고, 데이터는 Webflow 서버에 갇힌다. Webstudio는 같은 사용 경험을 제공하면서 AGPL 오픈소스 + 완전한 코드 내보내기 + 셀프호스팅을 내세운다. "디자이너용 도구인데 개발자가 코드 소유권을 잃지 않는다"는 조합이 팀 협업 시장에서 반응을 얻고 있다.

③ 실제로 "제품"으로서 완성도가 높다

단순 데모가 아니라 5년 넘게 다듬어진 실제 SaaS 제품(webstudio.is)과 그 오픈소스 코어가 같은 저장소에서 나온다. 37개 패키지로 나뉜 모노레포, 엄격한 패키지 경계 검사 스크립트, CI에서 포맷·린트·타입체크·시각적 회귀 테스트(Chromatic/Lost Pixel)까지 자동화돼 있다 — "장난감이 아니라 유지되는 제품"이라는 신호가 뚜렷하다.

경쟁 지형
Webflow · Framer · Onlook · Plasmic 사이에서

Webflow·Framer는 강력하지만 폐쇄형·구독형·비공개 인프라다. Onlook·Plasmic 같은 오픈소스/코드 우선 도구도 있지만 접근 방식이 조금씩 다르다(10장에서 표로 비교). Webstudio의 차별점은 "Webflow 수준의 완성된 노코드 UX""완전한 코드 소유권"을 동시에 주장한다는 점이다.

3무엇을 해결하나

"디자이너의 편함"과 "개발자의 코드 소유권" 둘 다 포기하지 않기

전통적으로 웹사이트를 만드는 방법은 둘 중 하나였다. ① 개발자가 코드를 짠다 — 정확하고 유지보수하기 좋지만 느리고, 디자이너·마케터는 문구 하나 못 고친다. ② 노코드 빌더를 쓴다(Webflow, Wix 등) — 누구나 화면을 고칠 수 있지만, 결과물이 그 회사 서버 안에 갇히고(벤더 락인), 진짜 복잡한 로직이 필요해지면 막힌다.

Webstudio는 이 둘을 한 파이프라인으로 잇는다. 비개발자는 화면에서 마우스로 편집하고, 그 결과는 항상 정돈된 데이터로 쌓인다. 개발자가 필요하면 그 데이터를 진짜 코드로 뽑아 자기 저장소에 넣고, 직접 로직을 더 얹거나 리뷰하고, 원하는 인프라에 배포한다. "누가 화면을 고쳤는가"와 "그 결과가 어떤 코드가 되는가"가 완전히 분리되어 있다는 것이 핵심이다.

용어
벤더 락인 (vendor lock-in)
특정 회사의 서비스에 데이터·설정을 저장하다 보니, 나중에 다른 서비스로 옮기거나 직접 운영하기 어려워지는 현상. Webflow에서 만든 사이트는 Webflow 밖으로 통째로 빼낼 방법이 마땅치 않다. Webstudio는 데이터와 코드 내보내기를 처음부터 기능으로 만들어 이 문제를 정면으로 피한다.
용어
노코드 / 로우코드
코드를 직접 타이핑하지 않고 화면 조작만으로 프로그램·웹사이트를 만드는 방식(노코드), 또는 대부분은 화면 조작이지만 필요하면 약간의 코드(표현식·바인딩)를 더할 수 있는 방식(로우코드). Webstudio는 로우코드에 가깝다 — 값 바인딩·조건식 같은 표현식을 UI 안에서 쓸 수 있다.

더 구체적으로는 세 가지 실무 문제를 겨냥한다.

4전체 구조

pnpm 모노레포 — apps/builder(편집기) + packages/*(SDK·컴포넌트·엔진·CLI)

저장소는 pnpm 워크스페이스 모노레포다. 앱은 딱 하나, apps/builder(웹에서 도는 편집기 겸 SaaS 본체)뿐이고, 나머지는 전부 packages/* 아래 37개 패키지로 쪼개져 있다. 역할별로 묶으면 이렇게 보인다.

┌──────────────────────────────────────────────────────────────────┐ │ WEBSTUDIO 모노레포 역할 지도 │ └──────────────────────────────────────────────────────────────────┘ apps/builder/ # 편집기 겸 SaaS 서버 (Remix 2 + React 18) └ app/{builder,canvas,dashboard,routes} · builder = 편집 UI 쉘(패널·툴바) · canvas = 실제 페이지가 그려지는 iframe 쪽 코드 · dashboard = 프로젝트 목록·로그인 화면 packages/ ├── sdk/ # ★ 데이터 스키마의 진실원 (zod) │ schema/{instances,styles,style-sources, │ style-source-selections,data-sources,props,component-meta} ├── css-engine/ # ★ 스타일 데이터 → 실제 CSS 텍스트 변환 ├── react-sdk/ # 인스턴스 트리를 실제 React 트리로 렌더 ├── project-build/ # 빌더 서버측 런타임 — 트리 조작 API·검증·MCP 계약 ├── project / project-migrations / prisma-client / postgrest │ # 프로젝트 저장·마이그레이션·Postgres 접근 ├── sdk-components-registry/ # 모든 컴포넌트 메타를 한곳에 모음 ├── sdk-components-react/ # 기본 HTML 계열 컴포넌트(버튼·이미지·폼 등) ├── sdk-components-react-radix/ # Radix 기반 UI 컴포넌트(다이얼로그 등) ├── sdk-components-react-remix/ # Remix 라우팅 연동 컴포넌트 ├── sdk-components-react-router/ # React Router 라우팅 연동 컴포넌트 ├── sdk-components-animation/ # (submodule, 비공개) 유료 애니메이션 컴포넌트 ├── design-system/ # 편집기 자체 UI 키트 (Stitches 기반) ├── cli/ # ★ webstudio CLI — link·sync·build·mcp ├── sync-client/ # 빌더 ↔ 클라우드 동기화 클라이언트 └── template / domain / fonts / image / icons / feature-flags / plans ...

이 배치가 말해주는 설계 의도는 하나다. sdk 패키지의 zod 스키마가 "데이터가 어떤 모양이어야 하는가"의 유일한 진실원이고, 그 위에 css-engine(스타일→CSS 변환), react-sdk(트리→React), project-build(트리 조작·검증), 여러 sdk-components-*(실제 컴포넌트 구현)가 각자의 일만 한다. 편집기(apps/builder)와 CLI(packages/cli)는 둘 다 이 공용 패키지들을 가져다 쓰는 "소비자"일 뿐이다 — 그래서 편집기에서 만든 데이터를 CLI가 그대로 코드로 구워낼 수 있다.

용어
모노레포 (monorepo) / 워크스페이스
여러 개의 관련 패키지를 하나의 저장소 안에 두고, 패키지 매니저(여기선 pnpm)가 서로 의존하며 함께 빌드되게 관리하는 방식. Webstudio는 "스키마 정의·CSS 엔진·컴포넌트 구현·편집기·CLI"가 서로 다른 패키지지만 같은 저장소에서 버전을 맞춰 간다.
경계 규칙
공개 패키지가 비공개 패키지를 몰래 의존하지 못한다

scripts/check-package-boundaries.mjs가 CI에서 모든 package.json을 훑어, 공개(오픈소스) 패키지가 private:true인 패키지를 런타임 의존성으로 참조하면 빌드를 실패시킨다. 이 규칙 덕분에 뒤에서 볼 "유료 애니메이션 컴포넌트"가 오픈소스 코어에 몰래 스며드는 일이 구조적으로 막힌다.

5핵심 설계·동작 원리

데이터 모델 → 캔버스 통신 → CSS 엔진 → 컴포넌트 메타 → 내보내기 파이프라인 → AI 연동 → DB

5-1. 데이터 모델 — "인스턴스 트리 + 스타일 소스 + 스타일 선언"이 전부

Webstudio가 화면 하나를 표현하는 데 쓰는 데이터는 크게 다섯 종류다. 실제 스키마(packages/sdk/src/schema/*.ts, zod로 정의)를 그대로 보면 이렇다.

┌───────────────────────────────────────────────────────────────┐ │ 페이지 하나를 구성하는 데이터 다섯 조각 │ └───────────────────────────────────────────────────────────────┘ Instance "무엇이 있는가" { id, component: "Button", tag?: "button", children: [ {type:"id", value:"자식id"} | {type:"text", value:"글자"} | {type:"expression", value:"바인딩식"} ] } StyleSource "스타일 뭉치의 정체" { type:"token", name:"버튼-기본" } ← 여러 인스턴스가 공유(디자인 토큰처럼) { type:"local" } ← 이 인스턴스 하나만의 개별 스타일 StyleSourceSelection "이 인스턴스가 어떤 스타일 소스들을 쓰는가" { instanceId, values: [styleSourceId, ...] } ← 순서=적용 우선순위 StyleDecl "실제 스타일 값 한 줄" { styleSourceId, breakpointId, state?: ":hover", property:"color", value:{type:"rgb",...} } DataSource "값이 있는 변수 / 바인딩" { type:"variable", name:"count", value:{type:"number", value:0} } { type:"resource", resourceId:"..." } ← API 호출 결과를 바인딩

이 다섯 조각이 조합되는 방식이 핵심이다. 인스턴스는 스타일 값을 직접 들고 있지 않는다. 대신 StyleSourceSelection으로 "나는 이 스타일 소스들을 쓴다"고만 가리키고, 실제 색·크기 값은 StyleDecl(styleSourceId, breakpointId, state) 조합 키로 따로 저장된다. 그래서 같은 StyleSource(토큰)를 버튼 100개가 같이 쓰다가, 그 토큰의 StyleDecl 값 하나만 바꾸면 100개가 한 번에 바뀐다 — CSS의 클래스 개념을 데이터 레벨에서 그대로 재현한 것이다.

비유

인스턴스는 "이 옷을 입은 사람"이고, 스타일 소스는 "옷 자체"다. 사람(인스턴스)은 "나는 파란 셔츠를 입는다"고 셔츠(스타일 소스)를 가리키기만 한다. 셔츠의 실제 색상·재질 정보(스타일 선언)는 옷장에 따로 걸려있다. 같은 파란 셔츠를 100명이 나눠 입고 있다가, 옷장에서 그 셔츠의 색을 초록으로 바꾸면 100명이 동시에 옷을 갈아입는 셈이다.

DataSource(변수·파라미터·리소스)는 텍스트나 프로퍼티 값에 고정값 대신 표현식을 바인딩할 수 있게 해준다. 예를 들어 버튼의 children{type:"expression", value:"고객이름 + \"님 환영합니다\""}처럼 넣으면, 실제 값이 변수에 따라 바뀐다. 이 표현식들은 CLI 내보내기 시점에 실제 JS/JSX 표현식으로 컴파일된다.

5-2. 빌더 UI ↔ 캔버스 iframe 통신 — postMessage + 토큰 검증 버스

편집기 화면은 사실 두 개의 독립된 브라우저 문서로 이루어져 있다. 바깥쪽(빌더 UI, 패널·툴바)안쪽(캔버스, 진짜 컴포넌트가 렌더링되는 iframe)이다. 이 둘은 서로 다른 문서라 직접 함수를 호출할 수 없고, postMessage로만 대화한다. Webstudio는 이 통신을 apps/builder/app/shared/pubsub/create.ts토큰 검증형 pub/sub 버스로 감싼다.

// apps/builder/app/shared/pubsub/create.ts (요지)
const token = window.self === window.top
  ? getRandomToken()          // 빌더(최상위 창)에서 토큰 생성
  : window.top?.[apiTokenKey]; // 캔버스(iframe)는 부모가 심어둔 토큰을 읽음

// 보낼 때: 액션을 { action, token } 으로 감싼다
parentPostMessageInternal(wrapAction(action), "*");

// 받을 때: 토큰이 안 맞으면 그 즉시 버린다
if (payload.token !== token) throw new Error("Invalid token");

왜 굳이 토큰까지 검증할까? 코드 주석에 이유가 그대로 적혀 있다 — 캔버스 iframe 안에는 사용자가 만든 임의의 HTML 임베드·서드파티 스크립트가 돌 수 있는데, 이런 스크립트가 window.postMessage 자체를 가로채 가짜 메시지를 흘려보내는 공격을 막기 위해서다. 무작위 토큰을 CSRF 토큰처럼 매 세션 발급해 "이 메시지가 진짜 신뢰할 수 있는 상대에게서 왔는가"를 확인한다.

용어
postMessage
서로 다른 브라우저 문서(iframe·다른 탭)끼리 직접 함수 호출 없이 메시지를 주고받는 표준 웹 API. 보안상 아무 곳에서나 보낸 메시지를 무조건 신뢰하면 안 되므로, 보통 발신처 확인이나 토큰 검증을 곁들인다.

5-3. CSS 엔진 — 스타일 데이터를 "해시 기반 아토믹 클래스"로 굽기

화면에 실제로 적용되는 CSS는 @webstudio-is/css-engine이 만든다. 핵심 아이디어는 같은 스타일 선언은 항상 같은 클래스 이름을 갖게 해시로 이름을 짓는 것이다(packages/css-engine/src/core/atomic.ts).

// core/atomic.ts (요지) — 선언 하나마다 해시로 클래스명을 만든다
const atomicHash = hash(
  descendantSuffix + declaration.breakpoint +
  declaration.selector + declaration.property +
  toValue(declaration.value)
);
const className = `c${atomicHash}`;  // "c" 접두어 = 클래스명은 항상 문자로 시작

즉 "이 브레이크포인트에서, 이 상태(:hover 등)에서, 이 속성에 이 값"이라는 조합이 완전히 같으면 해시값도 같고, 그래서 생성되는 클래스도 같다. 다른 요소 10개가 똑같이 color: blue를 쓰면 CSS에는 그 규칙이 딱 한 번만 등장하고, 10개의 요소가 같은 클래스 하나를 공유한다 — 이게 바로 아토믹 CSS(atomic CSS)다. 겹치는 스타일 선언은 merger.ts가 같은 브레이크포인트·셀렉터 그룹으로 묶어 최종 한 줄로 병합한다.

비유

마치 도장(스탬프)을 새기는 것과 같다. "파란 배경 + 굵은 글씨"라는 조합을 처음 쓸 때만 그 조합 전용 도장(클래스)을 새기고, 다음에 똑같은 조합이 필요하면 새 도장을 파지 않고 이미 새겨둔 도장을 재사용한다. 그 결과 최종 CSS 파일에는 실제로 쓰인 조합의 수만큼만 규칙이 남고, 중복이 없다.

5-4. 컴포넌트 메타 시스템 — "구현 파일 + 메타 파일" 짝

캔버스에 그려지는 버튼·이미지·폼 같은 컴포넌트는 전부 파일 두 개가 한 쌍으로 존재한다(packages/sdk-components-react/src/). button.tsx는 진짜 React 구현체이고, button.ws.ts는 그 컴포넌트의 "메타 정보"다.

// button.tsx — 실제 렌더링되는 컴포넌트(빌더 캔버스에서도, 내보낸 사이트에서도 동일하게 쓰인다)
export const Button = forwardRef((props, ref) =>
  <button type={props.type ?? "submit"} {...props} ref={ref} />
);

// button.ws.ts — 빌더가 참고하는 "설명서"
export const meta: WsComponentMeta = {
  presetStyle: { button },        // 처음 드롭했을 때 기본 스타일
  initialProps: ["id","class","type","aria-label"], // 패널에 먼저 보여줄 속성
  props,                          // 이 컴포넌트가 받을 수 있는 프로퍼티 전체 목록
};

메타 파일에는 contentModel도 있다 — "이 컴포넌트 안에는 어떤 종류의 자식만 들어갈 수 있는가"를 정의해, 예컨대 <tr> 안에 아무 컴포넌트나 넣지 못하게 막는다. 모든 컴포넌트 패키지(sdk-components-react, -react-radix, -react-remix, -react-router, 비공개 -animation)의 메타는 sdk-components-registry 패키지가 한곳에 모아, 빌더의 "컴포넌트 추가" 패널과 CLI의 코드 생성기가 똑같은 목록을 참조한다.

5-5. CLI 내보내기 파이프라인 — 데이터를 실제 프로젝트로 굽기

webstudio CLI(packages/cli)는 linksyncbuild 세 단계로 클라우드의 데이터를 로컬 코드로 바꾼다.

단계 ①

webstudio link — 프로젝트 연결

빌더의 "Share" 기능으로 만든 인증 링크를 CLI에 붙여넣으면, 이 프로젝트의 인스턴스 트리·스타일·페이지 데이터에 접근할 권한을 로컬에 저장한다.

단계 ②

webstudio sync — 데이터 내려받기

클라우드에 저장된 최신 인스턴스 트리·스타일 소스·프로퍼티·데이터 소스 JSON을 로컬로 내려받는다. 이 시점까지는 아직 "데이터"일 뿐, 실행 가능한 코드가 아니다.

단계 ③

webstudio build — 실제 프로젝트로 컴파일

내려받은 데이터를 packages/cli/templates/*의 프레임워크 템플릿(기본 React Router 7, 또는 Remix·정적 사이트(SSG) 등)에 얹어, 페이지마다 실제 JSX 파일과 css-engine이 구운 CSS 파일을 만든다. 이렇게 나온 프로젝트는 npm run build·npm run dev가 되는 평범한 Remix/React Router 저장소다.

템플릿은 배포 대상별로 여러 벌 준비돼 있다 — react-router(기본), react-router-vercel·-netlify·-cloudflare·-docker(각 인프라 특화), ssg·ssg-vercel·ssg-netlify(완전 정적 사이트로 미리 렌더링), 그리고 구버전 호환용 defaults(Remix 2 기반)까지. 어떤 인프라에 배포하든 같은 데이터에서 시작한다는 점이 핵심이다.

5-6. AI 에이전트 연동 — webstudio mcp와 시각 검증 루프

가장 최근에 추가된, 이 프로젝트가 지금 왜 화제인지를 잘 보여주는 부분이다. webstudio mcp 명령은 MCP(Model Context Protocol) 서버를 표준입출력으로 띄워, Claude Code 같은 AI 코딩 에이전트가 "인스턴스를 옮겨라", "이 위치에 컴포넌트를 삽입해라" 같은 구조화된 도구 호출로 프로젝트를 직접 편집하게 해준다.

// packages/project-build/src/runtime/instances.ts — MCP 도구 인자 스키마의 실제 예
insertIndex: insertIndexInput.optional().describe(
  "Zero-based position in the target parent's children before the moved instance is removed. Omit it or use position: end to append."
),
position: instanceEndPositionInput.optional().describe(
  'Use "end" to append deterministically without calculating an insertIndex.'
),

.describe() 문구들이 바로 AI 에이전트에게 "이 인자가 뭘 뜻하는지" 알려주는 설명이다. 즉 빌더 화면에서 사람이 하는 조작과 AI가 MCP로 하는 조작이 같은 런타임 API(BuilderRuntimeContext)를 공유한다 — 사람 손이든 AI 도구 호출이든 결과 데이터는 똑같은 검증을 통과해야 한다.

더 나아가 내부 문서(packages/cli/src/docs/mcp-vision.md)에는 "시각 검증 루프"까지 설계돼 있다. AI 에이전트가 스타일을 바꾼 뒤 preview.start로 미리보기 서버를 띄우고, screenshot으로 여러 화면 폭의 스크린샷을 찍고, 기준 이미지와 screenshot.diff로 픽셀 차이·OCR 텍스트 변화까지 비교하게 하는 워크플로다. "AI가 스타일을 고쳤다고 주장하는 것"과 "실제로 화면이 그렇게 바뀐 것"을 눈으로 확인시키는 장치다.

용어
MCP (Model Context Protocol)
AI 모델이 외부 도구·데이터에 표준화된 방식으로 접근하게 해주는 프로토콜. Webstudio는 이걸 이용해 AI 에이전트가 "직접 화면을 조작하는 사람"처럼 프로젝트를 편집할 수 있게 만들었다.

5-7. 저장소 — Postgres 위에 "JSON 문자열 칼럼"으로

DB는 Prisma로 관리되는 Postgres다(packages/prisma-client/prisma/schema.prisma, 375줄). 흥미로운 점은 인스턴스 트리 등이 세부 관계형 테이블이 아니라, Build 테이블의 각 칼럼에 직렬화된 JSON 문자열 통째로 저장된다는 것이다.

// prisma/schema.prisma — Build 모델 (일부)
model Build {
  pages                 String
  breakpoints           String @default("[]")
  styles                String @default("[]")
  styleSources          String @default("[]")
  styleSourceSelections String @default("[]")
  props                 String @default("[]")
  dataSources           String @default("[]")
  instances             String @default("[]")
  ...
}

즉 Prisma는 여기서 "연결·마이그레이션·타입 안전성"을 담당할 뿐, 인스턴스 트리 자체의 관계를 SQL 조인으로 표현하지 않는다. 트리 구조의 진짜 검증·조작은 전부 애플리케이션 레이어(project-build 패키지의 zod 스키마와 런타임 함수들)에서 이뤄진다 — "관계형 DB를 문서 저장소처럼 쓰는" 실용적 타협이다.

6기술 스택·의존성

편집기=Remix 2+React 18 · 내보낸 사이트=React Router 7 · nanostores·Stitches·Zod 4
영역선택비고
편집기 프레임워크Remix 2.16웹 SaaS 본체(apps/builder)가 직접 쓰는 메타프레임워크. React 18.3(canary 빌드) 사용
내보낸 사이트 기본값React Router 7Remix의 후신. CLI 템플릿의 기본 대상이며, 구버전 호환용 Remix 템플릿(defaults)도 남아있다
상태 관리nanostoresRedux/Zustand 대신 가벼운 atom 기반 스토어. 편집기·CLI·컴포넌트 패키지 여럿이 공유
편집기 UI 스타일링Stitches(@stitches/react)편집기 자체(패널·툴바)의 CSS-in-JS. 캔버스 안 사용자 콘텐츠 스타일링과는 별개(그건 css-engine)
스키마·검증Zod 4인스턴스·스타일·프로퍼티 등 모든 데이터 모델과 MCP 도구 인자 검증에 사용
DB / ORMPostgres + Prisma 5연결·마이그레이션 담당. 트리 데이터 자체는 JSON 문자열 칼럼(5-7절)
REST 계층PostgREST(@webstudio-is/postgrest)Postgres 위에 REST API를 자동 생성해주는 계층을 감싼 내부 패키지
포맷·린트oxfmt · oxlintRust로 짜인 차세대 고속 포맷터·린터(oxc 프로젝트 계열). Prettier/ESLint 대체
테스트Vitest(단위) · Playwright(E2E)pnpm -r test로 전 패키지 단위 테스트, apps/builder/e2e에 별도 브라우저 E2E
시각 회귀Storybook + Chromatic / Lost Pixel컴포넌트 스토리 기준 UI 스크린샷 비교로 의도치 않은 화면 변화를 잡음
패키지 매니저pnpm 9.14 워크스페이스Node 22 고정(.nvmrc, engines.node)

라이선스 구조도 스택의 일부로 봐야 한다. 저장소 안의 모든 기능은 AGPL-3.0-or-later다(README에 명시). 단 하나 예외는 packages/sdk-components-animation인데, 이는 git submodule로 연결된 별도 비공개 저장소이고 Webstudio, Inc.의 EULA(최종사용자 라이선스 계약)에 동의해야 쓸 수 있는 유료 애니메이션 컴포넌트다. 즉 "오픈소스 코어 + 선택적 유료 부속품"이 한 모노레포 안에 공존하되, 4장의 패키지 경계 검사로 서로 섞이지 않게 막아둔 구조다.

7설치·요구사항

Node 22 고정 · pnpm 워크스페이스 · Postgres 필요(로컬 개발 시)
항목요구사항
Node.js정확히 22(.nvmrc=22, engines.node="22"). 다른 버전은 워크스페이스 스크립트가 어긋날 수 있다
패키지 매니저pnpm 9.14.x 고정(packageManager 필드). "yarn은 지원하지 않는다"는 안내가 engines에 박혀 있다
저장소 클론서브모듈(sdk-components-animation)은 비공개 저장소라 일반 클론으로는 내용이 비어있다. ./submodules.sh가 있지만 접근 권한이 없으면 실패한다
로컬 편집기 실행pnpm installpnpm dev(내부적으로 apps/builder/dev/run-local.sh 실행). Postgres 연결 문자열(DATABASE_URL) 등 환경변수 필요
CLI만 쓰기웹 앱 전체를 안 띄우고 내보내기만 하려면 npm install -g webstudio로 CLI만 설치해도 된다(별도 Node 22 권장)
CI 환경GitHub Actions, ubuntu-24.04-arm 러너에서 포맷·린트·타입체크·패키지 경계·생성 문서 검사·Vitest·Playwright를 전부 수행
주의
"레포를 통째로 클론"해도 완전한 빌드는 안 될 수 있다

· 유료 애니메이션 컴포넌트 서브모듈은 접근 권한이 있는 사람만 내려받을 수 있다. 공개 저장소만 클론하면 이 부분은 비어있는 채로 남는다 — 하지만 오픈소스 코어 기능에는 영향이 없다(패키지 경계 규칙 덕분에 의존이 분리돼 있음).

· 편집기(apps/builder) 전체를 로컬에서 띄우려면 Postgres·인증(OAuth) 설정 등이 필요해 진입장벽이 있다. 스키마·CSS 엔진·컴포넌트 메타 같은 핵심 설계를 공부하는 목적이라면 packages/sdk, packages/css-engine만 읽어도 충분하다.

8실제 사용 흐름

웹에서 편집 → link/sync/build → 내 인프라에 배포, 또는 AI 에이전트에게 맡기기

흐름 A — 일반적인 팀 워크플로 가장 흔한 경로

① webstudio.is(또는 셀프호스팅한 빌더)에서 화면을 마우스로 조립한다. 텍스트·이미지·컴포넌트를 끌어다 놓고, 스타일 패널로 색·간격을 조절한다. 저장은 자동으로 인스턴스 트리 데이터로 쌓인다.
② 배포 준비가 되면 프로젝트에서 "Share" 링크를 만든다.
③ 로컬(또는 CI)에서 webstudio link(링크 붙여넣기) → webstudio sync(데이터 내려받기) → webstudio build(React Router/Remix 프로젝트로 컴파일)를 실행한다.
④ 결과물은 평범한 프론트엔드 저장소이므로 그대로 Vercel·Netlify·Cloudflare Pages·Docker 등 원하는 곳에 배포한다.

흐름 B — AI 에이전트에게 편집을 맡기기 최신 워크플로

① 로컬에서 webstudio mcp로 MCP 서버를 띄우고, Claude Code 같은 AI 코딩 에이전트를 그 서버에 연결한다.
② 사람이 "히어로 섹션 배경을 더 어둡게 하고 버튼을 크게 해줘" 같은 자연어 지시를 내리면, 에이전트가 MCP 도구(인스턴스 이동·스타일 값 변경 등)를 구조화된 호출로 실행한다.
③ 에이전트는 preview.start로 미리보기를 띄우고 screenshot으로 여러 화면 폭을 캡처해, 실제로 의도한 대로 바뀌었는지 스스로 확인한 뒤 마무리한다.
④ 이후 흐름은 A와 동일하게 sync·build·배포로 이어진다.

흐름 C — 정적 사이트로만 필요한 경우 서버 없이

블로그·소개 페이지처럼 서버 로직이 필요 없으면 ssg·ssg-vercel·ssg-netlify 템플릿으로 빌드해, 완전히 정적인 HTML/CSS/JS 묶음만 내보낸다. 이 경우 배포 대상은 그냥 정적 파일 호스팅이면 충분하다.

9함정·오해 방지

"버전 번호가 없다" · "편집기와 내보낸 사이트는 다른 프레임워크" · "Prisma인데 관계형이 아니다"

함정 ① — package.json의 버전은 전부 가짜 값이다

저장소 안 거의 모든 package.jsonversion 필드는 "0.0.0" 또는 "0.0.0-webstudio-version"처럼 플레이스홀더다. 진짜 배포 버전은 local:version-snapshot 스크립트가 배포 직전에 임의의 프리패치 버전을 채워 넣고 레지스트리에 publish한 뒤, local:dangerously-undo-version-snapshot으로 다시 되돌리는 방식으로 관리된다. 릴리스 자체도 날짜 기반 브랜치(release-DD-MM-YYYY.staging)로 만들어진다. "이 저장소의 버전이 몇이냐"는 질문에 package.json만 봐서는 답이 안 나온다 — npm 레지스트리나 GitHub 릴리스를 봐야 한다.

함정 ② — "Webstudio는 Remix 기반"이라는 말은 절반만 맞다

편집기 자체(apps/builder, 즉 webstudio.is가 돌아가는 그 SaaS 앱)는 지금도 Remix 2.16 + React 18로 짜여 있다. 하지만 CLI로 내보낸(export한) 사이트는 기본적으로 React Router 7(Remix의 후신) 템플릿을 쓴다. "Webstudio가 무슨 프레임워크냐"고 물으면 "편집기냐, 내가 받는 결과물이냐"에 따라 답이 다르다는 점을 헷갈리기 쉽다.

함정 ③ — Prisma를 쓰지만 관계형 스키마로 트리를 설계하지 않았다

Build 테이블의 instances·styles·props 등은 전부 직렬화된 JSON 문자열 칼럼이다(5-7절). Prisma가 있다고 해서 "인스턴스마다 행이 있고 부모-자식 관계가 외래키로 걸려있을 것"이라 짐작하면 소스를 읽을 때 헤매게 된다. 실제 트리 무결성 검증은 애플리케이션 코드(zod 스키마 + project-build의 런타임 함수)가 담당한다.

함정 ④ — 비공개 서브모듈이 "빌드 실패"처럼 보일 수 있다

sdk-components-animation은 git submodule로 연결된 비공개 저장소다. 접근 권한 없이 클론하면 해당 폴더가 텅 비어 있거나 체크아웃이 실패하는데, 이는 저장소 자체의 결함이 아니라 "유료 부속 컴포넌트는 원래 공개돼 있지 않다"는 설계다. CI도 이 부분은 시크릿이 있을 때만 체크아웃한다(.github/actions/submodules-checkout).

문서와 실제 코드가 일치하는 부분
README의 라이선스 고지는 정확하다

"핵심부는 AGPL, 애니메이션 컴포넌트만 별도 EULA"라는 README 문구는 실제 LICENSE 파일(AGPL 전문)과 서브모듈 구조·패키지 경계 검사 스크립트로 실제 코드에서도 그대로 확인된다. 겉으로만 오픈소스라고 홍보하고 실제로는 핵심 기능을 잠가둔 "오픈코어 눈속임"과는 다르다.

10대안 비교

Webflow · Framer · Onlook · Plasmic 사이에서 Webstudio는 어디에 있나
도구소스코드 소유권셀프호스팅편집 대상
Webstudio완전 내보내기(Remix/React Router 코드) + AGPL 오픈소스가능(편집기·내보낸 사이트 모두)인스턴스 트리(데이터) → 코드로 컴파일
Webflow제한적 내보내기(정적 HTML/CSS만, 로직 없음)불가(완전 SaaS)Webflow 자체 포맷, 비공개
Framer내보내기 매우 제한적불가(완전 SaaS)Framer 자체 포맷, 비공개
Onlook실제 React/Next.js 소스 코드를 직접 편집(오픈소스)가능기존 코드베이스 자체를 비주얼로 열어 수정
Plasmic여러 프레임워크로 코드 내보내기 지원(일부 오픈소스)부분 가능독자적인 컴포넌트 트리 데이터

가장 중요한 축은 "편집 대상이 코드냐 데이터냐"다. Onlook은 이미 존재하는 React 코드베이스를 열어 그 자리에서 시각적으로 고치는 쪽(코드 우선)이고, Webstudio는 애초에 코드가 아닌 데이터로 저장했다가 필요할 때 코드를 생성해내는 쪽(데이터 우선)이다. 데이터 우선 쪽은 "비개발자가 안전하게 편집"하기 유리하고, 코드 우선 쪽은 "이미 복잡한 기존 코드베이스에 손대기" 유리하다. Webflow·Framer는 이 축에서 아예 코드를 온전히 내주지 않는다는 점이 근본적으로 다르다.

비유

Webflow·Framer가 "완성된 요리만 파는 레스토랑"이라면, Webstudio는 "손질된 재료(데이터)와 레시피(스키마)를 함께 주는 밀키트"에 가깝다. 요리는 대신 해주지만, 재료 목록과 조리법을 그대로 가져가 내 주방(내 서버)에서 다시 만들 수 있다. Onlook은 반대로 "이미 완성된 남의 요리를 가져와서, 그 요리 자체를 계속 고쳐 먹는" 쪽에 가깝다.

11정리 · 누가 쓰면 좋은가

"Webflow의 편함 + 내 서버·내 코드"가 필요한 팀에게

Webstudio를 한 문장으로 다시 정리하면: 편집 결과를 코드가 아니라 데이터로 저장해두고, 필요할 때 실제 Remix/React Router 코드로 컴파일해 어디에나 배포할 수 있는 오픈소스 비주얼 웹 빌더다. 이 구조 덕분에 "비개발자가 화면을 안전하게 고치는 편의성"과 "개발자가 코드 소유권을 잃지 않는 통제력"을 동시에 얻는다. 최근에는 여기에 AI 에이전트가 MCP로 직접 편집하고 스크린샷으로 검증하는 기능까지 더해지며, "AI가 화면을 만드는 시대의 백엔드"로서의 성격도 강해지고 있다.