사내 React 공용 컴포넌트를 구축하면서 마주친 DX, 커스터마이징, 스타일 일관성 사이의 트레이드오프와 그 고민들을 정리합니다.
React 환경에서 사내 공용 컴포넌트를 설계할 때, 처음에는 세 가지를 동시에 달성할 수 있을 것 같아 보입니다.
“세 마리 토끼를 동시에 잡을 수 있을까?
사용감 좋은 컴포넌트는 Required Props의 수가 적은 컴포넌트입니다. 코드에 올려놓는 순간 TypeScript가 자연스럽게 이를 요구하고, 개발자는 최소한의 데이터만 채워 넣으면 컴포넌트가 완성됩니다.
하지만 여기서 첫 번째 충돌이 발생합니다.
Required Props를 줄이면 DX는 올라가지만 커스터마이징이 제한됩니다. 커스터마이징을 열어두면 Props가 늘어나고 DX가 나빠집니다. 그리고 커스터마이징이 과도하게 열리면 결국 스타일 일관성이 사라집니다.
그렇다면 모든 UI 디자인에 대응할 수 있는 슈퍼 UX 시스템 같은 것이 존재할 수 있을까요?
Radix UI, shadcn/ui, MUI 같은 헤드리스 또는 풀 컴포넌트 라이브러리들도 결국 동일한 문제에 부딪힙니다. 완전히 열어두면 스타일 일관성을 보장할 수 없고, 닫아두면 "이 케이스는 왜 안 되냐"는 불만이 나옵니다.
템플릿은 개발 생산성을 향상시킬 수 있습니다. 그런데 일관된 스타일 제공의 영역까지 책임지려는 것은 무리가 아닐까요?
색상, 사이즈, 패딩 크기를 정규화하는 정도에서 그치는 것이 현실적일 수 있습니다.
정규화의 범위를 색상, 간격, 타이포그래피 토큰 수준으로 제한한다고 가정하면, 또 다른 질문이 생깁니다.
공용 시스템을 도입하면 반드시 학습 비용이 발생합니다.
단순 색상 팔레트 정규화라면 CSS 변수 파일 하나로도 가능합니다. 그것을 위해 컴포넌트 시스템 전체를 도입하는 것이 맞는 접근인지는 팀 규모와 프로젝트 수명에 따라 달라집니다.
스타일 정규화를 어떤 방식으로 소스코드와 CSS에 반영할 것인지도 선택해야 합니다.
:root {
--color-primary: #6366f1;
--spacing-md: 16px;
--radius-lg: 12px;
}
브라우저 네이티브이며, 다크모드 대응에도 자연스럽습니다.
JS에서 getComputedStyle로 읽을 수 있고, Tailwind의 theme() 함수와도 연동 가능합니다.
가장 넓은 호환성을 가지며 컴포넌트 라이브러리에 적합한 방식입니다.
설계를 진행하면서 아직 명확한 답을 내리지 못한 질문들입니다.