7d. 더 최적화하기

여기서 더 최적화를 하실 수 있는데요, 바로 react-virtualized 라는 라이브러리를 사용하는 것 입니다. 이 라이브러리를 필수로 사용해야하는 것은 아니고, 아예 직접 구현 할 수도 있지만 보통 라이브러리를 사용하는 편이 여러분의 시간을 단축시켜줍니다.

react-virtualized 는, 실제로 화면에 보여지는 데이터만 렌더링해주고 나머지는 숨겨주고 그냥 사이즈만 채워주게끔 합니다. 그렇게 함으로써 성능을 엄청나게 최적화 할 수도 있는데요, 이 방식은 만약에 여러분이 리스트를 렌더링하고 아주 자주 바뀌며 보여주는 갯수가 매우 많을때 고려해보시면 됩니다. (쉽게 말하자면, 리스트를 렌더링하는데 렉이 걸리면 그때가서 고려하셔도 됩니다.)

이 섹션은 예제 위주로 살펴보도록 하겠습니다.

src/components/TodoList.js

여기서 핵심은 renderRow 함수와 List 컴포넌트입니다. List 에서는 우리가 보여줄 아이템의 크기, 그리고 리스트 전체의 크기를 사전에 정해주고, 컴포넌트를 렌더링 시켜줄 함수 rowRenderer 를 전달해줍니다.

rowRenderer 에서는, List 내부에서 전달해주는 index, key 값을 사용하여, 현재 화면에 보여야하는지 유무를 판별하여 렌더링을 해줍니다. 그리고, 여기서 style 값도 전달받게되는데 이는 아이템 컴포넌트한테 전달해줘야합니다.

import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { List } from 'react-virtualized';

class TodoList extends Component {
  renderRow = ({ index, key, style, parent }) => {
    // parent.props.[List 에 전달해준 props는 parent.props 로 조회 가능]
    const { todos, onCheck, onRemove } = this.props;
    const todo = todos[index];
    return (
      <TodoItem
        id={todo.id}
        checked={todo.checked}
        text={todo.text}
        todo={todo}
        onCheck={onCheck}
        onRemove={onRemove}
        key={key}
        style={style}
      />
    );
  };

  render() {
    const { todos, onCheck, onRemove } = this.props;
    return (
      <List
        width={600}
        height={364}
        rowCount={todos.length}
        rowHeight={62}
        rowRenderer={this.renderRow}
        list={todos}
        style={{ outline: 'none' }}
      />
    );
  }
}

export default TodoList;

src/components/TodoItem.js

기존 컴포넌트를 div 로 한번 감싸주고, virtualized-todoitem 이라는 임의 CSS 클래스를 생성해주었습니다. 그리고, 부모에게서 받은 style props 를 넣어주었습니다.

import React, { Component } from 'react';
import './TodoItem.css';

class TodoItem extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.todo !== this.props.todo;
  }
  render() {
    const { checked, text, id, onCheck, onRemove, style } = this.props;
    return (
      <div className="virtualized-todoitem" style={style}>
        <div
          className={`TodoItem ${checked && 'active'}`}
          onClick={() => onCheck(id)}
        >
          <div className="check">&#10004;</div>
          <div className="text">{text}</div>
          <div
            className="remove"
            onClick={e => {
              e.stopPropagation();
              onRemove(id);
            }}
          >
            [지우기]
          </div>
        </div>
      </div>
    );
  }
}

export default TodoItem;

그리고, 이에 따른 스타일 변경도 조금 있습니다.

src/components/TodoItem.css

.TodoItem {
  padding: 1rem; /* 패딩 설정함 */
  display: flex;
  align-items: center;
  cursor: pointer;
}

/* border 를 virtualized-todoitem 사이에 적용 */
.virtualized-todoitem + .virtualized-todoitem {
  border-top: 1px solid #e9ecef;
}

/* ... */

src/App.css

/* ... */

.App .white-box {
  /* 기존 padding 값 변경 */
  padding-top: 1rem;
  padding-bottom: 1rem;
  background: white;
  min-height: 8rem;
  max-height: 25rem;
  overflow-y: auto;
}

Edit Optimized with react-virtualized

production 빌드

이렇게 최적화를 하고나면, 개발모드에서도 10ms 내외로 작업이 끝나고, 실제로 6개 가량의 아이템 컴포넌트만이 렌더링되는것을 확인 할 수 있습니다.

스크롤바가 있어서, 데이터가 더 있는것처럼 보이지만 사실은, 그냥 자리만 차지하고있는 공백칸이 있는것이고, 스크롤 위치에 따라 자연스럽게 보여줘야 할 데이터를 교체하는것이죠.

만약에 아이템의 높이가 가변적이라면, List 대신 AutoSizer 를 사용하면 됩니다.

이런 라이브러리가 있다는것을 잘 알아두고, 써야하는 상황에서 잘 쓰시면, 다루는 데이터가 많아도 랙걸리지 않는 앱을 개발하실 수 있을겁니다.

results matching ""

    No results matching ""