React UseMemo Hook 에 대해 알아보자

useMemo 훅은 함수 컴포넌트 내부에서 발생하는 연산을 최적화 하기 위해서 사용되는 로직입니다.

해당 Hook 이 실제로 어떻게 작동이 되는지 확인하기 위해서

실제 코드를 작성하여 확인해 보겠습니다.

예시 코드

우선은 위와 같은 코드를 만들었습니다.

아직은 useMemo 과 활용되지 않은 코드입니다.

Input 창에 숫자를 넣고 추가 버튼을 누르면 리스트 내부로 숫자가 들어가고,

해당 숫자들을 가지고 평균값을 출력하는 간단한 코드입니다.

				
					import { useState } from "react";

const getAverage = (numbers) => {
  console.log("Calcuating Average...");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

  const onChange = (e) => {
    setNumber(e.target.value);
  };

  const onInsert = (e) => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber("");
  };

  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}> 리스트에 추가</button>

      <ul>
        {list.map((value, index) => {
          <li key={index}>{value}</li>;
        })}
      </ul>

      <div>
        <b>평균값 :</b> {getAverage(list)}
      </div>
    </div>
  );
};

export default Average;

				
			

실행되는 화면을 한 번 보겠습니다.

Input 창에 숫자를 적을 때마다, Console 창에 Average 를 계산중이라는 메시자가 발생합니다.

이 메시지는 분명 getAverage 라는 함수 내부에 정의를 했죠?

그리고 이 getAverage 는 버튼을 눌렀을 때만 작동되도록 했는데, Input 창에 변화를 줘도 계속해서 나타납니다.

변화가 있을 때마다 DOM 을 랜더링 하는 리액트 특성 때문인데요.

아무 상관도 없는 변화 때문에 다시 한 번 모든 DOM을 랜더링 하는 것은 비효율적이니 변환을 해봅시다.

useMemo 를 반영한 코드

				
					import { useState, useMemo } from "react";

const getAverage = (numbers) => {
  console.log("Calcuating Average...");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

  const onChange = (e) => {
    setNumber(e.target.value);
  };

  const onInsert = (e) => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber("");
  };

  //해당 부분을 추가한다.
  const avg = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}> 리스트에 추가</button>

      <ul>
        {list.map((value, index) => {
          return <li key={index}>{value}</li>;
        })}
      </ul>

      <div>
        <b>평균값 :</b> {avg}
      </div>
    </div>
  );
};

export default Average;

				
			

기존에 곧장 getAverage(list) 를 사용하여 평균값을 보여주던 부분에 새로운 변수인 avg 를 할당합니다.

이 avg 에는 useMemo 훅을 사용해줍니다.

getAverage(list) 사용은 그대로 해주되, [list] 를 추가하여 해당 변수에 변화가 생겼을 때에만 랜더링이 일어나도록 변형을 주었습니다. 

마치며...

결과적으로 리스트에 추가라는 버튼을 눌러 list 내부에 변화가 생겼을 때에만

getAverage 가 실행되는 것을 알 수 있습니다. 

조금 더 최적화 가능한 기법을 위해 다음 버튼을 눌러주세요.