refactor(반응형): ResponsiveDataView 를 container query 기반으로 전환

기존: viewport 기준 (lg:hidden, sm:grid-cols-2) — 사이드바 펼친 상태에서 콘텐츠 영역의 실제 width 와 무관하게 동작 → 좁은 영역에 2열 카드가 들어가 카드가 잘려보이는 문제

신: @container 기반 — 컴포넌트가 자기 부모 컨테이너 width 에 반응
- 컨테이너 < 32rem (512px): 카드 1열
- 32~48rem (512~768px): 카드 2열
- ≥ 48rem (768px): 데스크톱 테이블

Tailwind v4 의 first-class container query 활용 (별도 플러그인 불필요). 데스크톱 테이블의 viewport 기준 max-height 스크롤은 유지.

근거: 2026 베스트프랙티스 — page layout=media query, 컴포넌트=container query (LogRocket / NN-Group / Tailwind v4 가이드).
This commit is contained in:
2026-05-13 08:43:13 +09:00
parent 0d5d1fe10d
commit 6d5ca2f23a
@@ -110,11 +110,11 @@ export function ResponsiveDataView<T>({
// --- 로딩 스켈레톤 ---
if (isLoading) {
return (
<>
<div className="@container">
{/* 데스크톱 테이블 스켈레톤 */}
<div
className={cn(
"hidden rounded-lg border bg-card shadow-sm lg:block",
"hidden rounded-lg border bg-card shadow-sm @3xl:block",
tableContainerClassName
)}
>
@@ -162,10 +162,11 @@ export function ResponsiveDataView<T>({
</Table>
</div>
{/* 모바일 카드 스켈레톤 */}
{/* 모바일 카드 스켈레톤 — container query 기반:
컨테이너 < 32rem(512px) = 1열, 32~48rem = 2열, ≥ 48rem(768px) = 테이블 */}
<div
className={cn(
"grid gap-4 sm:grid-cols-2 lg:hidden",
"grid gap-4 @lg:grid-cols-2 @3xl:hidden",
scrollContainer && "max-h-[calc(100vh-280px)] overflow-y-auto",
cardContainerClassName
)}
@@ -199,7 +200,7 @@ export function ResponsiveDataView<T>({
</div>
))}
</div>
</>
</div>
);
}
@@ -213,12 +214,14 @@ export function ResponsiveDataView<T>({
}
// --- 실제 데이터 렌더링 ---
// 부모 wrapper 가 @container — 자식들은 viewport 가 아닌 자기 컨테이너 width 기준으로 분기.
// 사이드바 펼친 상태에서도 콘텐츠 영역 실제 width 에 맞게 자연스럽게 테이블↔카드 전환.
return (
<>
{/* 데스크톱 테이블 (lg 이상) */}
<div className="@container">
{/* 데스크톱 테이블 (컨테이너 ≥ 48rem / 768px) */}
<div
className={cn(
"hidden rounded-lg border bg-card shadow-sm lg:block",
"hidden rounded-lg border bg-card shadow-sm @3xl:block",
// scrollContainer 모드: 뷰포트 기반 max-height + 자체 세로 스크롤 + sticky 헤더.
// flex 기반 계산이 shadcn Table 의 내부 wrapper(overflow-x-auto) 와 충돌해
// 신뢰성 떨어지므로 viewport 기준으로 명시. 페이지 헤더/툴바/페이지네이션 약 280px 가정.
@@ -283,10 +286,10 @@ export function ResponsiveDataView<T>({
</Table>
</div>
{/* 모바일 카드 (lg 미만) */}
{/* 모바일 카드 (컨테이너 < 48rem) — < 32rem 1열, 32~48rem 2열 */}
<div
className={cn(
"grid gap-4 sm:grid-cols-2 lg:hidden",
"grid gap-4 @lg:grid-cols-2 @3xl:hidden",
// scrollContainer 모드: 카드 뷰도 자체 세로 스크롤. 부모가 overflow-hidden 이라
// 별도 height 제약 없으면 카드들이 잘려 보임. 데스크톱 테이블과 동일 viewport 기준.
scrollContainer && "max-h-[calc(100vh-280px)] overflow-y-auto",
@@ -345,7 +348,7 @@ export function ResponsiveDataView<T>({
);
})}
</div>
</>
</div>
);
}