사용자가 웹 브라우저를 켜고 여러 페이지를 넘나들 때 가장 많이 의존하는 인터페이스는 바로 화면 왼쪽에 상시 배치된 사이드바(Sidebar) 내비게이션입니다.
단순히 <a> 링크들을 일렬로 나열해 놓는 사이드바는 세련미가 떨어집니다. 현재 사용자가 들어와 있는 페이지 주소(Route)를 똑똑하게 감지해 불을 켜주고, 주식·가상자산·원자재 등 자산 군에 맞게 서브 메뉴가 유연하게 분류되어 나타나는 지능형 다이내믹 사이드바를 구현하겠습니다.
지능형 사이드바의 핵심 요건
- 지능형 경로 활성화(Active Route Highlight): Next.js App Router가 제공하는
usePathname을 사용하여, 현재 접속한 URL 경로와 메뉴 링크의 경로를 실시간 대조하여 활성화된 메뉴 배경색과 폰트 컬러를 다이내믹하게 부각합니다. - 동적 그룹화(Category-based Grouping): 단순히 메뉴 목록을 하드코딩하지 않고, 대시보드, 투자 자산군 분석, 관리자 메뉴 등으로 계층 구조화하고, 유저의 클릭 여부에 따라 서브 메뉴가 슬라이딩 형태로 나타나도록 설계합니다.
- 사이드바 토글 및 최소화(Collapsible State): 대용량 차트나 복잡한 Ag-Grid 테이블을 분석할 때는 화면 가로 영역이 넓어야 좋습니다. 버튼 클릭 하나로 사이드바 너비를 좁혀 아이콘만 남겨 차트 영역을 극대화하는 반응형 전환 기능을 지원합니다.
🤖 실전! 지능형 사이드바 컴포넌트 추가(2-19)
🧠 이번 실습은 Next.js의 전체 레이아웃에 적용될 지능형 사이드바(Sidebar.tsx)를 추가하여, 현재 라우트 경로 감지 및 카테고리별 아코디언 메뉴 기능을 구현하는 과정입니다.
✨ 사용자의 의도(Vibe)
사용자가 편리하게 대시보드를 탐색할 수 있도록 라우팅 상태와 메뉴가 반응하는 지능형 사이드바 컴포넌트를 설계하고 적용해줘.
- 파일명:
frontend/src/components/layout/Sidebar.tsx - 기능 1:
usePathname을 활용하여 현재 사용자가 보고 있는 페이지 경로(예:/dashboard,/assets-list)와 매칭되는 사이드바 메뉴에 하이라이트 효과(배경색 및 아이콘 반전)를 실시간 반영해줘. - 기능 2: 대시보드, 자산군 리스트, 세부 차트 분석으로 아코디언 서브 메뉴를 그룹화하여 깔끔하게 정리해줘.
- 기능 3: 사이드바 하단부에는 사이드바 전체 너비를 줄이는 Collapsible (토글 접기) 버튼을 추가하여 차트와 테이블 분석 시 화면 넓이를 확보할 수 있게 해줘.
💻 AI가 생성한 코드 (Syntax) 및 결과
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> ); }
- 화면 왼쪽 사이드바의 접기 단추를 누르면 깔끔하게 아이콘만 표시되는 작은 바로 변하고, 활성화된 페이지만 파란색 배경으로 밝게 켜집니다.