REPO DEEP DIVE · 2026-07-29 · MUI/MATERIAL-UI · React 컴포넌트 라이브러리의 대명사

mui/material-ui 딥다이브
"버튼 하나 안 만들고" 앱을 완성하게 해주는 React UI 표준

Material UI(MUI)는 구글의 Material Design을 React로 독자 구현한 컴포넌트 라이브러리다. 버튼·다이얼로그·테이블·폼 같은 UI 부품을 미리 만들어 제공해, 개발자가 "디자인 시스템"부터 새로 짜지 않고 바로 제품을 조립할 수 있게 한다.

이 저장소는 실제로는 "Material UI 하나"가 아니라 여러 패키지의 모노레포다. 스타일 엔진을 감추는 @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(유료/일부 상용 라이선스)는 별도 저장소)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 지금 주목받는가
  3. 무엇을 해결하나
  4. 전체 구조 — 패키지 지도
  5. 핵심 설계·동작 원리
  6. 기술 스택 · 의존성
  7. 설치 · 요구사항
  8. 실제 사용 흐름
  9. 함정 · 오해 방지
  10. 대안 비교
  11. 정리 · 누가 쓰면 좋은가

1프로젝트 한 줄 요약

한 문장으로: "구글 Material Design을 React 컴포넌트로 미리 만들어 둔, 세계에서 가장 많이 쓰이는 UI 라이브러리"

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로 깎는 대신, 이미 접근성·반응형·다국어까지 검증된 블록을 가져다 조립하고, 팔레트(테마)만 바꿔 내 서비스 색으로 물들인다.

2왜 지금 주목받는가

10년 넘게 표준 자리 유지 · shadcn/ui식 "복사형" 대안의 부상 속 여전한 기본값 · CSS 변수·다크모드·제로런타임으로 계속 진화

① "React UI 라이브러리"라는 카테고리 자체를 만든 원조

Material UI는 2014년부터 이어져 온, React UI 컴포넌트 생태계의 사실상 표준이다. React 프로젝트에서 "UI 라이브러리 뭐 쓸까"라는 질문에 가장 먼저 나오는 이름 중 하나이고, 채용 공고·튜토리얼·부트캠프 커리큘럼에 광범위하게 등장한다. "오래돼서 유행이 지났다"는 인식과 달리, npm 주간 다운로드 기준으로도 여전히 최상위권을 지킨다.

② shadcn/ui식 "코드 복사" 흐름 속에서도 흔들리지 않는 이유

최근 shadcn/ui처럼 "라이브러리를 설치하는 대신 소스 코드를 복사해 온다"는 접근이 뜨면서 전통적 컴포넌트 라이브러리가 낡아 보인다는 말이 나온다. 하지만 Material UI는 "완성된 디자인 시스템 + 방대한 접근성·국제화(i18n) 테스트"가 이미 끝나 있다는 점에서 여전히 다른 선택지다. 처음부터 커스텀 디자인을 원하면 shadcn류가 맞고, 빠르게 완성도 있는 화면이 목표면 MUI가 여전히 강하다.

③ 계속되는 내부 진화 — CSS 변수 테마, 제로런타임 실험

겉보기엔 "오래된 라이브러리"지만 내부는 계속 바뀐다. CSS 변수 기반 테마(cssVariables: true)로 서버 사이드 렌더링에서도 깜빡임 없는 다크모드를 지원하게 됐고, Pigment CSS라는 빌드타임 제로런타임 스타일링 실험이 진행 중이다(현재는 별도 저장소로 독립). Base UI·Joy UI를 별도 프로젝트로 떼어내면서 이 레포 자체는 "@mui/material에 집중"하는 쪽으로 재편되기도 했다.

경쟁 지형
"완성형" vs "복사형" vs "헤드리스형"

Ant Design·Chakra UI·Mantine 같은 완성형 라이브러리와 경쟁하는 동시에, shadcn/ui 같은 복사형(소스를 내 코드로 가져오는) 접근, Radix·Base UI 같은 헤드리스(스타일 없는 동작만 제공) 접근과도 비교된다. Material UI는 "스타일까지 다 되어 있는 완성형"의 대표 주자 자리를 지키고 있다.

3무엇을 해결하나

"버튼 하나도 접근성·상태·반응형까지 신경 써야 하는" 문제를 없앤다

겉보기엔 간단해 보이는 버튼 하나에도 실은 포커스 링·키보드 조작·스크린리더 라벨·눌림 효과(ripple)·비활성 상태·로딩 상태·반응형 크기까지 신경 쓸 게 많다. 체크박스·드롭다운·모달은 더하다. 이걸 매 프로젝트 처음부터 만들면 시간이 많이 들고, 접근성 실수가 나기 쉽다.

Material UI는 이런 "당연히 필요하지만 반복적이고 까다로운" UI 로직을 미리 검증된 형태로 제공한다. 회사가 새 디자인 시스템 없이도 일관된 톤·간격·색상 체계로 화면을 빠르게 완성할 수 있고, 테마 하나로 전체 앱의 색·타이포그래피를 한 번에 바꿀 수 있다. 즉 "디자인 시스템 구축 비용"과 "접근성 준수 비용"을 동시에 줄여준다.

용어
디자인 시스템 (Design System)
색상·타이포그래피·간격·컴포넌트 규칙을 정리해 여러 화면에서 일관되게 쓰도록 만든 규약 모음. Material UI는 구글의 Material Design을 규약으로 삼아 이를 컴포넌트로 미리 구현해 둔 것이다.
용어
접근성 (a11y)
시각·운동 장애가 있는 사용자도 키보드·스크린리더로 이용할 수 있게 만드는 것. 버튼의 포커스 표시, 다이얼로그의 ARIA 속성 같은 것들이 예다. MUI 컴포넌트는 이런 처리가 기본 내장돼 있다.

4전체 구조 — 패키지 지도

pnpm 워크스페이스 안 16개 공개/내부 패키지 — "스타일 엔진 감추기 → CSS 유틸 → 컴포넌트" 3층

실제로 클론해 packages/를 열어 보면, "Material UI"라는 하나의 패키지가 아니라 여러 패키지가 계층을 이루는 모노레포임을 바로 확인할 수 있다. 핵심만 추리면 다음과 같다.

┌───────────────────────────────────────────────────────────────┐ │ MUI/MATERIAL-UI 패키지 계층 │ └───────────────────────────────────────────────────────────────┘ [3] @mui/material # 컴포넌트 본체 (Button, Dialog, TextField…) └ @mui/material-nextjs # Next.js App Router용 캐시 프로바이더 └ @mui/material-pigment-css # 제로런타임(Pigment CSS) 어댑터 │ depends on ▼ [2] @mui/system # styled(), sx prop, createTheme, 반응형 유틸 └ @mui/private-theming # 테마 컨텍스트 공유(레거시 @mui/styles 호환용) │ depends on ▼ [1] @mui/styled-engine # Emotion 래퍼 (기본) @mui/styled-engine-sc # styled-components 래퍼 (교체용, 동일 인터페이스) │ [0] @mui/utils · @mui/types # 공용 훅·헬퍼(useSlotProps 등)·공용 타입 곁가지: @mui/icons-material # 구글 Material 아이콘 2,000개+ (용량이 가장 큰 패키지, 130MB+) @mui/lab # 실험적/불안정 컴포넌트(정식 승격 전 대기실) @mui/codemod # 메이저 업그레이드 시 코드 자동 변환 CLI @mui/envinfo # 이슈 리포트용 환경 정보 수집 @mui/stylis-plugin-rtl # 오른쪽에서 왼쪽 언어(RTL) 지원 플러그인

이 레포에서 "빠진" 것들 — Base UI · Joy UI · Pigment CSS

예전 자료를 보면 @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/materialMaterial Design 컴포넌트 본체. 이 저장소의 핵심 산출물
@mui/systemstyled(), sx prop, createTheme, 반응형 브레이크포인트 등 CSS 유틸 — Joy UI 같은 다른 컴포넌트 라이브러리도 이 위에 지어졌었음
@mui/styled-engine(-sc)Emotion↔styled-components를 교체 가능하게 감싸는 얇은 어댑터 계층
@mui/utilsuseSlotProps·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가 어댑터로만 남음

5핵심 설계·동작 원리

styled-engine 추상화 · createTheme/ThemeProvider · CSS 변수 다크모드 · slots/slotProps · Pigment CSS · MUI X 경계

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/systemcreateStyled(packages/mui-system/src/createStyled/createStyled.js)가 한 겹 더 쌓인다. 여기서 모든 스타일드 컴포넌트에 sx prop을 자동으로 붙여준다 — attachTheme으로 테마를 주입하고, styleFunctionSxsx={{ p: 2, color: 'primary.main' }} 같은 객체를 실제 CSS로 변환한 뒤 기존 스타일 뒤에 이어붙인다. 즉 "Emotion 스타일 + sx 인라인 스타일"을 한 컴포넌트가 동시에 받는 구조다.

비유

전기 콘센트(styled-engine)와 가전제품(MUI 컴포넌트)의 관계다. 컴포넌트는 "표준 규격 콘센트"에만 꽂혀 있을 뿐, 그 뒤에 어떤 발전소(Emotion 또는 styled-components)가 있는지 신경 쓰지 않는다. 발전소를 바꿔도 콘센트 모양(인터페이스)만 같으면 가전제품은 그대로 쓸 수 있다.

용어
sx prop
MUI 컴포넌트 어디에나 붙일 수 있는 스타일 지름길. sx={{ mt: 2, display:'flex' }}처럼 테마 값(간격 단위·팔레트 색 이름)을 그대로 CSS로 바꿔준다. 매번 styled()로 새 컴포넌트를 안 만들어도 되는 "빠른 오버라이드" 용도다.

createTheme·ThemeProvider — 테마 스케일과 팔레트 생성

@mui/system의 기본 createTheme(packages/mui-system/src/createTheme/createTheme.js)는 breakpoints·spacing·shape만 만드는 저수준 버전이다. 실제로 앱에서 쓰는 건 @mui/material/stylescreateTheme(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 색 하나만 주면, 내부적으로 createPalettelight/dark 보조색을 자동 생성하고, 컴포넌트들은 ThemeProvider의 React Context를 통해 이 값을 읽어 자기 스타일을 결정한다. "버튼 색을 하나하나 바꾸는 대신 테마 하나로 앱 전체 톤을 바꾼다"는 MUI의 핵심 사용 방식이 여기서 나온다.

③ CSS 변수 기반 테마(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·slotPropsuseSlotProps — 내부 구조를 열어주는 커스터마이징 API

Autocomplete·DataGrid류의 복합 컴포넌트는 내부에 여러 하위 요소(팝업, 리스트박스, 페이퍼 등)를 갖는다. 이를 개별적으로 바꿔치기·prop 추가할 수 있게 하는 것이 slots(컴포넌트 교체)와 slotProps(그 컴포넌트에 넘길 prop)다. 실제 구현은 @mui/utilsuseSlotProps(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 변경 로그).

⑤ Pigment CSS — 제로런타임 시도의 현황

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를 얹어주는 얇은 어댑터"일 뿐이다.

⑥ MUI X와의 경계 — "복잡한 유료 컴포넌트는 여기 없다"

중요한 경계
DataGrid·DatePicker의 고급 기능은 이 레포에 없다

README에 명시된 대로, MUI X(mui/mui-x 별도 저장소)가 DataGrid·Date/Time Pickers·Charts·Tree View 같은 복합 컴포넌트를 담당한다. 이 중 상당수는 Community(MIT) 버전과 Pro/Premium(상용 라이선스) 버전이 나뉘어 있다. material-ui 레포 자체는 순수 MIT지만, "MUI 생태계 전체가 MIT다"라고 오해하면 안 된다.

6기술 스택 · 의존성

pnpm 워크스페이스 + Lerna(independent) + Nx 캐싱 · TypeScript 전면 전환 진행 중 · 버전 진실원은 각 package.json
영역내용
모노레포 관리pnpm 워크스페이스(pnpm-workspace.yaml) + Lerna(version: independent — 패키지별 독립 버전) + Nx(빌드 캐시·의존 그래프)
CSS-in-JS기본 Emotion(@emotion/react, @emotion/styled) — @mui/styled-engine-scstyled-components로 교체 가능
언어TypeScript 전면 전환이 진행형(레거시 .js + .d.ts 쌍을 .ts로 옮기는 작업이 최근 커밋 로그에도 계속 등장)
테스트Vitest(단위 테스트, cross-env TZ=UTC vitest) + Playwright(e2e·시각 회귀). 과거 Karma+Mocha 체계에서 이관 완료
문서 사이트docs/ 하위의 Next.js 기반 mui.com 문서 — 데모 코드가 곧 통합 테스트 역할도 겸함
CIGitHub Actions 워크플로 17개(ci.yml, CodeQL 보안 스캔, OpenSSF Scorecards, 릴리스 자동화 등)
코드 변환@mui/codemod — 메이저 버전 업그레이드 시 import 경로·prop 이름을 자동으로 고쳐주는 CLI

버전의 진실원(source of truth)은 각 패키지 폴더의 package.json이다. 저장소 루트 package.jsonversion(예: 9.2.0)은 모노레포 관리용 사설(private) 버전이고, 실제로 npm에 배포되는 @mui/material·@mui/system·@mui/utils 등은 Lerna의 independent 모드 덕에 패키지마다 다른 속도로 버전이 오를 수 있다(실제로 @mui/styled-engine은 9.1.1인데 @mui/material은 9.2.0인 시점이 있다). "루트 버전 = 전체 버전"이라고 단정하면 안 된다.

용어
모노레포 (monorepo)
서로 관련된 여러 패키지를 하나의 저장소에 두고 함께 빌드·테스트·버전 관리하는 방식. 패키지 간 의존을 workspace 프로토콜(workspace:^)로 연결해 로컬 개발 중에도 항상 최신 코드끼리 맞물리게 한다.
용어
Lerna independent 버전
모노레포 안 모든 패키지가 같은 버전 번호를 쓰는 "fixed" 모드와 달리, 각 패키지가 자기 변경 이력에 따라 서로 다른 버전 번호를 갖게 하는 모드. MUI는 이 방식이라 @mui/material과 @mui/utils의 버전이 항상 같지 않다.

7설치 · 요구사항

사용자 입장: npm install 한 줄 · 기여자 입장: pnpm + Node 22+ 필요

일반 사용자(라이브러리로 쓰는 경우)는 매우 가볍다.

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 installpnpm build
동시 지원 스타일 엔진Emotion(기본) 또는 styled-components — 필수 peerDependency는 둘 중 하나
제로런타임 실험Pigment CSS를 쓰려면 별도 저장소(mui/pigment-css)의 번들러 플러그인을 추가로 설정해야 함
주의
레포 클론해서 빌드하는 건 별개 난이도

이 저장소 자체를 빌드/기여하려면 pnpm install 시점부터 전체 워크스페이스(패키지 16개 + 문서 사이트)를 함께 설치하게 되어 상당히 무겁다. "라이브러리로 쓰기""레포를 클론해 내부 코드를 고치기"는 완전히 다른 난이도의 작업임을 구분해야 한다.

8실제 사용 흐름

설치 → ThemeProvider로 감싸기 → 컴포넌트 조립 → sx/슬롯으로 미세 조정 → 다크모드

실제 화면 하나를 만드는 전형적인 흐름을 코드로 옮기면 다음과 같다.

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)가 실제 사용자에게 노출되는 표면이다.

9함정 · 오해 방지

Base UI·Joy UI는 이 레포에 없다 · MUI X는 부분 유료 · 아이콘 패키지 통짜 임포트 금지 · 루트 버전 ≠ 전체 버전
함정 ①
"Base UI로 커스텀 디자인 만들어보자" → 이 레포에서 코드가 안 보인다

블로그·튜토리얼에서 "MUI의 헤드리스 버전 Base UI"를 이 저장소 안에서 찾으려 하면 실패한다. Base UI는 완전히 새로 설계돼 mui/base-ui라는 별도 저장소로 독립했고, 패키지명도 @base-ui/react 계열로 바뀌었다. 이 레포의 docs/package.json에는 데모용으로만 @base-ui/react: "catalog:docs"가 들어 있을 뿐, 소스 자체는 없다.

함정 ②
Joy UI·Pigment CSS도 최근 정리됨

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는 전부 MIT다" → MUI X 상당 부분은 상용 라이선스

이 레포(@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'처럼 개별 경로 임포트가 안전하다.

함정 ⑤
styled-engine을 styled-components로 바꾸는 건 설치만으로 안 끝난다

@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을 직접 확인하는 습관이 안전하다.

10대안 비교

Ant Design · Chakra UI · Mantine · shadcn/ui — "완성형" 안에서도, "복사형"과 비교해도
라이브러리포지션 · 차이점
Material UI구글 Material Design 기반 "완성형" 컴포넌트. 방대한 컴포넌트 수·성숙한 접근성·긴 실전 검증 기간이 강점. 디자인이 "구글스럽다"는 인상을 벗기려면 테마 커스터마이징 공수가 든다
Ant Design중국 기업(엔터프라이즈) 화면에 강한 "완성형" 라이브러리. 테이블·폼 등 데이터 중심 대시보드 컴포넌트가 촘촘함. 번들 크기와 커스터마이징 자유도에서 MUI와 종종 비교됨
Chakra UI유틸리티 스타일 prop과 접근성을 앞세운 "완성형". Material Design 같은 고정 스타일 언어가 없어 처음부터 자유로운 톤을 원할 때 선호됨. 최근 버전은 스타일 엔진을 자체 시스템(Panda CSS 계열)으로 전환하는 중
Mantine비교적 최근에 뜬 "완성형" 라이브러리. 훅(hooks) 모음이 강력하고 다크모드·폼 처리가 기본기가 좋다는 평. 커뮤니티·생태계 크기는 MUI보다 아직 작음
shadcn/ui"복사형" — 라이브러리를 설치하는 대신 Radix/Base UI 기반 소스 코드를 프로젝트에 직접 복사해 넣는다. 처음부터 완전히 다른 커스텀 디자인을 원하면 유리하지만, MUI처럼 "설치 즉시 완성된 스타일"은 제공하지 않는다
용어
완성형 vs 헤드리스 vs 복사형
완성형은 스타일까지 포함된 컴포넌트를 설치해 바로 쓰는 방식(MUI·Ant Design). 헤드리스는 동작·접근성만 제공하고 스타일은 직접 입히는 방식(Radix·Base UI). 복사형은 헤드리스 컴포넌트에 스타일을 입힌 소스 코드를 프로젝트로 복사해 오는 방식(shadcn/ui)이다.

11정리 · 누가 쓰면 좋은가

"디자인 시스템 없이 빠르게 완성도 있는 화면"이 목표라면 여전한 정답

Material UI는 "React 위에서 완성된 UI 부품 세트를 원하는 사람"에게 여전히 강력한 선택이다. 스타트업 MVP, 사내 어드민, 접근성이 중요한 공공·금융 서비스, "디자이너 없이도 봐줄 만한 화면"이 필요한 개인 프로젝트까지 폭넓게 맞는다. 반대로 처음부터 완전히 고유한 브랜드 룩을 원한다면 shadcn/ui 같은 복사형이나 Chakra·Mantine처럼 스타일 언어가 덜 고정된 쪽이 나을 수 있다.

레포 자체를 공부 대상으로 본다면, styled-engine 같은 "구현체를 갈아 끼울 수 있게 감추는" 어댑터 패턴, createTheme의 팔레트/스케일 자동 생성, CSS 변수로 리렌더링 없이 다크모드를 전환하는 기법, slots/slotProps로 "정해진 마크업 안에서 정밀하게 커스터마이징을 열어주는" API 설계, 모노레포 안에서 패키지 경계·라이선스 경계(MUI X)를 어떻게 긋는지가 좋은 학습 포인트다.

이런 사람에게 추천

디자인팀 없이 빠르게 견고한 화면이 필요한 개발자, React 상태·테마 아키텍처를 공부하려는 학습자

버튼 하나까지 픽셀 단위로 새로 디자인하고 싶은 프로젝트보다는, "이미 검증된 UI 부품으로 빠르게 완성하고 테마로 톤만 맞추면 되는" 프로젝트에 잘 맞는다. 동시에 대규모 오픈소스 모노레포가 스타일 엔진 추상화·테마 시스템·커스터마이징 API를 어떻게 설계하는지 보고 싶은 사람에게도 좋은 교재다.