"use client"; /** * Phase 1 PoC — 카드 폭 기반 반응형 메커니즘 시각 검증 (2026-04-10) * * 스펙: notes/gbpark/2026-04-10-card-engine-final-spec.md (§2, §10) * * 확인 항목: * 1. v2-table-list 래퍼의 ResizeObserver 가 카드 폭에 따라 data-mode 를 전환 * 2. v2-table-search-widget 의 CSS @container 가 narrow 에서 세로 스택으로 전환 * * 실제 v2-table-list/search-widget 의 전체 렌더링은 ComponentRegistry + 백엔드 * 데이터가 필요하므로, 이 페이지는 두 반응형 메커니즘의 **레이아웃 로직만** * 동일하게 재현해 시각 검증한다. 실 컴포넌트 연동 검증은 Phase 2 대시보드가 * 작동한 뒤 수행. */ import { useEffect, useRef, useState } from "react"; import "@/lib/registry/components/v2-table-search-widget/table-search-widget-responsive.css"; const NARROW_BREAKPOINT = 600; export default function TestCardResponsivePage() { const [width, setWidth] = useState(800); const rootRef = useRef(null); const [detectedMode, setDetectedMode] = useState<"wide" | "narrow">("wide"); useEffect(() => { const el = rootRef.current; if (!el || typeof ResizeObserver === "undefined") return; const apply = (w: number) => { setDetectedMode((prev) => { const next = w < NARROW_BREAKPOINT ? "narrow" : "wide"; return prev === next ? prev : next; }); }; apply(el.getBoundingClientRect().width); const ro = new ResizeObserver((entries) => { for (const entry of entries) apply(entry.contentRect.width); }); ro.observe(el); return () => ro.disconnect(); }, []); return (

Phase 1 PoC — 카드 폭 반응형 검증

스펙: notes/gbpark/2026-04-10-card-engine-final-spec.md §2, §10

setWidth(Number(e.target.value))} className="flex-1" /> {width}px 감지된 모드: {detectedMode}
{[320, 520, 800, 1200].map((w) => ( ))}
카드 시뮬레이션 (width: {width}px)
{/* ── 1. v2-text-display (경량, 항상 동일) ── */}
수주관리
{/* ── 2. canonical stats (경량, container-type 만 부착) ── */}
{[ { label: "전체", v: "128" }, { label: "진행", v: "42" }, { label: "완료", v: "74" }, { label: "대기", v: "12" }, ].map((k) => (
{k.label}
{k.v}
))}
{/* ── 3. v2-table-search-widget (CSS @container 완전 마이그레이션) ── */}
128건
{/* ── 4. v2-button-primary (경량) ── */}
{/* ── 5. v2-table-list (ResizeObserver 완전 마이그레이션) ── */}
v2-table-list data-v2-table-list-mode={detectedMode}
{detectedMode === "wide" ? ( {[ { no: "SO-2026-0001", cust: "ACME 코리아", status: "완료", amt: 12_300_000 }, { no: "SO-2026-0002", cust: "델타 산업", status: "진행", amt: 8_450_000 }, { no: "SO-2026-0003", cust: "글로벌 테크", status: "대기", amt: 5_200_000 }, { no: "SO-2026-0004", cust: "한국전자", status: "완료", amt: 15_800_000 }, ].map((row, i) => ( ))}
# 수주번호 고객 상태 금액
{i + 1} {row.no} {row.cust} {row.status} {row.amt.toLocaleString()}
) : (
{[ { no: "SO-2026-0001", cust: "ACME 코리아", status: "완료", amt: 12_300_000 }, { no: "SO-2026-0002", cust: "델타 산업", status: "진행", amt: 8_450_000 }, { no: "SO-2026-0003", cust: "글로벌 테크", status: "대기", amt: 5_200_000 }, { no: "SO-2026-0004", cust: "한국전자", status: "완료", amt: 15_800_000 }, ].map((row) => (
{row.no} {row.status}
{row.cust}
{row.amt.toLocaleString()} 원
))}
)}
✅ 검증 포인트
  • 카드 폭을 800 → 400 으로 줄이면{" "} v2-table-list 가 테이블 → 카드 리스트로 전환 (ResizeObserver 기반).
  • 같은 조건에서 v2-table-search-widget 의 필터/버튼이 가로 → 세로 스택으로 재배열 (CSS @container 기반).
  • 나머지 컴포넌트(text-display, stats, button-primary)는 container-type: inline-size 만 부착된 상태. 모드 분기는 Phase 2 에서 개별 재작성.
); }