[프로젝트] 게시판 front 리펙토링 (3) material-table을 이용한 게시판 만들기

rin·2020년 6월 3일
2

React

목록 보기
11/16
post-thumbnail

목표
1. material-table을 이용해 Board 컴포넌트를 구성한다.
2. 스토어를 추가한다.
3. Board 컴포넌트에서 이뤄지던 상태변화를 BoardContainer 컴포넌트에 위임한다.

게시판 페이지 만들기

Board 컴포넌트 생성

material-table

src/component 하위에 Board.js 파일을 생성한다.
Board 또한 이미 만들어진 템플릿을 가져와 적당히 바꿀 것이다.

material-ui를 기반으로 만들어져 있는 오픈 소스인 material-table을 사용하였다. 자세한 내용은 링크에서 확인할 수 있으니 참고하자.

yarn add material-table 명령어를 이용해 종속을 추가한다.

material-ui에 있는 가공된 코드를 복사하여 붙여넣어보자.

만약 아이콘이 깨져서 제대로 나오지 않는다면, Board.js에 전역 함수로 아래 코드를 추가하고 MaterialTable의 props로 전달해주면 된다.

import { forwardRef } from 'react';

import AddBox from '@material-ui/icons/AddBox';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/ViewColumn';

const tableIcons = {
    Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
    Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
    Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
    Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
    DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
    Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
    Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
    Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
    FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
    LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
    NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
    PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
    ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
    Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
    SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref} />),
    ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
    ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />)
  };

복사한 코드가 가지고 있던 샘플 데이터(아래 빨간 네모 박스)를 지우고 아래 데이터를 추가해주자.

            {name: 'Mehmet', surname: '안녕하세요 ^^ 5-1', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터 5-2', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다 5-3', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
            {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기 5-4', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 5-5', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
            {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다. 5-6', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!! 5-7', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: 'MacBook Pro 팔아요 5-8', birthYear: 2017, birthCity: 11},
            {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기 5-9', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니.. 5-10', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: '마지막 데이터입니다!!!!!!!', birthYear: 2017, birthCity: 11}

App.js에 라우터를 추가한 뒤, MainContainer에서 로그인 성공시 자동으로 페이지 이동을 하도록 변경한다.

로그인을 성공시 페이지가 이동되고, 아래와 같이 페이징 처리가 잘 된 테이블이 출력되는지 확인해보자. 🙋🏻 (BirthPlace는 귀찮아서 추가안했으니 3개의 데이터를 제외하고는 빈칸인게 맞다.)

커스텀 마이징

✔️ 페이지 정보 가져오기
RestApi에 보낼 요청을 생각해보자.
필요한 데이터는 pageNumber와 pageSize이다. (검색은 일단 뒤로 미뤄두자.)

위 props를 이용하여 필요한 데이터를 받아 올 것이다.

    const [pageNumber, setPageNumber] = React.useState(0);
    const [pageSize, setPageSize] = React.useState(5);

    const changePage = (pageNumber) => {
        setPageNumber(pageNumber);
    }

    const changeRowsPerPage = (rowSize) => {
        setPageSize(rowSize);
    }

✔️ 페이징 타입 변경하기
option props를 활용하여 숫자로 현재 페이지를 나타낼 수 있도록 변경해주었다.

paginationType: "normal" (default)paginationType: "stepped"

현재 코드 상태는 이러하다. changePage, changeRowsPerPage 함수에 console.log 등을 이용해 pageNumber와 rowSize가 잘 들어오고 있는지 확인해 볼 수 있을 것이다.

✔️ 가용 데이터 추출해 페이징하기
우선은 이 js 파일에 하드 코딩된 데이터 샘플을 이용해 RestApi를 이용하듯 코드를 작성한다.
눈치챘을지 모르겠지만 현재 코드는 한꺼번에 데이터를 가져와 프론트에서 페이징하여 보여주고 있다. 즉, 단순한 UI 조작으로 페이징 처리가 되고 있는 것이다. 🤔

물론 이전에 개발해 둔 서버에서는 페이징하여 필요한 데이터"만" 넘겨주도록 하고 있다. (위에서 pageNumber와 pageSize를 갱신하는 코드를 작성한 이유이다.)

레퍼런스를 읽으면서 여러가지를 시도해보니

  • 무조건 "전체 데이터"를 기반으로 페이징한다(애초에 다른 설정이 없음).
    따라서 이전처럼 첫 페이지 번호와 마지막 페이지 번호를 이용해 페이지바만 만들고, 별개로 페이지를 클릭하는 순간 그 데이터만 이용해 테이블을 생성하는 방식을 쓸 수 없다.
    예를 들어 20개의 데이터가 있고, 한 페이지당 5개씩 보여주고 싶으면 이전에는 페이지 바에는 1,2,3,4의 페이지 버튼을 만들고 3페이지를 클릭하면 5개의 데이터를 가져와 테이블을 만들었을 것이다. 물론 이 경우에 페이지 바는 변경되지 않고 형태가 유지된다.
    하지만 MaterialTable에서 5개의 데이터만 가져오면 이렇게 생성된다.
    전체 데이터가 5개만 존재한다고 생각하기 때문에 한 페이지만 출력되는 것이다.

  • 페이지바에 노출되는 페이지 버튼 수를 커스텀마이징 할 수 없다.
    첫페이지와 마지막 페이지는 2개 버튼으로 고정이며, 그 외에는 3개의 버튼을 가지게 된다.

  • 첫 페이지중간 페이지마지막 페이지
  • material-ui에서 제공하는 TablePagination을 사용하고 있으나, 기본적으로 TablePagination이 제공하는 props를 이용할 수 없다.
    페이지바의 버튼 개수를 설정할 수 없는 것도 같은 위와 같은 이유 때문이다.
    설정은 material-table의 공식 레퍼런스에 써져있는 것만 사용할 수 있으며 localization을 이용해 접근해보려고 했으나 방도가 없었다. (물론 직접 material-table을 다운받아 m-table-pagination.js의 디폴트 설정을 변경하면 가능할 것 같기는 하다. 😑)

그럼 현재 페이지의 데이터만 가져오면서 페이지바를 유지하려면 어떻게 해야할까?

페이지바를 만들기 위한 충분한 데이터가 있으면 된다. 하지만 "보여지는" 페이지를 제외하고는 내용이 없는 빈 오브젝트가 머릿수만 채워주도록 해도 무관하다.

그냥 전체 데이터를 한번에 받아와서 써도 되지 않겠냐고 한다면,

  1. 서버에서 이미 제공중인 Api에 맞춰야하며,
  2. 데이터 개수가 무한정 많을 경우에는 쓸데없는 부하를 가져올 수 있을 것이므로

매 페이지에 필요한 데이터만 가져올 수 있도록 하는 것이다.

현재 하드 코딩된 데이터를 기반으로 작성하고 있기 때문에 마치 Api 요청으로 받아온 것과 같은 데이터를 담기 위한 상태를 추가해주었다.

const [selectedData, setSelectedData] = React.useState(state.data.slice(pageNumber * pageSize, (pageNumber * pageSize + pageSize)));

위 상태는 MaterialTable의 Props로 전달된다.

//변경 전
data={state.data}

//변경 후
data={selectedData}

그리고 페이지가 이동하거나 페이지 당 게시글 개수가 변경되면 데이터를 갱신하고, 페이지바를 유지할 수 있도록 useEffect 훅을 사용하였다.

    useEffect(() => {
        let prevFakeData = createPrevFakeData(pageSize * pageNumber);
        let nextFakeData = createNextFakeData(pageSize);

        if (isFirstPage()) {
            let realData = state.data.slice(pageNumber * pageSize, pageSize);
            setSelectedData(realData.concat(nextFakeData));
        } else if (isLastPage()) {
            let realData = state.data.slice(pageNumber * pageSize, state.data.length);
            setSelectedData(prevFakeData.concat(realData));
        } else {
            let realData = state.data.slice(pageNumber * pageSize, (pageNumber * pageSize) + pageSize);
            setSelectedData(prevFakeData.concat(realData).concat(nextFakeData));
        }
    }, [pageSize, pageNumber]);

위 로직은 아래와 같이 움직인다.

  1. 현재 페이지보다 앞 단에 붙일 빈 데이터 리스트를 생성한다.
  2. 현재 페이지보다 뒷 단에 붙일 빈 데이터 리스트를 생성한다.
  3. 첫페이지 => 앞페이지는 필요없으므로 현재페이지 데이터 + 2
    중간페이지 => 1 + 현재페이지 데이터 + 2
    마지막페이지 => 뒷페이지는 필요없으므로 1 + 현재페이지 데이터
    를 생성하여 selectedData에 갱신해준다.

4 페이지로 이동한 경우의 데이터이다. 인덱스 15에서 19까지의 데이터만 실제로 화면에 나오는 4페이지의 데이터이고, 앞단의 데이터와 뒷단의 데이터는 id만 가진 빈 오브젝트이다.
앞단의 데이터가 1~3페이지를 구축하고 뒷단의 데이터가 5페이지를 구축하여 4페이지는 중간 페이지로써 존재할 수 있는 것이다.

id가 없는 오브젝트는 에러처리되며 랜더링을 하지 못하기에 가짜데이터에도 index를 이용해 채워넣었다.

Board 컴포넌트의 전체 코드는 아래와 같다.

import React, {useEffect} from 'react';
import MaterialTable from 'material-table';

import {forwardRef} from 'react';

import AddBox from '@material-ui/icons/AddBox';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/ViewColumn';

const tableIcons = {
    Add: forwardRef((props, ref) => <AddBox {...props} ref={ref}/>),
    Check: forwardRef((props, ref) => <Check {...props} ref={ref}/>),
    Clear: forwardRef((props, ref) => <Clear {...props} ref={ref}/>),
    Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref}/>),
    DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref}/>),
    Edit: forwardRef((props, ref) => <Edit {...props} ref={ref}/>),
    Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref}/>),
    Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref}/>),
    FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref}/>),
    LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref}/>),
    NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref}/>),
    PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref}/>),
    ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref}/>),
    Search: forwardRef((props, ref) => <Search {...props} ref={ref}/>),
    SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref}/>),
    ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref}/>),
    ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref}/>)
};


export default function Board() {
    const [state, setState] = React.useState({
        columns: [
            {title: '작성자', field: 'name'},
            {title: 'Surname', field: 'surname'},
            {title: 'Birth Year', field: 'birthYear', type: 'numeric'},
            {
                title: 'Birth Place',
                field: 'birthCity',
                lookup: {34: 'İstanbul', 63: 'Şanlıurfa'},
            },
        ],
        data: [
            {name: 'Mehmet', surname: '안녕하세요 ^^ 5-1', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터 5-2', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다 5-3', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
            {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기 5-4', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 5-5', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
            {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다. 5-6', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!! 5-7', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: 'MacBook Pro 팔아요 5-8', birthYear: 2017, birthCity: 11},
            {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
            {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
            {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
            {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
            {name: 'Sera', surname: '페이징 처리 이용하기 5-9', birthYear: 2007, birthCity: 17},
            {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
            {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
            {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
            {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
            {name: 'K.Son', surname: 'ㅋ아니.. 5-10', birthYear: 2002, birthCity: 37},
            {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
            {name: 'Merry', surname: '마지막 데이터입니다!!!!!!!', birthYear: 2017, birthCity: 11}
        ],
    });

    const [pageNumber, setPageNumber] = React.useState(0);
    const [pageSize, setPageSize] = React.useState(5);
    const [selectedData, setSelectedData] = React.useState(state.data.slice(pageNumber * pageSize, (pageNumber * pageSize + pageSize)));

    const changePage = (pageNumber) => {
        setPageNumber(pageNumber);
    }

    const changeRowsPerPage = (rowSize) => {
        setPageSize(rowSize);
    }

    useEffect(() => {
        let prevFakeData = createPrevFakeData(pageSize * pageNumber);
        let nextFakeData = createNextFakeData(pageSize);

        if (isFirstPage()) {
            let realData = state.data.slice(pageNumber * pageSize, pageSize);
            setSelectedData(realData.concat(nextFakeData));
        } else if (isLastPage()) {
            let realData = state.data.slice(pageNumber * pageSize, state.data.length);
            setSelectedData(prevFakeData.concat(realData));
        } else {
            let realData = state.data.slice(pageNumber * pageSize, (pageNumber * pageSize) + pageSize);
            setSelectedData(prevFakeData.concat(realData).concat(nextFakeData));
        }
    }, [pageSize, pageNumber]);

    const createPrevFakeData = size => {
        let fakeData = Array.apply(null, new Array(size)).map(Object.prototype.valueOf, new Object());
        return fakeData.map((currentValue, index) => setFakeData(index));
    }

    const createNextFakeData = size => {
        let fakeData = Array.apply(null, new Array(size)).map(Object.prototype.valueOf, new Object());
        return fakeData.map((currentValue, index) => setFakeData(index + (pageNumber + 1) * pageSize));
    }

    const setFakeData = id => {
        let fakeData = new Object();
        fakeData.tableData = {id: id};
        return fakeData;
    }

    const isFirstPage = () => {
        return pageNumber == 0;
    }

    const isLastPage = () => {
        return state.data.length <= pageNumber * pageSize + pageSize;
    }

    return (
        <MaterialTable
            onChangePage={changePage}
            onChangeRowsPerPage={changeRowsPerPage}
            icons={tableIcons}
            title="게시판"
            columns={state.columns}
            data={selectedData}
            options={{
                paginationType: "stepped"
            }}
            editable={{
                onRowAdd: (newData) =>
                    new Promise((resolve) => {
                        setTimeout(() => {
                            resolve();
                            setState((prevState) => {
                                const data = [...prevState.data];
                                data.push(newData);
                                return {...prevState, data};
                            });
                        }, 600);
                    }),
                onRowUpdate: (newData, oldData) =>
                    new Promise((resolve) => {
                        setTimeout(() => {
                            resolve();
                            if (oldData) {
                                setState((prevState) => {
                                    const data = [...prevState.data];
                                    data[data.indexOf(oldData)] = newData;
                                    return {...prevState, data};
                                });
                            }
                        }, 600);
                    }),
                onRowDelete: (oldData) =>
                    new Promise((resolve) => {
                        setTimeout(() => {
                            resolve();
                            setState((prevState) => {
                                const data = [...prevState.data];
                                data.splice(data.indexOf(oldData), 1);
                                return {...prevState, data};
                            });
                        }, 600);
                    }),
            }}
        />
    );
}

Redux 붙이기

위의 코드는 리덕스없이 리액트와 컴포넌트 내 상태를 이용하여 구성한 것이기에 Redux를 사용하는 구성으로 변경해보도록 하겠다.
우선 스토어와 관련된 코드를 작성한 뒤 Board 컴포넌트를 Board, BoardContainer로 분리할 것이다.

constant

export const BOARD_PAGE_SIZE = 5;

state의 초기화값과 MaterialTable 컴포넌트의 props로 사용되므로 상수로 정의하였다.

Store/board

type

export default {
    CHANGE_PAGE: 'BOARD/CHANGE_PAGE',
    CHANGE_PAGE_SIZE: 'BOARD/CHANGE_PAGE_SIZE'
}

action

import {createAction} from "redux-actions";
import type from './type'

export const changePage = createAction(
    type.CHANGE_PAGE,
    (pageNumber, selectedData) => ({
        pageNumber, selectedData
    })
)

export const changePageSize = createAction(
    type.CHANGE_PAGE_SIZE,
    (pageSize, selectedData) => ({
        pageSize, selectedData
    })
)

액션이 생성 될 때 마다 Data를 새로가져와서 selectedData state에 저장한다.
payloadCreator를 사용할 때 argument가 두가지 이상인 경우에는 위와 같이 작성하는 것을 잘 기억해두자.
접근할 때는 payload.pageNumber 등을 사용한다.

reducer

import {handleActions} from 'redux-actions'
import type from './type'
import {BOARD_PAGE_SIZE} from '../../../static/constant';

const initialState = {
    pageNumber : 0,
    pageSize : BOARD_PAGE_SIZE,
    selectedData : [{name: 'Mehmet', surname: '안녕하세요 ^^ 5-1', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터 5-2', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},]
}

export default handleActions({
        [type.CHANGE_PAGE]: (state, action) => ({
            ...state,
            pageNumber: action.payload.pageNumber,
            selectedData: action.payload.selectedData
        }),
        [type.CHANGE_PAGE_SIZE]: (state, action) => ({
            ...state,
            pageSize: action.payload.pageSize,
            selectedData: action.payload.selectedData
        })
    }, initialState
)

추후 변경될 여지가 많은 코드이다.

이전에 Board 컴포넌트의 state로 사용되던(아래 이미지 참고) 값들을 빼내 스토어의 state로 넣어주었다.

또한 하드코딩된 데이터를 받기 때문에 액션 생성 함수의 argument로 얻어온 selectedData를 이용해 갱신하고, 처음 초기화시에도 하드 코딩된 데이터를 이용하였다.

Component 분리하기

Board 컴포넌트에 포함되어있던 비즈니스 로직을 컨테이너로 올려주고, Board에서는 렌더링만 담당하도록 변경한다.

BoardContainer

1️⃣ 스토어 연동
Board 컴포넌트의 state로 관리되고 있던 columns와 data는 아래처럼 일반 상수로 빼주었다.

const BoardContainer = ({pageNumber, pageSize, selectedData, changePage, changePageSize}) => {
    const columns = [
        {title: '작성자', field: 'name'},
        {title: 'Surname', field: 'surname'},
        {title: 'Birth Year', field: 'birthYear', type: 'numeric'},
        {
            title: 'Birth Place',
            field: 'birthCity',
            lookup: {34: 'İstanbul', 63: 'Şanlıurfa'},
        },
    ]

    const data = [
        {name: 'Mehmet', surname: '안녕하세요 ^^ 5-1', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터 5-2', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다 5-3', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
        {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기 5-4', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 5-5', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
        {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다. 5-6', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!! 5-7', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: 'MacBook Pro 팔아요 5-8', birthYear: 2017, birthCity: 11},
        {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기 5-9', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니.. 5-10', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: '마지막 데이터입니다!!!!!!!', birthYear: 2017, birthCity: 11}
    ]

}

const mapStateToProps = state => ({
    pageNumber : state.board.pageNumber,
    pageSize : state.board.pageSize,
    selectedData : state.board.selectedData
})

const mapDispatchToProps = dispatch => ({
    changePage : (pageNumber, selectedData) => dispatch(changePage(pageNumber, selectedData)),
    changePageSize : (pageSize, selectedData) => dispatch(changePageSize(pageSize, selectedData)),
})

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(BoardContainer)

2️⃣ 데이터 조합
보여질 데이터와 가짜 데이터를 조합하는 여러 함수를 handleData라는 상수로 묶어 관리하도록 변경하였다.
우선, 이전 코드를 보자.
데이터를 조합하는 코드의 시작은 pageSizepageNumber가 변경되면 userEffect 훅이 실행되는 것이었다.

스토어에 저장된 selectedData는 액션 생성함수에 조합된 데이터를 payload로 전달하면 그 값으로 갱신된다. 따라서 useEffect를 사용해 최종적으로 selectedData를 갱신해주는 코드를 액션 생성함수로 전달할 데이터를 생성하는 코드로 변경한다.

useEffect에서 사용되는 함수인

  • createPrevFakeData
  • createNextFakeData
  • isFirstPage
  • isLastPage

또한 데이터를 조합하는 과정에서 사용되는 코드이므로 함께 묶어주었다.

const handleData = {
        get : (pageNumber, pageSize) => {
            let prevFakeData = handleData.createPrevFakeData(pageSize * pageNumber);
            let nextFakeData = handleData.createNextFakeData(pageSize);

            if (handleData.isFirstPage(pageNumber)) {
                let realData = data.slice(pageNumber * pageSize, pageSize);
                return realData.concat(nextFakeData);
            } else if (handleData.isLastPage(pageNumber)) {
                let realData = data.slice(pageNumber * pageSize, data.length);
                return prevFakeData.concat(realData);
            } else {
                let realData = data.slice(pageNumber * pageSize, (pageNumber * pageSize) + pageSize);
                return prevFakeData.concat(realData).concat(nextFakeData);
            }
        },

        createPrevFakeData : size => {
            let fakeData = Array.apply(null, new Array(size)).map(Object.prototype.valueOf, new Object());
            return fakeData.map((currentValue, index) => handleData.setFakeData(index));
        },

        createNextFakeData : size => {
            let fakeData = Array.apply(null, new Array(size)).map(Object.prototype.valueOf, new Object());
            return fakeData.map((currentValue, index) => handleData.setFakeData(index));
        },

        setFakeData : id => {
            let fakeData = new Object();
            fakeData.tableData = {id: id};
            return fakeData;
        },

        isFirstPage : pageNumber => {
            return pageNumber == 0;
        },

        isLastPage : pageNumber => {
            return data.length <= pageNumber * pageSize + pageSize;
        }

    }

3️⃣ 페이지 이동 or 페이지 당 Row 수 변경
그럼 위 코드는 언제 사용되는가? 페이지가 변경되거나 페이지 당 출력할 데이터 개수를 변경하여 dispatch된 액션 생성 함수를 호출 할 때 사용된다.

(전) Board(후) BoardContainer

이전에 상태 변경 흐름은 이러하였다. (페이지 변경 시)

  1. changePage 함수 호출
  2. setPageNumber 함수 호출 - pageNumber state 변경
  3. 재렌더링
  4. 렌더링 후에 useEffect 훅 발동
  5. 데이터 추출
  6. setSelectedData 함수 호출 - selectedData state 변경
  7. 재렌더링

리펙토링 후의 상태 변경 흐름은 이러하다. (페이지 변경 시)

  1. handleChangePage 함수 호출
  2. handleData.get 함수 호출
  3. 데이터 추출해서 반환
  4. changePage 액션 생성 함수 호출 - pageNumer, selectedData state 변경
  5. 재렌더링

✔️ BoardContainer 전체코드

import React, {useEffect} from "react";
import {connect} from 'react-redux';
import {changePage, changePageSize} from "../store/modules/board/action";
import Board from "../components/Board";

const BoardContainer = ({pageNumber, pageSize, selectedData, changePage, changePageSize}) => {

    const columns = [
        {title: '작성자', field: 'name'},
        {title: 'Surname', field: 'surname'},
        {title: 'Birth Year', field: 'birthYear', type: 'numeric'},
        {
            title: 'Birth Place',
            field: 'birthCity',
            lookup: {34: 'İstanbul', 63: 'Şanlıurfa'},
        },
    ]

    const data = [
        {name: 'Mehmet', surname: '안녕하세요 ^^ 5-1', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터 5-2', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다 5-3', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
        {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기 5-4', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 5-5', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: 'MacBook Pro 팔아요', birthYear: 2017, birthCity: 11},
        {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다. 5-6', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!! 5-7', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니..', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: 'MacBook Pro 팔아요 5-8', birthYear: 2017, birthCity: 11},
        {name: 'Mehmet', surname: '안녕하세요 ^^', birthYear: 1987, birthCity: 63},
        {name: 'Zerya Betül', surname: '글 제목입니다.', birthYear: 2017, birthCity: 34},
        {name: 'Bread', surname: '게시판 테스트', birthYear: 2011, birthCity: 34},
        {name: 'Jonny', surname: 'Material-table 이용하기', birthYear: 2012, birthCity: 17},
        {name: 'Sera', surname: '페이징 처리 이용하기 5-9', birthYear: 2007, birthCity: 17},
        {name: 'Simson', surname: '테스트 데이터', birthYear: 1999, birthCity: 8},
        {name: 'Cerry', surname: '야호 ㅋㅋ!!', birthYear: 1997, birthCity: 53},
        {name: 'Zebra', surname: '잘 넘어가나요?', birthYear: 1987, birthCity: 15},
        {name: 'M.J.', surname: '오키오키 ', birthYear: 1999, birthCity: 54},
        {name: 'K.Son', surname: 'ㅋ아니.. 5-10', birthYear: 2002, birthCity: 37},
        {name: 'Json', surname: '넵 열심히 하겠습니다', birthYear: 2015, birthCity: 98},
        {name: 'Merry', surname: '마지막 데이터입니다!!!!!!!', birthYear: 2017, birthCity: 11}
    ]

    const handleData = {
        get : (pageNumber, pageSize) => {
            let prevFakeData = handleData.createPrevFakeData(pageSize * pageNumber);
            let nextFakeData = handleData.createNextFakeData(pageSize);

            if (handleData.isFirstPage(pageNumber)) {
                let realData = data.slice(pageNumber * pageSize, pageSize);
                return realData.concat(nextFakeData);
            } else if (handleData.isLastPage(pageNumber)) {
                let realData = data.slice(pageNumber * pageSize, data.length);
                return prevFakeData.concat(realData);
            } else {
                let realData = data.slice(pageNumber * pageSize, (pageNumber * pageSize) + pageSize);
                return prevFakeData.concat(realData).concat(nextFakeData);
            }
        },

        createPrevFakeData : size => {
            let fakeData = Array.apply(null, new Array(size)).map(Object.prototype.valueOf, new Object());
            return fakeData.map((currentValue, index) => handleData.setFakeData(index));
        },

        createNextFakeData : size => {
            let fakeData = Array.apply(null, new Array(size)).map(Object.prototype.valueOf, new Object());
            return fakeData.map((currentValue, index) => handleData.setFakeData(index));
        },

        setFakeData : id => {
            let fakeData = new Object();
            fakeData.tableData = {id: id};
            return fakeData;
        },

        isFirstPage : pageNumber => {
            return pageNumber == 0;
        },

        isLastPage : pageNumber => {
            return data.length <= pageNumber * pageSize + pageSize;
        }

    }

    const handleChangePage = (pageNumber) => {
        changePage(pageNumber, handleData.get(pageNumber, pageSize));
    };

    const handleChangeRowPerPage = (pageSize) => {
        changePageSize(pageSize, handleData.get(pageNumber, pageSize));
    }

    return (
        <Board
            pageNumber={pageNumber}
            pageSize={pageSize}
            selectedData={selectedData}
            handleChangePage={handleChangePage}
            handleChangeRowPerPage={handleChangeRowPerPage}
            columns={columns}
            data={selectedData}
        />
    );
}

const mapStateToProps = state => ({
    pageNumber : state.board.pageNumber,
    pageSize : state.board.pageSize,
    selectedData : state.board.selectedData
})

const mapDispatchToProps = dispatch => ({
    changePage : (pageNumber, selectedData) => dispatch(changePage(pageNumber, selectedData)),
    changePageSize : (pageSize, selectedData) => dispatch(changePageSize(pageSize, selectedData)),
})

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(BoardContainer)

Board

import React, {useEffect} from 'react';
import MaterialTable from 'material-table';

import {forwardRef} from 'react';

import {BOARD_PAGE_SIZE} from '../static/constant';

import AddBox from '@material-ui/icons/AddBox';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/ViewColumn';

const tableIcons = {
    Add: forwardRef((props, ref) => <AddBox {...props} ref={ref}/>),
    Check: forwardRef((props, ref) => <Check {...props} ref={ref}/>),
    Clear: forwardRef((props, ref) => <Clear {...props} ref={ref}/>),
    Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref}/>),
    DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref}/>),
    Edit: forwardRef((props, ref) => <Edit {...props} ref={ref}/>),
    Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref}/>),
    Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref}/>),
    FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref}/>),
    LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref}/>),
    NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref}/>),
    PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref}/>),
    ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref}/>),
    Search: forwardRef((props, ref) => <Search {...props} ref={ref}/>),
    SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref}/>),
    ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref}/>),
    ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref}/>)
};


export default function Board({pageNumber, pageSize, selectedData, columns, data, handleChangePage, handleChangeRowPerPage}) {

    return (
        <MaterialTable
            onChangePage={handleChangePage}
            onChangeRowsPerPage={handleChangeRowPerPage}
            icons={tableIcons}
            title="게시판"
            columns={columns}
            data={selectedData}
            options={{
                paginationType: "stepped",
                pageSize: BOARD_PAGE_SIZE
            }}
            /*editable={{
                onRowAdd: (newData) =>
                    new Promise((resolve) => {
                        setTimeout(() => {
                            resolve();
                            setState((prevState) => {
                                const data = [...prevState.data];
                                data.push(newData);
                                return {...prevState, data};
                            });
                        }, 600);
                    }),
                onRowUpdate: (newData, oldData) =>
                    new Promise((resolve) => {
                        setTimeout(() => {
                            resolve();
                            if (oldData) {
                                setState((prevState) => {
                                    const data = [...prevState.data];
                                    data[data.indexOf(oldData)] = newData;
                                    return {...prevState, data};
                                });
                            }
                        }, 600);
                    }),
                onRowDelete: (oldData) =>
                    new Promise((resolve) => {
                        setTimeout(() => {
                            resolve();
                            setState((prevState) => {
                                const data = [...prevState.data];
                                data.splice(data.indexOf(oldData), 1);
                                return {...prevState, data};
                            });
                        }, 600);
                    }),
            }}*/
        />
    );
}

import {BOARD_PAGE_SIZE} from '../static/constant';를 추가하여 상수로 정의된 페이지 당 row 수를 초기화 시켰다.

아래 CRUD 작업이 필요한 editable 부분은 주석처리 하였다.

App

Board가 컴포넌트로 지정되어있더 라우트를 BoardContainer로 변경한다.

import React from "react";
import {BrowserRouter, Route, Switch} from "react-router-dom";
import MainContainer from "./containers/MainContainer";
import BoardContainer from "./containers/BoardContainer";

const App = () => {
    return (
        <BrowserRouter>
            <Switch>
                <Route path="/" exact component={MainContainer}/>
                <Route path="/board" component={BoardContainer} />
            </Switch>
        </BrowserRouter>
    )

}

export default App;

🙋🏻 잘 작동하는지 실행해보자!!

전체 코드는 github에서 확인할 수 있습니다.

profile
🌱 😈💻 🌱

0개의 댓글