글 목록
releasedesign-systemsai

v2.0.0 — 에이전트가 붙들 수 있는 디자인 시스템

모델이 좋은 UI를 뱉게 만드는 대신, 스스로 시스템을 유도하고 그 시스템에 책임지게 만들기로 했어요. 철학 사슬, 프리셋 바닥, 번호 붙은 게이트, 그리고 omd book 이야기예요.


안녕하세요. oh-my-design을 만들고 있는 팀이에요.

AI 에이전트로 UI를 만들어 본 분이라면 아마 겪어보셨을 거예요. 프롬프트를 꽤 공들여 써요. 화면이 하나 나와요. 나쁘지 않아요. 그다음 화면을 요청해요. 그것도 나쁘지 않은데, 다른 제품의 화면 같아요.

보통은 "모델이 취향이 없어서"라고 설명해요. 저희는 다르게 봤어요. 그 루프 안에서 아무도 일관성을 요구받은 적이 없었던 거예요.

오늘은 v2.0.0에서 뭘 바꿨는지 나눠보려고 해요.

프롬프트는 시스템이 아니에요

프롬프트는 한 번의 요청이에요. 다음 요청과 이어지지 않아요.

컴포넌트 킷은 시스템이 맞아요. 그런데 남의 시스템이에요. 같은 킷 위에 올린 제품들이 결국 같은 얼굴로 수렴하는 이유죠.

저희가 필요했던 건 그 사이였어요. 프로젝트마다 다르게 유도되지만, 한번 정해지면 에이전트가 거기에 답해야 하는 것.

사슬을 따라 내려가요

v2.0.0에서 에이전트는 더 이상 빈 화면과 선의에서 시작하지 않아요. 순서대로 걸어야 하는 사슬에서 시작해요.

철학 → 결정 표 → 토큰 → 컴포넌트 계약 → 레이아웃 문법 → 빌드 → 렌더 비평

각 고리가 다음 고리를 제약해요.

철학은 무엇을 포기하는지 반드시 말해야 해요. 아무것도 내주지 않는 원칙은 장식이니까요. 모든 결정에는 ID가 붙고, 모든 토큰은 자기를 만든 결정을 되가리켜요.

결정 없이 등장한 값은 게이트에서 걸려요. 그래서 시스템이 조용히 즉흥적인 숫자 더미로 무너지지 않아요. 대부분의 "AI 디자인 시스템"이 그렇게 무너지거든요.

실제로 생성된 DESIGN.md를 열어보면 이런 식이에요. D-P2-4 같은 결정 ID가 있고, 그 아래 토큰들이 자기가 어느 결정에서 나왔는지 달고 있어요. 컴포넌트 프리셋을 상속받은 자리에는 어떤 상태가 왜 해당 없음인지까지 적혀 있고요. 나중에 "이 값 왜 이래요?"라고 물었을 때 답이 문서 안에 있어요.

프리셋 93개를 깔아둔 이유

0에서 유도하는 구간이 편차가 제일 큰 구간이에요. 그래서 사슬이 뭔가를 발명하기 전에 먼저 93개의 계약 카탈로그에서 고르게 했어요.

네 층으로 되어 있어요. 어떤 제품에나 필요한 fundamentals 10개, 실제 shadcn/ui 컴포넌트와 그 뒤의 Radix 프리미티브, 대응하는 ARIA APG 패턴까지 묶은 primitives 35개, 화면 장르 43개, 그리고 레퍼런스 카탈로그에서 뽑은 flavor 5개예요.

프리셋은 설치하는 컴포넌트가 아니에요. 구조는 shadcn이 줘요. 그건 shadcn이 정말 잘하는 일이고요. 프리셋이 말하는 건 다른 거예요. 어떤 값이 라이브러리 기본값이 아니라 당신의 결정 표에서 와야 하는지, 어떤 상태가 반드시 존재해야 하는지, 접근성 계약이 뭔지. 두 층은 겹치지 않고 쌓여요.

같은 픽스처와 같은 브리프로 프리셋 층이 있을 때와 없을 때를 재봤어요. 첫 렌더 결함이 7건에서 3건으로 줄었고, 남은 3건은 게이트 위반이 아니라 마감 수준의 문제였어요. 입력 토큰은 47% 줄었어요. 모델이 매번 다시 발견하는 대신 고르는 데 토큰을 쓰니까요. (2026-08-19 자체 검수 기록, docs/design-excellence/에 있어요.)

flavor는 베끼기가 아니에요

93개 중 마지막 층이 flavor 5개예요. 토스, 당근, 오늘의집, 무신사, 배민이요.

이름만 보면 오해하기 쉬운데, 그 회사의 화면을 흉내 내는 게 아니에요. 레퍼런스 문서의 어느 절에서 나온 주장인지를 인용으로 달고, 레퍼런스에 없는 건 "레퍼런스에 없음"이라고 적어둬요. 근거 없는 값이 flavor라는 이름으로 흘러드는 걸 막으려고요.

한국 제품을 만들 때 이게 왜 필요한지는 화면을 보면 바로 느껴져요. 여백을 쓰는 방식, 가격을 얼마나 지배적으로 놓는지, 리스트 한 줄에 정보를 몇 개까지 얹는지. 이런 건 글로벌 컴포넌트 킷의 기본값에는 들어 있지 않아요.

게이트에 번호를 붙였어요

AI가 만든 UI는 늘 같은 방식으로 실패해요. 그래서 실패마다 번호를 붙였어요.

한국어 화면을 만드는 분들에게 특히 반가울 것들부터 말할게요.

  • 한글에 세리프 폴백이 깔리는 것. 폰트 스택에 한글 글리프가 없으면 브라우저가 명조 계열로 떨어뜨려요. 화면 전체의 인상이 한 번에 바뀌는데, 영어 화면만 보면 안 보여요.
  • 마우스로 눌렀는데 포커스 링이 뜨는 것. 키보드 사용자에게 필요한 표시가 마우스 클릭에도 나와요.
  • 커스텀 시스템 안에 네이티브 select 팝업이 튀어나오는 것.
  • disabled를 opacity로 흉내 내는 것. 눌리지 않는다는 걸 투명도로만 말하고 있어요.
  • 넓은 화면이 텅 비는 것. 데이터가 적어서 그런 건데, 화면은 그냥 빈 채로 나와요.

기계로 확인 가능한 게이트는 자가 비평 중에 grep으로 검증하도록 강제했어요. "위반 없음"은 느낌으로 할 수 있는 주장이 아니니까요.

화면으로 판단해주세요

여기는 솔직하게 적을게요.

저희는 게이트를 세 겹으로 나눠요. 결정론적 감사(G1), 스크린샷을 보고 하는 렌더 비평(G2), 그리고 사람이 "이건 쓰겠다"고 말하는 판정(G3)이에요.

v2.0.0을 내기 전 마지막 실사용 테스트에서 결함 6건이 나왔어요. 그런데 6건 전부 자가 비평이 아니라 외부 렌더 비평에서 나왔어요. 자가 비평은 여전히 소스를 읽지, 화면을 보지 못해요.

그래서 저희는 결정론적 게이트 통과를 릴리스 근거로 쓰지 않아요. 화면을 보는 루프가 품질의 실질 게이트예요. 이건 앞으로도 그럴 거예요.

시스템을 직접 읽어보세요

omd book을 새로 넣었어요. 로컬 포트에서 채택된 시스템을 브라우즈하는 커맨드예요.

npx oh-my-design-cli@latest book            # http://localhost:6060
npx oh-my-design-cli@latest book --static ./out

Storybook이 스토리를 렌더한다면, omd book계약을 렌더해요. 토큰 옆에 그 값을 만든 결정이 붙어 있고, 컴포넌트 상태 매트릭스에는 일부러 해당 없음으로 둔 상태와 그 이유까지 나와요. 대비는 선언값이 아니라 실측값으로 대조하고, 이 빌드가 어느 프리셋에서 나왔는지도 보여줘요.

전부 여러분 레포 안에 있어요

철학도, 프리셋도, 게이트도, 레퍼런스도 전부 여러분 레포 안의 파일이에요.

게이트 임계값을 하나 바꾸면 다음 빌드가 거기에 답해요. 제품에 필요한 장르 프리셋을 추가할 수 있어요. 공개 레퍼런스 대신 여러분 브랜드의 근거를 유도의 출발점으로 삼을 수도 있고요.

이게 이 구조를 택한 진짜 이유예요. 들여다볼 수 없는 취향은 개선할 수 없어요. 규칙이 적혀 있고 번호가 붙어 있으면, 의견 충돌이 "좀 더 프리미엄하게 만들어줘" 한 판 더가 아니라 한 줄의 수정이 돼요.

여기까지 v2.0.0에서 바뀐 것들을 나눠봤어요. 직접 돌려보시고 화면으로 판단해주세요.

npx oh-my-design-cli@latest

직접 해보기

npx oh-my-design-cli@latest
CLI가 에이전트에게 무엇을 주는지 →