콘텐츠로 건너뛰기

10-3. 지능형 사이드바와 다이내믹 메뉴

  • 기준

사용자가 웹 브라우저를 켜고 여러 페이지를 넘나들 때 가장 많이 의존하는 인터페이스는 바로 화면 왼쪽에 상시 배치된 사이드바(Sidebar) 내비게이션입니다.

단순히 <a> 링크들을 일렬로 나열해 놓는 사이드바는 세련미가 떨어집니다. 현재 사용자가 들어와 있는 페이지 주소(Route)를 똑똑하게 감지해 불을 켜주고, 주식·가상자산·원자재 등 자산 군에 맞게 서브 메뉴가 유연하게 분류되어 나타나는 지능형 다이내믹 사이드바를 구현하겠습니다.

지능형 사이드바의 핵심 요건

  1. 지능형 경로 활성화(Active Route Highlight): Next.js App Router가 제공하는 usePathname을 사용하여, 현재 접속한 URL 경로와 메뉴 링크의 경로를 실시간 대조하여 활성화된 메뉴 배경색과 폰트 컬러를 다이내믹하게 부각합니다.
  2. 동적 그룹화(Category-based Grouping): 단순히 메뉴 목록을 하드코딩하지 않고, 대시보드, 투자 자산군 분석, 관리자 메뉴 등으로 계층 구조화하고, 유저의 클릭 여부에 따라 서브 메뉴가 슬라이딩 형태로 나타나도록 설계합니다.
  3. 사이드바 토글 및 최소화(Collapsible State): 대용량 차트나 복잡한 Ag-Grid 테이블을 분석할 때는 화면 가로 영역이 넓어야 좋습니다. 버튼 클릭 하나로 사이드바 너비를 좁혀 아이콘만 남겨 차트 영역을 극대화하는 반응형 전환 기능을 지원합니다.

🤖 실전! 지능형 사이드바 컴포넌트 추가(2-19)

🧠 이번 실습은 Next.js의 전체 레이아웃에 적용될 지능형 사이드바(Sidebar.tsx)를 추가하여, 현재 라우트 경로 감지 및 카테고리별 아코디언 메뉴 기능을 구현하는 과정입니다.

✨ 사용자의 의도(Vibe)

사용자가 편리하게 대시보드를 탐색할 수 있도록 라우팅 상태와 메뉴가 반응하는 지능형 사이드바 컴포넌트를 설계하고 적용해줘.

  1. 파일명: frontend/src/components/layout/Sidebar.tsx
  2. 기능 1: usePathname을 활용하여 현재 사용자가 보고 있는 페이지 경로(예: /dashboard, /assets-list)와 매칭되는 사이드바 메뉴에 하이라이트 효과(배경색 및 아이콘 반전)를 실시간 반영해줘.
  3. 기능 2: 대시보드, 자산군 리스트, 세부 차트 분석으로 아코디언 서브 메뉴를 그룹화하여 깔끔하게 정리해줘.
  4. 기능 3: 사이드바 하단부에는 사이드바 전체 너비를 줄이는 Collapsible (토글 접기) 버튼을 추가하여 차트와 테이블 분석 시 화면 넓이를 확보할 수 있게 해줘.

💻 AI가 생성한 코드 (Syntax) 및 결과

  1. frontend/src/components/layout/Sidebar.tsx
"use client";  import React, { useState } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import {    LayoutDashboard,    LineChart,    TableProperties,    ChevronLeft,    ChevronRight,    TrendingUp } from "lucide-react";  interface MenuItem {   title: string;   path: string;   icon: React.ReactNode; }  interface MenuGroup {   groupName: string;   items: MenuItem[]; }  export default
function Sidebar() {   const pathname = usePathname();   const [isCollapsed, setIsCollapsed] = useState(false);    const menuGroups: MenuGroup[] = [     {       groupName: "Overview",       items: [         { title: "Dashboard", path: "/dashboard", icon: <LayoutDashboard className="w-5 h-5" /> }       ]     },     {       groupName: "Analysis",       items: [         { title: "Assets List", path: "/assets-list", icon: <TableProperties className="w-5 h-5" /> },         { title: "Technical Charts", path: "/line-chart", icon: <LineChart className="w-5 h-5" /> }       ]     }   ];
return (     <aside className={`bg-gray-900 text-gray-100 flex flex-col transition-all duration-300 h-screen sticky top-0 ${isCollapsed ? "w-16" : "w-64"}`}>       <div className="flex items-center justify-between p-4 border-b border-gray-800">         {!isCollapsed && (           <span className="flex items-center gap-2 font-bold text-xl tracking-wider text-blue-400">             <TrendingUp className="w-6 h-6" /> PULSE           </span>         )}         <button            onClick={() => setIsCollapsed(!isCollapsed)}            className="p-1 rounded bg-gray-800 hover:bg-gray-700 text-gray-400 ml-auto"         >           {isCollapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}         </button>       </div>        <nav className="flex-1 px-3 py-4 space-y-6 overflow-y-auto">         {menuGroups.map((group, groupIdx) => (           <div key={groupIdx} className="space-y-1">             {!isCollapsed && (               <h3 className="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider">                 {group.groupName}               </h3>             )}             {group.items.map((item, itemIdx) => {               const isActive = pathname === item.path;
return (                 <Link                    key={itemIdx}                    href={item.path}                    className={`flex items-center gap-4 px-3 py-2.5 rounded-lg text-sm font-medium transition-all ${                     isActive                        ? "bg-blue-600 text-white shadow-lg"                        : "text-gray-400 hover:bg-gray-800 hover:text-white"                   }`}                 >                   {item.icon}                   {!isCollapsed && <span>{item.title}</span>}                 </Link>               );             })}           </div>         ))}       </nav>     </aside>     ); }
  1. 화면 왼쪽 사이드바의 접기 단추를 누르면 깔끔하게 아이콘만 표시되는 작은 바로 변하고, 활성화된 페이지만 파란색 배경으로 밝게 켜집니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다