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">✔</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;
}
이렇게 최적화를 하고나면, 개발모드에서도 10ms 내외로 작업이 끝나고, 실제로 6개 가량의 아이템 컴포넌트만이 렌더링되는것을 확인 할 수 있습니다.
스크롤바가 있어서, 데이터가 더 있는것처럼 보이지만 사실은, 그냥 자리만 차지하고있는 공백칸이 있는것이고, 스크롤 위치에 따라 자연스럽게 보여줘야 할 데이터를 교체하는것이죠.
만약에 아이템의 높이가 가변적이라면, List 대신 AutoSizer 를 사용하면 됩니다.
이런 라이브러리가 있다는것을 잘 알아두고, 써야하는 상황에서 잘 쓰시면, 다루는 데이터가 많아도 랙걸리지 않는 앱을 개발하실 수 있을겁니다.