# Phase 2: 레이아웃 컴포넌트 구현 계획 > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 접힘 가능한 사이드바, 새로운 헤더, 모바일 반응형 레이아웃을 구현한다. **Architecture:** App Router의 (dashboard) 라우트 그룹을 사용하여 인증된 페이지에 공통 레이아웃을 적용한다. **Tech Stack:** Next.js 16, React 19, shadcn/ui, Lucide Icons, next-themes --- ## Task 1: 사이드바 컴포넌트 생성 **Files:** - Create: `frontend/src/components/layout/new-sidebar.tsx` **Code:** ```typescript "use client"; import * as React from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { LayoutDashboard, Briefcase, TrendingUp, FlaskConical, Database, ChevronLeft, ChevronRight, } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { ThemeToggle } from "@/components/ui/theme-toggle"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; const navItems = [ { href: "/", label: "대시보드", icon: LayoutDashboard }, { href: "/portfolio", label: "포트폴리오", icon: Briefcase }, { href: "/strategy", label: "전략", icon: TrendingUp }, { href: "/backtest", label: "백테스트", icon: FlaskConical }, { href: "/admin/data", label: "데이터 관리", icon: Database }, ]; interface SidebarProps { collapsed: boolean; onToggle: () => void; } export function Sidebar({ collapsed, onToggle }: SidebarProps) { const pathname = usePathname(); return ( ); } ``` **Commit:** `feat(frontend): add new Sidebar component with collapse support` --- ## Task 2: 헤더 컴포넌트 생성 **Files:** - Create: `frontend/src/components/layout/new-header.tsx` **Code:** ```typescript "use client"; import * as React from "react"; import { usePathname } from "next/navigation"; import { Menu, LogOut } from "lucide-react"; import { Button } from "@/components/ui/button"; import { api } from "@/lib/api"; import { useRouter } from "next/navigation"; const pageTitles: Record = { "/": "대시보드", "/portfolio": "포트폴리오", "/portfolio/new": "새 포트폴리오", "/strategy": "퀀트 전략", "/strategy/multi-factor": "멀티팩터 전략", "/strategy/quality": "슈퍼 퀄리티 전략", "/strategy/value-momentum": "밸류-모멘텀 전략", "/backtest": "백테스트", "/admin/data": "데이터 관리", }; interface HeaderProps { onMenuClick?: () => void; showMenuButton?: boolean; username?: string; } export function Header({ onMenuClick, showMenuButton, username }: HeaderProps) { const pathname = usePathname(); const router = useRouter(); const getPageTitle = () => { if (pageTitles[pathname]) { return pageTitles[pathname]; } if (pathname.startsWith("/portfolio/") && pathname.includes("/rebalance")) { return "리밸런싱"; } if (pathname.startsWith("/portfolio/") && pathname.includes("/history")) { return "히스토리"; } if (pathname.startsWith("/portfolio/")) { return "포트폴리오 상세"; } if (pathname.startsWith("/backtest/")) { return "백테스트 결과"; } return "Galaxy-PO"; }; const handleLogout = () => { api.logout(); router.push("/login"); }; return (
{showMenuButton && ( )}

{getPageTitle()}

{username && ( {username} )}
); } ``` **Commit:** `feat(frontend): add new Header component` --- ## Task 3: 대시보드 레이아웃 생성 **Files:** - Create: `frontend/src/components/layout/dashboard-layout.tsx` **Code:** ```typescript "use client"; import * as React from "react"; import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { Sidebar } from "@/components/layout/new-sidebar"; import { Header } from "@/components/layout/new-header"; import { Sheet, SheetContent } from "@/components/ui/sheet"; import { api } from "@/lib/api"; interface DashboardLayoutProps { children: React.ReactNode; } export function DashboardLayout({ children }: DashboardLayoutProps) { const [collapsed, setCollapsed] = useState(false); const [mobileOpen, setMobileOpen] = useState(false); const [username, setUsername] = useState(); const [loading, setLoading] = useState(true); const router = useRouter(); useEffect(() => { const checkAuth = async () => { try { const user = await api.getCurrentUser(); setUsername(user.username); } catch { router.push("/login"); } finally { setLoading(false); } }; checkAuth(); }, [router]); if (loading) { return (
로딩 중...
); } return (
{/* Desktop Sidebar */}
setCollapsed(!collapsed)} />
{/* Mobile Sidebar */} setMobileOpen(false)} /> {/* Main Content */}
setMobileOpen(true)} showMenuButton={true} username={username} />
{children}
); } ``` **Commit:** `feat(frontend): add DashboardLayout with responsive sidebar` --- ## Task 4: 페이지에 레이아웃 적용 **Files:** - Modify: `frontend/src/app/page.tsx` **Code:** ```typescript "use client"; import { DashboardLayout } from "@/components/layout/dashboard-layout"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Wallet, TrendingUp, Briefcase, RefreshCw } from "lucide-react"; export default function DashboardPage() { return (

대시보드

{/* Summary Cards */}
총 자산
₩0

데이터 없음

총 수익률
0%

데이터 없음

포트폴리오
0개

포트폴리오 없음

리밸런싱
0개

리밸런싱 필요 없음

{/* Placeholder for charts */}
자산 추이 차트 영역 (Phase 3에서 구현) 섹터 배분 차트 영역 (Phase 3에서 구현)
); } ``` **Commit:** `feat(frontend): apply DashboardLayout to main page` --- ## Task 5: 포트폴리오 목록 페이지에 레이아웃 적용 **Files:** - Modify: `frontend/src/app/portfolio/page.tsx` Read the existing file and wrap content with DashboardLayout, update styling to use shadcn/ui components. **Commit:** `feat(frontend): apply DashboardLayout to portfolio list page` --- ## Task 6: 나머지 페이지에 레이아웃 적용 **Files to modify:** - `frontend/src/app/portfolio/new/page.tsx` - `frontend/src/app/portfolio/[id]/page.tsx` - `frontend/src/app/strategy/page.tsx` - `frontend/src/app/backtest/page.tsx` - `frontend/src/app/admin/data/page.tsx` For each page, wrap existing content with DashboardLayout. **Commit:** `feat(frontend): apply DashboardLayout to all pages` --- ## Task 7: 로그인 페이지 스타일 개선 **Files:** - Modify: `frontend/src/app/login/page.tsx` Update to use shadcn/ui components (Card, Input, Button, Label). **Commit:** `feat(frontend): improve login page styling with shadcn/ui` --- ## Task 8: 빌드 및 검증 Run build and verify all pages work correctly. **Commit:** Final cleanup if needed