Files
2nd/10_Wiki/Topics/Frontend/SaaS 대시보드 및 이커머스 레이아웃 구축.md
T
2026-05-10 22:08:15 +09:00

7.7 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-saas-대시보드-및-이커머스-레이아웃-구축 SaaS 대시보드 및 이커머스 레이아웃 구축 10_Wiki/Topics verified self
SaaS Dashboard Layout
Ecommerce Layout
Admin Layout
none A 0.9 applied
frontend
layout
saas
dashboard
ecommerce
nextjs
shadcn
2026-05-10 pending
language framework
TypeScript Next.js 16 / Tailwind v4 / shadcn-ui

SaaS 대시보드 및 이커머스 레이아웃 구축

매 한 줄

"매 SaaS 의 sidebar+content shell + 매 Ecommerce 의 PLP/PDP/Cart". 2026 stack 매 Next.js 16 (App Router) + Tailwind v4 + shadcn-ui + Radix Primitive + TanStack Query. 매 Server Component 로 data fetch, Client Component 로 interactivity.

매 핵심

매 SaaS Dashboard 패턴

  • Shell: sidebar (collapsible) + topbar (search/user menu) + content area.
  • Navigation: 매 nested section, persistent state, breadcrumb.
  • Data widgets: 매 stat card, chart (Recharts/Tremor v3), table (TanStack Table).
  • Multi-tenant: 매 workspace switcher.

매 Ecommerce 패턴

  • PLP (Product List Page): 매 grid + filter sidebar + sort + pagination/infinite scroll.
  • PDP (Product Detail Page): 매 image gallery + variant selector + add-to-cart.
  • Cart/Checkout: 매 cart drawer + multi-step checkout + Stripe Elements.
  • Search: 매 typeahead + facet filtering (Algolia/MeiliSearch).

매 Stack (2026)

  • Framework: Next.js 16 App Router / Remix 3 / Astro 5.
  • UI: shadcn-ui v2 + Radix + Tailwind v4 (Oxide engine).
  • Data: TanStack Query 5 + Server Action / RSC.
  • Forms: react-hook-form + Zod.
  • Tables: TanStack Table v8.
  • Charts: Tremor v3 / Recharts.

매 응용

  1. SaaS admin: Linear-style sidebar.
  2. Ecommerce: Shopify Hydrogen / Next.js Commerce 템플릿.
  3. Internal tool: Retool-like 매 form/table heavy.

💻 패턴

App Router Layout (Next.js 16)

// app/(dashboard)/layout.tsx
import { Sidebar } from '@/components/sidebar';
import { Topbar } from '@/components/topbar';

export default async function DashboardLayout({ children }: { children: React.ReactNode }) {
  const user = await getCurrentUser();
  return (
    <div className="grid h-screen grid-cols-[260px_1fr]">
      <Sidebar />
      <div className="flex flex-col overflow-hidden">
        <Topbar user={user} />
        <main className="flex-1 overflow-auto p-6">{children}</main>
      </div>
    </div>
  );
}

shadcn-ui Sidebar (collapsible)

'use client';
import { Sidebar, SidebarContent, SidebarMenu, SidebarMenuItem } from '@/components/ui/sidebar';
import { Home, Users, Settings } from 'lucide-react';

const items = [
  { title: 'Home', url: '/', icon: Home },
  { title: 'Users', url: '/users', icon: Users },
  { title: 'Settings', url: '/settings', icon: Settings },
];

export function AppSidebar() {
  return (
    <Sidebar collapsible="icon">
      <SidebarContent>
        <SidebarMenu>
          {items.map((item) => (
            <SidebarMenuItem key={item.title}>
              <a href={item.url}>
                <item.icon className="size-4" />
                <span>{item.title}</span>
              </a>
            </SidebarMenuItem>
          ))}
        </SidebarMenu>
      </SidebarContent>
    </Sidebar>
  );
}

Stat Card Grid

function StatCards({ stats }: { stats: Stat[] }) {
  return (
    <div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
      {stats.map((s) => (
        <div key={s.id} className="rounded-lg border bg-card p-6">
          <div className="text-sm text-muted-foreground">{s.label}</div>
          <div className="mt-2 text-3xl font-semibold">{s.value}</div>
          <div className="mt-1 text-sm text-emerald-600">{s.delta}</div>
        </div>
      ))}
    </div>
  );
}

TanStack Table (server pagination)

'use client';
import { useReactTable, getCoreRowModel } from '@tanstack/react-table';
import { useQuery } from '@tanstack/react-query';

export function UsersTable() {
  const [page, setPage] = useState(0);
  const { data } = useQuery({
    queryKey: ['users', page],
    queryFn: () => fetch(`/api/users?page=${page}`).then(r => r.json()),
  });

  const table = useReactTable({
    data: data?.users ?? [],
    columns,
    getCoreRowModel: getCoreRowModel(),
    manualPagination: true,
    pageCount: data?.totalPages,
  });

  return <table>...</table>;
}

Ecommerce PLP (RSC + filter)

// app/products/page.tsx
export default async function ProductsPage({ searchParams }: { searchParams: Promise<{ category?: string; sort?: string }> }) {
  const params = await searchParams;
  const products = await getProducts({ category: params.category, sort: params.sort });
  return (
    <div className="grid grid-cols-[240px_1fr] gap-6">
      <FilterSidebar />
      <div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
        {products.map(p => <ProductCard key={p.id} product={p} />)}
      </div>
    </div>
  );
}

Cart Drawer (Zustand + shadcn Sheet)

'use client';
import { create } from 'zustand';
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';

const useCart = create<{ items: Item[]; add: (i: Item) => void; remove: (id: string) => void }>((set) => ({
  items: [],
  add: (i) => set((s) => ({ items: [...s.items, i] })),
  remove: (id) => set((s) => ({ items: s.items.filter(x => x.id !== id) })),
}));

export function CartDrawer() {
  const items = useCart((s) => s.items);
  return (
    <Sheet>
      <SheetTrigger>Cart ({items.length})</SheetTrigger>
      <SheetContent>
        {items.map(i => <div key={i.id}>{i.name}</div>)}
      </SheetContent>
    </Sheet>
  );
}

Server Action (Add to cart)

// app/actions/cart.ts
'use server';
import { cookies } from 'next/headers';

export async function addToCart(productId: string, qty: number) {
  const cart = (await cookies()).get('cart');
  // ... persist
  return { success: true };
}

매 결정 기준

상황 Approach
New SaaS dashboard Next.js 16 + shadcn + Tailwind v4
Ecommerce Next.js Commerce 또는 Shopify Hydrogen
Internal admin Refine.dev / Retool
Mobile-first ecommerce Astro + Solid Islands
Real-time dashboard Next.js + WebSocket (Pusher/Ably)
Heavy data viz Tremor + Recharts

기본값: 매 SaaS 매 Next.js + shadcn-ui + Tailwind v4. 매 Ecommerce 매 Next.js Commerce 템플릿 fork.

🔗 Graph

🤖 LLM 활용

언제: greenfield SaaS / ecommerce 의 layout shell 구축, design system 적용. 언제 X: 매 micro-frontend / heavy SSR-disabled 의 SPA — 매 다른 stack (Vite + React Router) 적합.

안티패턴

  • 모든 component 'use client': 매 RSC 의 benefit 상실 — server-first.
  • inline style + Tailwind 혼재: 매 inconsistency — design token 통일.
  • Custom UI library scratch: 매 shadcn-ui copy-paste 가 maintenance 우위.
  • Cart in localStorage only: 매 cross-device sync 불가 — server-side cart + cookie.
  • No skeleton during loading: 매 CLS / UX 저하 — Suspense + skeleton.

🧪 검증 / 중복

  • Verified (Next.js 16 docs, shadcn-ui v2, Tailwind v4, Vercel Commerce).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — Next.js 16 / shadcn / Tailwind v4 modern stack