npx shadcn add button)로 부르면 Button 컴포넌트의 실제 소스 코드 파일이 내 프로젝트 폴더 안에 그대로 복사되고, 그 순간부터는 100% 내 코드다. 저장소가 관리하는 건 완제품 라이브러리가 아니라 "레지스트리" — 수백 개 컴포넌트의 소스와 메타데이터, 그리고 그걸 프로젝트 사정에 맞게 고쳐 붙여넣는 CLI 엔진이다.
shadcn-ui/ui · 제작 shadcn · 라이선스 MIT · CLI 패키지명은 "shadcn" v4.16.0 · pnpm 워크스페이스 모노레포(패키지 4개+앱 1개) · 등록 컴포넌트 411개(classic 레지스트리 기준) · Next.js 16.2.7 / React 19.2.3 / Tailwind CSS v4.3 · 최신 커밋 2026-07-29, 해시 29ea362)
shadcn/ui는 npm 패키지가 아니다. 이 문장이 이 저장소를 이해하는 가장 중요한 출발점이다. Material UI(MUI)나 Chakra UI 같은 전통적인 컴포넌트 라이브러리는 npm install로 검은 상자(black box) 같은 완제품 패키지를 프로젝트에 얹고, 그 패키지의 API를 익혀 사용한다. 반면 shadcn/ui는 CLI가 컴포넌트의 실제 소스 코드 파일을 통째로 복사해 내 프로젝트의 components/ui 폴더에 붙여넣는다. 설치가 끝나는 순간 그 파일은 라이브러리의 일부가 아니라 내가 작성한 것처럼 자유롭게 고칠 수 있는 내 코드가 된다.
루트 README.md는 이 철학을 "Open Source. Open Code."라는 슬로건으로 못 박는다. 실제로 저장소 안에는 두 가지 큰 축이 공존한다. 하나는 레지스트리 — 버튼·다이얼로그·폼 같은 UI 조각과 대시보드·로그인 화면 같은 블록(block)의 소스 코드 및 메타데이터를 모아둔 JSON+파일 더미(apps/v4/registry, 컴파일 결과물 registry.json에 411개 항목). 다른 하나는 CLI(npm 패키지명 shadcn) — 그 레지스트리에서 필요한 항목을 골라, 내 프로젝트의 폴더 구조·별칭(alias)·Tailwind 설정에 맞게 코드를 변형(transform)해 옮겨 심는 엔진이다.
보통의 UI 라이브러리는 이케아 완제품 가구와 같다. 정해진 규격대로 조립만 하면 되지만, 내 집 벽 색깔에 맞춰 페인트를 새로 칠하거나 다리 길이를 바꾸는 건 어렵거나 아예 불가능하다.
shadcn/ui는 목공소에 가서 "이 치수, 이 나무 종류로 의자 하나 만들어 주세요"라고 주문하는 것에 가깝다. CLI가 내 프로젝트의 치수(폴더 구조·별칭·Tailwind 설정)를 재고, 그에 맞게 소스를 재단해서 완성된 원목 그대로 내 손에 넘긴다. 그 순간부터 사포질도, 페인트칠도 전부 내 몫이자 내 자유다.
shadcn/ui는 이미 여러 해 전부터 React 생태계의 사실상 표준으로 자리 잡았지만, 2026년 시점에 이 저장소를 다시 들여다볼 이유는 따로 있다. 코드 자체가 계속 구조적으로 진화하고 있고, 그 방향이 정확히 "AI 에이전트가 UI 코드를 직접 다루는 시대"를 정조준하고 있기 때문이다.
저장소 안의 skills/shadcn/SKILL.md는 Claude Code 같은 코딩 에이전트가 이 프로젝트를 만질 때 지켜야 할 규칙을 담은 공식 에이전트 스킬 문서다. "기존 컴포넌트를 먼저 검색해라", "className은 레이아웃에만 써라, 색상은 시맨틱 토큰으로", "Dialog는 반드시 Title이 있어야 한다" 같은 실전 규칙이 Incorrect/Correct 코드 쌍과 함께 정리돼 있다. 즉 이 저장소 자체가 "AI가 UI를 짤 때 참고하는 교과서" 역할을 자처한다.
오랫동안 shadcn/ui는 사실상 Radix UI 위의 스타일링 레이어였다. 그런데 현재 저장소의 apps/v4/registry/bases/ 아래에는 radix · base · aria 세 개의 병렬 디렉터리가 있다. 같은 Button이라도 Radix UI 기반, Base UI(MUI 팀의 새 헤드리스 라이브러리) 기반, React Aria(Adobe) 기반 세 가지 소스가 따로 존재한다. 심지어 skills/migrate-radix-to-base라는 전용 마이그레이션 스킬까지 갖췄다. 특정 헤드리스 벤더에 락인(lock-in)되지 않겠다는 의지가 코드로 드러난다.
packages/shadcn/src/preset/preset.ts는 베이스(radix/base/aria) · 스타일(nova, vega, maia, lyra, mira, luma, sera, rhea 8종) · 기본 색상 · 테마 색상을 비트 단위로 압축해 하나의 base62 문자열로 인코딩한다. npx shadcn init --preset <코드> 한 줄이면 팀 전체가 똑같은 디자인 시스템 조합으로 시작할 수 있다. "우리 팀 스타일은 이 코드입니다"라고 URL처럼 공유하는 문화가 가능해진 것이다.
MUI·Chakra UI·Mantine 같은 전통 라이브러리는 여전히 강력하지만 커스터마이징이 결국 그 라이브러리의 테마 API 안에 갇힌다. shadcn/ui는 "라이브러리 유지보수 없는 오픈소스"라는 독특한 자리를 차지했고, v0·Claude Code 같은 AI 코드 생성 도구들이 사실상 shadcn/ui 코드를 표준 출력 형식처럼 채택하면서 그 입지가 더 굳어졌다.
React로 UI를 만들 때 개발자는 늘 두 가지 나쁜 선택지 사이에서 골랐다. 하나는 완제품 라이브러리를 설치하는 것 — 빠르지만, 디자인을 깊이 바꾸려면 라이브러리가 허용하는 테마 옵션 안에서만 움직여야 하고, 접근성(accessibility) 버그를 만나도 내가 고칠 수 없어 이슈 트래커에 줄을 서야 한다. 다른 하나는 Radix UI 같은 헤드리스 프리미티브 위에 처음부터 직접 스타일링하는 것 — 자유롭지만, 버튼 하나 다이얼로그 하나에도 접근성·포커스 트랩·애니메이션을 처음부터 다 챙겨야 해서 느리다.
shadcn/ui는 이 둘을 "이미 잘 만들어진 답을 받아서, 그 답을 내 것처럼 고친다"는 제3의 길로 풀었다. 컴포넌트는 이미 Radix UI/Base UI/React Aria라는 검증된 접근성 엔진 위에서 동작하도록 완성돼 있고, 스타일은 Tailwind 유틸리티 클래스로 눈에 훤히 보이게 짜여 있다. 개발자는 "접근성이 되는 컴포넌트를 밑바닥부터 만드는 시간"을 아끼면서도, "그 컴포넌트의 모든 코드 한 줄까지 내 마음대로 바꿀 자유"를 동시에 얻는다.
같은 문제의식이 CLI의 변형기(transformer) 파이프라인에도 이어진다. 컴포넌트를 그냥 복사만 하면 프로젝트마다 다른 import 별칭(@/components vs ~/ui 등), CSS 변수 사용 여부, Tailwind 접두사(prefix) 설정 때문에 코드가 그대로 동작하지 않는다. CLI는 이 지점을 ts-morph 기반 AST 변형으로 자동 보정해, "복사"가 아니라 "내 프로젝트에 맞게 재조립된 이식"이 되게 한다.
@내레지스트리 이름으로 등록할 수 있다.
저장소는 pnpm-workspace.yaml로 관리되는 모노레포이며, 빌드는 Turborepo, 릴리스는 Changesets가 맡는다. 최상위는 apps/와 packages/ 둘로 나뉘는데, 역할 분담이 뚜렷하다.
이 배치가 말해주는 것은 명확하다. apps/v4는 "레지스트리 데이터의 원천이자 문서 사이트"이고, packages/shadcn은 "그 데이터를 소비해 사용자 프로젝트에 이식하는 CLI"다. 이 두 축을 분리해 뒀기 때문에, 레지스트리 콘텐츠(컴포넌트 종류·스타일)와 CLI 엔진(변형·설치 로직)이 독립적으로 버전업된다 — 실제로 CLI는 v4.16.0인 반면 레지스트리 콘텐츠는 매일같이 커밋되며 계속 늘어난다.
packages/shadcn/src/commands/add.ts는 npx shadcn add button 같은 명령이 들어왔을 때 실제로 일어나는 일을 조율하는 진입점이다. 핵심 흐름은 다음과 같다.
주목할 지점은 4단계가 단순 파일 복사가 아니라는 것이다. 컴포넌트 파일 하나를 옮기기 전에, CLI는 그 파일을 ts-morph로 AST(추상 구문 트리)까지 파싱해 15종의 변형기(transformer)를 순서대로 통과시킨다.
packages/shadcn/src/utils/transformers/ 폴더에는 transform-import.ts, transform-css-vars.ts, transform-tw-prefix.ts, transform-rsc.ts, transform-icons.ts, transform-jsx.ts 등 15개 파일이 있다. 각각이 컴포넌트 소스 코드의 특정 측면 하나씩을 프로젝트 사정에 맞춰 고쳐 쓴다.
레지스트리 소스는 항상 @/registry/... 같은 공통 경로로 쓰여 있다. updateImportAliases() 함수는 이 경로를 components.json의 aliases 설정(예: @/components/ui, ~/lib/utils)에 맞춰 정규식으로 치환한다. 예를 들어 @/registry/(.+)/ui 패턴을 만나면 config.aliases.ui 값으로, @/registry/(.+)/lib/utils는 config.aliases.utils 값으로 바꾼다. 워크스페이스 모노레포처럼 유틸 별칭이 @myorg/ui/lib/utils처럼 복잡한 경우까지 getWorkspaceAliasFromUtilsAlias()가 별도로 처리한다.
컴포넌트 기본 소스는 bg-primary처럼 CSS 변수 기반 시맨틱 클래스를 쓴다. 그런데 components.json의 tailwind.cssVariables가 false인 프로젝트(CSS 변수 대신 고정 팔레트를 쓰는 경우)라면, applyColorMapping() 함수가 splitClassName()으로 변형자(variant)·클래스명·투명도(alpha)를 분해한 뒤 bg-primary → bg-zinc-900 식으로 라이트/다크 모드 클래스 쌍을 직접 만들어 넣는다. "네 프로젝트가 CSS 변수를 안 쓴다면, 대신 구체적인 색상값을 박아주겠다"는 안전망이다.
변형기 파이프라인은 이삿짐센터의 "치수 재조립 서비스"와 같다. 원래 집(레지스트리)의 붙박이 옷장(컴포넌트)을 통째로 뽑아 새 집(내 프로젝트)에 그냥 놓으면 문틀 크기가 안 맞는다. 이삿짐센터는 옷장을 분해(AST 파싱)해서, 새 집의 실측(components.json의 별칭·설정)에 맞게 못 위치와 나사 길이(import 경로·클래스명)를 재조정한 뒤 다시 조립해 넣는다.
packages/shadcn/src/preset/preset.ts는 매우 실용적인 엔지니어링 트릭을 보여준다. 베이스(PRESET_BASES = ["radix","base","aria"]) · 스타일(PRESET_STYLES 8종: nova·vega·maia·lyra·mira·luma·sera·rhea) · 기본 색상 8종 · 테마 색상을 각각 배열의 인덱스로 표현하고, 이 인덱스들을 비트 단위로 이어붙여 JS의 안전 정수 범위(53비트) 안에서 하나의 숫자로 합친 뒤 base62 문자열로 인코딩한다. 파일 맨 위 주석에 못박힌 규칙이 인상적이다: "기존 값 배열은 절대 재정렬하지 말고 뒤에만 추가하라" — 한 번 배포된 프리셋 코드가 나중 버전에서도 똑같이 해석돼야 하기 때문에, 하위 호환을 위한 규율을 코드 주석으로 강제하고 있다.
이 덕분에 npx shadcn init --preset <62진수 코드> 한 줄로 "Base UI 베이스 + vega 스타일 + zinc 기본색 + amber 테마"라는 조합 전체를 순식간에 복원할 수 있다. parsePresetStyle()은 반대로 "radix-nova" 같은 문자열에서 베이스와 스타일 이름을 다시 분리해낸다.
apps/v4/registry/bases/radix/ui/button.tsx와 bases/base/ui/button.tsx, bases/aria/ui/button.tsx를 나란히 열어 보면, Radix 버전은 import { Slot } from "radix-ui"를, Base 버전은 Base UI의 프리미티브를, Aria 버전은 React Aria의 훅을 각각 가져다 쓴다. 겉으로 드러나는 Button의 props와 사용법은 거의 동일하게 맞춰져 있지만, 내부에서 접근성·포커스·이벤트를 처리하는 엔진 자체가 다르다. 그래서 프로젝트가 이미 Base UI 생태계를 쓰고 있다면 Radix 의존성을 하나도 늘리지 않고 shadcn 컴포넌트를 쓸 수 있다.
더 미묘하지만 중요한 변화가 하나 더 있다. 클래식 new-york-v4 스타일은 지금도 bg-primary text-primary-foreground hover:bg-primary/90처럼 Tailwind 유틸리티 클래스를 컴포넌트 파일 안에 그대로 박아 둔다("보이는 그대로가 전부"라는 원조 철학). 반면 새 프리셋 베이스(radix/base/aria)의 Button은 "cn-button cn-button-variant-default"처럼 의미만 담은 클래스 이름을 쓰고, 실제 Tailwind 유틸리티는 apps/v4/registry/styles/style-nova.css 같은 별도 스타일시트 안에 @apply로 정의돼 있다. 스타일 이름(nova·vega 등)을 갈아 끼우면 컴포넌트 코드를 한 글자도 안 건드리고 룩앤필 전체가 바뀌는 구조다.
packages/shadcn/src/mcp/index.ts(576줄)는 MCP(Model Context Protocol) 서버를 구현한다. get_project_registries(components.json에 등록된 레지스트리 목록 조회), list_items_in_registries, search_items_in_registries(퍼지 검색), get_item_examples_from_registries 같은 도구를 노출해, Claude 같은 에이전트가 "이 프로젝트에 데이터 테이블 컴포넌트가 있나?"를 코드를 뒤지지 않고 구조화된 도구 호출로 바로 물어볼 수 있게 한다. 실제 설치는 여전히 npxShadcn() 헬퍼가 내부적으로 npx shadcn add를 실행해 처리한다 — MCP는 "탐색"을, CLI는 "실행"을 맡는 분업이다.
| 영역 | 구성 |
|---|---|
| CLI 코어 | ts-morph 26(AST 변형) · commander 14(명령어 파싱) · zod 3 + zod-to-json-schema(스키마 검증·MCP 툴 스키마 변환) · execa 9(하위 프로세스) · fast-glob·fs-extra(파일 처리) · fuzzysort(검색) · diff 8(--diff 명령) · cosmiconfig(components.json 탐색) |
| MCP | @modelcontextprotocol/sdk 1.26 |
| 스타일링(컴포넌트 소스) | Tailwind CSS · class-variance-authority(cva, variant 조합 관리) · tailwind-merge 3(클래스 충돌 정리) · radix-ui(통합 패키지) · Base UI · React Aria |
| 문서·레지스트리 앱(apps/v4) | Next.js 16.2.7 · React 19.2.3 · Tailwind CSS v4.3 · TypeScript 5 |
| 빌드·릴리스 | pnpm 10.33.4 워크스페이스 · Turborepo(태스크 오케스트레이션) · Changesets(버저닝·체인지로그) · tsup(패키지 번들링) |
| 테스트 | Vitest 워크스페이스 구성 — 저장소 전체에 .test.ts 111개, .test.tsx 3개 |
버전의 진실원(source of truth)은 하나가 아니라 세 갈래라는 점이 헷갈리기 쉽다. CLI 자체(packages/shadcn/package.json)는 npm에 "shadcn"이라는 이름으로 배포되며 버전은 4.16.0이다. 헤드리스 전용 패키지 @shadcn/react는 별도로 0.2.1, AI 채팅 UI 헬퍼 @shadcn/helpers는 0.1.0이다. 반면 컴포넌트 그 자체(레지스트리 콘텐츠)는 npm 버전이 없다 — 복사된 순간의 스냅샷이 곧 버전이고, 최신화는 shadcn diff로 수동 비교하는 방식이다.
buttonVariants = cva(...) 형태로 스타일 분기를 정의한다.| 항목 | 요구사항 |
|---|---|
| Node.js | packages/shadcn/package.json의 engines 기준 ≥20.18.1 |
| 초기화 | npx shadcn@latest init (또는 pnpm dlx/bunx --bun) — components.json 생성, base color CSS 변수를 globals.css에 주입 |
| 프리셋 초기화 | npx shadcn@latest init --preset <코드> — 베이스·스타일·색상 조합을 한 번에 복원 |
| 컴포넌트 추가 | npx shadcn@latest add button dialog — 여러 개를 한 번에 지정 가능, -a/--all로 전체 설치 |
| 필수 전제 | 이미 React + Tailwind CSS가 세팅된 프로젝트(Next.js·Vite·Remix 등). CLI는 프레임워크를 스캐폴딩하는 도구가 아니라, 기존 프로젝트에 코드를 이식하는 도구다(단, 빈 폴더에서 실행하면 대화형으로 새 프로젝트 생성도 도와준다). |
| 패키지 매니저 무관 | npm·pnpm·yarn·bun 어디서든 npx/dlx/bunx로 실행 가능. CLI 자체가 특정 패키지 매니저에 종속되지 않는다. |
· 컴포넌트를 "설치"한다는 말은 npm 패키지를 추가한다는 뜻이 아니라 소스 파일을 복사한다는 뜻이다. 다만 그 컴포넌트가 필요로 하는 진짜 npm 의존성(예: @radix-ui/react-slot)은 CLI가 package.json에 실제로 추가해 준다 — 이건 여전히 npm install 대상이다.
· components.json의 aliases 설정(어디에 ui/components/hooks/lib를 둘지)을 CLI가 매번 참조해 import 경로를 재작성한다. 이 파일을 지우거나 손으로 잘못 고치면, 다음 add 실행 때 엉뚱한 경로로 파일이 떨어질 수 있다.
npx shadcn@latest init을 실행하면 대화형 프롬프트로 베이스(Radix/Base UI/React Aria)와 스타일을 고르고, components.json이 생성되며 globals.css에 색상 CSS 변수와 @import "shadcn/tailwind.css" 한 줄이 추가된다. 이 시점부터 프로젝트는 "shadcn 준비 완료" 상태가 된다.
npx shadcn add buttonCLI가 레지스트리에서 button 항목을 찾고, 의존 트리(예: alert-dialog라면 그 안에 쓰이는 button까지)를 재귀적으로 계산한 뒤, 각 파일을 ts-morph 변형 파이프라인에 통과시켜 src/components/ui/button.tsx(또는 별칭 경로) 위치에 실제로 써 넣는다. 필요한 npm 의존성은 package.json에 추가되고(사용자가 직접 pnpm install 실행), Tailwind 설정·CSS 변수·폰트도 함께 갱신된다.
설치가 끝나면 button.tsx 안의 buttonVariants(cva 정의)를 열어 variant: "brand" 같은 새 옵션을 직접 추가해도 아무 문제가 없다. 이건 "커스터마이징 API를 배우는" 게 아니라 "그냥 내 파일을 고치는" 행위다. 나중에 shadcn 쪽에서 같은 컴포넌트를 업데이트해도 자동으로 반영되지 않는다 — 필요하면 npx shadcn diff button으로 수동 비교해야 한다.
components.json이 있는 프로젝트에서 에이전트는 MCP 도구 search_items_in_registries로 "테이블에 정렬 기능이 있는 컴포넌트"를 퍼지 검색하고, get_item_examples_from_registries로 사용 예시를 확인한 뒤, 실제 설치는 npxShadcn()을 통해 평범한 npx shadcn add data-table 셸 명령으로 수행한다. 사람이 터미널에 치는 것과 최종 경로는 같다.
절반만 맞다. 컴포넌트 코드 자체는 복사된 순간의 스냅샷이라 자동 업데이트가 없는 게 맞지만, 그 컴포넌트가 쓰는 진짜 npm 패키지(@radix-ui/react-slot, class-variance-authority 등)는 package.json에 그대로 등록돼 일반적인 npm 의존성처럼 버전이 올라간다. 그리고 shadcn 쪽 원본 코드가 나중에 버그를 고쳐도 내 프로젝트엔 자동 반영되지 않으므로, shadcn diff로 주기적으로 비교하는 습관이 필요하다.
한때는 정확한 설명이었지만 지금은 절반만 맞다. apps/v4/registry/bases/에 radix·base·aria 세 베이스가 나란히 존재하고, 심지어 Radix에서 Base UI로 옮겨가도록 돕는 skills/migrate-radix-to-base 공식 마이그레이션 스킬까지 있다. "전부 Radix 위에서 돈다"는 설명은 클래식 new-york-v4 스타일에는 아직 맞지만, 신규 프리셋 시스템 전체를 대표하지 않는다.
클래식 new-york-v4 스타일은 맞는 말이다 — bg-primary hover:bg-primary/90 같은 Tailwind 클래스가 컴포넌트 파일 안에 그대로 보인다. 하지만 신규 프리셋 베이스(radix/base/aria)의 컴포넌트는 cn-button-variant-default처럼 의미만 담은 이름을 쓰고, 실제 색상·간격 값은 style-nova.css 같은 별도 스타일시트의 @apply 규칙 안에 있다. "컴포넌트 파일 하나가 스타일의 전부"라는 원조 철학이 이 신규 시스템에서는 스타일시트 파일까지 함께 봐야 하는 형태로 바뀌었다.
GitHub 조직·저장소 이름은 여전히 shadcn-ui/ui이지만, 실제로 npx로 실행하고 npm에 배포되는 CLI 패키지 이름은 "shadcn"이다(packages/shadcn/package.json의 name 필드). 예전 자료에서 npx shadcn-ui@latest 같은 명령을 본 적이 있다면 구버전 이름이며, 현재는 npx shadcn@latest가 맞는 표기다.
Radix 기반 컴포넌트는 asChild prop으로 렌더링 요소를 갈아 끼우지만, Base UI 기반 컴포넌트는 같은 목적에 render prop을 쓴다. 저장소의 스킬 문서(skills/shadcn/rules/base-vs-radix.md)가 이 차이를 별도 문서로 떼어 다룰 만큼, 어떤 베이스를 쓰는 프로젝트인지 먼저 확인하지 않으면 다른 베이스의 문법을 그대로 갖다 써서 타입 에러를 만나기 쉽다.
CI 워크플로 validate-registries.yml은 registry.json과 각 항목이 registryItemSchema(zod)를 만족하는지 매번 검증하고, browser-tests.yml은 실제 브라우저에서 컴포넌트 렌더링을 확인한다. test.yml은 레지스트리를 빌드한 뒤 v4:dev 서버를 띄우고 Vitest(.test.ts 111개)를 돌린다 — "복붙 코드니까 테스트가 허술하겠지"라는 짐작과 달리, 스키마·빌드·브라우저 3단 검증이 자동화돼 있다.
| 도구 | 배포 방식 · 커스터마이징 한계 |
|---|---|
| MUI (Material UI) | npm 패키지 설치. Material Design을 충실히 구현한 완제품 컴포넌트 + 테마 API. 깊은 시각적 변경은 sx/테마 오버라이드를 배워야 하고, 컴포넌트 내부 마크업 구조 자체는 손댈 수 없다. |
| Chakra UI | npm 패키지 설치. 접근성이 좋고 테마 토큰 시스템이 유연하지만, 여전히 "라이브러리의 컴포넌트 API"를 배우고 그 틀 안에서 커스터마이징한다. |
| Mantine | npm 패키지 설치. 컴포넌트 가짓수가 매우 많고 폼·차트 등 부가 패키지도 풍부하지만, 역시 완제품을 설치해 쓰는 전통적 라이브러리 모델이다. |
| daisyUI | Tailwind CSS 플러그인(CSS 클래스 프리셋)이지 React 컴포넌트가 아니다. btn btn-primary 같은 클래스명만 제공하고 마크업·상호작용 로직·접근성은 직접 짜야 한다. |
| shadcn/ui | CLI로 소스 코드를 프로젝트에 복사. 접근성·상호작용 로직은 Radix/Base UI/React Aria가 이미 검증해 뒀고, 마크업과 스타일은 전부 내 코드로 들어와 자유롭게 편집 가능. 대신 업데이트는 자동이 아니라 shadcn diff로 수동 추적. |
선택 기준을 한 문장으로 요약하면 이렇다. "빠르게 완제품을 쓰고 싶고 깊은 커스터마이징은 드물다"면 MUI·Chakra·Mantine 같은 전통 라이브러리가 유지보수 부담이 적다. "디자인을 세밀하게 통제하고 싶고, 컴포넌트 내부 구현까지 언제든 고칠 각오가 있다"면 shadcn/ui의 "코드를 내가 소유한다"는 모델이 압도적으로 유리하다. daisyUI는 애초에 React 컴포넌트 레이어가 없으므로 비교 대상이라기보다 "Tailwind 클래스 프리셋"이라는 다른 층위의 도구다.
shadcn/ui를 한 문장으로 다시 정리하면: "검증된 헤드리스 접근성 엔진 위에, 내가 100% 소유하고 고칠 수 있는 스타일 코드를 CLI가 대신 재단해 붙여주는 레지스트리 시스템"이다. npm 라이브러리처럼 설치해서 쓰는 게 아니라, 목공소에서 치수 맞춰 받은 가구처럼 "완성된 내 코드"로 프로젝트에 들어온다는 점이 다른 모든 특징의 출발점이다.
이 저장소에서 배울 게 많은 사람은 다음과 같다. 첫째, 사내 디자인 시스템을 처음부터 만들어야 하는 팀 — Radix/Base UI/React Aria 위에서 접근성을 걱정하지 않고 스타일링부터 시작할 수 있는 뼈대로 쓰기 좋다. 둘째, AI 코드 생성 도구를 실무에 쓰는 개발자 — v0·Claude Code 같은 도구가 사실상 표준 출력으로 삼는 형식이라, 이 저장소의 CLI 변형 로직과 MCP 서버를 이해해 두면 에이전트가 왜 이런 파일 구조로 코드를 내놓는지 납득할 수 있다. 셋째, ts-morph를 이용한 AST 코드 변형이나 CLI 설계를 공부하려는 개발자 — transform-import.ts·preset.ts는 "설정에 따라 소스 코드를 안전하게 재작성하는" 실전 패턴의 좋은 교재다.
반대로 "설치 한 번으로 끝내고 다시는 그 코드를 보고 싶지 않다"는 팀에는 맞지 않는다. 컴포넌트가 내 코드가 됐다는 건 결국 그 코드의 유지보수 책임도 내가 진다는 뜻이며, 이 트레이드오프를 받아들일 준비가 됐을 때 shadcn/ui의 진짜 가치가 드러난다.