reopt designreopt design
DocsExploreToolsPricingBuilder
Start
Start
Playground
Core Concepts
Core Concepts
DataGrid
DataGrid 개요
Editable Data Entry
Operations Monitoring
Large Data
Remote Protocol
Migration Playbook
App Composition Guide
Column Playbook
Build & Operate
Skills
Release Notes
Reference
Hook Reference
Type Reference
Oopt-datagrid
reopt designreopt design

A design system for the AI era

  • Docs
  • Pricing
  • Releases
  • GitHub
  • Terms of Service
  • Privacy Policy

© 2026 reopt-ai. All rights reserved.

Reference
  1. Docs
  2. /
  3. Reference
  4. /
  5. Hook Reference

Hook 레퍼런스

useAsyncDataSource, useColumnSort, useMovableColumns 등 Hook의 시그니처와 사용 예제

reopt design · Updated Jun 26, 2026

시작하기핵심 개념개요편집형 데이터 입력운영 모니터링대규모 데이터원격 연동 계약마이그레이션 플레이북앱 조합 가이드컬럼 설계 플레이북Skills릴리즈 노트Hook 레퍼런스타입 레퍼런스

1. useAsyncDataSource

대규모 데이터를 페이지 단위로 지연 로딩합니다. 뷰포트에 보이는 영역 기준으로 필요한 페이지만 자동으로 요청합니다.

Options

속성타입설명
rowCountnumber전체 행 수 (서버에서 제공)
pageSizenumber한 페이지당 행 수 (기본 200)
preloadPagesnumber현재 페이지 앞/뒤로 미리 로드할 페이지 수 (기본 1)
getRowData(range) => Promise<Row[]>페이지 데이터를 비동기로 가져오는 함수
makePlaceholderRow(index) => Row로딩 중 표시할 플레이스홀더 행 생성

Returns

속성타입설명
rowsRow[]전체 길이 배열 (로딩 안 된 행은 placeholder)
onVisibleRegionChangedcallbackDataGrid의 onVisibleRegionChanged에 연결
updateRow(index, row) => void특정 행 데이터를 수동 업데이트
isRowLoaded(index) => boolean해당 행이 로드 완료인지 확인
tsx
"use client";

import {
  DataGrid,
  useAsyncDataSource,
  type DataGridColumn,
} from "@reopt-ai/opt-datagrid";

interface Order {
  id: string;
  product: string;
  amount: number;
}

const columns: DataGridColumn<Order>[] = [
  { id: "product", title: "상품", getValue: (r) => r.product, width: 200 },
  { id: "amount", title: "금액", getValue: (r) => r.amount, width: 120 },
];

export default function AsyncGrid() {
  const { rows, onVisibleRegionChanged } = useAsyncDataSource<Order>({
    rowCount: 50_000,
    pageSize: 200,
    preloadPages: 1,
    getRowData: async ({ offset, limit }) => {
      const res = await fetch(`/api/orders?offset=${offset}&limit=${limit}`);
      return res.json();
    },
    makePlaceholderRow: (index) => ({
      id: `placeholder-${index}`,
      product: "로딩 중...",
      amount: 0,
    }),
  });

  return (
    <DataGrid
      columns={columns}
      rows={rows}
      getRowId={(r) => r.id}
      onVisibleRegionChanged={onVisibleRegionChanged}
      height={600}
    />
  );
}

2. useColumnSort

컬럼 헤더 클릭으로 정렬 상태를 관리합니다. 3가지 정렬 모드를 지원합니다.

mode동작
defaultgetValue 반환값 기준 기본 비교 (문자열/숫자 자동 판별)
raw원본 Row 객체를 직접 비교 함수에 전달
smart숫자 접두어, 날짜 문자열 등을 자동 인식하여 자연 정렬
tsx
import { DataGrid, useColumnSort } from "@reopt-ai/opt-datagrid";

function SortableGrid({ initialRows, columns }) {
  const [sort, setSort] = useState<{
    column: string;
    direction: "asc" | "desc";
  }>();

  const { rows, getOriginalIndex } = useColumnSort({
    rows: initialRows,
    columns,
    sort: sort
      ? { column: sort.column, direction: sort.direction, mode: "smart" }
      : undefined,
  });

  return (
    <DataGrid
      columns={columns}
      rows={rows}
      height={400}
      onHeaderClick={(columnId) => {
        setSort((prev) => {
          if (prev?.column === columnId) {
            return prev.direction === "asc"
              ? { column: columnId, direction: "desc" }
              : undefined; // 세 번째 클릭: 정렬 해제
          }
          return { column: columnId, direction: "asc" };
        });
      }}
    />
  );
}

3. useMovableColumns

드래그 앤 드롭으로 컬럼 순서를 변경합니다. 변경된 순서를 localStorage 등에 저장하면 사용자별 레이아웃 유지가 가능합니다.

tsx
import {
  DataGrid,
  useMovableColumns,
  type DataGridColumn,
} from "@reopt-ai/opt-datagrid";

function ReorderableGrid({ baseColumns, rows }) {
  const { columns, moveColumn, resetOrder } = useMovableColumns({
    columns: baseColumns,
    onColumnMoved: (fromIndex, toIndex) => {
      // 컬럼 순서를 localStorage에 저장
      const ids = columns.map((c) => c.id);
      const moved = [...ids];
      const [item] = moved.splice(fromIndex, 1);
      moved.splice(toIndex, 0, item);
      localStorage.setItem("column-order", JSON.stringify(moved));
    },
  });

  return (
    <>
      <button onClick={resetOrder}>순서 초기화</button>
      <DataGrid
        columns={columns}
        rows={rows}
        height={400}
        onColumnMoved={moveColumn}
      />
    </>
  );
}

4. useCollapsingGroups

컬럼 그룹 헤더를 클릭하면 해당 그룹의 컬럼을 접을 수 있습니다. 접힌 그룹은 지정된 너비로 축소됩니다.

tsx
import {
  DataGrid,
  useCollapsingGroups,
} from "@reopt-ai/opt-datagrid";

function CollapsibleGroupGrid({ baseColumns, rows }) {
  const {
    columns,
    collapsedGroups,
    onGroupHeaderClicked,
    setCollapsedGroups,
  } = useCollapsingGroups({
    columns: baseColumns,
    collapsedGroups: new Set(), // 초기 접힘 상태
    collapsedWidth: 40,         // 접힌 컬럼 너비 (px)
  });

  return (
    <>
      <button onClick={() => setCollapsedGroups(new Set())}>
        모든 그룹 펼치기
      </button>
      <DataGrid
        columns={columns}
        rows={rows}
        height={400}
        onGroupHeaderClicked={onGroupHeaderClicked}
      />
    </>
  );
}

5. useDataGridRemoteDataSource

서버와의 전체 CRUD 연동을 위한 프로토콜 Hook입니다. 뷰 열기/닫기, 행 로딩, 편집 저장, 실시간 무효화 구독까지 하나의 계약으로 다룹니다.

Protocol 콜백

콜백설명
openView뷰 초기화. rowCount, 초기 정렬/필터 설정 반환
closeView뷰 정리. 구독 해제, 리소스 해제
loadRowsoffset/limit 범위의 행 데이터를 서버에서 로드
saveEdits편집된 셀 배치를 서버에 저장. 실패/거부 셀 목록 반환
subscribeToInvalidationsWebSocket 등으로 실시간 행 무효화 알림 수신

Returns

속성설명
isViewReady뷰 초기화 완료 여부
rows현재 로드된 행 배열 (placeholder 포함)
failedEdits네트워크 오류로 저장 실패한 편집 목록
rejectedEdits서버가 거부한 편집 목록 (비즈니스 검증 실패)
telemetry로드/저장 요청 수, 평균 레이턴시 등 통계
tsx
import {
  DataGrid,
  useDataGridRemoteDataSource,
} from "@reopt-ai/opt-datagrid";

function RemoteGrid({ viewId }: { viewId: string }) {
  const {
    rows,
    isViewReady,
    failedEdits,
    onVisibleRegionChanged,
    onRowsChange,
  } = useDataGridRemoteDataSource({
    openView: async () => {
      const meta = await fetch(`/api/views/${viewId}`).then((r) => r.json());
      return { rowCount: meta.totalRows };
    },
    closeView: async () => {
      // 정리 작업
    },
    loadRows: async ({ offset, limit }) => {
      return fetch(
        `/api/views/${viewId}/rows?offset=${offset}&limit=${limit}`
      ).then((r) => r.json());
    },
    saveEdits: async (edits) => {
      const res = await fetch(`/api/views/${viewId}/edits`, {
        method: "POST",
        body: JSON.stringify(edits),
      });
      return res.json(); // { failed: [], rejected: [] }
    },
    subscribeToInvalidations: (onInvalidate) => {
      const ws = new WebSocket(`ws://api/views/${viewId}/live`);
      ws.onmessage = (e) => onInvalidate(JSON.parse(e.data));
      return () => ws.close();
    },
  });

  if (!isViewReady) return <div>뷰 로딩 중...</div>;

  return (
    <>
      {failedEdits.length > 0 && (
        <div className="text-sm text-red-600">
          {failedEdits.length}건 저장 실패
        </div>
      )}
      <DataGrid
        columns={columns}
        rows={rows}
        height={600}
        onVisibleRegionChanged={onVisibleRegionChanged}
        onRowsChange={onRowsChange}
      />
    </>
  );
}

6. useDataGridUndoRedo

편집 히스토리를 배치(batch) 단위로 관리합니다. Ctrl+Z / Ctrl+Y 키보드 단축키와 자동 연동됩니다.

속성타입설명
canUndoboolean실행 취소 가능 여부
canRedoboolean다시 실행 가능 여부
pushBatch(batch) => void편집 배치를 히스토리에 푸시
popUndo() => Batch | undefined마지막 편집 되돌리기
popRedo() => Batch | undefined되돌린 편집 다시 실행
clear() => void전체 히스토리 초기화
tsx
import {
  DataGrid,
  useDataGridUndoRedo,
} from "@reopt-ai/opt-datagrid";

function UndoableGrid({ columns, initialRows }) {
  const [rows, setRows] = useState(initialRows);
  const { canUndo, canRedo, pushBatch, popUndo, popRedo, clear } =
    useDataGridUndoRedo();

  const handleRowsChange = (newRows, changes) => {
    // 변경 배치를 히스토리에 기록
    pushBatch({
      previousRows: rows,
      newRows,
      changes,
    });
    setRows(newRows);
  };

  const handleUndo = () => {
    const batch = popUndo();
    if (batch) setRows(batch.previousRows);
  };

  const handleRedo = () => {
    const batch = popRedo();
    if (batch) setRows(batch.newRows);
  };

  return (
    <>
      <div className="flex gap-2">
        <button onClick={handleUndo} disabled={!canUndo}>Undo</button>
        <button onClick={handleRedo} disabled={!canRedo}>Redo</button>
        <button onClick={clear}>히스토리 초기화</button>
      </div>
      <DataGrid
        columns={columns}
        rows={rows}
        height={400}
        onRowsChange={handleRowsChange}
      />
    </>
  );
}

7. useDataGridController

외부에서 DataGrid에 명령(command)을 디스패치합니다. 프로그래밍 방식으로 스크롤, 선택, 편집 등을 제어할 때 사용합니다.

Command설명
scrollTo지정한 행/열 위치로 스크롤
setSelection프로그래밍 방식으로 선택 영역 설정
startEdit지정 셀의 편집 모드 시작
appendRow새 행 추가 후 첫 번째 편집 가능 셀로 포커스
copySelection현재 선택 영역을 클립보드에 복사
focusDataGrid에 키보드 포커스 설정
tsx
import {
  DataGrid,
  createDataGridController,
  useDataGridController,
} from "@reopt-ai/opt-datagrid";

function ControlledGrid({ columns, rows }) {
  const controller = useDataGridController();

  return (
    <>
      <div className="flex gap-2">
        <button
          onClick={() =>
            controller.dispatch({ type: "scrollTo", row: 0, column: 0 })
          }
        >
          맨 위로
        </button>
        <button
          onClick={() =>
            controller.dispatch({
              type: "setSelection",
              selection: { start: { row: 0, col: 0 }, end: { row: 4, col: 2 } },
            })
          }
        >
          처음 5행 선택
        </button>
        <button
          onClick={() =>
            controller.dispatch({
              type: "appendRow",
              row: { id: crypto.randomUUID(), name: "", value: 0 },
            })
          }
        >
          행 추가
        </button>
      </div>
      <DataGrid
        columns={columns}
        rows={rows}
        height={400}
        controller={controller}
      />
    </>
  );
}
PreviousRelease Notesopt-datagrid 버전별 변경 사항과 하이라이트를 패키지 기준으로 정리합니다.Build & Operate
Go to Release Notes
NextType ReferenceDataGridColumn, DataGridSelection, 에디터 타입 등 핵심 타입 정의 모음Reference