로딩 및 빈 상태 (Loading & Empty State)

데이터 비동기 로딩 시 스피너 오버레이 표시 및 검색 결과가 없을 때의 안내 메시지 커스터마이징을 학습합니다.

#loading#spinning#msg#empty-state#overlay
검토일: 2026-08-17
GitHub
import * as React from 'react';
import { AXDataGrid, AXDGColumn } from '@axboot/datagrid';
import { Button, Space } from 'antd';
import DataGridContainer from '../components/DataGridContainer';
import { useContainerSize } from '../hooks/useContainerSize';

interface Props {}

interface IListItem {
  id: string;
  title: string;
  writer: string;
  createAt: string;
}

const list = Array.from(Array(1000)).map((v, i) => ({
  values: {
    id: `ID_${i}`,
    title: `title_${i}`,
    writer: `writer_${i}`,
    createAt: `2022-09-08`,
  },
}));

function LoadingExample(props: Props) {
  const [loading, setLoading] = React.useState(false);
  const [spinning, setSpinning] = React.useState(false);
  const [columns, setColumns] = React.useState<AXDGColumn<IListItem>[]>([
    {
      key: 'id',
      label: '아이디 IS LONG !',
      width: 100,
      sortDisable: true,
    },
    {
      key: 'title',
      label: '제목',
      width: 300,
    },
    {
      key: 'writer',
      label: '작성자',
      width: 100,
    },
    {
      key: 'createAt',
      label: '작성일',
      width: 100,
    },
  ]);

  const containerRef = React.useRef<HTMLDivElement>(null);
  const { width: containerWidth, height: containerHeight } = useContainerSize(containerRef);

  return (
    <div>
      <div className={'flex items-center gap-[5px] py-2.5'}>
        <Button onClick={() => setLoading(true)}>Loading : true</Button>
        <Button onClick={() => setLoading(false)}>Loading : false</Button>
        <Button onClick={() => setSpinning(true)}>Spinning : true</Button>
        <Button onClick={() => setSpinning(false)}>Spinning : false</Button>
      </div>

      <DataGridContainer ref={containerRef}>
        <AXDataGrid<IListItem>
          width={containerWidth}
          height={containerHeight}
          headerHeight={35}
          data={list}
          columns={columns}
          onChangeColumns={(columnIndex, { width, columns }) => {
            console.log('onChangeColumnWidths', columnIndex, width, columns);
            setColumns(columns);
          }}
          rowChecked={{
            checkedIndexes: [],
            onChange: (ids, selectedAll) => {
              console.log('onChange rowSelection', ids, selectedAll);
            },
          }}
          loading={loading}
          spinning={spinning}
        />
      </DataGridContainer>
    </div>
  );
}

export default LoadingExample;

1. 언제 사용하며 왜 필요한가요?

API 호출 중 사용자가 빈 화면을 보고 시스템이 멈춘 것으로 오해하지 않도록 반투명 로딩 오버레이와 스피너를 띄우고, 검색 결과가 0건일 때 **“조회된 데이터가 없습니다”**라는 친절한 안내 문구를 보여주는 것은 완성도 높은 UX의 기본입니다.

AXBOOT DataGrid는 loading, spinning, msg.emptyList 속성으로 로딩 표시와 빈 데이터 메시지를 제어합니다.


2. 실무 완성형 예제: 로딩 및 빈 데이터 상태 시뮬레이션

import React, { useState } from 'react';
import { AXDataGrid, type AXDGColumn, type AXDGDataItem } from '@axboot/datagrid';

interface Product {
  id: number;
  name: string;
  price: number;
}

export default function LoadingDemoGrid() {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState<AXDGDataItem<Product>[]>([]);

  const columns: AXDGColumn<Product>[] = [
    { key: 'id', label: 'ID', width: 80, align: 'center' },
    { key: 'name', label: '상품명', width: 220 },
    { key: 'price', label: '가격', width: 140, align: 'right', itemRender: ({ values }) => `${values.price.toLocaleString()}원` },
  ];

  const handleFetch = () => {
    setLoading(true);
    setTimeout(() => {
      setData([
        { values: { id: 1, name: '에르고노믹 마우스', price: 65000 } },
        { values: { id: 2, name: '텐키리스 키보드', price: 129000 } },
      ]);
      setLoading(false);
    }, 1000);
  };

  const handleClear = () => {
    setData([]);
  };

  return (
    <div>
      <div style={{ marginBottom: 12, display: 'flex', gap: 8 }}>
        <button onClick={handleFetch} style={{ padding: '6px 12px', background: '#2563eb', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer' }}>
          데이터 불러오기 (로딩 1초)
        </button>
        <button onClick={handleClear} style={{ padding: '6px 12px', border: '1px solid #cbd5e1', background: '#fff', borderRadius: 4, cursor: 'pointer' }}>
          데이터 비우기 (Empty State)
        </button>
      </div>

      <AXDataGrid<Product>
        width={500}
        height={240}
        columns={columns}
        data={data}
        rowKey="id"
        loading={loading} // 로딩 상태 활성화 시 오버레이 스피너 자동 렌더링
        msg={{
          emptyList: '조회 조건에 일치하는 상품 데이터가 없습니다.',
        }}
      />
    </div>
  );
}