woongsnote

반응형 사이드바 구현하기

Next.js 프로젝트에서 shadcn/ui를 사용해서 반응형 사이드바를 구현하는 방법

5분 읽기
목차

개요

Next.js 프로젝트에서 shadcn/ui를 사용해서, 반응형 사이드바를 쉽게 구현할 수 있다.

구현 방법

dashboard-02의 코드를 베이스로 구현하려고 한다.

※ 해당 컴포넌트에 대한 전체 코드: dashboard-02

  1. 패키지 설치

    • 먼저, shadcn/ui를 설치한다.
    install shadcn/ui
    npx shadcn-ui@latest init
  2. 코드 복사

    • copy code버튼을 클릭해서, dashboard-02의 코드를 가져와서 app/page.tsx에 붙여넣어 준다.
  3. 컴포넌트 추가

    • 아직 컴포넌트가 없으므로 오류가 생길 것이다. 아래 명령어로 컴포넌트를 추가한다.
    add components
    npx shadcn-ui@latest add badge button card dropdown-menu input sheet
  4. 컴포넌트 분리

    • mobile navigation에 해당하는 부분을 components/ui에 mobile-nav.tsx 파일을 생성해서 옮겨준다.
    mobile-nav.tsx
    import Link from 'next/link';
    import {
      Menu,
      Package2,
      Home,
      ShoppingCart,
      Package,
      Users,
      LineChart,
    } from 'lucide-react';
    import { Button } from './button';
    import {
      Card,
      CardHeader,
      CardTitle,
      CardDescription,
      CardContent,
    } from './card';
    import { Sheet, SheetTrigger, SheetContent } from './sheet';
    import { Badge } from './badge';
     
    export default function MobileNav() {
      return (
        <Sheet>
          <SheetTrigger asChild>
            <Button variant="outline" size="icon" className="shrink-0 md:hidden">
              <Menu className="h-5 w-5" />
              <span className="sr-only">Toggle navigation menu</span>
            </Button>
          </SheetTrigger>
          <SheetContent side="left" className="flex flex-col">
            <nav className="grid gap-2 text-lg font-medium">
              <Link
                href="#"
                className="flex items-center gap-2 text-lg font-semibold"
              >
                <Package2 className="h-6 w-6" />
                <span className="sr-only">Acme Inc</span>
              </Link>
              <Link
                href="#"
                className="text-muted-foreground hover:text-foreground mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2"
              >
                <Home className="h-5 w-5" />
                Dashboard
              </Link>
              <Link
                href="#"
                className="bg-muted text-foreground hover:text-foreground mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2"
              >
                <ShoppingCart className="h-5 w-5" />
                Orders
                <Badge className="ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-full">
                  6
                </Badge>
              </Link>
              <Link
                href="#"
                className="text-muted-foreground hover:text-foreground mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2"
              >
                <Package className="h-5 w-5" />
                Products
              </Link>
              <Link
                href="#"
                className="text-muted-foreground hover:text-foreground mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2"
              >
                <Users className="h-5 w-5" />
                Customers
              </Link>
              <Link
                href="#"
                className="text-muted-foreground hover:text-foreground mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2"
              >
                <LineChart className="h-5 w-5" />
                Analytics
              </Link>
            </nav>
            <div className="mt-auto">
              <Card>
                <CardHeader>
                  <CardTitle>Upgrade to Pro</CardTitle>
                  <CardDescription>
                    Unlock all features and get unlimited access to our support
                    team.
                  </CardDescription>
                </CardHeader>
                <CardContent>
                  <Button size="sm" className="w-full">
                    Upgrade
                  </Button>
                </CardContent>
              </Card>
            </div>
          </SheetContent>
        </Sheet>
      );
    }
  • sidebar에 해당하는 부분을 components/ui에 sidebar.tsx 파일을 생성해서 옮겨준다.

    sidebar.tsx
    import Link from 'next/link';
    import {
      Bell,
      Home,
      ShoppingCart,
      Package,
      Users,
      LineChart,
      Package2,
    } from 'lucide-react';
    import { Button } from './button';
    import { Badge } from './badge';
    import {
      Card,
      CardHeader,
      CardTitle,
      CardDescription,
      CardContent,
    } from './card';
     
    export default function Sidebar() {
      return (
        <aside className="bg-muted/40 hidden border-r md:block">
          <div className="flex h-full max-h-screen flex-col gap-2">
            <div className="flex h-14 items-center border-b px-4 lg:h-15 lg:px-6">
              <Link href="/" className="flex items-center gap-2 font-semibold">
                <Package2 className="h-6 w-6" />
                <span className="">Acme Inc</span>
              </Link>
              <Button variant="outline" size="icon" className="ml-auto h-8 w-8">
                <Bell className="h-4 w-4" />
                <span className="sr-only">Toggle notifications</span>
              </Button>
            </div>
            <div className="flex-1">
              <nav className="grid items-start px-2 text-sm font-medium lg:px-4">
                <Link
                  href="#"
                  className="text-muted-foreground hover:text-primary flex items-center gap-3 rounded-lg px-3 py-2 transition-all"
                >
                  <Home className="h-4 w-4" />
                  Dashboard
                </Link>
                <Link
                  href="#"
                  className="text-muted-foreground hover:text-primary flex items-center gap-3 rounded-lg px-3 py-2 transition-all"
                >
                  <ShoppingCart className="h-4 w-4" />
                  Orders
                  <Badge className="ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-full">
                    6
                  </Badge>
                </Link>
                <Link
                  href="#"
                  className="bg-muted text-primary hover:text-primary flex items-center gap-3 rounded-lg px-3 py-2 transition-all"
                >
                  <Package className="h-4 w-4" />
                  Products{' '}
                </Link>
                <Link
                  href="#"
                  className="text-muted-foreground hover:text-primary flex items-center gap-3 rounded-lg px-3 py-2 transition-all"
                >
                  <Users className="h-4 w-4" />
                  Customers
                </Link>
                <Link
                  href="#"
                  className="text-muted-foreground hover:text-primary flex items-center gap-3 rounded-lg px-3 py-2 transition-all"
                >
                  <LineChart className="h-4 w-4" />
                  Analytics
                </Link>
              </nav>
            </div>
            <div className="mt-auto p-4">
              <Card x-chunk="dashboard-02-chunk-0">
                <CardHeader className="p-2 pt-0 md:p-4">
                  <CardTitle>Upgrade to Pro</CardTitle>
                  <CardDescription>
                    Unlock all features and get unlimited access to our support
                    team.
                  </CardDescription>
                </CardHeader>
                <CardContent className="p-2 pt-0 md:p-4 md:pt-0">
                  <Button size="sm" className="w-full">
                    Upgrade
                  </Button>
                </CardContent>
              </Card>
            </div>
          </div>
        </aside>
      );
    }
  1. 코드 정리
  • 사용하지 않는 부분에 대한 코드를 제거해준다. 예를 들어, 사이드바 하단의 Card 부분을 제거해준다.
  • 중복되는 코드에 대해 컴포넌트를 생성해서 분리해준다. 네비게이션에 들어갈 목록은 화면 사이즈에 상관없이 동일하므로 이를 새로운 컴포넌트로 분리해준다.

구현 결과

아래 링크를 통해 정리된 소스 코드의 완성본을 확인할 수 있다.

완성된 소스 코드

참고: Youtube Link