LLM Guide

행 순서 변경

AI 에이전트가 `@mycrm-ui/react-table`의 드래그앤드롭 행 순서 변경 상태와 핸들 컬럼을 정확히 구성할 수 있도록 정리한 Markdown 가이드입니다.

MD Viewer

@mycrm-ui/react-table LLM 가이드

행 순서 변경

이 문서는 @mycrm-ui/react-table에서 flat table 행을 드래그앤드롭으로 재정렬하는 기능을 생성하거나 수정해야 하는 AI 에이전트를 위한 가이드입니다.

이 파트는 사용자가 "행 순서를 바꾸고 싶다", "순서 변경 컬럼을 드래그 핸들로 쓰고 싶다", "드래그앤드롭으로 목록 우선순위를 조정하고 싶다"고 요청할 때 사용합니다.

목적

행 순서 변경의 목적은 rowReorder 옵션으로 드래그 가능한 핸들 컬럼을 지정하고, 변경된 row key 순서를 외부 상태에 반영하는 것입니다.

v1 기준 규칙:

  • 행 순서 변경은 flat table에서 사용합니다.
  • rowKey는 안정적인 문자열을 반환해야 합니다.
  • 테이블 내부가 데이터를 직접 바꾸지 않습니다. onOrderChange에서 소비자가 data 배열을 재정렬해야 합니다.
  • isRowReorderable를 쓰면 특정 행을 드래그 대상과 드롭 계산에서 제외하고 원래 인덱스를 유지시킬 수 있습니다.
  • expand 또는 rowPinning과 동시에 사용하지 않습니다.

필수 입력

handleColumnKey

드래그 핸들로 사용할 컬럼의 key입니다.

const columns: ColumnDef<Row>[] = [
  { key: "reorder", label: "순서", render: () => "⋮⋮" },
  { key: "name", label: "이름", render: (row) => row.name },
];
rowReorder={{
  enabled: true,
  handleColumnKey: "reorder",
  onOrderChange: handleOrderChange,
}}

onOrderChange

드롭 후 변경된 row key 순서를 받는 콜백입니다.

중요 규칙:

  • 콜백 인자는 rowKey가 반환한 문자열 배열입니다.
  • 이 배열을 기준으로 기존 data를 재정렬합니다.
  • 없는 key는 방어적으로 제외합니다.

최소 동작 패턴

import { useState } from "react";
import { Table } from "@mycrm-ui/react-table";
import type { ColumnDef } from "@mycrm-ui/react-table";

interface Task {
  id: number;
  title: string;
  owner: string;
  locked: boolean;
}

const columns: ColumnDef<Task>[] = [
  {
    key: "reorder",
    label: "순서",
    width: "64px",
    align: "center",
    render: (row) => (row.locked ? "고정" : "⋮⋮"),
  },
  { key: "title", label: "업무", render: (row) => row.title },
  { key: "owner", label: "담당자", render: (row) => row.owner },
];

const initialRows: Task[] = [
  { id: 2, title: "견적 발송", owner: "박서준", locked: true },
  { id: 1, title: "계약 검토", owner: "김하늘", locked: false },
  { id: 3, title: "후속 미팅", owner: "이유진", locked: false },
];

export default function TaskTable() {
  const [rows, setRows] = useState(initialRows);

  const handleOrderChange = (order: string[]) => {
    setRows((prev) => {
      const rowMap = new Map(prev.map((row) => [String(row.id), row]));
      return order
        .map((key) => rowMap.get(key))
        .filter((row): row is Task => row !== undefined);
    });
  };

  return (
    <Table
      columns={columns}
      data={rows}
      rowKey={(row) => String(row.id)}
      rowReorder={{
        enabled: true,
        handleColumnKey: "reorder",
        onOrderChange: handleOrderChange,
        isRowReorderable: (row) => !row.locked,
      }}
    />
  );
}

선택 입력

dragOverColor

드래그 중 삽입 위치 표시 색상을 바꾸고 싶을 때 사용합니다.

rowReorder={{
  enabled: true,
  handleColumnKey: "reorder",
  onOrderChange: handleOrderChange,
  dragOverColor: "rgba(79, 70, 229, 0.8)",
}}

isRowReorderable

특정 행을 순서 변경에서 제외하고 원래 배열 인덱스를 유지하고 싶을 때 사용합니다.

rowReorder={{
  enabled: true,
  handleColumnKey: "reorder",
  onOrderChange: handleOrderChange,
  isRowReorderable: (row) => !row.locked,
}}

중요 규칙:

  • false를 반환한 행은 드래그 시작이 막힙니다.
  • 고정된 행은 최종 순서 계산에서도 원래 위치를 유지합니다.
  • 고정 행끼리의 상대 순서가 중요하지 않더라도, 결과 배열에서는 기존 슬롯에 다시 배치된다고 이해하면 안전합니다.

classNames

행 순서 변경 상태를 스타일링할 수 있습니다.

classNames={{
  trDragging: "opacity-50",
  trDragOver: "bg-primary/5",
  tdRowDragHandle: "cursor-grab active:cursor-grabbing",
}}

LLM 판단 규칙

사용자 요청이 아래 의미에 가깝다면 이 가이드를 사용합니다.

  • "행 순서를 드래그해서 바꾸고 싶어"
  • "순서 변경 컬럼을 잡고 row를 이동하게 해줘"
  • "우선순위 목록을 드래그앤드롭으로 정렬하고 싶어"

이 경우 LLM은 아래 순서로 처리합니다.

  1. 대상이 flat table인지 확인합니다.
  2. 안정적인 rowKey를 사용합니다.
  3. 드래그 핸들 전용 컬럼을 추가합니다.
  4. 필요하면 isRowReorderable로 고정 행 조건을 정의합니다.
  5. rowReorder.enabled, handleColumnKey, onOrderChange를 연결합니다.
  6. onOrderChange에서 data 상태를 재정렬합니다.
  7. 필요하면 tdRowDragHandle, trDragging, trDragOver 스타일을 추가합니다.

이 파트에서 추가해도 되는 것

  • selection
  • editing
  • rowActions
  • tooltip
  • copyable
  • 기본적인 classNames

단, 여러 상호작용이 있을 때는 드래그 핸들 컬럼에서만 드래그가 시작되도록 구성합니다.

이 파트에서 주의할 것

  • expand와 함께 사용하지 않습니다.
  • rowPinning과 함께 사용하지 않습니다.
  • 정렬이나 필터가 켜진 상태에서는 화면 순서와 원본 데이터 순서의 의미가 달라질 수 있습니다.
  • 서버 저장이 필요하다면 onOrderChange 이후 별도 저장 API를 호출합니다.

자주 하는 실수

실수 1. onOrderChange에서 상태를 바꾸지 않음

문제점:

  • 드롭 이벤트는 발생하지만 화면 순서가 돌아옵니다.

좋은 방식:

  • 전달받은 key 배열로 data 상태를 즉시 재정렬합니다.

실수 2. 배열 index를 rowKey로 사용함

문제점:

  • 순서 변경 후 key가 함께 바뀌어 React 렌더링과 DnD 결과가 불안정해집니다.

좋은 방식:

  • 실제 도메인 ID를 문자열로 변환해서 사용합니다.

실수 3. 모든 셀을 드래그 가능하게 만들려고 함

문제점:

  • 클릭, 편집, 선택, 컨텍스트 메뉴와 충돌합니다.

좋은 방식:

  • handleColumnKey로 지정한 전용 컬럼만 드래그 핸들로 사용합니다.

실수 4. 고정 행이 있어도 일반 reorder와 같은 방식으로만 생각함

문제점:

  • 사용자는 특정 행이 움직이지 않기를 기대하지만 결과 배열에서 위치가 바뀔 수 있습니다.

좋은 방식:

  • isRowReorderable로 고정 조건을 명시하고, 고정 행이 원래 인덱스를 유지한다는 전제로 onOrderChange 결과를 사용합니다.