40 분 소요

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의 주요 특징과 개념은 다음과 같다:

  1. 빠른 개발 서버: Vite는 개발 서버를 내장하고 있어서 개발 중에 빠르게 변경 사항을 확인할 수 있다. 개발 서버는 라이브 리로딩(Live Reloading)과 핫 모듈 교체(HMR, Hot Module Replacement)를 지원하여 수정 사항을 바로 반영하여 브라우저에 표시할 수 있다.

  2. 기존 빌드 도구 대비 빠른 빌드 시간: Vite는 웹 애플리케이션을 빌드할 때 기존의 Webpack과 같은 번들링 방식이 아닌, 자원 파일을 미리 복사하는 방식으로 빌드 시간을 크게 단축시킨다.

  3. 모듈 기반 개발: Vite는 ESM(ES Modules)을 기반으로 개발되어 모듈 시스템을 효율적으로 활용한다. 이로 인해 파일 간 의존성을 더욱 효과적으로 관리하고, 필요한 모듈만 불러와 사용하여 빠른 개발을 지원을 한다.

  4. 플러그인 생태계: Vite는 다양한 플러그인을 활용하여 기능을 확장할 수 있다. 예를 들어, TypeScript, SCSS, Less와 같은 전처리기를 지원하는 플러그인을 사용할 수 있다.

  5. Vue.js 특화: Vite는 Vue.js를 위해 최적화되어 있으며, Vue.js 공식 개발 팀에서 개발하고 관리한다. Vue.js 프로젝트에서 더 빠르게 개발 및 빌드를 할 수 있도록 지원한다.

  6. 다른 프레임워크 지원: 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는 다음과 같은 주요 기능을 제공한다:

  1. 라우팅: React Router는 URL과 컴포넌트를 매핑하여 웹 애플리케이션의 다른 페이지로 이동할 수 있도록 한다. 각 URL에 대해 해당하는 React 컴포넌트를 렌더링하거나 라우팅 설정에 따라 필요한 동작을 수행한다.

  2. 동적 라우팅: URL의 특정 부분을 동적으로 처리할 수 있다. 예를 들어, 사용자 ID가 포함된 URL을 다른 컴포넌트로 라우팅하거나, 특정 카테고리에 대한 URL로 다른 페이지를 표시하는 등의 동적 라우팅이 가능하다.

  3. 중첩된 라우팅: React Router는 중첩된 라우팅을 지원하여 여러 단계의 URL을 다룰 수 있다. 이로써 애플리케이션의 복잡한 레이아웃을 관리하고 각 레이아웃에 대해 적절한 컴포넌트를 렌더링할 수 있다.

  4. 라우팅 히스토리 관리: 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

리엑트 ui 툴

/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 bar tool bar typography

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>: TypographyLink 컴포넌트 안에 넣어서 Navigation Bar에 텍스트를 표시하고, 클릭 시 링크가 작동하도록 설정하고 있다. Typographyvariant 속성으로 텍스트의 스타일을 설정하고 있으며, 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를 사용할 수 있게 된다. primaryAppBarheight라는 숫자 속성을 갖는다.

createMuiTheme

위 코드에서는 createMuiTheme 함수를 정의하고 있다. 이 함수는 createTheme 함수를 이용하여 MUI 테마를 생성하고 반환한다.

  • createTheme({ ... }): createTheme 함수에 전달된 객체는 테마를 구성하는 속성들을 정의한다. 여기서는 primaryAppBar 속성을 추가하고 있다. primaryAppBarheight 속성을 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

breakpoints

Drawer Drawer API

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"
  1. useState를 사용하여 sideMenu 상태를 관리한다. sideMenu는 Drawer(좌측 사이드 메뉴)의 열림/닫힘 여부를 나타낸다. 초기값은 false로 설정

  2. useTheme를 사용하여 현재 MUI 테마를 가져온다.

  3. useMediaQuery를 사용하여 isSmallScreen 상태를 관리. isSmallScreen은 현재 화면 크기가 작은 스크린(스마트폰 등)인지 여부를 나타낸다. theme.breakpoints.up("sm")는 스크린의 너비가 “sm” 크기(600px 이상) 이상일 때를 의미.

  4. useEffect를 사용하여 isSmallScreensideMenu 상태를 감지한다. 만약 isSmallScreen이 참이고 sideMenu가 열려있을 때, setSideMenu(false)를 호출하여 화면 크기가 커질 때 Drawer를 닫도록 설정.

  5. toggleDrawer 함수는 Drawer를 열고 닫는 역할을 한다. open 매개변수를 받아서 setSideMenu를 호출하여 sideMenu 상태를 변경한다. Drawer를 열 때는 toggleDrawer(true)로, 닫을 때는 toggleDrawer(false)로 사용

  6. AppBar 컴포넌트를 생성하고, sx 속성을 사용하여 AppBar의 스타일을 설정.

  7. Toolbar 컴포넌트를 생성하고, sx 속성을 사용하여 Toolbar의 스타일을 설정. heightminHeighttheme.primaryAppBar.height로 설정하여 앞서 정의한 테마의 primaryAppBar.height 값으로 Toolbar의 높이를 조정.

  8. IconButton 컴포넌트를 생성하여 메뉴 버튼을 만들고, MenuIcon 아이콘을 표시 toggleDrawer(true) 함수를 클릭 이벤트 핸들러로 설정하여 버튼을 클릭하면 Drawer가 열리도록 한다.

  9. Drawer 컴포넌트를 생성하여 좌측 사이드 메뉴를 만든다. anchor 속성을 “left”로 설정하여 좌측에 나타나도록 한다. open 속성을 sideMenu 값으로 설정하여 sideMenu 상태에 따라 열림/닫힘을 관리한다. onClose 속성을 toggleDrawer(false)로 설정하여 사이드 메뉴의 오버레이를 클릭하거나 키보드 이벤트에 의해 닫히도록 한다. 그리고 Drawer 내부에 100개의 Typography 컴포넌트를 렌더링하여 예시로 사용한다..

이렇게 코드가 구성되어 있으며, PrimaryAppBar 컴포넌트를 사용하면 AppBar에 메뉴 버튼이 포함된 상단 바와 좌측에 Drawer(사이드 메뉴)가 구현된다. useMediaQueryuseEffect를 이용하여 화면 크기에 따라 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 아이콘은 화살표 모양을 나타내는 아이콘으로, 주로 이전으로 돌아가는 기능을 나타낼 때 사용한다.

컴포넌트 설명:

  1. import ChevronLeft from "@mui/icons-material/ChevronLeft";: Material-UI에서 제공하는 ChevronLeft 아이콘을 가져온다. 이 아이콘은 Material-UI의 아이콘 컴포넌트 중 하나로, Material Icons 라이브러리에 속해있다.

  2. import { Box, IconButton } from "@mui/material";: Material-UI에서 제공하는 BoxIconButton 컴포넌트를 가져온다. Box 컴포넌트는 레이아웃을 구성하는데 사용되며, IconButton 컴포넌트는 아이콘을 포함한 버튼을 만드는데 사용한다.

  3. const DrawerToggle = () => { ... }: DrawerToggle 함수형 컴포넌트를 정의한다.

  4. <Box sx={{ ... }}>: Box 컴포넌트를 사용하여 레이아웃을 구성한다. sx 속성을 사용하여 스타일을 설정한다.

  5. height: "50px": Box 컴포넌트의 높이를 50px로 설정한다.

  6. display: "flex": Box 컴포넌트 내부의 요소들을 Flexbox로 배치한다.

  7. alignItems: "center": Flexbox를 사용하여 요소들을 수직 정렬합니다. 여기서는 센터로 정렬한다.

  8. justifyContent: "center": Flexbox를 사용하여 요소들을 수평 정렬한다. 여기서는 센터로 정렬한다.

  9. <IconButton>: IconButton 컴포넌트를 생성합니다. 이 컴포넌트는 아이콘을 포함한 버튼 역할을 한다.

  10. <ChevronLeft/>: ChevronLeft 아이콘 컴포넌트를 IconButton 내부에 배치한다. 이 아이콘은 화살표를 나타내는 아이콘으로, 이전으로 돌아가는 기능을 나타내는데 사용한다.

  11. 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와 좌측 사이드 메뉴를 포함한 레이아웃을 만드는 역할을 한다.

컴포넌트 설명:

  1. import { Box, CssBaseline } from "@mui/material";: Material-UI에서 제공하는 BoxCssBaseline 컴포넌트를 가져온다. Box 컴포넌트는 레이아웃을 구성하는데 사용되고, CssBaseline 컴포넌트는 CSS 리셋을 적용하여 초기 스타일링을 설정하는데 사용한다.

  2. import PrimaryAppBar from "./templates/PrimaryAppBar";: PrimaryAppBar 컴포넌트를 가져온다. 이 컴포넌트는 상단에 고정된 AppBar를 생성하는데 사용된다.

  3. import PrimaryDraw from "./templates/PrimaryDraw";: PrimaryDraw 컴포넌트를 가져온다. 이 컴포넌트는 좌측 사이드 메뉴를 생성하는데 사용한다.

  4. const Home = () => { ... }: Home 함수형 컴포넌트를 정의한다.

  5. <Box sx={{ display: "flex" }}>: Box 컴포넌트를 사용하여 레이아웃을 구성한다. sx 속성을 사용하여 스타일을 설정한다. display: "flex" 스타일을 적용하여 자식 요소들을 Flexbox로 배치한다.

  6. <CssBaseline />: CssBaseline 컴포넌트를 렌더링하여 CSS 리셋을 적용한다. 이는 브라우저의 기본 스타일을 초기화하여 일관된 스타일링을 적용하는데 사용한다.

  7. <PrimaryAppBar />: PrimaryAppBar 컴포넌트를 렌더링하여 상단에 고정된 AppBar를 생성한다.

  8. <PrimaryDraw></PrimaryDraw>: PrimaryDraw 컴포넌트를 렌더링하여 좌측 사이드 메뉴를 생성한다. </PrimaryDraw>는 해당 컴포넌트의 마감 태그로, 빈 요소로 사용되기 때문에 </PrimaryDraw>를 사용해도 동일한 결과를 생성한다.

  9. 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 컴포넌트를 불러와 토글 버튼을 생성한다.

컴포넌트 설명:

  1. import { Drawer, Box, useMediaQuery, Typography, useTheme } from "@mui/material";: Material-UI에서 제공하는 Drawer, Box, useMediaQuery, Typography, useTheme 등의 컴포넌트와 훅을 가져온다.

  2. import { useState, useEffect } from "react";: React에서 제공하는 useStateuseEffect 훅을 가져온다.

  3. const PrimaryDraw = () => { ... }: PrimaryDraw 함수형 컴포넌트를 정의한다.

  4. const theme = useTheme();: useTheme 훅을 사용하여 현재 사용 중인 테마를 가져온다.

  5. const below600 = useMediaQuery("(max-width:599px)");: useMediaQuery 훅을 사용하여 화면 너비가 600px 미만인지를 검사하여 below600 변수에 저장한다.

  6. const [open, setOpen] = useState(!below600);: useState 훅을 사용하여 open 상태 변수를 생성합니다. 초기값은 below600 값의 반대로 설정한다. 즉, 화면 너비가 600px 미만이면 openfalse, 600px 이상이면 opentrue가 된다.

  7. console.log(below600): below600 값을 콘솔에 출력하여 확인한다.

  8. useEffect(() => { ... }, [below600]);: useEffect 훅을 사용하여 below600 값이 변경될 때마다 open 상태를 업데이트한다. 즉, 화면 너비가 600px 미만이면 open 상태를 false, 600px 이상이면 true로 설정한다.

  9. const handleDrawerOpen = () => { ... };: 좌측 사이드 메뉴를 열기 위한 함수를 정의한다.

  10. const handleDrawerClose = () => { ... };: 좌측 사이드 메뉴를 닫기 위한 함수를 정의한다.

  11. <Drawer open={open} variant={below600 ? "temporary" : "permanent"} PaperProps={{ ... }}> ... </Drawer>: Drawer 컴포넌트를 생성한다. open 속성으로 open 상태 값을 설정하고, variant 속성으로 화면 너비에 따라 임시 메뉴 또는 영구 메뉴로 설정한다. PaperProps 속성을 사용하여 메뉴의 스타일을 설정한다.

  12. <Box> ... </Box>: Box 컴포넌트를 사용하여 메뉴 내용을 감싸준다.

  13. <Box sx={{ ... }}>: Box 컴포넌트에 스타일을 적용하여 메뉴의 위치와 크기를 설정한다.

  14. <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%의 너비를 갖도록 설정한다.

  15. <DrawerToggle/>: DrawerToggle 컴포넌트를 렌더링하여 토글 버튼을 생성한다.

  16. {[...Array(50)].map((_, i)=>(...))}: 50개의 <Typography> 컴포넌트를 반복하여 렌더링한다. 이 부분은 테스트를 위해 1부터 50까지의 숫자를 화면에 출력하는 코드다.
  17. 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의 createThemeresponsiveFontSizes를 사용하여 사용자 정의 테마를 생성하는 코드다. 또한, 테마에 새로운 속성 primaryAppBarprimaryDraw를 추가하여 AppBar와 Drawer의 높이 및 너비 값을 설정한다.

코드 설명:

  1. import { createTheme, responsiveFontSizes } from "@mui/material";: Material-UI에서 createThemeresponsiveFontSizes를 가져온다. createTheme은 사용자 정의 테마를 생성하는 함수이고, responsiveFontSizes는 반응형 폰트 크기를 설정하는 함수다.

  2. declare module "@mui/material/styles" { ... }: TypeScript에서 사용자 정의 테마 속성을 선언하는 부분이다. primaryAppBarprimaryDraw 속성을 Theme 및 ThemeOptions에 추가하여 테마에서 사용할 수 있도록 한다.

  3. export const createMuiTheme = () => { ... }: createMuiTheme 함수를 정의한다.

  4. let theme = createTheme({ ... });: createTheme 함수를 사용하여 기본 테마를 생성한다. 기본 테마에는 typography, primaryAppBar, primaryDraw, components 등의 속성이 설정되어 있다.

  5. typography: 테마의 텍스트 스타일을 설정하는 속성이다. fontFamily를 지정하여 원하는 폰트를 사용할 수 있다. 여기서는 IBM Plex MonoIBM Plex Sans KR 폰트를 사용한다.

  6. primaryAppBar: AppBar의 높이를 설정하는 속성이다. height 값으로 50을 설정하여 높이를 50px로 지정한다.

  7. primaryDraw: Drawer의 너비와 닫힌 상태에서의 너비를 설정하는 속성이다. width 값으로 240을 설정하여 너비를 240px로 지정하고, closed 값으로 70을 설정하여 닫힌 상태에서의 너비를 70px로 지정한다.

  8. components: Material-UI의 컴포넌트에 대한 기본 속성을 설정하는 부분이다. 여기서는 MuiAppBar의 기본 속성을 color: "default"elevation: 0으로 설정한다. 이렇게 하면 AppBar가 기본 색상으로 보여지고, 그림자 효과가 적용되지 않는다.

  9. theme = responsiveFontSizes(theme);: 생성한 테마에 반응형 폰트 크기를 적용한다. 이렇게 하면 테마의 폰트 크기가 화면 크기에 따라 자동으로 조정된다.

  10. return theme;: 생성한 테마를 반환한다.

  11. 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: 드로어를 열기 위한 콜백 함수

요약:

  1. import { ChevronRight, ChevronLeft } from "@mui/icons-material";: MUI의 ChevronRightChevronLeft 아이콘을 가져온다. 이 아이콘들은 드로어가 열려 있는지 닫혀 있는지를 나타내는 화살표 아이콘이다.

  2. import { Box, IconButton } from "@mui/material";: MUI의 BoxIconButton 컴포넌트를 가져온다.

  3. type Props = { ... };: Props 타입을 정의합니다. open, handleDrawerClose, handleDrawerOpen 프로퍼티를 받아와야 한다.

  4. const DrawerToggle: React.FC<Props> = ({ ... }) => { ... }: DrawerToggle 컴포넌트를 정의한다. React.FC<Props>는 이 컴포넌트가 Props를 받아서 렌더링하는 함수형 컴포넌트임을 나타낸다.

  5. <Box ...>: 드로어 토글 버튼을 감싸는 Box 컴포넌트를 생성한다. sx 속성을 사용하여 스타일을 설정한다.

  6. <IconButton onClick={open ? handleDrawerClose : handleDrawerOpen}>: IconButton 컴포넌트를 생성한다. 버튼을 클릭하면 open 상태에 따라 handleDrawerClose 또는 handleDrawerOpen 콜백 함수를 호출한다. 이에 따라 드로어가 닫히거나 열린다.

  7. {open ? <ChevronLeft/> : <ChevronRight/>}: 드로어 상태에 따라 ChevronLeft 또는 ChevronRight 아이콘을 렌더링한다. 드로어가 열려 있으면 ChevronLeft(왼쪽 화살표), 드로어가 닫혀 있으면 ChevronRight(오른쪽 화살표)가 표시된다.

  8. 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)를 토글하는 기능과 드로어의 열림/닫힘에 따라 변화하는 스타일을 적용한다.

요약:

  1. import { Box, useMediaQuery, Typography, useTheme, styled } from "@mui/material";: MUI의 Box, useMediaQuery, Typography, useTheme, styled 컴포넌트를 가져온다.

  2. import { useState, useEffect } from "react";: React의 useState와 useEffect 훅을 가져온다.

  3. import DrawerToggle from "../../components/PrimaryDraw/DrawToggle";: DrawerToggle 컴포넌트를 가져온다.

  4. import MuiDrawer from "@mui/material/Drawer";: MUI의 Drawer 컴포넌트를 MuiDrawer라는 이름으로 가져온다.

  5. const PrimaryDraw = () => { ... }: PrimaryDraw 컴포넌트를 정의한다.

  6. const Drawer = styled(...)({...}): MUI의 Drawer 컴포넌트를 styled를 사용하여 커스터마이징합니다. MuiDrawer에 대한 스타일을 정의하여 Drawer 컴포넌트를 생성한다. open 상태에 따라 열린 상태와 닫힌 상태에 대한 스타일을 지정한다.

  7. useEffect(() => { ... }, [below600]);: 컴포넌트가 렌더링 될 때마다 below600 값에 따라 open 상태를 설정한다.

  8. handleDrawerOpen, handleDrawerClose: 드로어를 열고 닫기 위한 콜백 함수들을 정의한다.

  9. <Drawer ...>: 커스터마이징된 Drawer 컴포넌트를 사용하여 드로어를 생성한다. open 상태에 따라 variant 값을 설정하여 임시 드로어 또는 영구 드로어를 렌더링한다. PaperProps를 사용하여 드로어 내용의 스타일을 설정한다.

  10. <Box> ... </Box>: 드로어의 내용을 감싸는 Box 컴포넌트를 생성한다.

  11. <DrawerToggle ... />: DrawerToggle 컴포넌트를 사용하여 드로어를 토글하는 버튼을 생성한다. 이 버튼은 open 상태와 handleDrawerClose, handleDrawerOpen 콜백 함수를 전달받아 사용한다.

  12. {[...Array(50)].map((_, i)=>(...))}: 50개의 <Typography> 요소를 생성하여 숫자를 나타내는 단락들을 렌더링한다.

  13. 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)의 오른쪽 영역을 나타내는 컴포넌트로, 사이드바 형태로 구성된다.

요약:

  1. import { Box, Typography } from "@mui/material";: MUI의 Box와 Typography 컴포넌트를 가져온다.

  2. import { useTheme } from "@mui/material/styles";: MUI의 useTheme 훅을 가져온다.

  3. const SecondaryDraw = () => { ... }: SecondaryDraw 컴포넌트를 정의한다.

  4. const theme = useTheme();: useTheme 훅을 사용하여 현재 테마(theme)를 가져온다.

  5. <Box ...>: SecondaryDraw의 내용을 감싸는 Box 컴포넌트를 생성합니다. sx 속성을 사용하여 스타일을 설정한다.

  6. minWidth: ${theme.secondaryDraw.width}px``: 컴포넌트의 최소 가로 너비를 theme.secondaryDraw.width에 지정된 값으로 설정한다.

  7. height: calc(100wh - ${theme.primaryAppBar.height}px )``: 컴포넌트의 높이를 창의 높이에서 theme.primaryAppBar.height를 뺀 값으로 설정한다. 이를 통해 드로어의 높이가 윈도우 창 높이에 맞게 조정된다.

  8. mt: ${theme.primaryAppBar.height}px``: 드로어의 위쪽 여백(margin-top)을 theme.primaryAppBar.height에 지정된 값으로 설정한다.

  9. borderRight: 1px solid ${theme.palette.divider}``: 오른쪽 테두리에 theme.palette.divider에 지정된 색상의 1픽셀 두께의 실선 테두리를 설정한다.

  10. display: { xs: "none", sm: "block"}: 화면 크기에 따라 컴포넌트의 가시성을 설정한다. 스마트폰 화면(xs 크기)에서는 보이지 않고, 태블릿 이상의 큰 화면(sm 크기)에서는 블록 요소로 보이도록 설정한다.

  11. overflow: "auto": 내용이 컴포넌트의 영역을 넘어갈 경우 스크롤 바를 표시한다.

  12. {[...Array(50)].map((_, i)=>(...))}: 50개의 <Typography> 요소를 생성하여 숫자를 나타내는 단락들을 렌더링한다. 드로어의 오른쪽 영역에 더미 데이터로 1부터 50까지의 숫자를 나타내는 단락들을 표시한다.

  13. 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의 테마에 추가된 사용자 정의 변수이며, 다음과 같은 속성을 가지고 있다:

  1. 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 컴포넌트다.

  1. BoxTypography를 MUI에서 가져온다.
  2. useTheme 함수를 사용하여 현재 테마를 가져온다.
  3. Main 컴포넌트를 정의한다.
  4. 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"은 내부 컨텐츠가 넘치는 경우 스크롤바를 보이지 않도록 설정하는 스타일이다.
  5. 배열 [...Array(50)]를 사용하여 50개의 요소를 가진 배열을 생성한다.
  6. map 함수를 사용하여 각 요소마다 순회하면서 <Typography> 컴포넌트를 생성한다.
  7. <Typography> 컴포넌트의 key prop은 i 값을 사용하여 고유한 키를 설정한다.
  8. <Typography> 컴포넌트의 내용으로 i + 1 값을 출력하여 1부터 50까지의 숫자를 표시한다.
  9. Main 컴포넌트를 내보내어 다른 파일에서 사용할 수 있도록 한다.

이 컴포넌트는 상단 AppBar 아래에 높이가 조정된 Main 컨텐츠를 렌더링하는 역할을 한다. 1부터 50까지의 숫자를 <Typography> 컴포넌트를 사용하여 출력한다. 이 코드는 테마를 활용하여 AppBar의 높이를 계산하고, 남은 높이를 Main 컨텐츠로 사용하며, 스크롤바를 제어하여 사용자 경험을 최적화하는 예시다.

댓글남기기