@mui/styled-engine, CSS 유틸리티 @mui/system, 공용 헬퍼 @mui/utils 등이 뼈대를 이루고, 그 위에 @mui/material(컴포넌트 본체)이 얹힌다. 예전에 이 레포 안에 있던 Base UI·Joy UI·Pigment CSS는 최근 별도 저장소로 완전히 분리됐다 — "MUI 생태계 지도"가 계속 바뀌고 있다는 뜻이라 함정 섹션에서 자세히 다룬다.
mui/material-ui · 라이선스 MIT(핵심 패키지) · 현재 버전 @mui/material 9.2.0 · pnpm 워크스페이스 + Lerna(independent) + Nx 캐싱 · Node ≥22.23.1 · 최신 커밋 2026-07-29, 릴리스 9.2.0은 2026-07-03 · MUI X(유료/일부 상용 라이선스)는 별도 저장소)
Material UI는 "이미 완성된 UI 부품 상자"다. 버튼·체크박스·다이얼로그·앱바·테이블·폼 필드처럼 웹 앱에 필요한 화면 요소를 <Button>, <Dialog>, <TextField> 같은 React 컴포넌트로 이미 만들어 배포한다. 개발자는 이걸 가져다 조립하고, 필요하면 테마(색·글꼴·간격)만 바꿔 자기 브랜드에 맞춘다.
npm install @mui/material @emotion/react @emotion/styled 한 줄이면 시작할 수 있다. 흔히 "MUI"라고 줄여 부르며, 회사 이름도 MUI(예전 material-ui.com)다. 이 저장소 하나에 @mui/material(컴포넌트), @mui/system(스타일 유틸), @mui/icons-material(구글 아이콘 2,000개+), @mui/lab(실험적 컴포넌트) 등 10여 개 패키지가 함께 산다.
레고로 집을 지을 때, 벽돌 하나하나를 처음부터 사출 성형하는 사람은 없다. 이미 규격화된 블록을 사서 조립하고, 원하면 색만 바꿔 칠한다.
Material UI는 웹 화면의 "정식 규격 블록 세트"다. 버튼·모달·테이블을 처음부터 CSS로 깎는 대신, 이미 접근성·반응형·다국어까지 검증된 블록을 가져다 조립하고, 팔레트(테마)만 바꿔 내 서비스 색으로 물들인다.
Material UI는 2014년부터 이어져 온, React UI 컴포넌트 생태계의 사실상 표준이다. React 프로젝트에서 "UI 라이브러리 뭐 쓸까"라는 질문에 가장 먼저 나오는 이름 중 하나이고, 채용 공고·튜토리얼·부트캠프 커리큘럼에 광범위하게 등장한다. "오래돼서 유행이 지났다"는 인식과 달리, npm 주간 다운로드 기준으로도 여전히 최상위권을 지킨다.
최근 shadcn/ui처럼 "라이브러리를 설치하는 대신 소스 코드를 복사해 온다"는 접근이 뜨면서 전통적 컴포넌트 라이브러리가 낡아 보인다는 말이 나온다. 하지만 Material UI는 "완성된 디자인 시스템 + 방대한 접근성·국제화(i18n) 테스트"가 이미 끝나 있다는 점에서 여전히 다른 선택지다. 처음부터 커스텀 디자인을 원하면 shadcn류가 맞고, 빠르게 완성도 있는 화면이 목표면 MUI가 여전히 강하다.
겉보기엔 "오래된 라이브러리"지만 내부는 계속 바뀐다. CSS 변수 기반 테마(cssVariables: true)로 서버 사이드 렌더링에서도 깜빡임 없는 다크모드를 지원하게 됐고, Pigment CSS라는 빌드타임 제로런타임 스타일링 실험이 진행 중이다(현재는 별도 저장소로 독립). Base UI·Joy UI를 별도 프로젝트로 떼어내면서 이 레포 자체는 "@mui/material에 집중"하는 쪽으로 재편되기도 했다.
Ant Design·Chakra UI·Mantine 같은 완성형 라이브러리와 경쟁하는 동시에, shadcn/ui 같은 복사형(소스를 내 코드로 가져오는) 접근, Radix·Base UI 같은 헤드리스(스타일 없는 동작만 제공) 접근과도 비교된다. Material UI는 "스타일까지 다 되어 있는 완성형"의 대표 주자 자리를 지키고 있다.
겉보기엔 간단해 보이는 버튼 하나에도 실은 포커스 링·키보드 조작·스크린리더 라벨·눌림 효과(ripple)·비활성 상태·로딩 상태·반응형 크기까지 신경 쓸 게 많다. 체크박스·드롭다운·모달은 더하다. 이걸 매 프로젝트 처음부터 만들면 시간이 많이 들고, 접근성 실수가 나기 쉽다.
Material UI는 이런 "당연히 필요하지만 반복적이고 까다로운" UI 로직을 미리 검증된 형태로 제공한다. 회사가 새 디자인 시스템 없이도 일관된 톤·간격·색상 체계로 화면을 빠르게 완성할 수 있고, 테마 하나로 전체 앱의 색·타이포그래피를 한 번에 바꿀 수 있다. 즉 "디자인 시스템 구축 비용"과 "접근성 준수 비용"을 동시에 줄여준다.
실제로 클론해 packages/를 열어 보면, "Material UI"라는 하나의 패키지가 아니라 여러 패키지가 계층을 이루는 모노레포임을 바로 확인할 수 있다. 핵심만 추리면 다음과 같다.
예전 자료를 보면 @mui/base, @mui/joy가 이 모노레포 안에 있었다고 나온다. 실제로 클론해 확인하면 지금은 둘 다 존재하지 않는다. Base UI는 완전히 새로 설계돼 mui/base-ui 저장소(패키지명도 @base-ui/react·@base-ui-components/react)로 독립했고, Joy UI 관련 코드도 최근 커밋에서 "leftover Joy UI references"를 지우며 정리됐다. packages/pigment-css-react·packages/pigment-react 폴더를 열어보면 실제 코드 대신 "The package has moved to mui/pigment-css"라는 안내 README만 남아 있다 — 이는 문서와 실제 코드가 어긋나는 대표적 함정이라 9장에서 다시 다룬다.
| 패키지 | 역할 |
|---|---|
| @mui/material | Material Design 컴포넌트 본체. 이 저장소의 핵심 산출물 |
| @mui/system | styled(), sx prop, createTheme, 반응형 브레이크포인트 등 CSS 유틸 — Joy UI 같은 다른 컴포넌트 라이브러리도 이 위에 지어졌었음 |
| @mui/styled-engine(-sc) | Emotion↔styled-components를 교체 가능하게 감싸는 얇은 어댑터 계층 |
| @mui/utils | useSlotProps·useForkRef 등 컴포넌트 제작에 쓰이는 공용 훅/헬퍼 |
| @mui/icons-material | 구글 Material 아이콘을 컴포넌트화한 별도 패키지(용량 최대) |
| Base UI (외부 저장소) | 스타일 없는 "헤드리스" 컴포넌트 — mui/base-ui로 독립, 이 레포엔 없음 |
| Joy UI (레포에서 제거) | Material Design과 다른 자체 디자인의 대안 라이브러리였으나 이 레포에서 정리됨 |
| Pigment CSS (외부 저장소) | 빌드타임 제로런타임 CSS-in-JS — mui/pigment-css로 독립, @mui/material-pigment-css가 어댑터로만 남음 |
styled()·sx prop — Emotion을 "갈아 끼울 수 있게" 감춘다
Material UI의 모든 컴포넌트는 CSS-in-JS 라이브러리 Emotion 위에서 스타일을 만든다. 그런데 실제 소스(packages/mui-styled-engine/src/index.ts)를 보면 컴포넌트들이 Emotion을 직접 부르지 않는다. 대신 @mui/styled-engine이라는 얇은 재수출(re-export) 계층을 거친다.
// packages/mui-styled-engine/src/index.ts (발췌, 실제 소스)
import emStyled from '@emotion/styled';
function styled(tag, options) {
const stylesFactory = emStyled(tag, options);
// 개발 모드에서 styled(Comp)() 처럼 인자 없이 호출하면 경고를 띄운다
return stylesFactory;
}
export default styled;
export { ThemeContext, keyframes, css } from '@emotion/react';
@mui/styled-engine-sc는 같은 인터페이스로 Emotion 대신 styled-components를 감싼다. 그래서 프로젝트 번들러 설정에서 @mui/styled-engine을 @mui/styled-engine-sc로 별칭(alias) 처리하면, MUI 컴포넌트 코드는 한 줄도 안 건드리고 내부 CSS-in-JS 엔진을 바꿀 수 있다. 이게 "styled-engine 추상화"의 정체다.
그 위에 @mui/system의 createStyled(packages/mui-system/src/createStyled/createStyled.js)가 한 겹 더 쌓인다. 여기서 모든 스타일드 컴포넌트에 sx prop을 자동으로 붙여준다 — attachTheme으로 테마를 주입하고, styleFunctionSx로 sx={{ p: 2, color: 'primary.main' }} 같은 객체를 실제 CSS로 변환한 뒤 기존 스타일 뒤에 이어붙인다. 즉 "Emotion 스타일 + sx 인라인 스타일"을 한 컴포넌트가 동시에 받는 구조다.
전기 콘센트(styled-engine)와 가전제품(MUI 컴포넌트)의 관계다. 컴포넌트는 "표준 규격 콘센트"에만 꽂혀 있을 뿐, 그 뒤에 어떤 발전소(Emotion 또는 styled-components)가 있는지 신경 쓰지 않는다. 발전소를 바꿔도 콘센트 모양(인터페이스)만 같으면 가전제품은 그대로 쓸 수 있다.
sx={{ mt: 2, display:'flex' }}처럼 테마 값(간격 단위·팔레트 색 이름)을 그대로 CSS로 바꿔준다. 매번 styled()로 새 컴포넌트를 안 만들어도 되는 "빠른 오버라이드" 용도다.createTheme·ThemeProvider — 테마 스케일과 팔레트 생성
@mui/system의 기본 createTheme(packages/mui-system/src/createTheme/createTheme.js)는 breakpoints·spacing·shape만 만드는 저수준 버전이다. 실제로 앱에서 쓰는 건 @mui/material/styles의 createTheme(packages/mui-material/src/styles/createTheme.ts)인데, 이게 팔레트 생성(createPalette)·타이포그래피 스케일·트랜지션·컴포넌트별 기본값까지 얹은 "완전판"이다.
import { createTheme, ThemeProvider } from '@mui/material/styles';
const theme = createTheme({
palette: { primary: { main: '#1565c0' } },
});
function App() {
return (
<ThemeProvider theme={theme}>
<Button>눌러보세요</Button>
</ThemeProvider>
);
}
primary.main 색 하나만 주면, 내부적으로 createPalette가 light/dark 보조색을 자동 생성하고, 컴포넌트들은 ThemeProvider의 React Context를 통해 이 값을 읽어 자기 스타일을 결정한다. "버튼 색을 하나하나 바꾸는 대신 테마 하나로 앱 전체 톤을 바꾼다"는 MUI의 핵심 사용 방식이 여기서 나온다.
cssVariables)와 다크모드 — 실제 분기 코드
v5까지는 테마 값이 JS 객체로만 존재해, 다크모드 전환 시 리액트가 전체 트리를 다시 렌더링해야 했다. 최근 버전은 cssVariables: true 옵션으로 테마 값을 실제 CSS 커스텀 프로퍼티(--mui-palette-primary-main 등)로 뽑아내는 방식을 지원한다. createTheme.ts의 실제 분기 로직을 그대로 옮기면:
// packages/mui-material/src/styles/createTheme.ts (발췌)
if (cssVariables === false) {
// v5와 완전히 동일하게 동작 — JS 객체 테마
return createThemeNoVars(options, ...args);
}
// cssVariables: true 인 경우 — CSS 변수 + colorSchemes 기반 테마
return createThemeWithVars({
...other,
colorSchemes: colorSchemesInput,
defaultColorScheme: defaultColorSchemeInput,
}, ...args);
cssVariables: true로 만들면 colorSchemes: { light: {...}, dark: {...} }를 함께 정의할 수 있고, 실제 화면 전환은 CSS의 data-mui-color-scheme 어트리뷰트만 바꿔치기하면 된다 — React 리렌더링 없이 브라우저가 CSS 변수 값을 즉시 다시 계산해준다. useColorScheme() 훅으로 모드를 읽고 바꾸며, 서버 사이드 렌더링에서 "처음 깜빡이는(FOUC)" 문제도 InitColorSchemeScript가 막아준다.
신규 프로젝트는 cssVariables: true를 켜는 것이 이제 사실상 권장 경로다. 다크모드가 리렌더링 없이 즉시 전환되고, SSR 깜빡임도 없다. 다만 커스텀 CSS에서 테마 값을 theme.palette.primary.main처럼 JS로 직접 읽던 코드가 있다면, CSS 변수 문자열(var(--mui-palette-primary-main))로 바뀌는 부분을 마이그레이션 가이드로 확인해야 한다.
slots·slotProps와 useSlotProps — 내부 구조를 열어주는 커스터마이징 API
Autocomplete·DataGrid류의 복합 컴포넌트는 내부에 여러 하위 요소(팝업, 리스트박스, 페이퍼 등)를 갖는다. 이를 개별적으로 바꿔치기·prop 추가할 수 있게 하는 것이 slots(컴포넌트 교체)와 slotProps(그 컴포넌트에 넘길 prop)다. 실제 구현은 @mui/utils의 useSlotProps(packages/mui-utils/src/useSlotProps/useSlotProps.ts)가 담당한다.
// 사용 예 — Select의 팝업 부분(paper)만 커스텀 스타일 주기
<Select
slotProps={{
paper: { sx: { borderRadius: 2 } },
}}
>
...
</Select>
useSlotProps 내부는 외부에서 온 slotProps(함수 또는 객체 모두 허용)를 컴포넌트 내부 기본 props와 병합(mergeSlotProps)하고, ownerState(현재 상태값)를 필요하면 함께 넘겨준다. "라이브러리가 미리 정한 마크업 안에서, 내가 원하는 하위 요소만 정밀 타격으로 바꾼다"는 게 이 API의 핵심 아이디어이며, 최근 data-* 어트리뷰트도 slotProps에 직접 얹을 수 있도록 확장됐다(9.2.0 변경 로그).
MUI는 런타임에 스타일을 계산하는 Emotion 방식의 성능 한계(초기 렌더링·SSR 비용)를 넘기 위해 빌드타임에 CSS를 미리 뽑아내는 Pigment CSS를 실험해 왔다. 하지만 이 저장소 안의 packages/pigment-css-react·packages/pigment-react 폴더는 현재 빈 껍데기고, 실제 구현은 mui/pigment-css라는 별도 저장소로 옮겨졌다. 이 레포에 남아 있는 @mui/material-pigment-css는 "Pigment CSS 위에 Material UI와 같은 styled/테마 API를 얹어주는 얇은 어댑터"일 뿐이다.
README에 명시된 대로, MUI X(mui/mui-x 별도 저장소)가 DataGrid·Date/Time Pickers·Charts·Tree View 같은 복합 컴포넌트를 담당한다. 이 중 상당수는 Community(MIT) 버전과 Pro/Premium(상용 라이선스) 버전이 나뉘어 있다. material-ui 레포 자체는 순수 MIT지만, "MUI 생태계 전체가 MIT다"라고 오해하면 안 된다.
| 영역 | 내용 |
|---|---|
| 모노레포 관리 | pnpm 워크스페이스(pnpm-workspace.yaml) + Lerna(version: independent — 패키지별 독립 버전) + Nx(빌드 캐시·의존 그래프) |
| CSS-in-JS | 기본 Emotion(@emotion/react, @emotion/styled) — @mui/styled-engine-sc로 styled-components로 교체 가능 |
| 언어 | TypeScript 전면 전환이 진행형(레거시 .js + .d.ts 쌍을 .ts로 옮기는 작업이 최근 커밋 로그에도 계속 등장) |
| 테스트 | Vitest(단위 테스트, cross-env TZ=UTC vitest) + Playwright(e2e·시각 회귀). 과거 Karma+Mocha 체계에서 이관 완료 |
| 문서 사이트 | docs/ 하위의 Next.js 기반 mui.com 문서 — 데모 코드가 곧 통합 테스트 역할도 겸함 |
| CI | GitHub Actions 워크플로 17개(ci.yml, CodeQL 보안 스캔, OpenSSF Scorecards, 릴리스 자동화 등) |
| 코드 변환 | @mui/codemod — 메이저 버전 업그레이드 시 import 경로·prop 이름을 자동으로 고쳐주는 CLI |
버전의 진실원(source of truth)은 각 패키지 폴더의 package.json이다. 저장소 루트 package.json의 version(예: 9.2.0)은 모노레포 관리용 사설(private) 버전이고, 실제로 npm에 배포되는 @mui/material·@mui/system·@mui/utils 등은 Lerna의 independent 모드 덕에 패키지마다 다른 속도로 버전이 오를 수 있다(실제로 @mui/styled-engine은 9.1.1인데 @mui/material은 9.2.0인 시점이 있다). "루트 버전 = 전체 버전"이라고 단정하면 안 된다.
workspace:^)로 연결해 로컬 개발 중에도 항상 최신 코드끼리 맞물리게 한다.일반 사용자(라이브러리로 쓰는 경우)는 매우 가볍다.
npm install @mui/material @emotion/react @emotion/styled
# 또는 styled-components 를 쓰고 싶다면:
npm install @mui/material @mui/styled-engine-sc styled-components
Roboto 폰트와 아이콘까지 쓰려면 @fontsource/roboto, @mui/icons-material을 추가로 설치한다. Next.js App Router라면 @mui/material-nextjs로 서버 컴포넌트 캐시 처리를 얹는 게 공식 권장이다.
| 대상 | 요구사항 |
|---|---|
| 라이브러리 사용자 | React 18+ (React 19까지 지원), Node는 프레임워크(Vite·Next.js 등) 요구사항을 따름 |
| 레포 기여자(빌드) | Node ≥22.23.1, pnpm 11.17.0(engineStrict로 강제). pnpm install → pnpm build |
| 동시 지원 스타일 엔진 | Emotion(기본) 또는 styled-components — 필수 peerDependency는 둘 중 하나 |
| 제로런타임 실험 | Pigment CSS를 쓰려면 별도 저장소(mui/pigment-css)의 번들러 플러그인을 추가로 설정해야 함 |
이 저장소 자체를 빌드/기여하려면 pnpm install 시점부터 전체 워크스페이스(패키지 16개 + 문서 사이트)를 함께 설치하게 되어 상당히 무겁다. "라이브러리로 쓰기"와 "레포를 클론해 내부 코드를 고치기"는 완전히 다른 난이도의 작업임을 구분해야 한다.
실제 화면 하나를 만드는 전형적인 흐름을 코드로 옮기면 다음과 같다.
import { createTheme, ThemeProvider, CssBaseline } from '@mui/material';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
// 1) 앱 전역 테마 정의 — 색·다크모드 옵션까지 한 번에
const theme = createTheme({
cssVariables: true,
colorSchemes: { light: true, dark: true },
palette: { primary: { main: '#1565c0' } },
});
function App() {
return (
<ThemeProvider theme={theme}>
{/* 브라우저 기본 스타일 초기화 */}
<CssBaseline />
{/* 2) 컴포넌트 조립 — 이미 접근성·상태 처리 완료 */}
<TextField label="이메일" fullWidth sx={{ mb: 2 }} />
<Button variant="contained" sx={{ mt: 1 }}>
가입하기
</Button>
</ThemeProvider>
);
}
더 깊이 커스텀하고 싶으면 slots/slotProps로 내부 하위 요소를 겨냥하고, 아예 새 스타일드 컴포넌트가 필요하면 styled(Button)(...)로 확장한다. 다크모드 토글은 useColorScheme() 훅 하나로 끝난다 — 이 모든 게 위 5장에서 본 내부 설계(styled-engine 추상화·CSS 변수 테마·슬롯 API)가 실제 사용자에게 노출되는 표면이다.
블로그·튜토리얼에서 "MUI의 헤드리스 버전 Base UI"를 이 저장소 안에서 찾으려 하면 실패한다. Base UI는 완전히 새로 설계돼 mui/base-ui라는 별도 저장소로 독립했고, 패키지명도 @base-ui/react 계열로 바뀌었다. 이 레포의 docs/package.json에는 데모용으로만 @base-ui/react: "catalog:docs"가 들어 있을 뿐, 소스 자체는 없다.
Joy UI(Material Design과 다른 자체 톤의 대안 라이브러리)는 이 저장소에서 관련 코드가 제거됐고(최신 변경 로그에 "Remove leftover Joy UI references" 항목이 실제로 있다), Pigment CSS(제로런타임 CSS-in-JS)도 mui/pigment-css로 독립했다. packages/pigment-css-react 폴더를 열어보면 실제 코드가 아니라 "이동했다"는 안내 README만 있다. "material-ui 레포 = MUI 생태계 전부"라는 가정은 더 이상 맞지 않는다.
이 레포(@mui/material 등)는 확실히 MIT다. 하지만 DataGrid의 고급 기능, DatePicker의 일부, Charts, Tree View 같은 복합 컴포넌트를 담당하는 MUI X는 별도 저장소이며 Community(무료)와 Pro/Premium(유료 상용 라이선스)이 나뉜다. "MUI라서 전부 무료겠지"라고 넘겨짚고 프로덕션에 Pro 전용 기능을 쓰면 라이선스 위반이 될 수 있다.
@mui/icons-material를 통째로 import하면 번들이 폭발한다이 패키지 하나만 로컬 디스크에서 130MB가 넘는다(아이콘 2,000개+ 각각을 개별 컴포넌트 파일로 내보내기 때문). import { Delete } from '@mui/icons-material'처럼 배럴(barrel) 임포트를 하면 번들러 설정에 따라 전체가 딸려 들어올 수 있으니, import DeleteIcon from '@mui/icons-material/Delete'처럼 개별 경로 임포트가 안전하다.
@mui/styled-engine-sc를 깔았다고 자동으로 엔진이 바뀌지 않는다. 번들러(Webpack·Vite 등) 설정에서 @mui/styled-engine 임포트를 @mui/styled-engine-sc로 별칭(alias) 처리해야 실제로 적용된다. 이 설정을 빼먹으면 두 엔진이 동시에 번들에 들어가 크기만 커진다.
저장소 루트 package.json의 버전(예: 9.2.0)은 모노레포 관리용 사설 버전이다. @mui/material·@mui/system·@mui/styled-engine 등은 Lerna의 independent 모드로 패키지마다 버전이 따로 오른다. 실제로 쓰는 패키지의 package.json을 직접 확인하는 습관이 안전하다.
| 라이브러리 | 포지션 · 차이점 |
|---|---|
| Material UI | 구글 Material Design 기반 "완성형" 컴포넌트. 방대한 컴포넌트 수·성숙한 접근성·긴 실전 검증 기간이 강점. 디자인이 "구글스럽다"는 인상을 벗기려면 테마 커스터마이징 공수가 든다 |
| Ant Design | 중국 기업(엔터프라이즈) 화면에 강한 "완성형" 라이브러리. 테이블·폼 등 데이터 중심 대시보드 컴포넌트가 촘촘함. 번들 크기와 커스터마이징 자유도에서 MUI와 종종 비교됨 |
| Chakra UI | 유틸리티 스타일 prop과 접근성을 앞세운 "완성형". Material Design 같은 고정 스타일 언어가 없어 처음부터 자유로운 톤을 원할 때 선호됨. 최근 버전은 스타일 엔진을 자체 시스템(Panda CSS 계열)으로 전환하는 중 |
| Mantine | 비교적 최근에 뜬 "완성형" 라이브러리. 훅(hooks) 모음이 강력하고 다크모드·폼 처리가 기본기가 좋다는 평. 커뮤니티·생태계 크기는 MUI보다 아직 작음 |
| shadcn/ui | "복사형" — 라이브러리를 설치하는 대신 Radix/Base UI 기반 소스 코드를 프로젝트에 직접 복사해 넣는다. 처음부터 완전히 다른 커스텀 디자인을 원하면 유리하지만, MUI처럼 "설치 즉시 완성된 스타일"은 제공하지 않는다 |
Material UI는 "React 위에서 완성된 UI 부품 세트를 원하는 사람"에게 여전히 강력한 선택이다. 스타트업 MVP, 사내 어드민, 접근성이 중요한 공공·금융 서비스, "디자이너 없이도 봐줄 만한 화면"이 필요한 개인 프로젝트까지 폭넓게 맞는다. 반대로 처음부터 완전히 고유한 브랜드 룩을 원한다면 shadcn/ui 같은 복사형이나 Chakra·Mantine처럼 스타일 언어가 덜 고정된 쪽이 나을 수 있다.
레포 자체를 공부 대상으로 본다면, ① styled-engine 같은 "구현체를 갈아 끼울 수 있게 감추는" 어댑터 패턴, ② createTheme의 팔레트/스케일 자동 생성, ③ CSS 변수로 리렌더링 없이 다크모드를 전환하는 기법, ④ slots/slotProps로 "정해진 마크업 안에서 정밀하게 커스터마이징을 열어주는" API 설계, ⑤ 모노레포 안에서 패키지 경계·라이선스 경계(MUI X)를 어떻게 긋는지가 좋은 학습 포인트다.
버튼 하나까지 픽셀 단위로 새로 디자인하고 싶은 프로젝트보다는, "이미 검증된 UI 부품으로 빠르게 완성하고 테마로 톤만 맞추면 되는" 프로젝트에 잘 맞는다. 동시에 대규모 오픈소스 모노레포가 스타일 엔진 추상화·테마 시스템·커스터마이징 API를 어떻게 설계하는지 보고 싶은 사람에게도 좋은 교재다.