들어가며
지난 몇 달간 사내 디자인 시스템인 VUI(Vizend UI)를 구현해 왔습니다. VUI는 디자이너와 개발자가 제품을 같은 방식으로 만들도록 묶어 주는 공통의 언어이자 약속이며, 디자인 토큰·컴포넌트·패턴·원칙과 이를 코드까지 잇는 자동화를 하나로 엮은 디자인 시스템입니다. 그 기반 엔진으로는 MUI(Material UI)를 사용합니다. 디자인 시스템을 어떻게 만들고 있는지 간략하게 소개합니다.
순서는 이렇습니다. ① 왜 디자인 시스템이 필요했는지, ② MUI를 베이스로 사용한 이유, ③ 디자인 시스템에서 사용하는 디자인 토큰, ④ MUI 테마의 동작 원리, 디자인 토큰을 MUI 테마가 어떻게 소비하며 실제 컴포넌트에 토큰을 입힌 방법, ⑤ 모바일/데스크톱과 라이트/다크 등 모드별 테마를 분리 구성한 방법, 마지막으로 ⑥ 아직 해결하지 못한 문제들입니다.
1. 왜 디자인 시스템이 필요했나
디자인 시스템을 흔히 “재사용 가능한 UI 컴포넌트 모음”으로 생각합니다. 하지만 제가 체감한 디자인 시스템의 본질은 제품을 만드는 방식에 대한 공통의 언어이자 약속이었습니다. 비유하자면 UI Kit이 “레고 블록 한 바구니”라면, 디자인 시스템은 블록을 찍어내는 공장 + 조립 설명서 + 블록 그 자체를 모두 포함합니다.
출발점은 AI였습니다. AI를 통한 프론트엔드 코드 자동 생성을 시도하면서, AI가 이해할 수 있도록 잘 정리되고 구현된 시스템이 필요했습니다. 또한 시스템이 구축되면 적은 인원으로도 디자인/퍼블리싱 진행이 가능한 두 번째 이유였습니다. 사람의 기억력과 성실함에 의존하는 방식은 인원이 적을수록 빠르게 무너지고 AI 활용성도 떨어집니다. VUI의 원칙으로 “시스템의 강제성과 자동화”에 두고 디자인 자산화를 가장 큰 목표로 설정하였습니다.
디자인 시스템이 없을 때의 비용은 구체적이었습니다.
-
파편화된 경험: 같은 ‘버튼’인데도 화면마다 곡률·색·높이가 미묘하게 달라집니다. 통제하지 않으면 제품마다 다른 얼굴이 됩니다.
-
커뮤니케이션 오버헤드: “거기 파란색 좀 더 진하게”라는 모호한 요청이 디자인-개발 사이를 핑퐁처럼 오갑니다.
-
반복 작업(Toil): 화면 하나하나에 스타일을 손으로 입히는 단순 반복이 비즈니스 로직에 쓸 시간을 잡아먹습니다.
그래서 VUI가 추구한 것은 단순한 일관성이 아니라 공학적 일관성(Engineered Consistency)입니다. Semantic Token(의미론적 토큰)이라는 공통의 언어로 대화하게 만드는 것입니다. “배경을 회색으로 진하게”가 아니라 “Background-Default 토큰을 Background-Dark로 변경”이라고 말하게 되면, 디자인 결정이 곧 데이터가 되고 추적·롤백이 가능해집니다.
2. MUI 선택
디자인 시스템을 만든다고 하면 가장 먼저 부딪히는 갈림길이 Build vs Buy입니다. 모든 컴포넌트를 직접 만들 것인가, 검증된 라이브러리를 가져다 우리 옷을 입힐 것인가. 저는 후자를 택했고, 그 베이스로 MUI(Material UI)를 골랐습니다.
2.1 직접 구축
버튼 하나는 어렵지 않게 만들 수 있습니다. 문제는 그 버튼이 키보드 포커스, 스크린리더(ARIA), 비활성 상태, RTL, 크로스브라우징까지 모두 챙겨야 “제품 품질”이 된다는 데 있습니다. 여기에 DataGrid, DatePicker, Autocomplete 같은 복잡도 높은 컴포넌트까지 직접 만들고 유지보수하면, 작은 팀의 리소스는 비즈니스 로직이 아니라 “바퀴 재발명”에 소진됩니다. 접근성·호환성 품질도 팀 역량에 따라 들쭉날쭉해집니다.
2.2 후보 비교 — 우리 기준에서 MUI가 이긴 이유
라이브러리를 고를 때 제가 세운 기준은 세 가지였습니다. ① 브랜드를 입히기 위한 테마 커스터마이즈가 얼마나 깊고 안전한가, ② 엔터프라이즈 화면(표·날짜·자동완성)을 위한 복잡 컴포넌트 생태계가 충분한가, ③ Figma와의 정합(디자인-개발 동기화)이 가능한가.
|
후보 |
강점 |
약점 |
|---|---|---|
|
직접 구축(headless 조합) |
완전한 자유 |
구축·접근성·유지보수 비용이 가장 큼 — 작은 팀에 비현실적 |
|
Ant Design |
완성도 높은 엔터프라이즈 컴포넌트 |
디자인 언어가 강하게 고정 — 브랜드 재정의·깊은 테마가 어려움 |
|
Chakra UI |
가볍고 토큰 친화적 |
표/날짜 등 고복잡 컴포넌트 생태계가 상대적으로 얕음 |
|
MUI(선택) |
createTheme 오버라이드의 깊이 + x-data-grid·x-date-pickers 생태계 + Material Figma Kit 정합 + 거대한 커뮤니티 |
Material 디자인 색이 남음 → 토큰·래핑으로 덮어야 함 |
결정적이었던 것은 MUI의 공식 테마 오버라이드 API(createTheme)였습니다. 브랜드 스타일을 ‘설정(Configuration)’ 계층에서만 주입할 수 있다는 것은, 곧 다음 원칙을 가능하게 했습니다.
2.3 Natural Upgrade — “No Forking” 원칙
오픈소스를 쓸 때 가장 큰 리스크는 커스터마이즈를 위해 원본을 포크(수정)하는 순간 발생합니다. 포크하는 순간 업스트림 업데이트와 영영 멀어지기 때문입니다. 그래서 VUI는 MUI의 소스나 node_modules를 수정하지 않는다는 규칙을 고수합니다. 모든 커스터마이즈는 MUI가 공식 제공하는 Theme Override API로만 이뤄집니다. 그 결과는 “유지보수의 자유”입니다 — npm update 한 번으로 보안 패치와 신기능이 자연스럽게 따라 들어오고, 우리 브랜드 오버라이드는 그대로 유지됩니다.
VUI의 버튼은 MUI의 Button을 그대로 감싸기만 하고, 색·곡률·간격 같은 모양은 전부 토큰 → 테마 경로로 입혀집니다. 원본을 포크해 뜯어고치는 대신 이렇게 얇게 감싸 두면, MUI 버전이 올라가도 래퍼는 깨지지 않습니다.
3. Figma를 SSOT로 — 디자인 토큰의 코드화
VUI 6계층 아키텍처에서 가장 아래(Layer 1)에 있는 것이 디자인 토큰입니다. 토큰은 색(#Hex), 간격(px), 타이포그래피, 곡률 같은 모든 시각적 결정을 플랫폼 독립적인 데이터로 추상화한 것입니다. 하드코딩과 토큰의 차이는 “의미”에 있습니다.
// Bad — 이 색이 무슨 의미인지 코드만 봐선 모른다
background-color: #2196F3;
// Good — '브랜드의 메인 컬러'임이 이름에 드러난다
background-color: tokens.color.brand.primary.main;
핵심 원칙은 Figma가 단일 진실 공급원(SSOT)이라는 것입니다. 색을 바꾸고 싶으면 코드가 아니라 Figma에서 바꾸고, 코드는 그 결정을 “받아 적기만” 합니다. 이를 위해 토큰을 손으로 옮겨 적는 과정을 없애고 파이프라인으로 자동화했습니다.
3.1 토큰 코드화 파이프라인 (Figma → JSON → TS)
파이프라인은 세 단계입니다. 먼저 디자이너가 Figma Variables에 정의한 색·간격·타이포 값을 Figma 커스텀 플러그인을 사용하여 W3C DTCG 표준 JSON 형식으로 추출합니다. 다음으로 Style Dictionary라는 라이브러리를 사용하여 토큰끼리의 참조 체인(component → semantic → primitive)을 풀어 실제 값으로 확정합니다. 마지막으로 그 결과가 타입스크립트 파일로 저장되고 MUI 에서 이 디자인 토큰을 소비합니다. Figma의 시각적 결정이 사람 손을 거치지 않고 코드 토큰까지 흘러내려옵니다. 끊긴 참조가 있어도 빌드를 실패시키지 않고 로그에 기록하게 했는데, 디자인이 바뀌는 중에도 CI가 멈추지 않게 하기 위함이었습니다. 표준 포맷(W3C DTCG)으로 한 번 뽑아 두면, 그 뒤는 어떤 도구든 읽을 수 있어 자동화가 가능해집니다.
3.2 토큰 계층, 참조 관계 (Semantic Naming — primitive → semantic → component)
토큰은 이름만으로 용도를 알 수 있도록 3계층으로 짭니다. primitive(물감 자체: blue-500), semantic(의미·역할: primary-main), component(특정 부품의 쓰임: button-contained-bg). 맨 아래 물감만 바꿔도 위가 전부 따라 바뀌도록 단방향 의존(primitive ← semantic ← component)만 허용합니다.
// primitive — 물감 자체 (실제 색 값)
color/blue/500 = #2196F3
// semantic — 의미·역할 (primitive 를 가리킴)
color/primary/main = {color.blue.500}
// component — 특정 부품의 쓰임 (semantic 을 가리킴)
button/contained/bg = {color.primary.main}
// → blue-500 한 곳만 바꾸면 primary, button 까지 연쇄 반영된다
4. MUI Theme는 토큰을 어떻게 소비하는가
토큰이 데이터(Layer 1)라면, 그 데이터를 실제 컴포넌트의 “옷”으로 바꾸는 엔진이 MUI Theme(Layer 2)입니다. 여기서 MUI 테마의 작동 원리를 이해하는 것이 핵심입니다.
4.1 MUI Theme Configuration의 작동 원리 — 3채널
MUI 컴포넌트는 ThemeProvider 컨텍스트로 받은 theme 객체를 읽어 스타일을 계산합니다. 주입 통로는 크게 세 채널입니다.
-
글로벌 디자인값: palette / typography / spacing / shape 등 모든 컴포넌트가 공유하는 전역 값
-
기본 props: components.MuiX.defaultProps — 컴포넌트의 기본 동작·형태
-
스타일 오버라이드: components.MuiX.styleOverrides — variant·color·state 조합별 스타일
VUI는 이 세 채널을 전부 토큰으로 채웁니다. 그래서 한 곳(ThemeOptions)만 바꾸면 모든 컴포넌트가 동시에 바뀝니다. 테마 생성은 브랜드·모드를 인자로 받는 헬퍼로 감쌌습니다.
4.2 token-adapter — “값은 자동, 구조만 수동”
자동 생성된 토큰의 경로(예: component.button['md-radius'])를 컴포넌트 스타일에 직접 쓰면, 디자이너가 토큰 구조를 한 번 바꿀 때마다 코드가 깨집니다. 그래서 token-adapter.ts를 두어 자동 생성 토큰의 경로를 안정적인 슬롯 이름으로 1회 매핑했습니다. 이후 토큰 값이 바뀌면 경로는 그대로라 자동 반영되고, 토큰 구조(경로)가 바뀔 때만 어댑터 한 줄을 고칩니다. components.ts는 이 어댑터가 노출한 buttonTokens / chipTokens / alertTokens 등을 받아 MUI styleOverrides를 작성합니다.
// components.ts — token-adapter 의 슬롯을 MUI styleOverrides 로 연결
import { buttonTokens, chipTokens, alertTokens } from './token-adapter';
// MUI Theme 객체에 선언된 MUI Button 스타일 선언코드
MuiButton: {
styleOverrides: {
contained: ({ theme, ownerState }) => ({
backgroundColor: buttonTokens.variant.contained.bg[ownerState.color],
borderRadius: theme.shape.radiusControlSm,
}),
},
}
MUI 표준 palette에 없는 VUI 고유 슬롯(예: surface / field / border / icon / overlay)은 module augmentation으로 타입을 확장해, theme.vui 또는 확장 palette 슬롯으로 접근하게 했습니다(mui-augmentation.ts). IDE 자동완성만으로 수많은 토큰을 헷갈리지 않고 꺼내 쓸 수 있습니다 — 타입 시스템이 곧 참조할 수 있는 문서가 되는 셈입니다.
4.3 Figma MCP로 디자인 토큰 정합성 검증하기
디자인 시스템을 구축하며 중요하게 해야 하는 일 중 하나는 “Figma에 정의된 컴포넌트 스펙을 실제 토큰·테마로 연결”하고, 그 둘이 어긋나지 않는지 디자인 정합성을 검증하는 작업이었습니다. 최근에는 이 대조 과정을 AI Agent를 사용합니다.
핵심은 두 단계입니다. 먼저 Figma MCP를 통해 AI Agent가 Figma 프레임 안에서 특정 컴포넌트에 실제로 적용된 디자인 토큰 정보(색·타이포·간격·radius 등 어떤 변수가 어디에 바인딩됐는지)를 직접 읽어 분석합니다. 그 다음 이 Figma 측 토큰 정보를, 컴포넌트가 실제로 구현된 Storybook과 비교합니다.
<Storybook>
<Figma>
두 결과를 나란히 놓고 AI Agent가 비교하여, 구현 코드에서 디자인 토큰이 빠진 부분이나 Figma와 값이 일치하지 않는 부분을 검사해 낼 수 있습니다. 사람이 매번 눈으로 대조하던 일을 AI Agent가 1차로 걸러 주는 셈입니다. 이런 검증이 가능한 이유는 결국 토큰이라는 공통 언어 덕분입니다 — Figma와 코드가 같은 기준(토큰)으로 표현되기에, 비로소 자동 비교가 성립합니다.
5. 모드별 테마 구성 — 컴포넌트는 그대로, 테마만 바꾼다
실제 제품은 한 가지 화면만 있는 게 아닙니다. 큰 모니터에서도 작은 휴대폰에서도, 밝은 화면에서도 어두운 화면에서도, 또 서로 다른 브랜드의 제품에서도 똑같이 잘 보여야 합니다. VUI는 이런 변형들을 한꺼번에 다루는데, 핵심은 컴포넌트 코드는 그대로 두고 ‘테마’만 갈아 끼운다는 점입니다. 지금 이 전환의 축은 세 가지입니다.
-
화면 크기 (데스크톱 / 모바일) — 같은 컴포넌트라도 화면이 작아지면 글자 크기와 간격이 자동으로 더 촘촘해집니다. 화면 너비를 보고 테마가 알아서 데스크톱용·모바일용 값으로 바꿔 줍니다.
-
라이트 / 다크 모드 — 배경과 글자 색이 모드에 맞게 통째로 바뀝니다. 사용자가 다크 모드를 켜면, 컴포넌트는 그대로인데 색상 묶음만 어두운 세트로 교체됩니다.
-
브랜드 (예: vizend / devlime) — 제품마다 브랜드 색과 분위기를 다르게 입힐 수 있습니다. 새 브랜드가 필요하면 디자인에서 색만 새로 정의해 추가하면 됩니다.
중요한 점은, 이 세 축을 어떻게 조합하든 개발자는 컴포넌트 코드를 한 줄도 바꾸지 않는다는 것입니다. 화면 맨 위에서 ‘어떤 브랜드, 어떤 모드인지’만 한 번 정해 주면, 그 아래 모든 컴포넌트가 알아서 그에 맞는 테마를 입습니다. 복잡한 분기를 컴포넌트마다 넣는 대신, 그 복잡도를 테마라는 한 층으로 끌어올려 아래를 단순하게 만든 셈입니다.
// 맨 위에서 '브랜드 / 모드'만 정하면 끝 — 컴포넌트 코드는 그대로
<VuiThemeProvider brand="vizend" mode="dark">
<App />
</VuiThemeProvider>
6. 아직 해결해야 할 문제들
설계와 구현을 진행하며 분명해진, 아직 풀지 못한 과제입니다.
6.1 디자인 토큰 매핑 문제 — MUI Theme로 모든 스타일을 관리할 수 없다.
VUI의 의미 기반 색이 MUI 표준 theme.palette 슬롯 구조에 온전히 매핑되지 않습니다. MUI palette는primary/secondary/text/background 정도를 가정하는데, 우리의 semantic 토큰은 그보다 결이 많습니다. 그 결과 vuiColors.bg.surface 같은 헬퍼나 CSS 변수로 우회 접근하는 경로가 늘었습니다. “Figma에서 text.muted라는 의미를 정의했는데, MUI palette의 text 슬롯에 그 의미를 그대로 담을 자리가 없다”는 문제는 module augmentation으로 일부 완화했지만, 표준과 확장이 공존하는 어색함은 남아 있습니다.
6.2 구조 변경 감지의 비대칭, 그리고 컴포넌트 배선 미완성
token-adapter 덕에 토큰 값 변경은 무한 자동인데, 토큰 구조(경로) 변경은 사람이 build.log를 직접 읽고 어댑터를 고쳐야 합니다 — 자동/수동의 비대칭이 불편합니다. 또한 토큰 어댑터에는 슬롯이 있는데 components.ts의 styleOverrides가 아직 안 붙은 컴포넌트가 다수 남아 있어, 그 컴포넌트는 사용자가 sx로 토큰을 직접 가져다 써야 합니다. 데이터는 다 있는데 배선이 덜 된 상태로, 한 번 작업하면 미래 유지 보수 비용은 거의 없지만 지금은 미완입니다.
6.3 디자인 원칙의 수립과 문서화 필요성
지금까지는 “토큰을 코드화”하는 데 집중했지만, 정작 그 위에서 사람이 따라야 할 디자인 원칙과 컴포넌트 사용 가이드는 아직 충분히 정리되지 못했습니다. 시스템은 갖춰졌지만, “이걸 어떻게·언제 써야 하는가”를 알려 주는 문서가 부족합니다.
두 종류의 문서가 필요합니다. 하나는 디자인 원칙 문서입니다 — 왜 semantic과 component 레이어를 나눴는지, 어떤 요청은 토큰으로 받아들이고 어떤 요청은 거절하는지 같은 의사결정의 기준을 남겨야, 6개월 뒤 합류한 사람도 같은 판단을 내릴 수 있습니다. 다른 하나는 컴포넌트 사용 가이드입니다 — 각 VUI 컴포넌트를 언제 어떤 prop 조합으로 써야 표준인지, 흔한 잘못된 사용은 무엇인지를 정리해야 일관성이 사용 단계에서까지 유지됩니다.
토큰을 코드화하는 데까지는 왔으니, 이제 원칙과 사용법을 문서로 코드화할 차례입니다. 이것이 새 팀원의 온보딩 속도와 시스템의 지속가능성을 좌우합니다.
맺으며
VUI를 만들며 얻은 한 문장은 이것입니다 — “일관성의 책임을 사람의 성실함이 아니라 시스템에 둔다.” MUI를 베이스로 고른 것, Figma를 SSOT로 둔 것, 토큰을 코드화하고 테마로 소비하게 한 것, 변형을 설정으로 흡수한 것은 모두 이 한 방향을 가리킵니다.
이 작업의 의미는 두 겹이라고 생각합니다. 가까이에는, 작은 팀이 적은 인원으로도 표준 품질의 디자인과 퍼블리싱을 함께 해낼 수 있게 하는 레버리지입니다. 멀리는, 비젠드 플랫폼이 지향하는 프론트엔드 코드 자동 생성의 초석입니다 — 잘 정리되고 토큰으로 표준화된 디자인 시스템이 있어야, AI가 그 위에서 일관된 화면 코드를 만들어 낼 수 있기 때문입니다.
Brown