v6로 업그레이드하기

v6로 업그레이드하기 (Upgrade to v6)

이 가이드는 Material UI를 v5에서 v6으로 업그레이드해야 하는 이유와 방법을 설명해요.

출처: 문서

본문

Material UI v6으로 업그레이드해야 하는 이유

React Server Component 지원

Material UI v6은 Emotion과 styled-components를 대체하는 zero-runtime CSS-in-JS 스타일링 엔진인 Pigment CSS를 소개해요. React 19 이후에서 스타일을 작성하기 위한 더 미래 지향적인 솔루션이죠. Pigment CSS를 사용하면 스타일이 런타임이 아니라 빌드 시점에 추출되어, 클라이언트 사이드 재계산을 피하고 React Server Component (RSC) 호환성을 열어줘요. 이는 또한 Material UI 앱의 번들 크기를 크게 줄여줘요.

v6에서는 Pigment CSS가 opt-in 방식이에요. 향후 Material UI의 메이저 버전들은 Pigment CSS를 기본 스타일링 솔루션으로 사용할 가능성이 높아요. 선택 사항이지만, 여러분의 Material UI 앱에서 Pigment CSS를 시도해보는 것을 권장해요. 그렇게 하고 싶다면 Material UI v6 업그레이드를 마친 후 Pigment CSS로 마이그레이션 가이드를 참고하세요.

삶의 질 개선 (Quality-of-life improvements)

Material UI v6은 다음과 같은 다양한 삶의 질 개선을 포함해요:

다음 패키지 중 하나를 사용하고 있다면, 버전도 "6.0.0"으로 바꿀 수 있어요:

  • @mui/icons-material
  • @mui/system
  • @mui/lab
  • @mui/material-nextjs
  • @mui/styled-engine-sc
  • @mui/utils

MUI X 패키지는 Material UI와 동일한 버전 전략을 따르지 않는다는 점을 기억하세요. 아래 패키지 중 하나를 사용하고 있다면, 업그레이드 과정 동안 그대로 두어야 해요:

  • @mui/x-data-grid
  • @mui/x-data-grid-pro
  • @mui/x-data-grid-premium
  • @mui/x-date-pickers
  • @mui/x-date-pickers-pro
  • @mui/x-charts
  • @mui/x-tree-view
  • @mui/x-tree-view-pro

지원되는 브라우저와 버전 (Supported browsers and versions)

기본 번들의 타깃이 v6에서 변경됐어요.

정확한 버전은 릴리즈 시점에 browserslist 쿼리에서 고정될 거예요: "> 0.5%, last 2 versions, Firefox ESR, not dead, safari >= 15.4, iOS >= 15.4".

  • Node.js 14 (12에서 상향)
  • Chrome 109 (90에서 상향)
  • Edge 121 (91에서 상향)
  • Firefox 115 (78에서 상향)
  • Safari 15.4 — macOS와 iOS 둘 다 (macOS에서 14, iOS에서 12.5에서 상향)
  • 그 외 (.browserslistrc stable 항목 참고)

IE 11 지원 제거 (Removed support for IE 11)

IE 11에 대한 지원 — 레거시 번들과 모든 IE 11 관련 코드 — 는 v6에서 완전히 제거됐어요. 이는 Material UI의 번들 크기를 줄이고 향후 개발을 수월하게 만들어요.

IE 11을 지원해야 한다면 v5의 legacy bundle을 사용할 수 있어요. 다만 향후 업데이트나 버그 수정은 받지 못할 거예요.

최소 React 버전 (Minimum React version)

지원되는 최소 React 버전은 v17.0.0이에요 (v5와 동일). 아래 스니펫으로 프로젝트를 업데이트하세요 (<version>을 원하는 버전으로 바꾸세요):

npm install react@<version> react-dom@<version>
pnpm add react@<version> react-dom@<version>
yarn add react@<version> react-dom@<version>

React 18 이하 (React 18 and below)

React 18 이하를 사용한다면, 사용 중인 react와 동일한 버전으로 react-is 패키지의 resolution을 설정해야 해요.

예를 들어 [email protected]을 사용한다면, 다음 단계를 수행하세요:

  1. [email protected]을 설치해요.
npm install [email protected]
pnpm add [email protected]
yarn add [email protected]
  1. package.json에서 resolutions 또는 overrides를 설정해요.
{
  …
  "overrides": {
    "react-is": "^18.3.1"
  }
}
{
  …
  "overrides": {
    "react-is": "^18.3.1"
  }
}
{
  …
  "resolutions": {
    "react-is": "^18.3.1"
  }
}

왜 이게 필요한가요?

Material UI v6은 React 요소를 식별하는 방식을 바꾼 react-is@19를 사용해요. React 18 이하를 쓰고 있다면, 버전이 맞지 않는 react-is는 prop 타입 검사에서 런타임 오류를 일으킬 수 있어요. react-is를 여러분의 React 버전과 일치시키면 이러한 오류를 방지할 수 있어요.

최소 TypeScript 버전 (Minimum TypeScript version)

지원되는 최소 TypeScript 버전이 v3.5에서 4.7로 올라갔어요.

:::info 우리는 DefinitelyTyped (npm에서 @types 네임스페이스 아래 게시됨)가 릴리즈하는 타입과 정렬합니다. Material UI의 마이너 버전에서는 최소 지원 버전을 바꾸지 않을 거예요. 하지만 DefinitelyTyped가 지원하는 최저 버전보다 오래된 TypeScript 버전을 사용하지 않는 것을 권장해요. :::

프로젝트에 다음 패키지가 포함되어 있다면 업데이트해야 해요:

  • @types/react
  • @types/react-dom

:::warning 애플리케이션이 여전히 오류 없이 실행되는지 확인하고, 다음 단계로 진행하기 전에 변경 사항을 커밋하세요. :::

주요 변경 사항 (Breaking changes)

Material UI v6은 v5에서 업그레이드할 때 최소한의 주요 변경 사항만 도입하도록 설계됐어요. 여기에는 브라우저 지원 업데이트, Node.js 버전 상향, UMD 번들 제거가 포함돼요. 이러한 업데이트는 Material UI 패키지 크기를 v5 전체 크기의 거의 25%에 해당하는 2.5MB 줄여요.

대부분의 주요 변경 사항을 처리하기 위해 codemod가 제공돼요.

:::info 이 목록은 작업 진행 중이에요. 새로운 주요 변경 사항이 도입될 때마다 업데이트를 기대하세요. :::

UMD 번들 제거 (UMD bundle removed)

React 19의 UMD 빌드 제거에 맞춰, Material UI도 UMD 번들을 제거했어요. 이로 인해 @mui/material 패키지 크기가 2.5MB(전체 패키지 크기의 25%) 줄었어요. 자세한 내용은 Package Phobia를 참고하세요.

대체 설치 방법은 installation 문서를 참고하세요.

Accordion

헤딩으로 감싸진 Summary

W3C Accordion Pattern 표준을 충족하기 위해, Accordion Summary가 이제 기본 <h3> 헤딩 요소로 감싸져요. 이 변경은 이전 DOM 구조와 CSS 특이성(specificity)에 의존하던 커스터마이즈에 영향을 줄 수 있어요. 또한 기본 헤딩 요소가 페이지의 기존 헤딩 구조와 충돌할 수도 있어요.

스타일이나 DOM 조작이 이전 구조에 의존한다면, 새 헤딩 요소를 수용하도록 업데이트해야 해요. 기본 헤딩 요소가 기존 구조와 충돌한다면, slotProps.heading.component prop을 사용해 헤딩 요소를 바꿀 수 있어요.

<Accordion slotProps={{ heading: { component: 'h4' } }}>
  <AccordionSummary
    expandIcon={<ExpandMoreIcon />}
    aria-controls="panel1-content"
    id="panel1-header"
  >
    Accordion
  </AccordionSummary>
  <AccordionDetails>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada
    lacus ex, sit amet blandit leo lobortis eget.
  </AccordionDetails>
</Accordion>

버튼으로서의 Summary (v6.3.0부터)

  • Accordion Summary의 HTML 구조가 위에서 보여준 것처럼 헤딩으로 감싸면서 생긴 잘못된 HTML을 고치도록 업데이트됐어요:
    • 루트 요소는 이제 button이에요.
    • Summary 콘텐츠와 아이콘 래퍼는 span으로 렌더링돼요.
  • 이전 div 요소를 AccordionSummary 스타일링에 사용하던 개발자는 스타일을 업데이트해야 해요. 또한 기본적으로 p 태그를 렌더링하는 Typography를 텍스트에 사용한다면, span으로 교체해야 해요. Accordion 데모에서 보여준 것처럼 component prop으로 HTML 태그를 바꿀 수 있어요 (<Typography component=\"span\" />).

Autocomplete

Autocomplete 컴포넌트의 onInputChange 콜백에 있는 reason 인자에 세 가지 새 값이 도입됐어요. 이 값들은 이전에 "reset"이 덮었던 세 가지 특정 사용 사례에 대한 더 세분화된 옵션을 제공해요:

  • "blur": "reset"과 비슷하지만, 포커스가 입력에서 벗어날 때 트리거돼요. clearOnBlur가 true여야 해요.
  • "selectOption": 옵션을 선택한 후 입력 값이 바뀔 때 트리거돼요.
  • "removeOption": 다중 선택 모드에서 해당 옵션이 선택된 결과로 칩(chip)이 제거될 때 트리거돼요.

이 값들은 기존의 "input", "reset", "clear" 값들에 더해 사용할 수 있어요.

Chip

이전 버전에서는 사용자가 esc 키를 누르면 Chip 컴포넌트가 포커스를 잃었는데, 이는 다른 버튼류 컴포넌트가 동작하는 방식과 달랐어요. v6에서는 Chip이 예상대로 포커스를 유지해요.

이전 동작을 유지하려면 아래처럼 커스텀 onKeyUp 핸들러를 추가하세요:

import * as React from 'react';
import Chip from '@mui/material/Chip';

export default function ChipExample() {
  const chipRef = React.useRef(null);
  const keyUpHandler = (event) => {
    if (event.key === 'Escape' && chipRef.current) {
      chipRef.current.blur();
    }
  };
  return (
    <Chip
      label="Chip Outlined"
      variant="outlined"
      ref={chipRef}
      onKeyUp={keyUpHandler}
    />
  );
}

Divider

세로 방향을 사용할 때, Divider는 이제 WAI-ARIA 스펙을 준수하기 위해 <hr> 대신 해당 접근성 속성을 가진 <div>를 렌더링해요. CSS에서 hr 태그를 타깃으로 한다면 그에 맞춰 스타일을 조정해야 할 수도 있어요.

-import Divider from '@mui/material/Divider';
+import Divider, { dividerClasses } from '@mui/material/Divider';

 const Main = styled.main({
-  '& hr': {
+  [`& .${dividerClasses.root}`]: {
     marginTop: '16px',
   },
 });

Grid2

Grid2 (이전 Unstable_Grid2)가 업데이트되고 안정화됐어요:

  • 테마의 브레이크포인트 이름을 따르던 이전 크기(xs, sm, md, ...)와 오프셋(xsOffset, smOffset, mdOffset, ...) props가 size와 offset props로 대체됐어요.
  • 간격 매커니즘이 gap CSS 속성을 사용하도록 재작업됐어요.

이는 다음 절에서 설명하는 몇 가지 주요 변경 사항을 가져와요.

Unstable 접두사 제거

Grid2 컴포넌트 API가 안정화되어, 임포트에 더 이상 Unstable_ 접두사가 포함되지 않아요:

-import { Unstable_Grid2 as Grid2 } from '@mui/material';
+import { Grid2 } from '@mui/material';
-import Grid from '@mui/material/Unstable_Grid2';
+import Grid from '@mui/material/Grid2';

크기와 오프셋 props 이름 변경

v5에서 크기와 오프셋 props는 테마의 브레이크포인트에 맞춰 이름 지어졌어요. 기본 테마의 경우 다음이었죠:

  • 크기 (Size): xs, sm, md, lg, xl
  • 오프셋 (Offset): xsOffset, smOffset, mdOffset, lgOffset, xlOffset

v6에서는 이 props가 size와 offset으로 이름이 바뀌었어요:

 <Grid
-  xs={12}
-  sm={6}
-  xsOffset={2}
-  smOffset={3}
+  size={{ xs: 12, sm: 6 }}
+  offset={{ xs: 2, sm: 3 }}
 >

크기나 오프셋이 모든 브레이크포인트에서 같다면, 단일 값을 사용할 수 있어요:

-<Grid xs={6} xsOffset={2}>
+<Grid size={6} offset={2}>

또한, size prop의 true 값이 "grow"로 이름이 바뀌었어요:

-<Grid xs>
+<Grid size="grow">

다음 codemod를 사용해 프로젝트를 새 size와 offset props로 마이그레이션하세요:

npx @mui/codemod@latest v6.0.0/grid-v2-props <path/to/folder>

:::warning codemod를 실행하기 전에 임포트를 @mui/material/Unstable_Grid2에서 @mui/material/Grid2로 수정해야 해요. :::

커스텀 브레이크포인트 사용 (Using custom breakpoints)

위의 사용법은 커스텀 브레이크포인트에도 동일하게 적용돼요:

-<Grid mobile={12} mobileOffset={2} desktop={6} desktopOffset={4}>
+<Grid size={{ mobile: 12, desktop: 6 }} offset={{ mobile: 2, desktop: 4 }}>

브레이크포인트를 인자로 제공해 커스텀 브레이크포인트에도 같은 codemod를 사용할 수 있어요:

npx @mui/codemod@latest v6.0.0/grid-v2-props <path/to/folder> --jscodeshift='--muiBreakpoints=mobile,desktop'

disableEqualOverflow prop 제거

v5에서 Grid는 부모를 넘쳐흘렀어요(overflow). v6에서 Grid는 부모의 padding 안에 올바르게 포함돼요:

Before and after of the Grid no longer overflowing its parent in v6.

이로 인해 disableEqualOverflow prop이 더 이상 필요 없게 됐어요:

-<Grid disableEqualOverflow>
+<Grid>

Grid 항목 간격 변경 (Grid item spacing change)

v5에서 Grid 항목은 박스 안에 간격(spacing)을 포함했어요. v6에서 Grid 항목은 CSS gap 속성을 사용해 더 이상 박스 안에 간격을 포함하지 않아요.

항목의 위치는 변하지 않는다는 점을 기억하세요.

Before and after of the Grid items no longer including spacing in their boxes.

:::warning 이러한 업데이트는 앱 레이아웃에 예상치 못한 변화를 일으킬 수 있어요. 그럼에도 불구하고, 새 버전이 더 예측 가능하고 현대적이므로 옛 패턴을 재현하려고 하기보다 새 동작을 채택할 것을 강력히 권장해요. :::

컨테이너 너비 (Container width)

업데이트된 Grid 컴포넌트는 기본적으로 컨테이너의 전체 너비로 자라지 않아요. 그리드가 전체 너비로 자라야 한다면 sx prop을 사용하면 돼요:

-<Grid container>
+<Grid container sx={{ width: '100%' }}>

 // alternatively, if the Grid's parent is a flex container:
-<Grid container>
+<Grid container sx={{ flexGrow: 1 }}>

ListItem

v5에서 deprecated 됐던 ListItem의 props autoFocus, button, disabled, selected가 제거됐어요. button prop을 대체하려면 ListItemButton을 대신 사용하세요. 나머지 제거된 props도 ListItemButton 컴포넌트에서 사용할 수 있어요.

-<ListItem button />
+<ListItemButton />

다음 codemod를 사용해 프로젝트를 ListItemButton 컴포넌트로 마이그레이션하세요:

npx @mui/codemod@latest v6.0.0/list-item-button-prop <path/to/folder>

ListItem이 더 이상 이 props를 지원하지 않으므로, 이 props와 관련된 클래스 이름도 제거됐어요. 대신 listItemButtonClasses 객체를 사용해야 해요.

-import { listItemClasses } from '@mui/material/ListItem';
+import { listItemButtonClasses } from '@mui/material/ListItemButton';

-listItemClasses.button
+listItemButtonClasses.root

-listItemClasses.focusVisible
+listItemButtonClasses.focusVisible

-listItemClasses.disabled
+listItemButtonClasses.disabled

-listItemClasses.selected
+listItemButtonClasses.selected

로딩 상태의 Button (Button with Loading State)

@mui/material v6.4.0부터 Lab의 LoadingButton이 제거됐어요. 로딩 기능은 이제 표준 Button 컴포넌트에 포함돼요. 아래처럼 임포트를 업데이트하세요:

-import { LoadingButton } from '@mui/lab';
+import { Button } from '@mui/material';
-import LoadingButton from '@mui/lab/LoadingButton';
+import Button from '@mui/material/Button';

자세한 내용은 Material UI Button 문서의 Loading 섹션을 참고하세요.

Typography

Typography 컴포넌트의 color prop은 더 이상 시스템 prop이 아니에요. 대신 sx prop을 사용할 수 있어요:

-<Typography color={(theme) => theme.palette.primary.main}>
+<Typography sx={{ color: (theme) => theme.palette.primary.main }}>

:::info 시스템 props는 sx prop을 선호하며 deprecated 되었어요. 자세한 내용은 마이그레이션 가이드를 확인하세요. :::

여전히 color prop을 사용해 일부 테마 색상에 직접 접근할 수 있어요. 전체 색상 목록은 Typography 컴포넌트 API 페이지를 확인하세요.

<Typography color="textSecondary">Secondary text</Typography>

useMediaQuery 타입 (useMediaQuery types)

다음 deprecated 타입들이 v6에서 제거됐어요:

  • MuiMediaQueryList: 대신 MediaQueryList (lib.dom.d.ts에서)를 사용하세요.
  • MuiMediaQueryListEvent: 대신 MediaQueryListEvent (lib.dom.d.ts에서)를 사용하세요.
  • MuiMediaQueryListListener: 대신 (event: MediaQueryListEvent) => void를 사용하세요.

테스팅에 영향을 주는 주요 변경 사항 (Breaking changes affecting testing)

리플 효과 (Ripple effect)

리플 효과의 성능이 v6에서 개선됐어요. 이 때문에 리플 효과가 있는 컴포넌트를 포함하는 테스트를 업데이트해야 할 수 있어요. @testing-library/react의 fireEvent를 사용해 사용자 상호작용을 시뮬레이션한다면, React 경고를 피하기 위해 이를 act와 await로 감싸야 해요:

- fireEvent.click(button);
+ await act(async () => fireEvent.mouseDown(button));

이 변경의 영향을 받는 컴포넌트는:

  • 모든 버튼
  • Checkbox
  • Chip
  • Radio Group
  • Switch
  • Tabs

타입에 영향을 주는 주요 변경 사항 (Breaking changes affecting types)

Box

component prop이 Box 타입에 이미 포함되어 있으므로 BoxOwnProps에서 제거됐어요. styled 함수를 Box 컴포넌트와 함께 사용한다면 이 변경이 여러분의 코드에 영향을 줄 수 있어요. 그렇다면 Box 대신 div 요소를 사용하세요:

-const StyledBox = styled(Box)`
+const StyledDiv = styled('div')`
   color: white;
 `;

이 결과는 동일해요. 이것이 잘 맞지 않는다면, styled가 반환한 값을 typeof Box로 캐스팅할 수도 있어요:

 const StyledBox = styled(Box)`
   color: white;
-`;
+` as typeof Box;

안정화된 API (Stabilized APIs)

CssVarsProvider와 extendTheme

CssVarsProvider와 extendTheme API가 이제 안정적이에요. v5에서 이미 사용하고 있다면 이제 experimental 접두사를 뺄 수 있어요:

-import { experimental_extendTheme as extendTheme, Experimental_CssVarsProvider as CssVarsProvider } from '@mui/material/styles';
+import { extendTheme, CssVarsProvider } from '@mui/material/styles';

이 API들 작업에 대한 자세한 내용은 CSS theme variables를 참고하세요.

색상 모드 테마 유틸리티 (Color mode theme utility)

Material UI v6은 라이트/다크 스타일을 적용할 때 theme.palette.mode를 대체하기 위해 설계된, 특정 색상 모드에 스타일을 추가하는 새 유틸리티 theme.applyStyles()를 소개해요:

 const MyComponent = styled('button')(({ theme }) => ({
   padding: '0.5rem 1rem',
   border: '1px solid,
-  borderColor: theme.palette.mode === 'dark' ? '#fff' : '#000',
+  borderColor: '#000',
+  ...theme.applyStyles('dark', {
+    borderColor: '#fff',
+  })
 }))

프로젝트를 theme.applyStyles()로 마이그레이션하려면 다음 codemod를 사용하세요:

npx @mui/codemod@latest v6.0.0/styled <path/to/folder-or-file>
npx @mui/codemod@latest v6.0.0/sx-prop <path/to/folder-or-file>
npx @mui/codemod@latest v6.0.0/theme-v6 <path/to/theme-file>

:::info 커스텀 테마가 있다면, 커스텀 styleOverrides가 들어 있는 파일에 대해 v6.0.0/theme-v6을 실행하세요. 그렇지 않으면 이 codemod를 무시하면 돼요. :::

Deprecations

Material UI v6을 사용하기 위해 deprecations를 즉시 처리할 필요는 없어요. deprecations 페이지를 확인하며 여러분의 속도에 맞춰 진행하면 돼요. 이 deprecations는 다음 메이저 버전에서 제거될 거예요.

Pigment CSS 통합 (선택 사항)

앱을 v6으로 업그레이드하는 것을 마쳤다면, RSC 지원과 더 작은 번들 크기를 위해 Pigment CSS로 마이그레이션을 시작할 준비가 된 거예요.

더 알아보기 (Learn more)