Masonry
Masonry
Masonry는 다양한 크기의 콘텐츠를 같은 너비, 다른 높이의 블록으로 배치하고, 그 사이 간격을 설정할 수 있게 해줘요. Masonry는 일관된 너비지만 높이는 서로 다른 콘텐츠 블록 목록을 유지하고, 콘텐츠는 행(row) 순서로 정렬돼요. 만약 행이 지정된 컬럼 수로 이미 채워졌다면, 다음 항목이 새 행을 시작하고 공간 사용을 최적화하기 위해 가장 짧은 컬럼에 추가돼요.
출처: 문서
본문
기본 Masonry (Basic masonry)
Masonry의 간단한 예시예요. Masonry는 하나 이상의 항목을 위한 컨테이너로, <div />와 <img />를 포함한 어떤 요소든 받을 수 있어요.
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function BasicMasonry() {
return (
<Box sx={{ width: 500, minHeight: 393 }}>
<Masonry columns={4} spacing={2}>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
);
}
이미지 Masonry (Image masonry)
이 예시는 이미지에 Masonry를 사용하는 방법을 보여줘요. Masonry는 자식을 행 순서로 정렬해요. 이미지를 컬럼 순서로 정렬하고 싶다면 ImageList를 확인해보세요.
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
import { styled } from '@mui/material/styles';
const Label = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function ImageMasonry() {
return (
<Box sx={{ width: 500, minHeight: 829 }}>
<Masonry columns={3} spacing={2}>
{itemData.map((item, index) => (
<div key={index}>
<Label>{index + 1}</Label>
<img
srcSet={`${item.img}?w=162&auto=format&dpr=2 2x`}
src={`${item.img}?w=162&auto=format`}
alt={item.title}
loading="lazy"
style={{
borderBottomLeftRadius: 4,
borderBottomRightRadius: 4,
display: 'block',
width: '100%',
}}
/>
</div>
))}
</Masonry>
</Box>
);
}
const itemData = [
{
img: 'https://images.unsplash.com/photo-1518756131217-31eb79b20e8f',
title: 'Fern',
},
{
img: 'https://images.unsplash.com/photo-1627308595229-7830a5c91f9f',
title: 'Snacks',
},
{
img: 'https://images.unsplash.com/photo-1597645587822-e99fa5d45d25',
title: 'Mushrooms',
},
{
img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383',
title: 'Tower',
},
{
img: 'https://images.unsplash.com/photo-1471357674240-e1a485acb3e1',
title: 'Sea star',
},
{
img: 'https://images.unsplash.com/photo-1558642452-9d2a7deb7f62',
title: 'Honey',
},
{
img: 'https://images.unsplash.com/photo-1516802273409-68526ee1bdd6',
title: 'Basketball',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Breakfast',
},
{
img: 'https://images.unsplash.com/photo-1627328715728-7bcc1b5db87d',
title: 'Tree',
},
{
img: 'https://images.unsplash.com/photo-1551782450-a2132b4ba21d',
title: 'Burger',
},
{
img: 'https://images.unsplash.com/photo-1522770179533-24471fcdba45',
title: 'Camera',
},
{
img: 'https://images.unsplash.com/photo-1444418776041-9c7e33cc5a9c',
title: 'Coffee',
},
{
img: 'https://images.unsplash.com/photo-1627000086207-76eabf23aa2e',
title: 'Camping Car',
},
{
img: 'https://images.unsplash.com/photo-1533827432537-70133748f5c8',
title: 'Hats',
},
{
img: 'https://images.unsplash.com/photo-1567306301408-9b74779a11af',
title: 'Tomato basil',
},
{
img: 'https://images.unsplash.com/photo-1627328561499-a3584d4ee4f7',
title: 'Mountain',
},
{
img: 'https://images.unsplash.com/photo-1589118949245-7d38baf380d6',
title: 'Bike',
},
];
가변 높이 항목 (Items with variable height)
이 예시는 가변 높이 항목에 Masonry를 사용하는 방법을 보여줘요. 항목은 "항상 가장 짧은 컬럼에 추가된다"는 규칙을 준수하기 위해 다른 컬럼으로 이동할 수 있고, 그로 인해 공간 사용이 최적화돼요.
import { styled } from '@mui/material/styles';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import Masonry from '@mui/lab/Masonry';
import Accordion from '@mui/material/Accordion';
import AccordionDetails from '@mui/material/AccordionDetails';
import AccordionSummary from '@mui/material/AccordionSummary';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80];
const StyledAccordion = styled(Accordion)(({ theme }) => ({
backgroundColor: '#fff',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function MasonryWithVariableHeightItems() {
return (
<Box sx={{ width: 500, minHeight: 377 }}>
<Masonry columns={3} spacing={2}>
{heights.map((height, index) => (
<Paper key={index}>
<StyledAccordion sx={{ minHeight: height }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography component="span">Accordion {index + 1}</Typography>
</AccordionSummary>
<AccordionDetails>Contents</AccordionDetails>
</StyledAccordion>
</Paper>
))}
</Masonry>
</Box>
);
}
컬럼 (Columns)
이 예시는 columns를 사용해 Masonry의 컬럼 수를 설정하는 방법을 보여줘요.
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function FixedColumns() {
return (
<Box sx={{ width: 500, minHeight: 253 }}>
<Masonry columns={4} spacing={2}>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
);
}
columns는 반응형(responsive) 값을 받아들여요:
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function ResponsiveColumns() {
return (
<Box sx={{ width: 500, minHeight: 253 }}>
<Masonry columns={{ xs: 3, sm: 4 }} spacing={2}>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
);
}
간격 (Spacing)
이 예시는 spacing을 사용해 항목 사이 간격을 설정하는 방법을 보여줘요. spacing prop에 주어진 값이 테마의 spacing 필드에 곱해진다는 점을 알아두는 게 중요해요.
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function FixedSpacing() {
return (
<Box sx={{ width: 500, minHeight: 377 }}>
<Masonry columns={3} spacing={3}>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
);
}
spacing은 반응형 값을 받아들여요:
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function ResponsiveSpacing() {
return (
<Box sx={{ width: 500, minHeight: 377 }}>
<Masonry columns={3} spacing={{ xs: 1, sm: 2, md: 3 }}>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
);
}
순차 배치 (Sequential)
이 예시는 순차 순서를 설정하는 sequential을 사용하는 방법을 보여줘요. sequential이 활성화되면 항목이 가장 짧은 컬럼에 추가되는 대신 왼쪽에서 오른쪽으로 순서대로 추가돼요.
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function Sequential() {
return (
<Box sx={{ width: 500, minHeight: 393 }}>
<Masonry
columns={4}
spacing={2}
defaultHeight={450}
defaultColumns={4}
defaultSpacing={1}
sequential
>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
);
}
서버 사이드 렌더링 (Server-side rendering)
이 예시는 defaultHeight, defaultColumns, defaultSpacing의 사용을 보여줘요. 이 값들은 서버 사이드 렌더링을 지원하는 데 사용돼요.
:::info
defaultHeight는 모든 행을 렌더링할 수 있을 만큼 충분히 커야 해요. 또한 서버 사이드 렌더링의 경우 항목이 가장 짧은 컬럼에 추가되지 않는다는 점을 언급할 가치가 있어요.
:::
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Masonry from '@mui/lab/Masonry';
const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(0.5),
textAlign: 'center',
color: (theme.vars || theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function SSRMasonry() {
return (
<Box sx={{ width: 500, minHeight: 393 }}>
<Masonry
columns={4}
spacing={2}
defaultHeight={450}
defaultColumns={4}
defaultSpacing={1}
>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
);
}
Masonry API
데모 (Demos)
이 React 컴포넌트의 사용 예시와 세부 사항은 컴포넌트 데모 페이지를 방문해보세요:
임포트 (Import)
import Masonry from '@mui/lab/Masonry';
// or
import { Masonry } from '@mui/lab';
Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | node |
- | Yes | |
| classes | object |
- | No | Override or extend the styles applied to the component. |
| columns | Array<number | string> | number | object | string |
4 |
No | |
| component | elementType |
- | No | |
| defaultColumns | number |
- | No | |
| defaultHeight | number |
- | No | |
| defaultSpacing | number |
- | No | |
| sequential | bool |
false |
No | |
| spacing | Array<number | string> | number | object | string |
1 |
No | |
| sx | Array<func | object | bool> | func | object |
- | No | The system prop that allows defining system overrides as well as additional CSS styles. |
Note: The
refis forwarded to the root element (HTMLDivElement).
Any other props supplied will be provided to the root element (native element).
테마 기본 props (Theme default props)
MuiMasonry를 사용하면 테마로 이 컴포넌트의 기본 props를 바꿀 수 있어요.
CSS 규칙 이름 (Rule name)
| Global class | Rule name | Description |
|---|---|---|
| - | root | Styles applied to the root element. |
소스 코드 (Source code)
이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트의 구현을 살펴보는 것을 고려해보세요.