Material-UI(MUI)란?
Material-UI(MUI)는 React 기반의 UI 컴포넌트 라이브러리.
Google의 Material Design을 기반으로 만들어졌으며, 미리 만들어진 다양한 컴포넌트들을 제공한다. 버튼, 입력창, 테이블 등 기본적인 UI 요소들을 바로 사용할 수 있다. 특히 테마 시스템을 통해 색상, 폰트, 간격 등을 일괄적으로 관리할 수 있어서 일관된 디자인 시스템을 구축하기 좋다.
스타일링은 sx prop을 통한 인라인 방식, styled()를 사용한 CSS-in-JS 방식, makeStyles()를 활용한 클래스 기반 방식 등 다양한 방법을 제공한다.
레이아웃 구성을 위한 12칸 그리드 시스템이 있어 반응형 디자인을 쉽게 만들 수 있고, Box나 Stack 같은 유틸리티 컴포넌트로 레이아웃 배치도 간편하다.
**1. 레이아웃 관련
<Container>
// 내용물을 중앙 정렬하고 최대 너비 제한
// maxWidth prop으로 xs, sm, md, lg, xl 설정 가능
<Box>
// div와 비슷한 기본 레이아웃 컴포넌트
// sx prop으로 스타일링 가능
// display, flexbox 등 설정 용이
<Grid>
// 12칼럼 그리드 시스템
// 반응형 레이아웃 구성에 사용
**2. 입력 관련
<TextField>
// 텍스트 입력 필드
// variant="outlined" | "filled" | "standard"
// size="small" | "medium"
// multiline - 여러 줄 입력
// error - 에러 상태
// helperText - 도움말 텍스트
<Select>
// 드롭다운 선택
<MenuItem>
// Select의 옵션들
<Checkbox>
// 체크박스
<Radio>
// 라디오 버튼
**3. 버튼 관련
<Button>
// variant="text" | "contained" | "outlined"
// color="primary" | "secondary" | "error" | etc
// size="small" | "medium" | "large"
<IconButton>
// 아이콘 버튼
<ButtonGroup>
// 여러 버튼을 그룹으로
4. 테이블 관련
<TableContainer>
// 테이블을 감싸는 컨테이너
<Table>
// 테이블 컴포넌트
<TableHead>
// 테이블 헤더 영역
<TableBody>
// 테이블 본문 영역
<TableRow>
// 테이블 행
<TableCell>
// 테이블 셀
// align="left" | "center" | "right"
**5. 디스플레이 관련:
<Typography>
// 텍스트 표시용
// variant="h1" ~ "h6", "body1", "body2" 등
// component로 실제 HTML 태그 지정
<Paper>
// 카드같은 효과를 주는 컨테이너
// elevation으로 그림자 강도 조절
<Card>
// 카드 형태의 컨테이너
<CardContent>
// 카드 내용
6. 네비게이션 관련
<AppBar>
// 상단 네비게이션 바
<Drawer>
// 사이드 메뉴
<Tabs>
<Tab>
// 탭 네비게이션
7. 피드백 관련
<Dialog>
// 모달 다이얼로그
<Snackbar>
// 토스트 메시지
<CircularProgress>
// 로딩 스피너
<Alert>
// 경고, 성공 등 메시지
8. 스타일링
sx={{
// 인라인 스타일링
mt: 2, // margin-top
p: 3, // padding
display: 'flex',
gap: 2,
backgroundColor: 'primary.main',
'&:hover': {
backgroundColor: 'primary.dark'
}
}}
MUI의 단위
Material-UI(MUI)의 주요 spacing 단위는 기본적으로 8px을 기준으로 한다.
- spacing 단위
- spacing(1) = 8px
- spacing(2) = 16px
- spacing(3) = 24px
- spacing(4) = 32px
- spacing(5) = 40px
- 사용 예시
// margin <Box m={2}> // 16px margin 모든 방향 <Box mt={2}> // 16px margin-top <Box mb={2}> // 16px margin-bottom <Box ml={2}> // 16px margin-left <Box mr={2}> // 16px margin-right // padding도 동일 <Box p={2}> // 16px padding 모든 방향 <Box pt={2}> // 16px padding-top
- breakpoints (반응형)
- xs: 0px
- sm: 600px
- md: 900px
- lg: 1200px
- xl: 1536px
MUI에서 사용되는 축약어
// padding 관련
p: 2 // padding: 16px (모든 방향)
pt: 2 // padding-top: 16px
pb: 2 // padding-bottom: 16px
pl: 2 // padding-left: 16px
pr: 2 // padding-right: 16px
px: 2 // padding-left, padding-right: 16px
py: 2 // padding-top, padding-bottom: 16px
// margin 관련
m: 2 // margin: 16px (모든 방향)
mt: 2 // margin-top: 16px
mb: 2 // margin-bottom: 16px
ml: 2 // margin-left: 16px
mr: 2 // margin-right: 16px
mx: 2 // margin-left, margin-right: 16px
my: 2 // margin-top, margin-bottom: 16px
sx prop
Material-UI에서 제공하는 인라인 스타일링을 위한 특별한 속성
- 직접적인 CSS 작성이 가능
- 테마 값에 접근 가능
- 반응형 스타일 적용 가능
- 중첩 선택자 사용 가능
// 기본 사용
<Box sx={{ margin: 2, padding: 1 }}>
// 테마 값 사용
<Box sx={{ backgroundColor: 'primary.main' }}>
// 반응형
<Box sx={{
width: {
xs: '100%', // 모바일
sm: '50%', // 태블릿
md: '33%' // 데스크탑
}
}}>
// 중첩 선택자
<Box sx={{
'&:hover': {
backgroundColor: 'gray'
}
}}>
// 여러 속성 한번에
<Box sx={{
display: 'flex',
justifyContent: 'center',
gap: 2,
p: 3 // padding: 24px와 동일
}}>
MUI에서 제공하는 기본 색상 팔레트
**주요 색상
import {
red, pink, purple, deepPurple, indigo, blue, lightBlue,
cyan, teal, green, lightGreen, lime, yellow, amber,
orange, deepOrange, brown, grey, blueGrey
} from '@mui/material/colors';
//각 색상은 50부터 900까지의 음영을 제공합니다.
**기본 색상들:
const theme = createTheme({
palette: {
// 빨간색 계열
primary: {
main: red[500], // #f44336
},
// 분홍색 계열
primary: {
main: pink[500], // #e91e63
},
// 보라색 계열
primary: {
main: purple[500], // #9c27b0
},
// 진한 보라색
primary: {
main: deepPurple[500], // #673ab7
},
// 파란색 계열
primary: {
main: blue[500], // #2196f3
},
// 초록색 계열
primary: {
main: green[500], // #4caf50
},
// 주황색 계열
primary: {
main: orange[500], // #ff9800
}
}
});
**자주 사용되는 조합 예시:
// 파란색 + 분홍색 조합
const theme1 = createTheme({
palette: {
primary: {
main: blue[700],
},
secondary: {
main: pink[400],
},
},
});
// 보라색 + 초록색 조합
const theme2 = createTheme({
palette: {
primary: {
main: purple[500],
},
secondary: {
main: green[500],
},
},
});
// 청록색 + 주황색 조합
const theme3 = createTheme({
palette: {
primary: {
main: teal[500],
},
secondary: {
main: orange[500],
},
},
});
// 남색 + 빨간색 조합
const theme4 = createTheme({
palette: {
primary: {
main: indigo[500],
},
secondary: {
main: red[500],
},
},
});
**다크 테마 예시:
const darkTheme = createTheme({
palette: {
mode: 'dark',
primary: {
main: blue[200], // 더 밝은 파란색
},
secondary: {
main: purple[200], // 더 밝은 보라색
},
background: {
default: '#303030',
paper: '#424242',
},
},
});
**상태 색상 커스터마이징:
const theme = createTheme({
palette: {
primary: {
main: blue[700],
},
secondary: {
main: purple[500],
},
error: {
main: red[700],
},
warning: {
main: orange[700],
},
info: {
main: lightBlue[700],
},
success: {
main: green[700],
},
},
});
**각 색상의 음영 단계:
50: 가장 밝은 색상
100~300: 밝은 색상
400~600: 중간 색상
700~900: 어두운 색상
실제 예시
"use client"; // Next.js의 클라이언트 사이드 렌더링을 명시
import Link from "next/link"; // Next.js의 페이지 간 이동을 위한 컴포넌트
import { useState } from "react"; // React의 상태 관리 Hook
import {
Table, // 행과 열로 구성된 데이터 표시 테이블 컴포넌트
TableBody, // 테이블의 본문 부분을 감싸는 컴포넌트
TableCell, // 테이블의 각 데이터 셀을 나타내는 컴포넌트
TableContainer, // 테이블을 감싸는 스크롤 가능한 컨테이너
TableHead, // 테이블의 헤더 부분을 감싸는 컴포넌트
TableRow, // 테이블의 가로 행을 나타내는 컴포넌트
Paper, // 그림자와 배경색이 있는 종이 형태의 컨테이너 컴포넌트
Button, // 사용자 상호작용을 위한 클릭 가능한 버튼
TextField, // 사용자 텍스트 입력을 받는 입력 필드
Container, // 컨텐츠의 최대 너비를 제한하고 중앙 정렬하는 레이아웃 컴포넌트
Typography, // 일관된 텍스트 스타일링을 위한 텍스트 컴포넌트
Box, // flex 레이아웃을 위한 기본 레이아웃 컴포넌트
} from "@mui/material";
import { Post } from "@/app/types"; // 게시글 타입 정의 import
// 초기 더미 데이터 배열 선언
const DUMMY_POSTS: Post[] = [
{
id: 1,
title: "첫 번째 게시글",
content: "내용입니다.",
author: "작성자1",
createdAt: "2024-01-10",
views: 0,
},
];
export default function Home() {
const [posts, setPosts] = useState<Post[]>(DUMMY_POSTS); // 게시글 목록 상태 관리
const [searchTerm, setSearchTerm] = useState<string>(""); // 검색어 상태 관리
return (
<Container maxWidth="lg"> // 최대 너비 1200px로 제한된 컨테이너
<Typography variant="h4" component="h1" sx={{ my: 4 }}> // margin top/bottom 4단위(32px)의 h4 크기 제목
게시판
</Typography>
<Box sx={{ mb: 2, display: "flex", gap: 1 }}> // margin bottom 16px, flex 레이아웃, 요소 간 간격 8px
<TextField
size="small" // 작은 크기의 입력 필드
placeholder="검색어를 입력하세요" // 입력 필드의 안내 텍스트
value={searchTerm} // 검색어 상태값
onChange={(e) => setSearchTerm(e.target.value)} // 입력값 변경 시 상태 업데이트
/>
<Button
variant="contained" // 배경색이 있는 버튼 스타일
onClick={() => {}} // 클릭 이벤트 핸들러
>
검색
</Button>
</Box>
<TableContainer component={Paper}> // Paper 스타일이 적용된 테이블 컨테이너
<Table> // 기본 테이블 컴포넌트
<TableHead> // 테이블 헤더 영역
<TableRow> // 테이블 헤더 행
<TableCell>번호</TableCell> // 각 열의 제목 셀
<TableCell>제목</TableCell>
<TableCell>작성자</TableCell>
<TableCell>작성일</TableCell>
<TableCell>조회수</TableCell>
</TableRow>
</TableHead>
<TableBody> // 테이블 본문 영역
{posts.map((post) => ( // 게시글 배열을 순회하며 행 생성
<TableRow key={post.id}> // 각 게시글의 행
<TableCell>{post.id}</TableCell> // 게시글 데이터를 표시하는 셀
<TableCell>{post.title}</TableCell>
<TableCell>{post.author}</TableCell>
<TableCell>{post.createdAt}</TableCell>
<TableCell>{post.views}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<Box sx={{ mt: 2, textAlign: "right" }}> // margin top 16px, 우측 정렬
<Link href="/write"> // 글쓰기 페이지로 이동하는 링크
<Button variant="contained">글쓰기</Button> // 배경색이 있는 버튼
</Link>
</Box>
</Container>
);
}
이 코드에서 사용된 단위
- spacing 단위 (MUI의 기본 단위, 1 = 8px)
- my: 4 (margin-top과 margin-bottom: 32px)
- mb: 2 (margin-bottom: 16px)
- mt: 2 (margin-top: 16px)
- gap: 1 (간격: 8px)
- Container 크기
- maxWidth="lg" (large 크기, 1200px)
- Typography 크기
- variant="h4" (h4 크기의 제목)
- TextField 크기
- size="small" (작은 크기의 입력 필드)
- Box의 display 속성
- display: "flex" (플렉스 박스 레이아웃)
- Box의 정렬
- textAlign: "right" (우측 정렬)