[TIL/React] 2023/08/07

์›๋ฏผ๊ด€ยท2023๋…„ 8์›” 7์ผ
0

[TIL]

๋ชฉ๋ก ๋ณด๊ธฐ
96/159

Sidebar Nav Menu ๐ŸŸ 

reference: https://www.youtube.com/watch?v=5R9jFHlG6ik&list=WL&index=1

sidebar๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค. pedro ํ˜•๋‹˜์ด ๋ง์•„์ฃผ์‹œ๋Š” sidebar ๊ฐ•์˜๋ฅผ ๋“ฃ๊ณ , styled-component ๋ฒ„์ „์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

src/App.js ๐ŸŸข

import React, { useState } from "react";
import Sidebar from "./Components/Sidebar";

function App() {
  const [sidebarOpen, setSidebarOpen] = useState(false);

  const toggleSidebar = () => {
    setSidebarOpen(!sidebarOpen);
  };

  return (
    <div>
      <button onClick={toggleSidebar}>Toggle Sidebar</button>
      <Sidebar open={sidebarOpen} onClose={toggleSidebar} />
    </div>
  );
}

export default App;

์ตœ์ƒ๋‹จ์—์„œ๋Š” 1)๋ฒ„ํŠผ, 2)Sidebar ์ปดํฌ๋„ŒํŠธ๋ฅผ returnํ•œ๋‹ค.

1)๋ฒ„ํŠผ์—๋Š” onClick์œผ๋กœ 'toggleSidebar'๋ผ๋Š” ํ•จ์ˆ˜๊ฐ€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š”๋ฐ, sidebarOpen์ด๋ผ๋Š” boolean ๊ฐ’์„ ๋ฐ˜๋Œ€๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์Šค์œ„์น˜์ฒ˜๋Ÿผ ๋”ธ๊น”๋”ธ๊น ใ…‡ใ…‡

2)Sidebar ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” props๋กœ, 1)open์ด๋ผ๋Š” ์ด๋ฆ„์˜ sidebarOpen์ด๋ผ๋Š” '์ƒํƒœ'์™€ 2)onClose๋ผ๋Š” ์ด๋ฆ„์˜ toggleSidebar 'ํ•จ์ˆ˜'๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

src/Components/SidebarData.js ๐ŸŸข

export const SidebarData = [
  {
    title: "Home",
    link: "/home",
  },
  {
    title: "Mailbox",
    link: "/mailbox",
  },
  {
    title: "Analytics",
    link: "/analytics",
  },
  {
    title: "Dashboard",
    link: "/dashboard",
  },
  {
    title: "Images",
    link: "/images",
  },
];

๋‹จ์ˆœํžˆ title๊ณผ link๋กœ ๊ตฌ์„ฑ๋œ data๋ฅผ returnํ•˜๋Š” ํŒŒ์ผ์ด๋‹ค. Sidebar ์ปดํฌ๋„ŒํŠธ์—์„œ ์š”๊ธดํ•˜๊ฒŒ ์“ธ ์˜ˆ์ •์ด๋‹ค.

src/Components/Sidebar.js ๐ŸŸข

import React from "react";
import styled from "styled-components";
import { SidebarData } from "./SidebarData";

const SidebarContainer = styled.div`
  height: 100vh;
  width: ${({ open1 }) => (open1 ? "250px" : "0")};
  background-color: #2f4050;
  position: fixed;
  top: 0;
  left: 0;
  transition: 0.5s;
  overflow-x: hidden;
`;

const CloseSidebarButton = styled.button`
  background-color: transparent;
  border: none;
  color: white;
  cursor: pointer;
  padding: 8px;
  position: absolute;
  top: 0;
  right: 0;
`;

const SidebarList = styled.ul`
  height: auto;
  padding: 0;
  width: 100%;
`;

const SidebarItem = styled.li`
  width: 100%;
  height: 60px;
  list-style-type: none;
  margin: 0;
  display: flex;
  flex-direction: row;
  color: white;
  justify-content: center;
  align-items: center;
  font-family: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande",
    "Lucida Sans", Arial, sans-serif;
  &:hover {
    cursor: pointer;
    background-color: #293846;
  }
  ${({ active }) =>
    active &&
    `
    background-color: #293846;
  `}
`;

const SidebarComponent = ({ open, onClose }) => {
  const handleLinkClick = (link) => {
    window.location.pathname = link;
  };

  return (
    <SidebarContainer open1={open}>
      <CloseSidebarButton onClick={onClose}>X</CloseSidebarButton>
      <SidebarList>
        {SidebarData?.map((val, key) => {
          return (
            <SidebarItem
              key={key}
              active={window.location.pathname === val.link}
              onClick={() => handleLinkClick(val.link)}
            >
              {val.title}
            </SidebarItem>
          );
        })}
      </SidebarList>
    </SidebarContainer>
  );
};

export default SidebarComponent;

return ๋ฌธ ์ดํ•˜๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž. Sidebar Container ๋‚ด๋ถ€์—์„œ 1)๋ฒ„ํŠผ๊ณผ 2)๋ฆฌ์ŠคํŠธ๋ฅผ returnํ•˜๊ณ  ์žˆ๋‹ค.

1)onClose๋Š” ์‚ฌ์‹ค์ƒ toggleSidebar ํ•จ์ˆ˜์ด๋‹ค. ์ตœ์ดˆ์— ํด๋ฆญํ–ˆ์„ ๋•Œ ์ดˆ๊ธฐ๊ฐ’์ด false์—์„œ true๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์œผ๋‹ˆ, ์—ฌ๊ธฐ์„œ toggleSidebar์˜ ์—ญํ• ์€ ๋‹ค์‹œ true๋ฅผ false๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ํ•œ๋งˆ๋””๋กœ sidebar๋ฅผ ๋„๊ฒ ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

2)SidebarData ๋ฐฐ์—ด์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•ด์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ Œ๋”๋งํ•œ๋‹ค. title์ด ๋‚˜์˜ค๊ณ  active์™€ onClick์ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ๋ฆฌ์ŠคํŠธ์ด๋‹ค.

styled component ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๊ฒ ๋‹ค.

์ปดํฌ๋„ŒํŠธ์˜ ๊ป๋ฐ๊ธฐ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค.

์ตœ์ดˆ์— open์€ ์‚ฌ์‹ค์ƒ true์ด๊ณ  open1์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ true๋ฅผ ๋ฐ›์•˜์„ ๊ฒƒ์ด๋‹ค. width๋Š” 250px์ด ๋  ๊ฒƒ์ด๋‹ค. transition์„ ํ†ตํ•ด ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  0.5์ดˆ๊ฐ„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ „ํ™˜๋˜๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

์œ„ ์ฝ”๋“œ์—์„œ๋Š” active์— ๋Œ€ํ•ด ์‚ดํŽด๋ณผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. map์„ ํ†ตํ•ด ๋ Œ๋”๋ง๋˜๋Š” sidebar item์—๋Š” active๋กœ window.location.pathname === val.link๋ฅผ ์„ค์ •ํ•˜๊ณ  ์žˆ๋‹ค. data๋กœ ์„ค์ •ํ•œ ๋งํฌ์™€ ํ˜„์žฌ ํŽ˜์ด์ง€์˜ pathname์ด ์ผ์น˜ํ•˜๋ฉด(์ฆ‰ true์ด๋ฉด) background๋ฅผ ์œ ์ง€ํ•˜๋ผ๋Š” ๋ช…๋ น์–ด๋‹ค.

๊ฒฐ๊ณผ ๐Ÿ”ต

ํšŒ๊ณ  ๐ŸŸฃ

์ œ์ž„์Šค ๋งˆ์ปค์Šค ๋ฐ”ํฌ๋Š” "์ง€์‹ ๋…ธ๋™์ž์˜ ์„ฑ๊ณต์€ ํ˜„์žฌ ์•„๋Š” ์‚ฌ์‹ค์ด ์•„๋‹ˆ๋ผ ๋ฐฐ์šฐ๋Š” ๋ฐฉ์‹์ด ์ขŒ์šฐํ•œ๋‹ค"๋ผ๊ณ  ์–ธ๊ธ‰ํ•œ ๋ฐ” ์žˆ๋‹ค. ๋ช‡ ๋‹ฌ ์ „๋งŒ ํ•ด๋„ ๋ฌธ์ œ๊ฐ€ ์•ˆ ํ’€๋ฆฌ๋ฉด ๊ต‰์žฅํžˆ ์ŠคํŠธ๋ ˆ์Šค๋ฅผ ๋ฐ›์•˜๋‹ค. ์š”์ฆ˜์€ ๊ฝค ๋‚˜์•„์กŒ๋‹ค. ํ˜„์žฌ ์•„๋Š” ์‚ฌ์‹ค์ด ๋„ˆ๋ฌด ๋งŽ์•„์ ธ์„œ? ์•ˆํƒ€๊น์ง€๋งŒ ์—ฌ์ „ํžˆ ํฐ ๋ฒจํŠธ๋‹ค.

ํ’€๋ฆฌ์ง€ ์•Š๋Š” ๋ฌธ์ œ๋ฅผ ๋” ์ž‘์€ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ๊ณ  '๋ถ„ํ•  ์ •๋ณต'ํ•˜๋Š”, ๋‚˜๋ฆ„์˜ '๋ฐฐ์šฐ๋Š” ๋ฐฉ์‹'์„ ์ •๋ฆฝํ–ˆ๊ธฐ์— ๋งˆ์Œ์ด ํŽธํ•ด์ง„ ๊ฒƒ ๊ฐ™๋‹ค. ํฌ๊ธฐํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๋ฐœ์•…ํ•˜๋Š” ๊ณผ์ •์—์„œ "์ž‘์€ ๋‹จ์œ„๋ถ€ํ„ฐ ์„ฑ์ทจํ•˜๋ฉด ๋œ๋‹ค๋Š”", ๋ˆ„๊ฐ€ ๋ด๋„ ๋ป”ํ•œ ์ „๋žต์„ ์Šต๋“ํ•œ ๊ฒƒ์ด๋‹ค. best practice๋ฅผ ๋ฌด๋ฃŒ๋กœ ์ œ๊ณตํ•ด ์ฃผ๋Š” ์œ ํŠœ๋ธŒ ์ธ๋„ ํ˜•๋‹˜๋“ค์—๊ฒŒ ๊ฐ์‚ฌํ•  ๋”ฐ๋ฆ„์ด๋‹ค. ๐Ÿ‡ฎ๐Ÿ‡ณ

profile
Write a little every day, without hope, without despair โœ๏ธ

0๊ฐœ์˜ ๋Œ“๊ธ€