Design
멀티 프레임워크 디자인 시스템, 결국 운영 가능한 구조를 선택한 이유
Henby여기어때
2026년 3월 10일
원문에서 보기 ↗
안녕하세요. 서비스웹개발팀의 헨비입니다.
이번 글에서는 React와 Vue를 함께 지원하는 디자인 시스템을 직접 만들고 배포/운영하면서, 왜 결국 “모든 것을 동일하게 지원”하는 전략에서 “운영 가능한 범위를 선택하는 전략”으로 전환했는지 정리해보려고 합니다.
처음에는 core + react + vue 구조로 컴포넌트까지 일관되게 제공하는 것을 목표로 했지만, 실제 운영에서는 기능 추가, QA, 배포, 회귀 검증 비용이 빠르게 커졌습니다.
현재는 React 컴포넌트를 운영 축으로 두고, Vue에서는 토큰/스타일/유틸리티 같은 디자인 파운데이션을 중심으로 소비하고 있습니다. 이 전환 과정에서 무엇을 얻고 무엇을 포기했는지, 그리고 비슷한 상황에서 어떤 기준으로 선택해야 하는지 공유하겠습니다.
- 왜 멀티 프레임워크 디자인 시스템을 시작했나?

- 서비스웹개발팀은 레거시 Vue 프로젝트와 신규 React/Next.js 프로젝트를 동시에 운영하고 있습니다.
- 문제는 기술 스택 자체보다, YDS 업데이트를 프로젝트별로 반복 반영하는 운영 비용이 매우 컸습니다.
- 그래서 특정 당장 프레임워크 통일보다는 프레임워크가 달라도 공통 규칙을 재사용할 수 있는 구조를 먼저 찾았습니다.
- 검토 기준은 SSR, SEO, 복잡도, 런타임 의존성, 운영 유지비였습니다.
- 결론적으로 우리 팀 조건에서는 Headless Core + Framework Adapter가 가장 운영 가능한 선택이었습니다.
- 초기 설계: core + adapter(react/vue) 구조

- 멀티 프레임워크 환경에서 중복 구현을 줄이기 위해 packages/core, packages/react, packages/vue로 구조를 분리했습니다.
- core는 디자인 토큰/스타일 유틸리티/headless 로직의 단일 출처로 두었습니다.
- react, vue는 프레임워크별 렌더링/이벤트/컴포넌트 표면만 담당하는 adapter로 설계했습니다.
// core (headless)
export function createBoxButton(props) {
return { state, bindings }
}
// React adapter (요약)
import { createBoxButton } from "@yeogiforge/core";
export function BoxButton(props) {
const { state, bindings } = createBoxButton(props);
return (
<button {...bindings} data-state={state}>
{props.children}
</button>
);
}
// Vue adapter (요약)
<script setup lang="ts">
import { createBoxButton } from "@yeogiforge/core";
const props = defineProps<{ disabled?: boolean }>();
const { state, bindings } = createBoxButton(props);
</script>
<template>
<button v-bind="bindings" :data-state="state">
<slot />
</button>
</template>
- 두 adapter는 같은 core 로직을 공유하고, 프레임워크별 렌더링 표면만 다르게 가져갑니다.
- 즉, 상태/의미 체계는 core에서 통일하고 UI 연결 지점만 React/Vue에서 분리했습니다.
- 핵심 의도는 “규칙은 한 곳에서 관리하고, 프레임워크별 연결만 분리”하는 것이었습니다.
- 초기에는 이 구조가 일관성과 확장성을 동시에 잡는 가장 현실적인 방법이라고 판단했습니다.
- 문제는 아키텍처 선택 자체보다, 이 구조를 실제 운영하면서 발생한 이중 구현과 동기화 비용에서 본격적으로 드러났습니다.
구축보다 어려웠던 운영: 이중 구현과 동기화 비용
초기에는 core + adapter 구조로 중복을 줄일 수 있다고 판단했습니다. 하지만 실제 운영에서는 컴포넌트 레이어의 비용이 크게 남아 있었습니다. 새 컴포넌트를 추가하거나 기존 컴포넌트를 수정할 때마다 React/Vue 양쪽 구현, 스토리, 검증 포인트가 함께 늘어났습니다.
공통 로직이 core에 있어도, 프레임워크별 렌더링/상태 연결/엣지 케이스 대응은 각 adapter에서 별도로 처리해야 했습니다.
- 결과적으로 변경 1건이 “코어 수정 + React 반영 + Vue 반영 + 각각 확인”으로 이어지며 릴리즈 리드타임이 길어졌습니다.
- 팀 입장에서는 기능 확장보다 동기화 유지에 더 많은 시간을 쓰게 되었고, 이 지점이 운영 모델 전환의 계기가 되었습니다.

대응 해야 할 파운데이션과 컴포넌트의 목록
운영 모델 전환: React 컴포넌트 집중, Vue는 파운데이션 중심
- 운영 데이터를 기준으로 지원 범위를 다시 정의했습니다. 컴포넌트 레이어는 React를 단일 운영축으로 집중하고, Vue는 토큰/스타일/유틸리티 같은 디자인 파운데이션 소비에 집중하도록 전환했습니다.
- 이 결정은 “두 프레임워크를 모두 동일 수준으로 유지” 하는 목표보다, “지속 가능한 속도로 품질을 유지”하는 목표를 우선한 결과였습니다.
- 이후 컴포넌트 변경 흐름이 단순해졌고, 신규 요구사항 반영 속도와 릴리즈 예측 가능성이 함께 개선되었습니다.
- 반면 Vue에서 컴포넌트 레벨의 즉시 확장성은 일부 포기했습니다. 대신 파운데이션 정합성을 유지해 시각/토큰 레벨의 일관성은 계속 보장했습니다.
결국 저희가 선택한 방향은 ‘모든 레이어를 동일하게 지원’하는 전략이 아니라, 레이어별로 지원 수준을 다르게 설계해 운영 가능성을 확보하는 전략이었습니다.
파운데이션 공통화 범위와 개발자 사용성
저희는 컴포넌트 공통화 범위를 줄이는 대신, 파운데이션 레이어의 재사용성과 사용성을 높이는 데 집중했습니다.
- Typography: 폰트는 CSS 변수(` — forge-font-sans`)로 노출해 프로젝트별 오버라이드가 가능하도록 했습니다.
- Color Palette + Semantic Color: 원색 팔레트와 시멘틱 컬러를 분리해, 브랜드 변경과 UI 의미 변경을 독립적으로 다룰 수 있게 했습니다.
- SVG Icon: 아이콘 fill을 `currentColor`로 정규화해 React/Vue 어디서든 동일한 방식으로 색상을 제어하도록 했습니다.
- Shadow: 그림자 값을 토큰화해 컴포넌트와 화면 레벨 모두에서 같은 시각 기준을 재사용하도록 했습니다.
중요했던 기준은 “모든 것을 강제 공통화” 가 아니라 “필요한 파운데이션만 가져다 쓸 수 있는 유연성” 이었습니다.
덕분에 팀 내부 공통 자산으로도 활용하고, 프로젝트별 선택적 도입도 가능하게 설계했습니다.
멀티 프레임워크에서 ‘지원 범위’를 설계하는 법
이번 경험을 통해 멀티 프레임워크 디자인 시스템의 핵심은 기술 선택 자체보다, 지원 범위를 어떻게 설계하고 운영할지에 있다는 점을 확인했습니다. 처음에는 React/Vue를 동일 수준으로 지원하려고 했지만, 실제 운영에서는 구현·검증·배포 비용이 빠르게 증가했습니다.
그래서 현재는 React 컴포넌트를 운영축으로 두고, Vue는 파운데이션 공통화에 집중하는 방식으로 운영 모델을 재정의했습니다.
이 전환은 “더 많은 지원” 보다 “지속 가능한 품질과 속도”를 우선한 선택이었습니다. 비슷한 상황에 있는 팀이라면, 아키텍처의 이상형보다 팀이 장기적으로 감당할 수 있는 지원 범위를 먼저 정의해보시길 권장드립니다.
디자인 시스템은 한 번의 구축으로 완성되는 결과물이 아니라, 운영 가능한 범위를 계속 조정해 나가는 제품이라고 생각합니다.