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

AI 시대를 위한 디자인 시스템

  • 문서
  • 가격
  • 릴리즈 노트
  • GitHub
  • 서비스 약관
  • 개인정보처리방침

© 2026 reopt-ai. All rights reserved.

DataGrid
  1. 문서
  2. /
  3. DataGrid
  4. /
  5. 컬럼 설계 플레이북

DataGrid - 컬럼 설계 플레이북

컬럼 정의를 재사용 가능한 설계 단위로 관리하면서 파생 값 invalidation과 renderer 비용을 제어하는 패턴입니다.

reopt design · 업데이트 2026년 6월 26일

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

1. 컬럼 설계 원칙

- 표시용 변환은 `getValue`에, 데이터 반영은 `setValue`에 분리합니다.

- 재사용 가능한 커스텀 편집기는 column-level `editor.component` 로 선언합니다.

- 포맷(통화/상태 배지)은 `renderCell`로 표현 레이어를 고정합니다.

- `getValue`는 deterministic하고 가능한 한 cheap하게 유지합니다.

- 계산 비용이 큰 파생 컬럼은 `dependsOnColumnIds`로 invalidation 범위를 좁힙니다.

- mount 비용이 큰 셀은 `refreshCellRenderer`로 renderer reuse 조건을 명시합니다.

- 화면별로 공통 컬럼 묶음을 export해서 재사용 단위를 만듭니다.

2. 파생 컬럼과 invalidation

파생 컬럼은 표시 자체보다 invalidation 범위를 먼저 설계하는 편이 중요합니다. `dependsOnColumnIds`를 지정하면 편집된 필드와 실제로 연결된 컬럼만 refresh 대상으로 좁힐 수 있습니다.

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

interface InvoiceRow {
  id: number;
  customer: string;
  owner: string;
  amount: number;
  status: "paid" | "pending";
}

export const columns: DataGridColumn<InvoiceRow>[] = [
  {
    id: "customer",
    title: "Customer",
    width: 240,
    editable: true,
    getValue: (row) => row.customer,
    setValue: (row, nextValue) => ({ ...row, customer: nextValue }),
  },
  {
    id: "owner",
    title: "Owner",
    width: 180,
    editable: true,
    getValue: (row) => row.owner,
    setValue: (row, nextValue) => ({ ...row, owner: nextValue }),
  },
  {
    id: "summary",
    title: "Summary",
    width: 260,
    editable: false,
    dependsOnColumnIds: ["customer", "owner"],
    getValue: (row) => `${row.customer} · ${row.owner}`,
  },
  {
    id: "amount",
    title: "Amount",
    width: 140,
    align: "right",
    editable: false,
    getValue: (row) => row.amount.toLocaleString(),
  },
  {
    id: "status",
    title: "Status",
    width: 140,
    editable: false,
    getValue: (row) => row.status,
    renderCell: ({ value }) =>
      value === "paid" ? (
        <span className="rounded-full bg-emerald-100 px-2 py-0.5 text-xs text-emerald-700">Paid</span>
      ) : (
        <span className="rounded-full bg-amber-100 px-2 py-0.5 text-xs text-amber-700">Pending</span>
      ),
    refreshCellRenderer: (prev, next) => prev.value === next.value,
  },
];

3. renderer refresh 전략

- badge/action/summary 셀처럼 mount 비용이 있는 경우 `refreshCellRenderer`로 reuse 조건을 명시합니다.

- renderer 전체 정책은 `rendererRefreshMode`로 조정하고, column 단위 조건은 `refreshCellRenderer`에서 다룹니다.

- 값이 바뀌지 않은 sibling cell까지 다시 mount할 필요가 없을 때 특히 유용합니다.

4. 컬럼 인터랙션 설계

컬럼 리사이즈/재정렬/메뉴 액션은 `onColumnsChange`, `onColumnResize`, `onColumnMoved`, `onHeaderMenuAction`으로 분리해 저장합니다.

tsx
import * as React from "react";
import { DataGrid, type DataGridColumn } from "@reopt-ai/opt-datagrid";

interface Row {
  id: number;
  name: string;
  status: "active" | "blocked";
}

function ColumnManagedGrid() {
  const rows = React.useMemo<Row[]>(
    () => [
      { id: 1, name: "Alpha", status: "active" },
      { id: 2, name: "Beta", status: "blocked" },
    ],
    [],
  );
  const [columns, setColumns] = React.useState<readonly DataGridColumn<Row>[]>(
    [
      { id: "name", title: "Name", width: 220, editable: true, resizable: true, reorderable: true, menu: true, getValue: (row) => row.name, setValue: (row, v) => ({ ...row, name: v }) },
      { id: "status", title: "Status", width: 140, editable: false, resizable: true, reorderable: true, menu: true, getValue: (row) => row.status },
    ],
  );

  const saveWidth = React.useCallback((columnId: string, size: number) => {
    console.log("save width", columnId, size);
  }, []);
  const saveOrder = React.useCallback((from: number, to: number) => {
    console.log("save order", from, to);
  }, []);
  const handleSortAction = React.useCallback((colIndex: number, action: string) => {
    console.log("sort action", colIndex, action);
  }, []);

  return (
    <DataGrid
      rows={rows}
      columns={columns}
      onColumnsChange={setColumns}
      onColumnResize={(column, size) => saveWidth(column.id, size)}
      onColumnMoved={(from, to) => saveOrder(from, to)}
      onHeaderMenuAction={(colIndex, action) => handleSortAction(colIndex, action)}
    />
  );
}
Previous앱 조합 가이드opt-ui 레이아웃 안에 DataGrid를 어떤 경계로 넣고 언제 별도 엔진으로 분리할지 정리합니다.DataGrid
앱 조합 가이드 페이지로 이동
NextSkillsopt-datagrid 설치, 마이그레이션, backend contract, 업그레이드, release용 action skill을 정리합니다.구축·운영