컨테이너 리사이즈 대응 (DataGridContainer)

ResizeObserver로 그리드 컨테이너의 실제 크기를 측정해, 레이아웃이 커지거나 작아져도 정확한 크기로 렌더링합니다.

#resize-observer#responsive-layout#container#absolute-positioning
검토일: 2026-08-18
GitHub
import * as React from 'react';
import { AXDataGrid } from '@axboot/datagrid';
import type { AXDGColumn, AXDGDataItem } from '@axboot/datagrid';
import DataGridContainer from '../components/DataGridContainer';
import { useContainerSize } from '../hooks/useContainerSize';

interface Order {
  id: string;
  customer: string;
  status: '준비 중' | '배송 중' | '완료';
  amount: number;
}

const columns: AXDGColumn<Order>[] = [
  { key: 'id', label: '주문 번호', width: 120 },
  { key: 'customer', label: '고객', width: 180 },
  { key: 'status', label: '상태', width: 110, align: 'center' },
  { key: 'amount', label: '금액', width: 140, align: 'right', itemRender: ({ value }) => `${value.toLocaleString()}원` },
];

const data: AXDGDataItem<Order>[] = Array.from({ length: 80 }, (_, index) => ({
  values: {
    id: `ORDER-${String(index + 1).padStart(4, '0')}`,
    customer: `고객 ${index + 1}`,
    status: ['준비 중', '배송 중', '완료'][index % 3] as Order['status'],
    amount: 18000 + index * 750,
  },
}));

export default function ContainerResizeExample() {
  const [isSidebarOpen, setIsSidebarOpen] = React.useState(true);
  const containerRef = React.useRef<HTMLDivElement>(null);
  const { width, height } = useContainerSize(containerRef);

  return (
    <div>
      <div style={{ alignItems: 'center', display: 'flex', gap: 12, marginBottom: 12 }}>
        <button
          type="button"
          onClick={() => setIsSidebarOpen(open => !open)}
          style={{ background: '#2563eb', border: 0, borderRadius: 6, color: '#fff', cursor: 'pointer', padding: '8px 12px' }}
        >
          {isSidebarOpen ? '사이드 패널 닫기' : '사이드 패널 열기'}
        </button>
        <span style={{ color: '#64748b', fontSize: 13 }}>컨테이너: {Math.round(width)} × {Math.round(height)}px</span>
      </div>

      <div
        style={{
          display: 'grid',
          gap: 12,
          gridTemplateColumns: isSidebarOpen ? 'minmax(150px, 0.35fr) minmax(0, 1fr)' : 'minmax(0, 1fr)',
          height: 440,
        }}
      >
        {isSidebarOpen && (
          <aside style={{ background: '#f1f5f9', borderRadius: 8, color: '#475569', padding: 16 }}>
            <strong style={{ display: 'block', marginBottom: 8 }}>필터 패널</strong>
            화면 폭이 줄어도 그리드는 이 영역의 정확한 크기를 다시 측정합니다.
          </aside>
        )}
        <DataGridContainer ref={containerRef} style={{ height: '100%', minWidth: 0 }}>
          <AXDataGrid<Order> width={width} height={height} columns={columns} data={data} />
        </DataGridContainer>
      </div>
    </div>
  );
}

1. 그리드 크기는 화면이 아니라 컨테이너에서 측정합니다

업무 화면은 사이드 패널, 탭, 분할 화면처럼 실행 중에 폭과 높이가 바뀌는 경우가 많습니다. 그리드에 최초의 window 크기나 고정 값을 전달하면 화면이 넓어질 때는 문제가 드러나지 않아도, 좁아질 때 이전 크기가 남아 가로 스크롤과 레이아웃이 어긋날 수 있습니다.

useContainerSizeResizeObserver그리드를 담는 요소의 실제 콘텐츠 영역을 관찰합니다. 측정된 width, heightAXDataGrid에 전달하면 부모 레이아웃의 확대·축소 모두에 맞춰 다시 렌더링됩니다.


2. DataGridContainer가 필요한 이유

DataGridContainerposition: relative인 측정 기준점입니다. 그 안의 DataGrid 루트는 position: absolute; inset: 0으로 배치합니다. 따라서 컨테이너는 일반 레이아웃 흐름 안에서 정확한 크기를 갖고, 그리드는 그 측정 영역을 빈틈없이 채웁니다.

특히 flex나 grid 레이아웃에서는 그리드를 담는 칸에 min-width: 0을 주어야 작은 폭으로 줄어들 수 있습니다. 컨테이너에는 명시적인 높이도 필요합니다. DataGrid는 그 높이를 기준으로 보이는 행 수와 가상 스크롤 범위를 계산합니다.


3. 적용 순서

  1. 그리드 영역을 DataGridContainer로 감싸고 높이를 정합니다.
  2. useContainerSize에 컨테이너 ref를 전달합니다.
  3. 반환된 width, heightAXDataGrid에 전달합니다.
  4. 부모가 flex/grid라면 그리드 칸에 minWidth: 0 또는 CSS min-width: 0을 적용합니다.

위 실행 예제에서 사이드 패널을 열고 닫아 보세요. 컨테이너 폭이 커질 때뿐 아니라 작아질 때도 그리드가 즉시 정확한 폭으로 맞춰집니다.


4. 피해야 할 방식

  • window.innerWidth만 한 번 읽어 그리드 크기로 사용하기
  • 컨테이너의 높이 없이 height="100%"만 전달하기
  • flex 항목에 기본 min-width: auto를 둬서 내용이 줄어들지 못하게 하기
  • 그리드 밖의 임의 DOM 크기를 측정해 여러 그리드에 공통으로 전달하기

각 그리드는 자신을 감싸는 컨테이너를 개별 관찰하는 편이 가장 예측 가능하고, 분할 화면·접이식 패널·반응형 레이아웃에서도 안정적입니다.