[Django DRF] React DjangoDRF project (2)
Front End Templating
Build: Create a new React Project with Vite
npm create vite
프로젝트 이름을 정한 후 frame work를 정해준다. -> 타입스크립트 + SWC
SWC는 러스트로 만들어진 컴파일러다.
딱히 지금 프로젝트에 베네핏은 없다.
프로젝트 폴더로 이동 한 후 npm 을 깔아준다.
npm install
npm run dev
vite
Vite는 웹 개발을 위한 최신 자바스크립트 빌드 도구(Build Tool) 및 개발 서버 개념으로 개발된 빠르고 간단한 웹 애플리케이션 빌드 도구다. 기존의 Webpack과 비교했을 때 더 빠른 개발과 빌드 속도, 간단한 구성 방식, 개선된 개발 경험을 제공한다.
Vite의 주요 특징과 개념은 다음과 같다:
-
빠른 개발 서버: Vite는 개발 서버를 내장하고 있어서 개발 중에 빠르게 변경 사항을 확인할 수 있다. 개발 서버는 라이브 리로딩(Live Reloading)과 핫 모듈 교체(HMR, Hot Module Replacement)를 지원하여 수정 사항을 바로 반영하여 브라우저에 표시할 수 있다.
-
기존 빌드 도구 대비 빠른 빌드 시간: Vite는 웹 애플리케이션을 빌드할 때 기존의 Webpack과 같은 번들링 방식이 아닌, 자원 파일을 미리 복사하는 방식으로 빌드 시간을 크게 단축시킨다.
-
모듈 기반 개발: Vite는 ESM(ES Modules)을 기반으로 개발되어 모듈 시스템을 효율적으로 활용한다. 이로 인해 파일 간 의존성을 더욱 효과적으로 관리하고, 필요한 모듈만 불러와 사용하여 빠른 개발을 지원을 한다.
-
플러그인 생태계: Vite는 다양한 플러그인을 활용하여 기능을 확장할 수 있다. 예를 들어, TypeScript, SCSS, Less와 같은 전처리기를 지원하는 플러그인을 사용할 수 있다.
-
Vue.js 특화: Vite는 Vue.js를 위해 최적화되어 있으며, Vue.js 공식 개발 팀에서 개발하고 관리한다. Vue.js 프로젝트에서 더 빠르게 개발 및 빌드를 할 수 있도록 지원한다.
-
다른 프레임워크 지원: Vue.js 이외에도 React, Preact, Svelte 등의 다른 프레임워크를 지원한다.
Vite는 주로 작고 간단한 프로젝트 또는 프로토타입 개발에 적합하며, 기존의 Webpack과 비교하여 빠른 빌드 시간과 개발 서버를 통해 웹 개발 생산성을 향상시키는 데 도움이 된다. 주로 모던 프론트엔드 프레임워크와 함께 사용되며, Vue.js 프로젝트에서는 특히 잘 맞아떨어진다. 자세한 블로그 글
Build : Pages Directory and Home Component
const Home = () => {
return(
<>
<p>home</p>
</>
);
};
export default Home;
Build : React Routing Introduction with React Router
npm install react-router-dom
루터 버전은 여러가지가 있는데 createBrowserRouter
를 사용할 예정이다.
app.tsx
import Home from "./pages/Home"
import { createBrowserRouter, Route, RouterProvider } from "react-router-dom"
const router = createBrowserRouter(
createRoutesFromElements(
<Route>
<Route path="/" element={<Home/>} />
</Route>
)
);
const App: React.FC = () => {
return <RouterProvider router={router} />;
};
export default App;
root 경로로 갈때마다 페이지 폴더에 있는 home 컴포넌트가 반환된다.
root 폴더 안에 SRC 폴더가 있고 여기서 pages 폴더로 들어가야한다. 현재디렉토리에서 pages 폴더로 이동한 후 home 컴포넌트를 불러온다. 그래서 루트 디렉토리로 이동하면 홈 컴포넌트가 화면에 출력된다.
<RouterProvider router={router}
이 부분에서 반환되는 것은 라우터이므로 이 컴포넌트에서 자식 컴포넌트를 사용 할 때 예를 들어 home 으로 이동하면 라우터 정보도 제공된다. 따라서 라우터 안에 정의한 모든 경로들에 접근할 수 있다.
이렇게 하면 home 페이지로 이동 시 이 컴포넌트가 표시되어야한다.
index 페이지는 main과 연결되어있고 main은 app을 가져오고 이 때 app이 초기화 될 때 컴포넌트도 초기화 된다.
기본 보내기는 app 이며 해당 라우터를 반환하고 이게 홈페이지에 있다면 홈 컴포넌트를 표시한다.
React Router
React Router는 React 기반의 웹 애플리케이션에서 라우팅을 관리하는 라이브러리다. 웹 애플리케이션에서 다른 페이지로 이동하거나 URL에 따라 다른 컴포넌트를 렌더링하는 데 사용된다.
React Router는 다음과 같은 주요 기능을 제공한다:
-
라우팅: React Router는 URL과 컴포넌트를 매핑하여 웹 애플리케이션의 다른 페이지로 이동할 수 있도록 한다. 각 URL에 대해 해당하는 React 컴포넌트를 렌더링하거나 라우팅 설정에 따라 필요한 동작을 수행한다.
-
동적 라우팅: URL의 특정 부분을 동적으로 처리할 수 있다. 예를 들어, 사용자 ID가 포함된 URL을 다른 컴포넌트로 라우팅하거나, 특정 카테고리에 대한 URL로 다른 페이지를 표시하는 등의 동적 라우팅이 가능하다.
-
중첩된 라우팅: React Router는 중첩된 라우팅을 지원하여 여러 단계의 URL을 다룰 수 있다. 이로써 애플리케이션의 복잡한 레이아웃을 관리하고 각 레이아웃에 대해 적절한 컴포넌트를 렌더링할 수 있다.
-
라우팅 히스토리 관리: React Router는 브라우저의 히스토리를 관리하여 뒤로가기, 앞으로 가기와 같은 브라우저 내 탐색 동작을 원활하게 처리할 수 있도록 한다.
React Router는 react-router-dom
패키지를 사용하여 설치하고, <BrowserRouter>
와 <Route>
와 같은 컴포넌트들을 사용하여 라우팅을 구성한다. 또한, Link
컴포넌트를 사용하여 내부 링크를 생성하고, useHistory
훅을 사용하여 라우팅 히스토리를 관리할 수 있다.
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function Home() {
return <h2>Home 페이지</h2>;
}
function About() {
return <h2>About 페이지</h2>;
}
function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
</nav>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</Router>
);
}
위의 코드는 React Router를 사용하여 간단한 두 개의 페이지를 가진 웹 애플리케이션을 구현한 예시다. /
경로는 Home
컴포넌트를 렌더링하고, /about
경로는 About
컴포넌트를 렌더링한다. Link
컴포넌트를 통해 내부 링크를 생성하여 두 페이지 사이를 이동할 수 있다.
JSX
JSX는 “JavaScript XML”의 약어로, 자바스크립트의 확장 문법이다. 리액트(React)에서 컴포넌트를 작성하고 렌더링하는데 사용되는 문법으로, XML과 비슷한 형태로 자바스크립트 코드와 HTML 비슷한 구조를 결합하여 UI를 표현하는 데 사용된다.
JSX를 사용하면 UI 컴포넌트를 간결하고 가독성 있게 작성할 수 있으며, 리액트에서 컴포넌트를 생성하는 데 편리하고 직관적인 방법을 제공한다. JSX는 브라우저에서 바로 실행되지 않으며, 일반 자바스크립트로 변환되어야 한다.
import React from 'react';
// JSX 사용한 컴포넌트
function MyComponent() {
return (
<div>
<h1>Hello, World!</h1>
<p>This is a JSX component.</p>
</div>
);
}
// 위의 JSX 컴포넌트는 아래와 같은 일반 자바스크립트 코드로 변환됩니다.
function MyComponent() {
return React.createElement('div', null,
React.createElement('h1', null, 'Hello, World!'),
React.createElement('p', null, 'This is a JSX component.')
);
}
위의 예시에서 JSX 코드로 작성된 MyComponent
컴포넌트는 리액트의 createElement
메서드를 사용하여 일반 자바스크립트 코드로 변환된다. JSX를 사용하면 컴포넌트를 선언하는데 좀 더 직관적이고 HTML과 비슷한 문법을 사용하여 작성할 수 있다
Build : Install MUI and integrate it into the existing project
/material-ui
npm install @mui/material @emotion/react @emotion/styled
/icons
npm install @mui/icons-material
import { Box, CssBaseline } from "@mui/material";
const Home = () => {
return(
<Box sx={{ display: "flex" }}>
<CssBaseline />
home
</Box>
);
};
export default Home;
layout box 사용 예
flex box 사용 예
이제 컴포넌트랑 템플릿을 관리할 폴더를 나눠준다.
Build : Creating the Primary App Bar (Part-1) Framework
App.tsx
import { ThemeProvider } from "@emotion/react";
import Home from "./pages/Home"
import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from "react-router-dom"
import { createMuiTheme } from "./theme/theme";
const router = createBrowserRouter(
createRoutesFromElements(
<Route>
<Route path="/" element={<Home/>} />
</Route>
)
);
const App = () => {
const theme = createMuiTheme();
return (
<ThemeProvider theme={theme}>
<RouterProvider router={router} />
</ThemeProvider>
);
};
export default App;
라우터 설정
react-router-dom
라이브러리를 사용하여 라우터를 설정
createBrowserRouter
함수를 이용하여 라우터를 생성하고, createRoutesFromElements
함수를 사용하여 라우트들을 생성
위 코드에서는 루트 라우트(<Route>
)를 설정하고, 경로(path)가 “/”인 경우에는 <Home />
컴포넌트를 렌더링하도록 설정
테마설정
react-router-dom
라이브러리를 사용하여 라우터를 설정
createBrowserRouter
함수를 이용하여 라우터를 생성하고, createRoutesFromElements
함수를 사용하여 라우트들을 생성
위 코드에서는 루트 라우트(<Route>
)를 설정하고, 경로(path)가 “/”인 경우에는 <Home />
컴포넌트를 렌더링하도록 설정
앱 컴포넌트
위 코드에서는 @emotion/react
라이브러리의 ThemeProvider
를 사용하여 전역적으로 테마를 적용 ThemeProvider
로 생성된 테마를 자식 컴포넌트에 전달하여 하위 컴포넌트들에서 해당 테마를 사용할 수 있도록 한다.
그리고 RouterProvider
를 사용하여 앞서 설정한 라우터를 앱에 적용하고 있다.
요약하면, 이 코드는 React 애플리케이션에서 라우팅을 설정하고, MUI(Material-UI) 테마를 생성하여 앱 전체에 적용하는 부분이다. react-router-dom
과 @emotion/react
라이브러리를 사용하여 라우팅과 테마를 적용하고 있다.
Home.tsx
import { Box, CssBaseline } from "@mui/material";
import PrimaryAppBar from "./templates/PrimaryAppBar";
const Home = () => {
return(
<Box sx={{ display: "flex" }}>
<CssBaseline />
<PrimaryAppBar />
</Box>
);
};
export default Home;
이 코드는 @mui/material
라이브러리를 사용하여 React 컴포넌트를 구성한다.
Box, CaseBaseline
Box
는 MUI(Material-UI) 라이브러리에서 제공하는 컨테이너 컴포넌트다. CssBaseline
은 브라우저의 기본 CSS 스타일을 리셋하여 표준화된 스타일로 애플리케이션을 시작하는 데 사용되는 컴포넌트다
PrimaryAppBar 컴포넌트
PrimaryAppBar
는 앱의 상단에 고정된 App Bar 또는 Navigation Bar와 같은 역할을 하는 컴포넌트다.
Home 컴포넌트
-
<Box sx={{ display: "flex" }}>
:Box
컴포넌트를 사용하여 유연한(Flexible) 레이아웃을 설정하고 있다.display: "flex"
를 사용하여 자식 요소를 수평으로 나란히 배치하도록 설정하고 있다. -
<CssBaseline />
:CssBaseline
컴포넌트를 사용하여 브라우저의 기본 CSS 스타일을 리셋하고 애플리케이션을 일관된 스타일로 시작할 수 있도록 설정하고 있다. 이를 통해 브라우저 간에 일관성 있는 스타일을 유지할 수 있다. -
<PrimaryAppBar />
: 앞서 가져온PrimaryAppBar
컴포넌트를 렌더링하고 있다. 이를 통해 애플리케이션 상단에 App Bar 또는 Navigation Bar와 같은 컴포넌트를 표시할 수 있다.
이렇게 Home
컴포넌트는 Box
, CssBaseline
, 그리고 PrimaryAppBar
컴포넌트를 활용하여 레이아웃과 스타일을 구성하는 역할을 수행한다. 이 코드가 Home
페이지의 레이아웃을 구성하는 일부분이며, 전체 앱의 레이아웃을 구성하는 데에 다른 컴포넌트들과 함께 사용될 수 있다.
PrimaryAppBar.tsx
import { AppBar, Toolbar, Link, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
const PrimaryAppBar = () => {
const theme = useTheme();
return (
<AppBar sx={{
backgroundColor: theme.palette.background.default,
borderBottom: `1px solid ${theme.palette.divider}`,
}}
>
<Toolbar variant="dense" sx={{
height: theme.primaryAppBar.height,
minHeight: theme.primaryAppBar.height
}}
>
<Link href="/" underline="none" color="inherit">
<Typography
variant="h6"
noWrap
component="div"
sx = {{
display:{
fontWeight: 700,
letterSpacing: "-0.5px"
}
}}
>
DJCHAT
</Typography>
</Link>
</Toolbar>
</AppBar>
);
};
export default PrimaryAppBar;
이 코드는 @mui/material
라이브러리를 사용하여 MUI(Material-UI) 앱에서 상단에 고정된 AppBar 또는 Navigation Bar를 만드는 컴포넌트다.
가져온 라이브러리
이 코드에서는 @mui/material
라이브러리에서 AppBar
, Toolbar
, Link
, 그리고 Typography
를 가져오고 있다. 또한 @mui/material/styles
에서 useTheme
을 가져와서 테마를 사용하고 있다.
PrimaryAppBar 컴포넌트
위 코드는 PrimaryAppBar
컴포넌트를 정의하고 있다. 이 컴포넌트는 MUI의 AppBar
컴포넌트를 활용하여 상단에 고정된 Navigation Bar를 만들고 있다.
-
<AppBar>
: MUI의AppBar
컴포넌트를 사용하여 상단 Navigation Bar를 생성한다.sx
속성을 사용하여 스타일을 설정하고 있다. 여기서backgroundColor
는 테마의 기본 배경색,borderBottom
은 테마의 구분선(divider) 색상을 사용하여 AppBar의 스타일을 설정하고 있다. -
<Toolbar>
:AppBar
안에Toolbar
컴포넌트를 렌더링하여 Navigation Bar의 내용을 구성한다.variant="dense"
속성을 이용하여 더 밀집된(dense) 모드로 설정하고 있다. 이는 Navigation Bar의 높이를 조정하는 역할을 한다.sx
속성을 사용하여 높이와 최소 높이를 테마에서 정의한theme.primaryAppBar.height
값으로 설정하고 있다. -
<Link>
와<Typography>
:Typography
을Link
컴포넌트 안에 넣어서 Navigation Bar에 텍스트를 표시하고, 클릭 시 링크가 작동하도록 설정하고 있다.Typography
의variant
속성으로 텍스트의 스타일을 설정하고 있으며,sx
속성을 사용하여 테마에서 정의한 스타일을 적용하고 있다.
이렇게 PrimaryAppBar
컴포넌트를 사용하면, MUI 앱의 상단에 고정된 Navigation Bar를 생성하고 DJCHAT라는 텍스트를 표시할 수 있다. Navigation Bar의 스타일은 테마와 관련하여 동적으로 적용되며, 테마에 따라 쉽게 변경할 수 있다.
theme.tsx
import { createTheme } from "@mui/material";
declare module "@mui/material/styles" {
interface Theme {
primaryAppBar: {
height: number,
};
}
interface ThemeOptions {
primaryAppBar: {
height: number;
};
}
}
export const createMuiTheme = () => {
let theme = createTheme({
primaryAppBar: {
height: 50,
},
components:{
MuiAppBar: {
defaultProps: {
color: "default",
elevation: 0,
}
}
}
});
return theme;
};
export default createMuiTheme;
createTheme
해당 함수는 사용자 정의 테마를 생성하는 데 사용한다.
테마설정
@mui/material/styles
모듈에 선언된 코드는 MUI의 기본 테마에 사용자 정의 속성 primaryAppBar
를 추가하고 있다. 이렇게 선언함으로써 테마에서 primaryAppBar
를 사용할 수 있게 된다. primaryAppBar
는 height
라는 숫자 속성을 갖는다.
createMuiTheme
위 코드에서는 createMuiTheme
함수를 정의하고 있다. 이 함수는 createTheme
함수를 이용하여 MUI 테마를 생성하고 반환한다.
-
createTheme({ ... })
:createTheme
함수에 전달된 객체는 테마를 구성하는 속성들을 정의한다. 여기서는primaryAppBar
속성을 추가하고 있다.primaryAppBar
의height
속성을 50으로 설정하여, 테마에서theme.primaryAppBar.height
로 해당 값을 사용할 수 있다. -
components: { ... }
: 이 부분은 MUI의 컴포넌트에 대한 기본 속성을 설정하는 옵션이다.MuiAppBar
은 MUI의 AppBar 컴포넌트를 가리킨다.MuiAppBar
컴포넌트에defaultProps
를 설정하여 기본적으로 AppBar의color
를 “default”로,elevation
를 0으로 설정하고 있다. 이를 통해 AppBar의 기본 스타일을 커스터마이징할 수 있다.
함수 반환:
createMuiTheme
함수는 createTheme
함수로 생성한 테마를 반환한다. 이렇게 반환된 테마는 MUI 앱의 ThemeProvider
를 사용하여 전역적으로 적용될 수 있으며, primaryAppBar
와 같이 사용자 정의 속성을 통해 테마를 세밀하게 조정할 수 있다.
이렇게 정의된 createMuiTheme
함수를 사용하면 MUI 앱에서 커스텀 테마를 적용할 수 있으며, 필요한 경우 다양한 속성들을 추가 또는 변경하여 테마를 맞춤화할 수 있다.
Build : Integrating Google Fonts into an existing React/MUI project
main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import "./theme/main.css"
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
앱 랜더링
-
ReactDOM.createRoot
:createRoot
메서드는 React 18 이상에서 사용할 수 있는 새로운 렌더링 메서드다. 이를 사용하면 컴포넌트 렌더링에 대한 초기화를 변경할 수 있으며, 리액트의 동시성(Concurrent Mode)을 활용하는 데 사용된다. -
document.getElementById('root')!
: 이 부분은 id가 ‘root’인 DOM 요소를 찾아서 렌더링할 대상으로 지정하고 있다. -
<React.StrictMode>
:<React.StrictMode>
는 React의 엄격 모드를 활성화하는 컴포넌트다. 엄격 모드를 사용하면 앱에서 잠재적인 문제를 감지하고 경고를 표시하는 데 도움이 됩니다. 개발 모드에서 사용하며, 프로덕션 환경에서는 영향을 주지 않는다. -
<App />
:App
컴포넌트를 렌더링하고 있다.App
컴포넌트는 앱의 루트 컴포넌트로 사용되는 것으로 예상된다.
위 코드는 ReactDOM.createRoot
메서드를 사용하여 App
컴포넌트를 앱의 루트 요소에 렌더링한다. createRoot
메서드는 React 18 이상에서 동작하며, React 17 이하 버전에서는 기존의 ReactDOM.render
메서드를 사용하여 렌더링하는 방식을 사용해야 한다. 또한, React.StrictMode
를 사용하여 개발 모드에서 잠재적인 문제를 확인할 수 있도록 설정하고 있다.
import "./theme/main.css"
main.tsx 에서 불러줘야 제대로 폰트가 적용된다.
theme.tsx
export const createMuiTheme = () => {
let theme = createTheme({
typography: {
fontFamily: [
'IBM Plex Mono', 'monospace',
'IBM Plex Sans KR', 'sans-serif'
].join(","),
},
primaryAppBar: {
height: 50,
},
위와 같은 방식으로 폰트를 설정한다.
main.css
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans+KR:wght@100;200;300;400;500;600;700&display=swap');
Build : Responsive Fonts
노트북, 스마트폰 등 다양한 환경에 맞춰 폰트가 알아서 반응형으로 되게 조정한다.
import { createTheme, responsiveFontSizes } from "@mui/material";
.
.
.
export const createMuiTheme = () => {
let theme = createTheme({
typography: {
fontFamily: [
'IBM Plex Mono', 'monospace',
'IBM Plex Sans KR', 'sans-serif'
].join(","),
},
primaryAppBar: {
height: 50,
},
components:{
MuiAppBar: {
defaultProps: {
color: "default",
elevation: 0,
}
}
}
});
theme = responsiveFontSizes(theme);
return theme;
};
Build : Creating the Primary App Bar (Part-2) Responsive
PrimaryAppBar.tsx 전체코드
import { AppBar, Toolbar, Link, Typography,Box, IconButton, Drawer, useMediaQuery, } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import MenuIcon from "@mui/icons-material/Menu";
import React, { useEffect, useState } from "react";
const PrimaryAppBar = () => {
const [sideMenu, setSideMenu] = useState(false);
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.up("sm"));
useEffect(() => {
if (isSmallScreen && sideMenu){
setSideMenu(false);
}
}, [isSmallScreen]);
const toggleDrawer =
(open: boolean) => (event: React.KeyboardEvent | React.MouseEvent)=> {
if(
event.type === "keydown" &&
((event as React.KeyboardEvent).key === "Tab" ||
(event as React.KeyboardEvent).key === "Shift")
) {
return;
}
setSideMenu(open);
};
return (
<AppBar sx={{
zIndex: (theme) => theme.zIndex.drawer + 2,
backgroundColor: theme.palette.background.default,
borderBottom: `1px solid ${theme.palette.divider}`,
}}
>
<Toolbar variant="dense" sx={{
height: theme.primaryAppBar.height,
minHeight: theme.primaryAppBar.height
}}
>
<Box sx={{ display: { xs: "block", sm: "none" } }}>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={toggleDrawer(true)}
sx={{mr:2}}
>
<MenuIcon />
</IconButton>
</Box>
<Drawer anchor="left" open={sideMenu} onClose={toggleDrawer(false)}>
{[...Array(100)].map((_, i)=>(
<Typography key={i} paragraph>
{i +1}
</Typography>
))}
</Drawer>
<Link href="/" underline="none" color="inherit">
<Typography
variant="h6"
noWrap
component="div"
sx = {{
display:{
fontWeight: 700,
letterSpacing: "-0.5px"
}
}}
>
DJCHAT
</Typography>
</Link>
</Toolbar>
</AppBar>
);
};
export default PrimaryAppBar;
Drawer 사이드 좌측 메뉴 부분
import { AppBar, Toolbar, Link, Typography } from "@mui/material";
import { AppBar, Toolbar, Link, Typography,Box, IconButton, Drawer, useMediaQuery, } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import MenuIcon from "@mui/icons-material/Menu";
import React, { useEffect, useState } from "react";
const PrimaryAppBar = () => {
const [sideMenu, setSideMenu] = useState(false);
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.up("sm"));
useEffect(() => {
if (isSmallScreen && sideMenu){
setSideMenu(false);
}
}, [isSmallScreen]);
const toggleDrawer =
(open: boolean) => (event: React.KeyboardEvent | React.MouseEvent)=> {
if(
event.type === "keydown" &&
((event as React.KeyboardEvent).key === "Tab" ||
(event as React.KeyboardEvent).key === "Shift")
) {
return;
}
setSideMenu(open);
};
return (
<AppBar sx={{
zIndex: (theme) => theme.zIndex.drawer + 2,
backgroundColor: theme.palette.background.default,
borderBottom: `1px solid ${theme.palette.divider}`,
}}
@ -15,6 +41,26 @@ const PrimaryAppBar = () => {
minHeight: theme.primaryAppBar.height
}}
>
<Box sx={{ display: { xs: "block", sm: "none" } }}>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={toggleDrawer(true)}
sx={{mr:2}}
>
<MenuIcon />
</IconButton>
</Box>
<Drawer anchor="left" open={sideMenu} onClose={toggleDrawer(false)}>
{[...Array(100)].map((_, i)=>(
<Typography key={i} paragraph>
{i +1}
</Typography>
))}
</Drawer>
<Link href="/" underline="none" color="inherit">
<Typography
variant="h6"
-
useState
를 사용하여sideMenu
상태를 관리한다.sideMenu
는 Drawer(좌측 사이드 메뉴)의 열림/닫힘 여부를 나타낸다. 초기값은false
로 설정 -
useTheme
를 사용하여 현재 MUI 테마를 가져온다. -
useMediaQuery
를 사용하여isSmallScreen
상태를 관리.isSmallScreen
은 현재 화면 크기가 작은 스크린(스마트폰 등)인지 여부를 나타낸다.theme.breakpoints.up("sm")
는 스크린의 너비가 “sm” 크기(600px 이상) 이상일 때를 의미. -
useEffect
를 사용하여isSmallScreen
과sideMenu
상태를 감지한다. 만약isSmallScreen
이 참이고sideMenu
가 열려있을 때,setSideMenu(false)
를 호출하여 화면 크기가 커질 때 Drawer를 닫도록 설정. -
toggleDrawer
함수는 Drawer를 열고 닫는 역할을 한다.open
매개변수를 받아서setSideMenu
를 호출하여sideMenu
상태를 변경한다. Drawer를 열 때는toggleDrawer(true)
로, 닫을 때는toggleDrawer(false)
로 사용 -
AppBar
컴포넌트를 생성하고,sx
속성을 사용하여 AppBar의 스타일을 설정. -
Toolbar
컴포넌트를 생성하고,sx
속성을 사용하여 Toolbar의 스타일을 설정.height
와minHeight
를theme.primaryAppBar.height
로 설정하여 앞서 정의한 테마의primaryAppBar.height
값으로 Toolbar의 높이를 조정. -
IconButton
컴포넌트를 생성하여 메뉴 버튼을 만들고,MenuIcon
아이콘을 표시toggleDrawer(true)
함수를 클릭 이벤트 핸들러로 설정하여 버튼을 클릭하면 Drawer가 열리도록 한다. -
Drawer
컴포넌트를 생성하여 좌측 사이드 메뉴를 만든다.anchor
속성을 “left”로 설정하여 좌측에 나타나도록 한다.open
속성을sideMenu
값으로 설정하여sideMenu
상태에 따라 열림/닫힘을 관리한다.onClose
속성을toggleDrawer(false)
로 설정하여 사이드 메뉴의 오버레이를 클릭하거나 키보드 이벤트에 의해 닫히도록 한다. 그리고Drawer
내부에 100개의Typography
컴포넌트를 렌더링하여 예시로 사용한다..
이렇게 코드가 구성되어 있으며, PrimaryAppBar
컴포넌트를 사용하면 AppBar에 메뉴 버튼이 포함된 상단 바와 좌측에 Drawer(사이드 메뉴)가 구현된다. useMediaQuery
와 useEffect
를 이용하여 화면 크기에 따라 Drawer를 적절히 제어하고 있으며, IconButton
을 클릭하면 Drawer가 열리고 닫히도록 동작한다.
Build : Primary Draw (Part-1) Framework
DrawToggle.tsx
import ChevronLeft from "@mui/icons-material/ChevronLeft";
import { Box, IconButton } from "@mui/material";
const DrawerToggle = () => {
return(
<Box
sx={{
height:"50px",
display: "flex",
alignItems: "content",
justifyContent: "center",
}}
>
<IconButton>
<ChevronLeft/>
</IconButton>
</Box>
)
}
export default DrawerToggle;
위 코드는 Material-UI의 ChevronLeft
아이콘과 IconButton
을 사용하여 작은 버튼을 만드는 DrawerToggle
컴포넌트를 생성하는 코드다. ChevronLeft
아이콘은 화살표 모양을 나타내는 아이콘으로, 주로 이전으로 돌아가는 기능을 나타낼 때 사용한다.
컴포넌트 설명:
-
import ChevronLeft from "@mui/icons-material/ChevronLeft";
: Material-UI에서 제공하는ChevronLeft
아이콘을 가져온다. 이 아이콘은 Material-UI의 아이콘 컴포넌트 중 하나로, Material Icons 라이브러리에 속해있다. -
import { Box, IconButton } from "@mui/material";
: Material-UI에서 제공하는Box
와IconButton
컴포넌트를 가져온다.Box
컴포넌트는 레이아웃을 구성하는데 사용되며,IconButton
컴포넌트는 아이콘을 포함한 버튼을 만드는데 사용한다. -
const DrawerToggle = () => { ... }
:DrawerToggle
함수형 컴포넌트를 정의한다. -
<Box sx={{ ... }}>
:Box
컴포넌트를 사용하여 레이아웃을 구성한다.sx
속성을 사용하여 스타일을 설정한다. -
height: "50px"
:Box
컴포넌트의 높이를 50px로 설정한다. -
display: "flex"
:Box
컴포넌트 내부의 요소들을 Flexbox로 배치한다. -
alignItems: "center"
: Flexbox를 사용하여 요소들을 수직 정렬합니다. 여기서는 센터로 정렬한다. -
justifyContent: "center"
: Flexbox를 사용하여 요소들을 수평 정렬한다. 여기서는 센터로 정렬한다. -
<IconButton>
:IconButton
컴포넌트를 생성합니다. 이 컴포넌트는 아이콘을 포함한 버튼 역할을 한다. -
<ChevronLeft/>
:ChevronLeft
아이콘 컴포넌트를IconButton
내부에 배치한다. 이 아이콘은 화살표를 나타내는 아이콘으로, 이전으로 돌아가는 기능을 나타내는데 사용한다. -
export default DrawerToggle;
:DrawerToggle
컴포넌트를 외부에서 사용할 수 있도록 내보난다.
위 코드를 사용하면 DrawerToggle
컴포넌트가 렌더링될 때, 화살표 모양의 아이콘이 포함된 작은 버튼이 생성된다. 이 버튼은 주로 좌측 사이드 메뉴를 열거나 닫는 토글 버튼으로 사용될 수 있다. 스타일은 sx
속성을 통해 설정되어 있으므로 필요에 따라 더 많은 스타일을 추가하거나 수정할 수 있다.
Home.tsx
import { Box, CssBaseline } from "@mui/material";
import PrimaryAppBar from "./templates/PrimaryAppBar";
import PrimaryDraw from "./templates/PrimaryDraw";
const Home = () => {
return(
<Box sx={{ display: "flex" }}>
<CssBaseline />
<PrimaryAppBar />
<PrimaryDraw></PrimaryDraw>
</Box>
);
};
export default Home;
위 코드는 React에서 Material-UI 컴포넌트를 사용하여 홈 페이지를 구성하는 Home
컴포넌트다. 이 컴포넌트는 상단에 고정된 AppBar와 좌측 사이드 메뉴를 포함한 레이아웃을 만드는 역할을 한다.
컴포넌트 설명:
-
import { Box, CssBaseline } from "@mui/material";
: Material-UI에서 제공하는Box
와CssBaseline
컴포넌트를 가져온다.Box
컴포넌트는 레이아웃을 구성하는데 사용되고,CssBaseline
컴포넌트는 CSS 리셋을 적용하여 초기 스타일링을 설정하는데 사용한다. -
import PrimaryAppBar from "./templates/PrimaryAppBar";
:PrimaryAppBar
컴포넌트를 가져온다. 이 컴포넌트는 상단에 고정된 AppBar를 생성하는데 사용된다. -
import PrimaryDraw from "./templates/PrimaryDraw";
:PrimaryDraw
컴포넌트를 가져온다. 이 컴포넌트는 좌측 사이드 메뉴를 생성하는데 사용한다. -
const Home = () => { ... }
:Home
함수형 컴포넌트를 정의한다. -
<Box sx={{ display: "flex" }}>
:Box
컴포넌트를 사용하여 레이아웃을 구성한다.sx
속성을 사용하여 스타일을 설정한다.display: "flex"
스타일을 적용하여 자식 요소들을 Flexbox로 배치한다. -
<CssBaseline />
:CssBaseline
컴포넌트를 렌더링하여 CSS 리셋을 적용한다. 이는 브라우저의 기본 스타일을 초기화하여 일관된 스타일링을 적용하는데 사용한다. -
<PrimaryAppBar />
:PrimaryAppBar
컴포넌트를 렌더링하여 상단에 고정된 AppBar를 생성한다. -
<PrimaryDraw></PrimaryDraw>
:PrimaryDraw
컴포넌트를 렌더링하여 좌측 사이드 메뉴를 생성한다.</PrimaryDraw>
는 해당 컴포넌트의 마감 태그로, 빈 요소로 사용되기 때문에</PrimaryDraw>
를 사용해도 동일한 결과를 생성한다. -
export default Home;
:Home
컴포넌트를 외부에서 사용할 수 있도록 내보낸다.
위 코드를 사용하면 Home
컴포넌트가 렌더링될 때, 상단에 고정된 AppBar와 좌측 사이드 메뉴를 포함한 레이아웃이 생성된다. PrimaryAppBar
컴포넌트는 상단에 고정된 AppBar를 구현하고, PrimaryDraw
컴포넌트는 좌측 사이드 메뉴를 구현하는 역할을 한다. Box
컴포넌트를 사용하여 두 개의 컴포넌트를 가로로 배치하고, CssBaseline
컴포넌트를 사용하여 초기 스타일링을 설정한다.
PrimaryDraw.tsx
import { Drawer, Box, useMediaQuery, Typography, useTheme } from "@mui/material";
import { useState, useEffect } from "react";
import DrawerToggle from "../../components/PrimaryDraw/DrawToggle";
const PrimaryDraw = () => {
const theme = useTheme();
const below600 = useMediaQuery("(max-width:599px)");
const [open, setOpen] = useState(!below600);
console.log(below600)
useEffect(() => {
setOpen(!below600);
}, [below600]);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return(
<Drawer
open={open}
variant={below600 ? "temporary" : "permanent"}
PaperProps={{
sx: {
mt: `${theme.primaryAppBar.height}px`,
height: `calc(100wh - ${theme.primaryAppBar.height}px )`,
width: theme.primaryDraw.width,
},
}}
>
<Box>
<Box
sx={{
position: "absolute",
top: 0,
right: 0,
p: 0,
width: open ? "auto" : "100%",
}}
>
<DrawerToggle/>
{[...Array(50)].map((_, i)=>(
<Typography key={i} paragraph>
{i +1}
</Typography>
))}
</Box>
</Box>
</Drawer>
);
};
export default PrimaryDraw;
위 코드는 Material-UI의 Drawer
컴포넌트를 사용하여 좌측 사이드 메뉴를 생성하는 PrimaryDraw
컴포넌트다. 또한, PrimaryDraw
컴포넌트 내에서 DrawerToggle
컴포넌트를 불러와 토글 버튼을 생성한다.
컴포넌트 설명:
-
import { Drawer, Box, useMediaQuery, Typography, useTheme } from "@mui/material";
: Material-UI에서 제공하는Drawer
,Box
,useMediaQuery
,Typography
,useTheme
등의 컴포넌트와 훅을 가져온다. -
import { useState, useEffect } from "react";
: React에서 제공하는useState
와useEffect
훅을 가져온다. -
const PrimaryDraw = () => { ... }
:PrimaryDraw
함수형 컴포넌트를 정의한다. -
const theme = useTheme();
:useTheme
훅을 사용하여 현재 사용 중인 테마를 가져온다. -
const below600 = useMediaQuery("(max-width:599px)");
:useMediaQuery
훅을 사용하여 화면 너비가 600px 미만인지를 검사하여below600
변수에 저장한다. -
const [open, setOpen] = useState(!below600);
:useState
훅을 사용하여open
상태 변수를 생성합니다. 초기값은below600
값의 반대로 설정한다. 즉, 화면 너비가 600px 미만이면open
이false
, 600px 이상이면open
이true
가 된다. -
console.log(below600)
:below600
값을 콘솔에 출력하여 확인한다. -
useEffect(() => { ... }, [below600]);
:useEffect
훅을 사용하여below600
값이 변경될 때마다open
상태를 업데이트한다. 즉, 화면 너비가 600px 미만이면open
상태를false
, 600px 이상이면true
로 설정한다. -
const handleDrawerOpen = () => { ... };
: 좌측 사이드 메뉴를 열기 위한 함수를 정의한다. -
const handleDrawerClose = () => { ... };
: 좌측 사이드 메뉴를 닫기 위한 함수를 정의한다. -
<Drawer open={open} variant={below600 ? "temporary" : "permanent"} PaperProps={{ ... }}> ... </Drawer>
:Drawer
컴포넌트를 생성한다.open
속성으로open
상태 값을 설정하고,variant
속성으로 화면 너비에 따라 임시 메뉴 또는 영구 메뉴로 설정한다.PaperProps
속성을 사용하여 메뉴의 스타일을 설정한다. -
<Box> ... </Box>
:Box
컴포넌트를 사용하여 메뉴 내용을 감싸준다. -
<Box sx={{ ... }}>
:Box
컴포넌트에 스타일을 적용하여 메뉴의 위치와 크기를 설정한다. -
<Box sx={{ position: "absolute", top: 0, right: 0, p: 0, width: open ? "auto" : "100%" }}> ... </Box>
: 토글 버튼이 들어갈 상단 위치에Box
컴포넌트를 생성하여 스타일을 적용한다.position: "absolute"
로 설정하여 위치를 지정하고,top: 0
,right: 0
으로 메뉴 오른쪽 상단에 위치하도록 설정한다.width: open ? "auto" : "100%"
으로open
상태에 따라 메뉴가 열렸을 때는 자동으로 크기를 설정하고, 닫혔을 때는 100%의 너비를 갖도록 설정한다. -
<DrawerToggle/>
:DrawerToggle
컴포넌트를 렌더링하여 토글 버튼을 생성한다. {[...Array(50)].map((_, i)=>(...))}
: 50개의<Typography>
컴포넌트를 반복하여 렌더링한다. 이 부분은 테스트를 위해 1부터 50까지의 숫자를 화면에 출력하는 코드다.export default PrimaryDraw;
:PrimaryDraw
컴포넌트를 외부에서 사용할 수 있도록 내보낸다.
위 코드를 사용하면 PrimaryDraw
컴포넌트가 렌더링될 때, 좌측 사이드 메뉴가 생성된다. 메뉴가 열리고 닫히는 토글 버튼을 포함한 메뉴 스타일은 PaperProps
를 통해 설정되고, 메뉴 내용은 상단 위치에 토글 버튼과 숫자를 포함하여 생성된다.
theme.tsx
import { createTheme, responsiveFontSizes } from "@mui/material";
declare module "@mui/material/styles" {
interface Theme {
primaryAppBar: {
height: number,
};
primaryDraw: {
width: number,
closed: number;
};
}
interface ThemeOptions {
primaryAppBar: {
height: number;
};
primaryDraw: {
width: number,
closed: number;
};
}
}
export const createMuiTheme = () => {
let theme = createTheme({
typography: {
fontFamily: [
'IBM Plex Mono', 'monospace',
'IBM Plex Sans KR', 'sans-serif'
].join(","),
},
primaryAppBar: {
height: 50,
},
primaryDraw: {
width: 240,
closed: 70,
},
components: {
MuiAppBar: {
defaultProps: {
color: "default",
elevation: 0,
}
}
}
});
theme = responsiveFontSizes(theme);
return theme;
};
export default createMuiTheme;
위 코드는 Material-UI의 createTheme
및 responsiveFontSizes
를 사용하여 사용자 정의 테마를 생성하는 코드다. 또한, 테마에 새로운 속성 primaryAppBar
와 primaryDraw
를 추가하여 AppBar와 Drawer의 높이 및 너비 값을 설정한다.
코드 설명:
-
import { createTheme, responsiveFontSizes } from "@mui/material";
: Material-UI에서createTheme
과responsiveFontSizes
를 가져온다.createTheme
은 사용자 정의 테마를 생성하는 함수이고,responsiveFontSizes
는 반응형 폰트 크기를 설정하는 함수다. -
declare module "@mui/material/styles" { ... }
: TypeScript에서 사용자 정의 테마 속성을 선언하는 부분이다.primaryAppBar
와primaryDraw
속성을 Theme 및 ThemeOptions에 추가하여 테마에서 사용할 수 있도록 한다. -
export const createMuiTheme = () => { ... }
:createMuiTheme
함수를 정의한다. -
let theme = createTheme({ ... });
:createTheme
함수를 사용하여 기본 테마를 생성한다. 기본 테마에는typography
,primaryAppBar
,primaryDraw
,components
등의 속성이 설정되어 있다. -
typography
: 테마의 텍스트 스타일을 설정하는 속성이다.fontFamily
를 지정하여 원하는 폰트를 사용할 수 있다. 여기서는IBM Plex Mono
와IBM Plex Sans KR
폰트를 사용한다. -
primaryAppBar
: AppBar의 높이를 설정하는 속성이다.height
값으로 50을 설정하여 높이를 50px로 지정한다. -
primaryDraw
: Drawer의 너비와 닫힌 상태에서의 너비를 설정하는 속성이다.width
값으로 240을 설정하여 너비를 240px로 지정하고,closed
값으로 70을 설정하여 닫힌 상태에서의 너비를 70px로 지정한다. -
components
: Material-UI의 컴포넌트에 대한 기본 속성을 설정하는 부분이다. 여기서는MuiAppBar
의 기본 속성을color: "default"
와elevation: 0
으로 설정한다. 이렇게 하면 AppBar가 기본 색상으로 보여지고, 그림자 효과가 적용되지 않는다. -
theme = responsiveFontSizes(theme);
: 생성한 테마에 반응형 폰트 크기를 적용한다. 이렇게 하면 테마의 폰트 크기가 화면 크기에 따라 자동으로 조정된다. -
return theme;
: 생성한 테마를 반환한다. -
export default createMuiTheme;
:createMuiTheme
함수를 외부에서 사용할 수 있도록 내보낸다.
이렇게 생성한 테마는 ThemeProvider
를 사용하여 React 앱에 적용할 수 있다. 테마를 적용하면 AppBar와 Drawer 등의 컴포넌트가 지정된 스타일을 따라서 렌더링된다.
Build : Primary Draw (Part-2) Functionality
DrawToggle.tsx
import { ChevronRight, ChevronLeft } from "@mui/icons-material";
import { Box, IconButton } from "@mui/material";
import React from "react";
type Props = {
open: boolean;
handleDrawerOpen: () => void;
handleDrawerClose: () => void;
};
const DrawerToggle: React.FC<Props> = ({
open,
handleDrawerClose,
handleDrawerOpen,
}) => {
return(
<Box
sx={{
height:"50px",
display: "flex",
alignItems: "content",
justifyContent: "center",
}}
>
<IconButton onClick={open ? handleDrawerClose : handleDrawerOpen}>
{open ? <ChevronLeft/> : <ChevronRight/>}
</IconButton>
</Box>
)
}
export default DrawerToggle;
위 코드는 MUI(마테리얼-UI) 아이콘과 버튼을 사용하여 드로어(Drawer)를 토글하는 DrawerToggle 컴포넌트다. DrawerToggle 컴포넌트는 open
, handleDrawerClose
, handleDrawerOpen
프로퍼티를 받아와서 드로어의 상태를 토글하는 기능을 구현한다.
-
open
: 드로어의 상태를 나타내는 boolean 값이다.true
이면 드로어가 열려 있는 상태를 의미하고,false
이면 드로어가 닫혀 있는 상태를 의미한다. -
handleDrawerClose
: 드로어를 닫기 위한 콜백 함수 -
handleDrawerOpen
: 드로어를 열기 위한 콜백 함수
요약:
-
import { ChevronRight, ChevronLeft } from "@mui/icons-material";
: MUI의ChevronRight
와ChevronLeft
아이콘을 가져온다. 이 아이콘들은 드로어가 열려 있는지 닫혀 있는지를 나타내는 화살표 아이콘이다. -
import { Box, IconButton } from "@mui/material";
: MUI의Box
와IconButton
컴포넌트를 가져온다. -
type Props = { ... };
: Props 타입을 정의합니다.open
,handleDrawerClose
,handleDrawerOpen
프로퍼티를 받아와야 한다. -
const DrawerToggle: React.FC<Props> = ({ ... }) => { ... }
: DrawerToggle 컴포넌트를 정의한다.React.FC<Props>
는 이 컴포넌트가 Props를 받아서 렌더링하는 함수형 컴포넌트임을 나타낸다. -
<Box ...>
: 드로어 토글 버튼을 감싸는 Box 컴포넌트를 생성한다.sx
속성을 사용하여 스타일을 설정한다. -
<IconButton onClick={open ? handleDrawerClose : handleDrawerOpen}>
: IconButton 컴포넌트를 생성한다. 버튼을 클릭하면open
상태에 따라handleDrawerClose
또는handleDrawerOpen
콜백 함수를 호출한다. 이에 따라 드로어가 닫히거나 열린다. -
{open ? <ChevronLeft/> : <ChevronRight/>}
: 드로어 상태에 따라 ChevronLeft 또는 ChevronRight 아이콘을 렌더링한다. 드로어가 열려 있으면 ChevronLeft(왼쪽 화살표), 드로어가 닫혀 있으면 ChevronRight(오른쪽 화살표)가 표시된다. -
export default DrawerToggle;
: DrawerToggle 컴포넌트를 외부로 내보낸다.
이렇게 구현된 DrawerToggle 컴포넌트는 드로어의 상태를 토글하는 버튼을 제공하며, 드로어가 열려 있는 상태와 닫혀 있는 상태에 따라 화살표 아이콘이 변경된다.
PrimaryDraw.tsx
import { Box, useMediaQuery, Typography, useTheme, styled } from "@mui/material";
import { useState, useEffect } from "react";
import DrawerToggle from "../../components/PrimaryDraw/DrawToggle";
import MuiDrawer from "@mui/material/Drawer";
const PrimaryDraw = () => {
const theme = useTheme();
const below600 = useMediaQuery("(max-width:599px)");
const [open, setOpen] = useState(!below600);
// console.log(below600)
const openedMixin = () => ({
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
overflowX: "hidden",
});
const closedMixin = () => ({
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
overflowX: "hidden",
width: theme.primaryDraw.closed,
});
const Drawer = styled(
MuiDrawer,
{}
)(({theme, open}) => ({
width: theme.primaryDraw.width,
whiteSpace: "nowrap",
boxSizing: "border-box",
...(open && {
...openedMixin(),
"& .MuiDrawer-paper": openedMixin(),
}),
...(!open && {
...openedMixin(),
"& .MuiDrawer-paper": closedMixin(),
}),
}));
useEffect(() => {
setOpen(!below600);
}, [below600]);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return(
<Drawer
open={open}
variant={below600 ? "temporary" : "permanent"}
PaperProps={{
sx: {
mt: `${theme.primaryAppBar.height}px`,
height: `calc(100wh - ${theme.primaryAppBar.height}px )`,
width: theme.primaryDraw.width,
},
}}
>
<Box>
<Box
sx={{
position: "absolute",
top: 0,
right: 0,
p: 0,
width: open ? "auto" : "100%",
}}
>
<DrawerToggle
open={open}
handleDrawerClose={handleDrawerClose}
handleDrawerOpen={handleDrawerOpen}
/>
{[...Array(50)].map((_, i)=>(
<Typography key={i} paragraph>
{i +1}
</Typography>
))}
</Box>
</Box>
</Drawer>
);
};
export default PrimaryDraw;
Drawer 는 아직 작동단계는 아니다.
위 코드는 MUI(마테리얼-UI)의 Drawer를 커스터마이징하여 PrimaryDraw 컴포넌트를 구현한다. PrimaryDraw 컴포넌트는 드로어(Drawer)를 토글하는 기능과 드로어의 열림/닫힘에 따라 변화하는 스타일을 적용한다.
요약:
-
import { Box, useMediaQuery, Typography, useTheme, styled } from "@mui/material";
: MUI의 Box, useMediaQuery, Typography, useTheme, styled 컴포넌트를 가져온다. -
import { useState, useEffect } from "react";
: React의 useState와 useEffect 훅을 가져온다. -
import DrawerToggle from "../../components/PrimaryDraw/DrawToggle";
: DrawerToggle 컴포넌트를 가져온다. -
import MuiDrawer from "@mui/material/Drawer";
: MUI의 Drawer 컴포넌트를 MuiDrawer라는 이름으로 가져온다. -
const PrimaryDraw = () => { ... }
: PrimaryDraw 컴포넌트를 정의한다. -
const Drawer = styled(...)({...})
: MUI의 Drawer 컴포넌트를 styled를 사용하여 커스터마이징합니다.MuiDrawer
에 대한 스타일을 정의하여Drawer
컴포넌트를 생성한다.open
상태에 따라 열린 상태와 닫힌 상태에 대한 스타일을 지정한다. -
useEffect(() => { ... }, [below600]);
: 컴포넌트가 렌더링 될 때마다below600
값에 따라open
상태를 설정한다. -
handleDrawerOpen
,handleDrawerClose
: 드로어를 열고 닫기 위한 콜백 함수들을 정의한다. -
<Drawer ...>
: 커스터마이징된 Drawer 컴포넌트를 사용하여 드로어를 생성한다.open
상태에 따라variant
값을 설정하여 임시 드로어 또는 영구 드로어를 렌더링한다.PaperProps
를 사용하여 드로어 내용의 스타일을 설정한다. -
<Box> ... </Box>
: 드로어의 내용을 감싸는 Box 컴포넌트를 생성한다. -
<DrawerToggle ... />
: DrawerToggle 컴포넌트를 사용하여 드로어를 토글하는 버튼을 생성한다. 이 버튼은open
상태와handleDrawerClose
,handleDrawerOpen
콜백 함수를 전달받아 사용한다. -
{[...Array(50)].map((_, i)=>(...))}
: 50개의<Typography>
요소를 생성하여 숫자를 나타내는 단락들을 렌더링한다. -
export default PrimaryDraw;
: PrimaryDraw 컴포넌트를 외부로 내보낸다.
이렇게 구현된 PrimaryDraw 컴포넌트는 드로어를 토글하는 기능과 드로어의 열림/닫힘에 따라 스타일을 변화시키는 기능을 제공한다. Drawer 컴포넌트를 styled를 사용하여 스타일링하고, DrawerToggle 컴포넌트를 사용하여 드로어를 토글하는 버튼을 구현한다. 이를 통해 사용자에게 더 나은 사용자 경험을 제공할 수 있다.
Build : Secondary Draw(Part-1) Framework
Home.tsx
import { Box, CssBaseline } from "@mui/material";
import PrimaryAppBar from "./templates/PrimaryAppBar";
import PrimaryDraw from "./templates/PrimaryDraw";
import SecondaryDraw from "./templates/SecondaryDraw";
const Home = () => {
return(
<Box sx={{ display: "flex" }}>
<CssBaseline />
<PrimaryAppBar />
<PrimaryDraw></PrimaryDraw>
<SecondaryDraw/>
</Box>
);
};
export default Home;
SecondaryDraw.tsx
import { Box, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
const SecondaryDraw = () => {
const theme = useTheme();
return(
<Box
sx={{
minWidth: `${theme.secondaryDraw.width}px`,
height: `calc(100wh - ${theme.primaryAppBar.height}px )`,
mt: `${theme.primaryAppBar.height}px`,
borderRight: `1px solid ${theme.palette.divider}`,
display: { xs: "none", sm: "block"},
overflow: "auto",
}}
>
{[...Array(50)].map((_, i)=>(
<Typography key={i} paragraph>
{i +1}
</Typography>
))}
</Box>
)
}
export default SecondaryDraw;
위 코드는 MUI(마테리얼-UI)의 Box와 Typography 컴포넌트를 사용하여 SecondaryDraw 컴포넌트를 구현했다. SecondaryDraw 컴포넌트는 드로어(Drawer)의 오른쪽 영역을 나타내는 컴포넌트로, 사이드바 형태로 구성된다.
요약:
-
import { Box, Typography } from "@mui/material";
: MUI의 Box와 Typography 컴포넌트를 가져온다. -
import { useTheme } from "@mui/material/styles";
: MUI의 useTheme 훅을 가져온다. -
const SecondaryDraw = () => { ... }
: SecondaryDraw 컴포넌트를 정의한다. -
const theme = useTheme();
: useTheme 훅을 사용하여 현재 테마(theme)를 가져온다. -
<Box ...>
: SecondaryDraw의 내용을 감싸는 Box 컴포넌트를 생성합니다.sx
속성을 사용하여 스타일을 설정한다. -
minWidth:
${theme.secondaryDraw.width}px``: 컴포넌트의 최소 가로 너비를theme.secondaryDraw.width
에 지정된 값으로 설정한다. -
height:
calc(100wh - ${theme.primaryAppBar.height}px )``: 컴포넌트의 높이를 창의 높이에서theme.primaryAppBar.height
를 뺀 값으로 설정한다. 이를 통해 드로어의 높이가 윈도우 창 높이에 맞게 조정된다. -
mt:
${theme.primaryAppBar.height}px``: 드로어의 위쪽 여백(margin-top)을theme.primaryAppBar.height
에 지정된 값으로 설정한다. -
borderRight:
1px solid ${theme.palette.divider}``: 오른쪽 테두리에theme.palette.divider
에 지정된 색상의 1픽셀 두께의 실선 테두리를 설정한다. -
display: { xs: "none", sm: "block"}
: 화면 크기에 따라 컴포넌트의 가시성을 설정한다. 스마트폰 화면(xs
크기)에서는 보이지 않고, 태블릿 이상의 큰 화면(sm
크기)에서는 블록 요소로 보이도록 설정한다. -
overflow: "auto"
: 내용이 컴포넌트의 영역을 넘어갈 경우 스크롤 바를 표시한다. -
{[...Array(50)].map((_, i)=>(...))}
: 50개의<Typography>
요소를 생성하여 숫자를 나타내는 단락들을 렌더링한다. 드로어의 오른쪽 영역에 더미 데이터로 1부터 50까지의 숫자를 나타내는 단락들을 표시한다. -
export default SecondaryDraw;
: SecondaryDraw 컴포넌트를 외부로 내보낸다.
이렇게 구현된 SecondaryDraw 컴포넌트는 드로어의 오른쪽 영역을 구성하며, 특정 화면 크기에 따라 가시성이 조절되고 스크롤 바를 제공한다. 드로어 내부에 더미 데이터로 1부터 50까지의 숫자를 나타내는 단락들을 표시하여 드로어의 내용을 채운다.
theme.tsx
import { createTheme, responsiveFontSizes } from "@mui/material";
declare module "@mui/material/styles" {
interface Theme {
primaryAppBar: {
height: number,
};
primaryDraw: {
width: number,
closed: number;
};
secondaryDraw: {
width: number,
};
}
interface ThemeOptions {
primaryAppBar: {
height: number;
};
primaryDraw: {
width: number,
closed: number;
};
secondaryDraw: {
width: number,
};
}
}
export const createMuiTheme = () => {
let theme = createTheme({
typography: {
fontFamily: [
'IBM Plex Mono', 'monospace',
'IBM Plex Sans KR', 'sans-serif'
].join(","),
},
primaryAppBar: {
height: 50,
},
primaryDraw: {
width: 240,
closed: 70,
},
secondaryDraw: {
width: 240,
},
components: {
MuiAppBar: {
defaultProps: {
color: "default",
elevation: 0,
}
}
}
});
theme = responsiveFontSizes(theme);
return theme;
};
export default createMuiTheme;
secondaryDraw
secondaryDraw
는 MUI(마테리얼-UI)의 테마(theme)에 새로 추가된 사용자 정의(theme extension) 변수다. 이는 테마 객체에 추가된 하위 객체로, 특정 속성 값을 나타내는 변수다.
MUI에서 테마(theme)는 앱의 전반적인 룩앤필(look and feel)를 제어하는 데 사용된다. 테마를 통해 색상, 폰트, 여백 등의 스타일을 일괄적으로 관리할 수 있다. 일반적으로 기본적으로 제공되는 속성 외에도 프로젝트의 특정 요구에 따라 사용자 정의(theme extension)를 추가할 수 있다.
위 코드에서 secondaryDraw
는 MUI의 테마에 추가된 사용자 정의 변수이며, 다음과 같은 속성을 가지고 있다:
width: number
:secondaryDraw
의 가로 너비를 나타내는 값입니다. 이 값은 숫자로 지정하며, 픽셀 단위로 드로어(Drawer)의 가로 너비를 조절한다.
예를 들어, 위 코드에서 secondaryDraw
는 다음과 같이 선언되어 있다
이를 통해 테마에서 secondaryDraw.width
를 사용하여 드로어의 가로 너비를 설정하거나 조절할 수 있다. secondaryDraw.width
에 원하는 값(예: 240)을 지정하면 해당 값으로 드로어의 가로 너비가 설정된다.
secondaryDraw
는 MUI의 컴포넌트가 아닌 단순히 테마(theme) 객체에 속하는 변수다. 즉, 이것은 컴포넌트가 아니라 스타일과 룩앤필을 조정하는 데 사용되는 데이터 구조다. 이러한 방식으로 테마를 정의하고 사용하여 MUI 앱의 디자인과 스타일을 관리할 수 있다.
Build : Main Component (Part-1) Framework
Home.tsx
import { Box, CssBaseline } from "@mui/material";
import PrimaryAppBar from "./templates/PrimaryAppBar";
import PrimaryDraw from "./templates/PrimaryDraw";
import SecondaryDraw from "./templates/SecondaryDraw";
import Main from "./templates/Main"
const Home = () => {
return(
<Box sx={{ display: "flex" }}>
<CssBaseline />
<PrimaryAppBar />
<PrimaryDraw></PrimaryDraw>
<SecondaryDraw/>
<Main/>
</Box>
);
};
export default Home;
Main 컴포넌트 추가
Main.tsx
import { Box, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
const Main = () => {
const theme = useTheme();
return (
<Box
sx={{
flexGrow: 1,
mt: `${theme.primaryAppBar.height}px`,
height: `calc(100vh - ${theme.primaryAppBar.height}px )`,
overflow: "hidden",
}}
>
{[...Array(50)].map((_, i)=>(
<Typography key={i} paragraph>
{i +1}
</Typography>
))}
</Box>
);
};
export default Main;
위 코드는 MUI(Material-UI) 라이브러리를 사용하여 구현된 Main 컴포넌트다.
Box
와Typography
를 MUI에서 가져온다.useTheme
함수를 사용하여 현재 테마를 가져온다.- Main 컴포넌트를 정의한다.
Box
컴포넌트를 사용하여 메인 컨텐츠를 감싸고,sx
prop을 사용하여 스타일을 설정한다.sx
prop은 MUI에서 제공하는 스타일링을 쉽게 할 수 있도록 해주는 prop다.flexGrow: 1
은 해당 박스가 부모 컨테이너의 남은 공간을 모두 차지하도록 하는 스타일다.mt:
${theme.primaryAppBar.height}px``는 상단 AppBar의 높이만큼 여백을 추가하는 스타일이다.height: calc(100vh - ${theme.primaryAppBar.height}px )
는 브라우저 뷰포트의 높이에서 AppBar의 높이를 빼서 메인 컨텐츠의 높이를 설정하는 스타일이다.overflow: "hidden"
은 내부 컨텐츠가 넘치는 경우 스크롤바를 보이지 않도록 설정하는 스타일이다.
- 배열
[...Array(50)]
를 사용하여 50개의 요소를 가진 배열을 생성한다. map
함수를 사용하여 각 요소마다 순회하면서<Typography>
컴포넌트를 생성한다.- 각
<Typography>
컴포넌트의key
prop은i
값을 사용하여 고유한 키를 설정한다. <Typography>
컴포넌트의 내용으로i + 1
값을 출력하여 1부터 50까지의 숫자를 표시한다.- Main 컴포넌트를 내보내어 다른 파일에서 사용할 수 있도록 한다.
이 컴포넌트는 상단 AppBar 아래에 높이가 조정된 Main 컨텐츠를 렌더링하는 역할을 한다. 1부터 50까지의 숫자를 <Typography>
컴포넌트를 사용하여 출력한다. 이 코드는 테마를 활용하여 AppBar의 높이를 계산하고, 남은 높이를 Main 컨텐츠로 사용하며, 스크롤바를 제어하여 사용자 경험을 최적화하는 예시다.
댓글남기기