v3에서 v4로 마이그레이션
v3에서 v4로 마이그레이션 (Migration from v3 to v4)
드디어 v4가 출시됐어요! 이 문서는 Material UI v3에서 v4로 사이트를 업그레이드하기 위한 참고 자료랍니다. 여기서 다루는 내용이 많지만, 여러분의 사이트에 모든 것을 적용할 필요는 없을 거예요. 따라가기 쉽고, 가능한 한 순차적으로 정리되어 있으니 v4에서 빠르게 작업을 시작할 수 있을 거예요.
출처: 문서
본문
v3 문서를 찾고 있나요? 최신 버전은 여기에서 확인할 수 있어요.
:::info 이 문서는 작업 진행 중(work in progress)이에요. 사이트를 업그레이드하면서 여기에 다루지 않은 문제를 만났나요? GitHub에서 여러분의 변경 사항을 추가해 주세요. :::
소개 (Introduction)
이 문서는 Material UI v3에서 v4로 사이트를 업그레이드하는 데 도움이 되는 참고 자료예요. 여기서 다루는 내용이 많지만, 여러분의 사이트에 모든 것을 적용할 필요는 없을 거예요. 따라가기 쉽고 가능한 한 순차적으로 정리되어 있으니 v4에서 빠르게 작업을 시작할 수 있게 도와드릴게요!
왜 마이그레이션해야 하나요 (Why you should migrate)
이 문서 페이지는 v3에서 v4로 마이그레이션하는 방법(how) 을 다룹니다. 이유(why) 는 Medium의 릴리스 블로그 포스트에서 확인할 수 있어요.
의존성 업데이트 (Updating your dependencies)
가장 먼저 해야 할 일은 의존성을 업데이트하는 거예요.
Material UI 버전 업데이트
package.json을 업데이트해서 최신 버전의 Material UI를 사용해야 해요.
"dependencies": {
"@material-ui/core": "^4.0.0"
}
또는 다음을 실행하세요
npm install @material-ui/core
or
yarn add @material-ui/core
React 버전 업데이트
React의 최소 요구 버전이 react@^16.3.0에서 react@^16.8.0으로 올라갔어요. 이제 Hooks에 의존할 수 있게 되었기 때문이에요 (더 이상 class API를 사용하지 않아요).
Material UI Styles 버전 업데이트
v3에서 @material-ui/styles를 사용하고 있었다면, package.json을 업데이트해서 최신 버전의 Material UI Styles를 사용해야 해요.
"dependencies": {
"@material-ui/styles": "^4.0.0"
}
또는 다음을 실행하세요
npm install @material-ui/styles
or
yarn add @material-ui/styles
브레이킹 체인지 처리 (Handling breaking changes)
Core (핵심)
- 모든 컴포넌트가 자신의 ref를 전달해요. 이는
React.forwardRef()를 사용해서 구현됩니다. 이는 내부 컴포넌트 트리와 display name에 영향을 주므로 shallow 또는 snapshot 테스트가 깨질 수 있어요.innerRef는 더 이상 인스턴스에 대한 ref를 반환하지 않고(내부 컴포넌트가 함수 컴포넌트라면 아무것도 반환하지 않음), 대신 루트 컴포넌트에 대한 ref를 반환해요. 해당 API 문서에 루트 컴포넌트가 명시되어 있습니다.
Styles (스타일)
-
⚠️ Material UI는 JSS v10에 의존해요. JSS v10은 v9와 하위 호환되지 않습니다. 환경에 JSS v9가 설치되어 있지 않은지 확인하세요. (
package.json에서react-jss를 제거하는 것이 도움이 될 수 있어요.) StylesProvider 컴포넌트가 JssProvider를 대체합니다. -
withTheme()의 첫 번째 옵션 인자를 제거하세요. (첫 번째 인자는 결코 실현되지 않았던 잠재적 미래 옵션을 위한 자리표시자였어요.)이는 emotion API와 styled-components API와 일치합니다.
-const DeepChild = withTheme()(DeepChildRaw); +const DeepChild = withTheme(DeepChildRaw); -
convertHexToRGB를hexToRgb로 이름을 바꾸세요.-import { convertHexToRgb } from '@material-ui/core/styles/colorManipulator'; +import { hexToRgb } from '@material-ui/core/styles'; -
keyframes API를 스코프하세요. 코드베이스에서 다음 변경 사항을 적용해야 해요. 이는 애니메이션 로직을 격리하는 데 도움이 됩니다:
rippleVisible: { opacity: 0.3, - animation: 'mui-ripple-enter 100ms cubic-bezier(0.4, 0, 0.2, 1)', + animation: '$mui-ripple-enter 100ms cubic-bezier(0.4, 0, 0.2, 1)', }, '@keyframes mui-ripple-enter': { '0%': { opacity: 0.1, }, '100%': { opacity: 0.3, }, },
Theme (테마)
-
theme.palette.augmentColor()메서드는 더 이상 입력 색상에 부작용(side effect)을 수행하지 않아요. 올바르게 사용하려면 반환된 값을 사용해야 합니다.-const background = { main: color }; -theme.palette.augmentColor(background); +const background = theme.palette.augmentColor({ main: color }); console.log({ background }); -
테마 생성에서 다음 변형을 안전하게 제거할 수 있어요:
typography: { - useNextVariants: true, }, -
theme.spacing.unit사용은 더 이상 사용되지 않아요(deprecated). 새 API를 사용하면 됩니다:label: { [theme.breakpoints.up('sm')]: { - paddingTop: theme.spacing.unit * 12, + paddingTop: theme.spacing(12), }, }팁: 인자를 1개 이상 제공할 수 있어요:
theme.spacing(1, 2) // = '8px 16px'.프로젝트에서 마이그레이션 헬퍼를 사용하면 더 수월하게 진행할 수 있어요.
Layout (레이아웃)
-
[Grid] 임의의 spacing 값을 지원하고 8 단위로 머릿속 계산할 필요를 없애기 위해 spacing API를 변경해요:
/** * Defines the space between the type `item` component. * It can only be used on a type `container` component. */ - spacing: PropTypes.oneOf([0, 8, 16, 24, 32, 40]), + spacing: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),앞으로 테마를 사용해 커스텀 Grid spacing 변환 함수를 구현할 수 있어요.
-
[Container]
@material-ui/lab에서@material-ui/core로 이동했어요.-import Container from '@material-ui/lab/Container'; +import Container from '@material-ui/core/Container';
TypeScript
value 타입
입력 컴포넌트의 value prop 타입을 unknown으로 정규화했어요. 이는 InputBase, NativeSelect, OutlinedInput, Radio, RadioGroup, Select, SelectInput, Switch, TextArea, TextField에 영향을 줍니다.
function MySelect({ children }) {
- const handleChange = (event: any, value: string) => {
+ const handleChange = (event: any, value: unknown) => {
// handle value
};
return <Select onChange={handleChange}>{children}</Select>
}
이 변경 사항은 TypeScript 가이드에서 더 자세히 설명하고 있어요.
Button (버튼)
-
[Button] 더 이상 사용되지 않는 버튼 변형(flat, raised, fab)을 제거하세요:
-<Button variant="raised" /> +<Button variant="contained" />-<Button variant="flat" /> +<Button variant="text" />-import Button from '@material-ui/core/Button'; -<Button variant="fab" /> +import Fab from '@material-ui/core/Fab'; +<Fab />-import Button from '@material-ui/core/Button'; -<Button variant="extendedFab" /> +import Fab from '@material-ui/core/Fab'; +<Fab variant="extended" /> -
[ButtonBase]
componentprop에 전달되는 컴포넌트는 ref를 보유할 수 있어야 해요. 컴포지션 가이드가 마이그레이션 전략을 설명합니다.이는
BottomNavigationAction,Button,CardActionArea,Checkbox,ExpansionPanelSummary,Fab,IconButton,MenuItem,Radio,StepButton,Tab,TableSortLabel그리고buttonprop이 true일 때의ListItem에도 적용돼요.
Card (카드)
- [CardActions]
disableActionSpacingprop을disableSpacing으로 이름을 바꾸세요. - [CardActions]
disableActionSpacingCSS 클래스를 제거하세요. - [CardActions]
actionCSS 클래스를spacing으로 이름을 바꾸세요.
ClickAwayListener
- [ClickAwayListener] react-event-listener props를 숨기세요.
Dialog (다이얼로그)
- [DialogActions]
disableActionSpacingprop을disableSpacing으로 이름을 바꾸세요. - [DialogActions]
actionCSS 클래스를spacing으로 이름을 바꾸세요. - [DialogContentText]
subtitle1대신 typography 변형body1을 사용하세요. - [Dialog] 자식은 ref를 보유할 수 있어야 해요. 컴포지션 가이드가 마이그레이션 전략을 설명합니다.
Divider (구분선)
-
[Divider] 더 이상 사용되지 않는
insetprop을 제거하세요.-<Divider inset /> +<Divider variant="inset" />
ExpansionPanel (확장 패널)
- [ExpansionPanelActions]
actionCSS 클래스를spacing으로 이름을 바꾸세요. - [ExpansionPanel]
disabled와expanded스타일 규칙의 CSS 특이성을 높이세요. - [ExpansionPanel]
CollapsePropsprop을TransitionProps로 이름을 바꾸세요.
List (목록)
-
[List] 스펙에 맞게 목록 컴포넌트를 재작업했어요:
- 아바타를 사용할 때는
ListItemAvatar컴포넌트가 필요해요. - 왼쪽 체크박스를 사용할 때는
ListItemIcon컴포넌트가 필요해요. - 아이콘 버튼에
edge속성을 설정해야 해요.
- 아바타를 사용할 때는
-
[List]
dense는 더 이상List요소의 위아래 padding을 줄이지 않아요. -
[ListItem]
disabled와focusVisible스타일 규칙의 CSS 특이성을 높이세요.
Menu (메뉴)
- [MenuItem] MenuItem의 고정 높이를 제거하세요. 이제 padding과 line-height를 브라우저가 높이를 계산하는 데 사용해요.
Modal (모달)
-
[Modal] 자식은 ref를 보유할 수 있어야 해요. 컴포지션 가이드가 마이그레이션 전략을 설명합니다.
이는
Dialog와Popover에도 적용돼요. -
[Modal] Modal 컴포넌트의 classes 커스터마이제이션 API를 제거하세요 (별도로 사용할 때 번들 크기가 -74% 줄어들어요).
-
[Modal]
event.defaultPrevented는 이제 무시돼요. 새 로직은 키 다운 이스케이프 이벤트에서event.preventDefault()가 호출되어도 Modal을 닫아요.event.preventDefault()는 체크박스를 클릭해 체크하는 것, 버튼을 눌러 폼을 제출하는 것, 왼쪽 화살표를 눌러 텍스트 입력에서 커서를 움직이는 것 같은 기본 동작을 막기 위한 것이에요. 이런 기본 동작을 가진 것은 특수 HTML 요소뿐입니다. 모달에서onClose이벤트를 트리거하고 싶지 않다면event.stopPropagation()을 사용해야 해요.
Paper
-
[Paper] 기본 elevation(고도)을 줄이세요. 기본 Paper elevation을 Card와 Expansion Panel과 일치하도록 변경하세요:
-<Paper /> +<Paper elevation={2} />이는
ExpansionPanel에도 영향을 줍니다.
Portal
- [Portal]
disablePortal을 사용할 때 자식은 ref를 보유할 수 있어야 해요. 컴포지션 가이드가 마이그레이션 전략을 설명합니다.
Slide
- [Slide] 자식은 ref를 보유할 수 있어야 해요. 컴포지션 가이드가 마이그레이션 전략을 설명합니다.
Slider (슬라이더)
-
[Slider]
@material-ui/lab에서@material-ui/core로 이동했어요.-import Slider from '@material-ui/lab/Slider' +import Slider from '@material-ui/core/Slider'
Switch (스위치)
-
[Switch] 스타일을 오버라이드하기 더 쉽도록 구현을 리팩터링했어요. 클래스 이름을 스펙 용어와 일치하도록 바꾸세요:
-icon -bar +thumb +track
Snackbar
- [Snackbar] 새 스펙과 일치하도록 변경했어요.
- 치수(dimensions)를 변경
- 기본 전환(transition)을
Slide에서Grow로 변경
SvgIcon
-
[SvgIcon] nativeColor -> htmlColor로 이름을 바꾸세요. React는
forHTML 속성과 같은 문제를 해결하면서 prop을htmlFor라고 부르기로 결정했어요. 이 변경은 같은 논리를 따릅니다.-<AddIcon nativeColor="#fff" /> +<AddIcon htmlColor="#fff" />
Tabs (탭)
-
[Tab] 단순화를 위해
labelContainer,label,labelWrapped클래스 키를 제거하세요. 이를 통해 중간 DOM 요소 2개를 제거할 수 있었어요. 커스텀 스타일을root클래스 키로 옮길 수 있을 거예요.
-
[Tabs] 더 이상 사용되지 않는 fullWidth와 scrollable props를 제거하세요:
-<Tabs fullWidth scrollable /> +<Tabs variant="scrollable" />
Table (테이블)
-
[TableCell] 더 이상 사용되지 않는
numeric속성을 제거하세요:-<TableCell numeric>{row.calories}</TableCell> +<TableCell align="right">{row.calories}</TableCell> -
[TableRow] 고정 높이 CSS 속성을 제거하세요. 이제 셀 높이는 브라우저가 padding과 line-height를 사용해 계산해요.
-
[TableCell]
dense모드를 다른 속성으로 이동하세요:-<TableCell padding="dense" /> +<TableCell size="small" /> -
[TablePagination] 컴포넌트는 더 이상 유효하지 않은 (
page,count,rowsPerPage) 속성 조합을 수정하려고 시도하지 않아요. 대신 경고를 발생시킵니다.
TextField (텍스트 필드)
-
[InputLabel] InputLabel 컴포넌트의 CSS API를 사용해 FormLabel 컴포넌트의 모든 스타일을 오버라이드할 수 있어야 해요.
FormLabelClasses속성이 제거되었어요.<InputLabel - FormLabelClasses={{ asterisk: 'bar' }} + classes={{ asterisk: 'bar' }} > Foo </InputLabel> -
[InputBase] 기본 box sizing 모델을 변경하세요. 이제 다음 CSS를 사용해요:
box-sizing: border-box;이는
fullWidthprop과 관련된 문제를 해결합니다. -
[InputBase]
InputBase에서inputType클래스를 제거하세요.
Tooltip (툴팁)
- [Tooltip] 자식은 ref를 보유할 수 있어야 해요. 컴포지션 가이드가 마이그레이션 전략을 설명합니다.
- [Tooltip] 모든 focus 대신 focus-visible focus 후에만 나타나요.
Typography (타이포그래피)
-
[Typography] 더 이상 사용되지 않는 typography 변형을 제거하세요. 다음 교체를 수행해 업그레이드할 수 있어요:
- display4 => h1
- display3 => h2
- display2 => h3
- display1 => h4
- headline => h5
- title => h6
- subheading => subtitle1
- body2 => body1
- body1 (default) => body2 (default)
-
[Typography] 독단적인
display: block기본 typography 스타일을 제거하세요. 새display?: 'initial' | 'inline' | 'block';속성을 사용할 수 있어요. -
[Typography]
headlineMapping속성을variantMapping으로 이름을 바꿔 목적에 더 잘 맞게 하세요.-<Typography headlineMapping={headlineMapping}> +<Typography variantMapping={variantMapping}> -
[Typography] 기본 변형을
body2에서body1로 변경하세요. 14px보다 16px 폰트 크기가 더 나은 기본값이에요. Bootstrap, material.io, 그리고 심지어 문서도 기본 폰트 크기로 16px을 사용해요. Ant Design이 사용하는 14px은 이해할 만한데, 중국 사용자는 다른 알파벳을 사용하기 때문이에요. 일본어의 기본 폰트 크기로는 12px이 권장됩니다. -
[Typography] typography 변형에서 기본 색상을 제거하세요. 색상은 대부분의 경우 상속되어야 해요. 그것이 웹의 기본 동작이에요.
-
[Typography] 이 스레드의 논리에 따라
color="default"를color="initial"로 이름을 바꾸세요. default 사용은 피해야 해요. 시맨틱이 부족하거든요.
Node
- node 6 지원 중단. node 8로 업그레이드해야 해요.
UMD
-
이 변경으로 CDN에서 Material UI를 더 쉽게 사용할 수 있어요:
const { Button, TextField, -} = window['material-ui']; +} = MaterialUI;이는 다른 React 프로젝트와 일관됩니다:
- material-ui => MaterialUI
- react-dom => ReactDOM
- prop-types => PropTypes