[React] useMemo()로 데이터 최적화하기

summereuna🐥·2023년 5월 17일
0

React JS

목록 보기
51/69

현재 리스트를 렌더링하고 있는데, 무작위로 입력된 배열은 간단한 내장 정렬 메소드를 사용하여 목록을 정렬하고 있다.

📍 App.js

import React, { useState, useCallback, useMemo } from 'react';

import './App.css';
import DemoList from './components/Demo/DemoList';
import Button from './components/UI/Button/Button';

function App() {
  const [listTitle, setListTitle] = useState('My List');

  //제목 바꾸는 함수
  const changeTitleHandler = useCallback(() => {
    setListTitle('New Title');
  }, []);

  return (
    <div className="app">
      <DemoList title={listTitle} items={[5, 3, 1, 10, 9]} />
      <Button onClick={changeTitleHandler}>Change List Title</Button>
    </div>
  );
}

export default App;

📍 /components/Demo/DemoList.js

import React from 'react';

import classes from './DemoList.module.css';

const DemoList = (props) => {
  //정렬 메소드
  const sortedList = props.items.sort((a, b) => a - b);
  
  console.log('DemoList RUNNING');

  return (
    <div className={classes.list}>
      <h2>{props.title}</h2>
      <ul>
        {sortedList.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

export default React.memo(DemoList);

지금은 정렬할 요소가 많지 않지만, 만약에 요소가 엄청나게 많다고 해보자.
그러면 이런 정렬 메소드가 있는 경우, 전체 컴포넌트가 재평가될 때 마다 매번 정렬하는 코드를 재실행하고 싶지는 않을 것이다.

위와 같이 React.memo()를 통해 DemoList를 감싸주면 불필요한 동작을 막을 수 있을 것 같다.

  1. 버튼을 클릭하면 {props.title}이 바뀌었기 때문에 DemoList가 재실행되는건 당연하다. props가 바뀌었기 때문에 React.memo()는 정상적으로 작동한다. (문제 X)

  2. 또한 현재 부모 컴포넌트가 재렌더링되었기 때문에 DemoList도 재렌더링되는 것이기도 하다. 왜냐하면 부모 컴포넌트가 실행될 때마다 부모컴포넌트에서 [5, 3, 1, 10, 9] 배열을 지속적으로 새로 만들기 때문이다.
    배열도 함수처럼 객체이고, 객체는 참조값이다. 같은 배열처럼 보이지만 메모리 안에서는 다른 위치를 가지는 다른 배열이다.

위 두 가지 이유 때문에 현재 DemoList는 리빌드되기에 타당한 이유를 가지고 있다.
하지만 정렬은 컴포넌트에서 수행가능한 대표적인 성능 집약적 작업 중 하나이다. 바뀌지 않는 저 배열을 지속적으로 새로 만들기는 원하지 않는다면, 2의 문제는 어떻게 하면 해결할 수 있을까?

useMemo() 훅을 사용하면 된다.

  • useCallback()에 함수에 대한 것을 저장하듯, useMemo()에는 모든 종류의 데이터를 저장할 수 있다.

📍 /components/Demo/DemoList.js

import React, { useMemo } from 'react';

import classes from './DemoList.module.css';

const DemoList = (props) => {
  //객체 구조분해할당으로 props.items 배열을 가져온다.
  const { items } = props;

  const sortedList = useMemo(() => {
    console.log('Items sorted');
    //첫번째 인자는 저장하고 싶은 데이터를 반환하는 함수
    return items.sort((a, b) => a - b);
    //두 번째 인자는 의존성 배열로, 의존성 배열을 통해 저장된 값에 변경사항이 생기면 업데이트됨
    //즉 items 배열에 새로운 값이 추가되거나 변경되면 다시 재정렬됨
  }, [items]); 
  
  console.log('DemoList RUNNING');

  return (
    <div className={classes.list}>
      <h2>{props.title}</h2>
      <ul>
        {sortedList.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

export default React.memo(DemoList);

const sortedList = useMemo(() => return items.sort((a, b) => a - b);}, [items]);

  • useMemo()의 첫 번째 인자에는 함수가 들어가야 한다. 그렇다고 함수를 기억하는 건 아니고, 이 함수는 저장하고 싶은 데이터를 반환하는 함수이다.
  • 두 번째 인자는 의존성 배열로, 의존성 배열을 통해 저장된 값에 변경사항이 생기면 업데이트된다.
    즉, items 배열에 새로운 값이 추가되거나 변경되면 다시 재정렬된다.

이렇게만 하면 여전히 2번 문제는 해결되지 않는다. App이 재실행될 때마다 props.item으로 가는 배열이 새롭게 생성되고 있기 때문이다. 따라서 그 부분에도 useMemo()를 사용하여 감싸주자.

📍 App.js

import React, { useState, useCallback, useMemo } from 'react';

import './App.css';
import DemoList from './components/Demo/DemoList';
import Button from './components/UI/Button/Button';

function App() {
  const [listTitle, setListTitle] = useState('My List');

  const changeTitleHandler = useCallback(() => {
    setListTitle('New Title');
  }, []);
  
  //useMemo(함수의 리턴값으로 기존 배열을 넣고, 바뀌는게 없이 하드코딩되어 있으니 종속배열은 비워둠)
  const listItems = useMemo(() => [5, 3, 1, 10, 9], []);

  return (
    <div className="app">
      <DemoList title={listTitle} items={listItems} />
      <Button onClick={changeTitleHandler}>Change List Title</Button>
    </div>
  );
}

export default App;

이렇게 해주면 잘 작동한다.
useMemo 덕분에 이제 더 이상 배열이 재정렬되지는 않는다.



요약

useMemo는 useCallback에 비하면 사용빈도는 낮다. 함수를 기억하는 것이 훨씬 더 도움이 되고, 데이터를 기억할 일보다 빈도수가 더 많기 때문이다.
물론 데이터 재계산과 같은 성능 집약적 작업 때문에 데이터를 저장해야할 필요도 있지만 이런 경우가 아니라면 별로 사용하지 않는다.

useMemo를 사용하여 데이터를 저장하면, 메모리를 사용하여 저장하는 것이다.
따라서 useMemo는 사용하는 모든 값에 다 사용하는 훅은 아니다. 위의 경우처럼 뭔가를 정렬하는 경우, 이후 컴포넌트 업데이트에서 불필요한 정렬을 막아야 할 때 useMemo를 적절히 사용하도록 하자.

뭐든지 정답은 없다. 앱이 느려지거나 개선해야할 때 이런 훅을 이용하여 최적화 가능한 부분을 찾아 볼 수 있다.

profile
Always have hope🍀 & constant passion🔥

0개의 댓글