본문 바로가기
React

React에서 DataTables 사용하기(반응형 테이블)

by Vintz 2022. 9. 22.
반응형

Photo by Lautaro Andreani - https://unsplash.com/photos/xkBaqlcqeb4

jQuery용 DataTables와 리액트를 함께 사용하는 일은 많이 없을 거라 생각합니다. MUI, Ant Design과 같은 호환성이 좋은 선택지가 이미 있기 때문입니다. 그럼에도 여러 상황에 따라 이 조합을 써야할 경우가 있습니다.(바로 저처럼..) 그런데 사용하면서 느낀 건, 생각보다 강력한 도구라는 것입니다. 테이블 형태로 노출되는 화면을 어떻게 반응형으로 만들어야 할지 막막했는데, 이 부분을 DataTables가 시원하게 해결해주었습니다.

 

이름에서도 알 수 있듯 DataTables는 jQuery(이하 제이쿼리)에 대한 의존성을 갖습니다.(v1.11부터 non-jQuery 방식으로 초기화가 가능하지만 여전히 제이쿼리에 의존성을 갖고 사용합니다.) 제이쿼리와 리액트 둘 다 DOM(Document Object Model)을 조작하기 때문에 사실 그렇게 좋은 조합은 아니지만 테이블 자체에서만 사용을 한다면 나쁘지 않습니다.

 

리액트용 레퍼런스가 한글은 거의 뭐 전무하고, 외국 글마저 클래스 컴포넌트로 구현한 자료가 많더군요. 저의 수많은 시행착오와 구글링, 공식 문서/포럼에서 찾은 자료를 통해 배운 것들을 정리 해보았습니다.

기본 사용법

먼저 관련 패키지들을 설치해보겠습니다. 제가 설치한 패키지는 다음과 같습니다.

npm i jquery datatables.net-dt datatables.net-responsive-dt

간단하게 의존성이 있는 (1)제이쿼리, (2)기본값의 데이터테이블 그리고 (3)반응형을 위한 패키지를 설치했습니다. 더 많은 확장 기능을 확인하시려면 DataTables Download를 참고해주세요. NPM 설치 방법 외에도 직접 다운로드, CDN 등으로도 사용 가능합니다.

DataTables 초기 설정

먼저 다운로드한 패키지들을 import 해옵니다. 그 후 초기 설정들을 해보겠습니다. npm에서 다운로드한 js 파일과 css 파일을 import 했습니다.

import $ from 'jquery';
import 'datatables.net-dt';
import 'datatables.net-responsive-dt';
import 'datatables.net-dt/css/jquery.dataTables.min.css';
import 'datatables.net-responsive-dt/css/responsive.dataTables.min.css';

이제 테이블에 필요한 기본적인 것들이 준비가 되었으니 초기화를 해주겠습니다. 제가 생각하는 기본적인 초기화 방법은 다음과 같습니다.

export function DataTable() {
  const tableRef = useRef();

  useEffect(() => {
    const table = $(tableRef.current).DataTable({
      data: [
        // ...
      ],
      columns: [
        // ...
      ],
      responsive: true, // 반응형 켜기
      // options
    });

    // 언마운트 시 destroy
    return () => {
      table.destroy();
    };
  }, []);

  return <table ref={tableRef} style={{ width: '100%' }}></table>;
}

위 코드는 원본 HTML 테이블에 많은 요소들과 이벤트 리스너, 그리고 기타 수정 사항들을 추가 시켜줍니다(테이블 내 검색 기능과 페이지네이션, 노출시킬 로우의 수, 반응형 테이블 등).

 

여기서 주의할 점은 cleanup 함수인데요, 언마운트 시에 destroy() 함수를 호출하여 기존(원본)의 HTML 테이블로 만들 수 있습니다. 이것을 통해 다른 테이블로 교체 시 메모리 누수를 방지할 수 있습니다.

data, columns 그리고 반응형 테이블

공식문서에 있는 더미 데이터를 이용해서 데이터와 컬럼을 채워보겠습니다.

data: [
  ['Suki Burks', 'Developer', 'London', '6832', '2020/10/22', '$114,500'],
  // ...
],
columns: [
  { title: 'Name' },
  { title: 'Position' },
  { title: 'Office' },
  { title: 'Extn.' },
  { title: 'Start data' },
  { title: 'Salary' },
],

그럼 다음과 같이 데이터가 노출이 됩니다.

Data Table - 1

여기서 DataTables는 놀라운 방법으로 모바일 및 태블릿 화면을 다음과 같이 대응을 합니다.

모바일 및 태블릿 화면을 대응 해줍니다. 제 고민이 해결되었습니다.

위의 이미지처럼 버튼을 토글하여 숨겨진 컬럼 값들을 확인 할 수 있지만, 세부값들을 즉시 펼치는 것도 가능합니다.

export function DataTable() {
  const tableRef = useRef();

  useEffect(() => {
    const table = $(tableRef.current).DataTable({
      data: [
        // ...
      ],
      columns: [
        // ...
      ],
      responsive: {
        details: {
          display: $.fn.dataTable.Responsive.display.childRowImmediate,
        },
      },
      // options
    });

    // 언마운트 시 destroy
    return () => {
      table.destroy();
    };
  }, []);

  return <table ref={tableRef} style={{ width: '100%' }}></table>;
}

Ajax를 통한 데이터 노출

실제로 사용을 할 때에는 일반적으로 서버와 통신을 하여 데이터를 노출시킬 것입니다. 저는 객체 자료구조로 예시를 들었습니다. 다른 자료 구조의 예시는 Ajax sourced data에서 확인하실 수 있습니다.

// objects.json

{
  "data": [
    {
      "id": "1",
      "name": "Tiger Nixon",
      "position": "System Architect",
      "salary": "$320,800",
      "startDate": "2022/09/22",
      "office": "Edinburgh",
      "extn": "5421"
    }
    ...
  ]
}

컴포넌트의 코드는 다음과 같이 변경이 됩니다.

export function DataTable() {
  const tableRef = useRef();

  useEffect(() => {
    const table = $(tableRef.current).DataTable({
      ajax: {
        type: 'GET',
        url: 'api/objects.json',
        dataType: 'json',
      },
      columns: [
        { data: 'name', title: 'Name' },
        { data: 'position', title: 'Position' },
        { data: 'office', title: 'Office' },
        { data: 'extn', title: 'Extn.' },
        { data: 'startDate', title: 'Start date' },
        { data: 'salary', title: 'Salary' },
      ],
      responsive: true, // 반응형 켜기
    });

    // 언마운트 시 destroy.
    return () => {
      table.destroy();
    };
  }, []);

  return <table ref={tableRef} style={{ width: '100%' }}></table>;
}

변경점이 보이시나요? 생각보다 코드가 깔끔하네요.

function(data, callback, settings)

  • data - 요청이 전송되기 전에 호출됩니다. XMLHttpRequest 객체를 조작하여 추가 header를 부여하는 등의 처리를 할 수 있습니다.
  • callback - 요청이 완료된 후 호출됩니다. ajax 호출에 대한 성공 또는 실패(에러)에 대해 재정의 할 수 있습니다.
  • settings - Data Tables 설정 객체입니다.

여기서 data를 통한 토큰 인증을 예시로 보겠습니다.

ajax: {
  type: 'GET',
  url: 'api/objects.json',
  dataType: 'json',
  beforeSend(request) {
    request.setRequestHeader('AuthToken', authToken);
  },
},
// ...

다른 예시는 DataTables ajax를 참고해주세요.

columns 숫자 포맷 변경

숫자에 대해 포맷을 변경해야할 때도 있습니다. 퍼센트가 될 수도 있고, 가독성을 위해 3자리마다 콤마를 붙일 수도 있으며, 3자리 수까지만 반올림을 하여 소수로 표현할 수도 있습니다.

// numberFormat.js

export function number(num) {
  return new Intl.NumberFormat().format(num);
}

export function decimal(num) {
  return new Intl.NumberFormat('ko-KR', {
    style: 'decimal',
  }).format(num);
}

export function percent(num) {
  return (
    new Intl.NumberFormat('ko-KR', {
      style: 'decimal',
    }).format(num) + '%'
  );
}

다른 방법도 있지만, 함수를 구현 해놓고 아래와 같이 필요할 때마다 불러와서 사용을 하는 것이 더 좋은 것 같습니다.

columns: [
  // ...
  {
    data: 'number',
    title: 'Number',
    render(data) {
      return number(data);
    },
  },
]

결론

Data Table - 2

이제 기본적인 사용법은 해당 글로 모두 익히셨길 바랍니다. 처음엔 제대로 된 설득없이 DataTables를 사용하게 되어서 많이 의아했습니다. 하지만 사용할수록 다양한 기능들을 지원한다는 것을 알게 되고, 뛰어난 도구란 것도 느꼈습니다. 마지막으로 전체코드를 보여드리고 마무리 짓겠습니다.

import { useEffect, useRef } from 'react';

import $ from 'jquery';
import './data/datatables'; // imports
// import { number, decimal, percent } from './utils/numberFormat'; 필요한 경우

export function DataTable() {
  const tableRef = useRef();

  useEffect(() => {
    const table = $(tableRef.current).DataTable({
      ajax: {
        type: 'GET',
        url: 'api/objects.json',
        dataType: 'json',
      },
      columns: [
        { data: 'name', title: 'Name' },
        { data: 'position', title: 'Position' },
        { data: 'office', title: 'Office' },
        { data: 'extn', title: 'Extn.' },
        { data: 'startDate', title: 'Start date' },
        { data: 'salary', title: 'Salary' },
      ],
      responsive: true, // 반응형 켜기
      scrollX: true,
    });

    // 언마운트 시 destroy
    return () => {
      table.destroy();
    };
  }, []);

  return (
    <table
      ref={tableRef}
      className='table stripe row-border order-column table-striped cell-border'
      style={{ width: '100%' }}></table>
  );
}

참고

datatables.net
Integrating React and Datatables — not as hard as advertised
반응형