웹/React

React 드롭다운 메뉴 혼자서 만들어보기 - 라이브러리 X

공대생철이 2023. 8. 7. 21:14
728x90

동아리에서 진행하는 프로젝트 중 드롭다운을 만들어야했는데 드롭다운 하나를 위해서 디자인 라이브러리를 받는 건 프로젝트의 용량으로 보나 여러모로 너무 낭비라고 판단하여 직접 구현하기로 했다.

 

먼저 구현된 결과물부터 바로 보고 어떻게 구현했는지 분석해보자.

const [activeIdx, setActiveIdx] = useState();

<div className="flex flex-col gap-[10px] items-center justify-center">
        {adminMenu.map((menu, index) => {
          const active = activeIdx === index + 1 ? true : false;
          return (
            <AdminMenu
              menuItem={menu.name}
              key={index}
              idx={menu.idx}
              activeIdx={activeIdx}
              active={active}
              setActiveIdx={setActiveIdx}
              subMenu={menu.subMenu}
            />
          );
        })}
  </div>
  
  // AdminMenu 컴포넌트
  const AdminMenu = ({ menuItem, idx, activeIdx, active, setActiveIdx, subMenu }) => {
  return (
    <>
      <div
        className=" w-[300px] h-[65px] bg-[#FCFCFC] border-[1px] border-black rounded-lg flex justify-center items-center cursor-pointer hover:bg-blue-gray-50 transition-all"
        onClick={() => {
          setActiveIdx(idx);
        }}
      >
        {menuItem}
      </div>
      <div
        className={`${active ? "visibile" : "invisible"} ${
          active && subMenu.length > 0 ? "h-[100px]" : "h-0"
        } transition-all duration-300`}
      >
        {active &&
          subMenu.map((submenu, index) => {
            return (
              <div
                key={index}
                className="w-[300px] h-[50px] bg-white flex justify-center items-center cursor-pointer hover:bg-blue-gray-50 transition-all"
              >
                {submenu.name}
              </div>
            );
          })}
      </div>
    </>
  );
};

여러 개의 메뉴를 flex로 감싼 거고 클릭한 컴포넌트를 제어하기 위해 activeIdx라는 state를 선언하였다.

 

드롭다운을 제어하는 AdminMenu의 속성으로는 idx, activeIdx, active, setActiveIdx 가 있다. 

컴포넌트들이 렌더링 될 때 본인의 idx와 activeIdx를 비교하여 active 여부를 기억하게 되고 그에 따라 active 된 애만 subMenu를 펼치는 방식으로 구현을 했다. 그리고 메뉴가 클릭될 때마다 setActiveIdx로 activeIdx를 새로 설정하게 되고 그에 따라 리렌더링 되면서 active가 새롭게 설정된 컴포넌트는 펼쳐지는 애니메이션이 동작하도록 했다.

 

css는 tailwindCSS를 사용하여 active 여부에 따라 class 명을 다르게 부여하도록 했다. 

 

active인 컴포넌트는 visibility를 visible로 가지게 되어 subMenu가 보이게 되고 아닌 컴포넌트들은 hidden이 되어 나타나지 않는다. 

 

여기서 애를 먹었던 부분이 display를 none으로 하면 되지 않을까?라고 생각하고 진행했는데 그렇게 되면 바로 컴포넌트가 사라지기 때문에 애니메이션이 작동하지 않는다. 애니메이션이 구현되는 것이 이번 드롭다운 구현의 핵심 목표였기 때문에 visiblity를 제어하여 애니메이션을 적용시켰다.

 

하지만 visibility는 순수하게 안의 내용이 안 보일 뿐 해당 태그의 높이나 너비값은 그대로 화면에 적용된다. 그렇기 때문에 해당 태그의 너비를 일괄적으로 정하게 되면 subMenu가 없어도 펼쳐지게 된다.



아래는 도서 신청 메뉴를 클릭했을 때의 모습인데 높이를 일괄적으로 주게 되면 이처럼 subMenu가 없는 항목을 클릭해도 subMenu가 너비값을 가지기 때문에 펼쳐진다. 이런 상황은 원하는 상황이 아니기 때문에 subMenu의 여부에 따라 높이 값을 다르게 설정하여 안 보이도록 했다.

className={`${active ? "visibile" : "invisible"} 
		${active && subMenu.length > 0 ? "h-[100px]" : "h-0"}
        transition-all duration-300`}

active가 아니라면 visibility도 hidden이고 높이가 0이기 때문에 subMenu는 펼쳐지지 않는다.

active일 경우 visible이 된다. 하지만 subMenu가 없다면 높이가 0이기 때문에 메뉴 아래에 드롭다운이 펼쳐지지 않는다. subMenu가 있으면 높이를 가지게 되어 드롭다운이 펼쳐지게 된다. transition과 duration을 통해 자연스럽게 펼쳐지게 된다.

 

다시 한번 구현된 화면을 봐보자.

결론

index를 잘 활용하자.

index는 여러개의 컴포넌트들이 있을 때 특정 컴포넌트를 제어할 수 있는 힘을 가진 녀석이라는 걸 절대 까먹지 말 것.

 

 

 

728x90