React UseMemo

미마모코딩·2022년 10월 14일
0

리액트 기본기

목록 보기
5/6
post-thumbnail

오늘은 유즈메모 훅에 대해 공부해보자.

먼저 useMemo에서 Memo는 무엇일까?

바로 Memoization을 뜻한다.

Memoization이란?

동일한 함수의 값을 리턴하는 함수가 있을 때 , 그 함수를 반복적으로 호출해야 한다면
맨 처음 리턴값을 메모리에 저장해서 필요할때마다 값을 메모리에서 꺼내는 것을 의미한다.
즉 계산을 하지않고 값만 꺼내서 한다는 말이다.

함수형 컴포넌트는 함수다.

위 Memoization을 구체적으로 설명한 이유가 뭔지 감이 올 것이다.

위의 핵심 내용은 동일한 함수의 값을 리턴 한다,라는 말과 함수형 컴포넌트는 함수다 라는 것이다.

함수형 컴포넌트가 랜더링이 된다는것은?

해당 함수가 호출된다는 것이다. 또한 모든 내부 변수들이 초기화된다.

memoization을 사용해야 하는 예

function Component (){
const value = calculate()
return <div>{value}</div>
}

//function calculate(){
  return 10
}

랜더링 과정 : 랜더링 - Component함수 호출 - 모든 내부 변수 초기화

우리의 컴포넌트는 state의 값이 변경되거나 props의 값이 변경 될 때 수많은 랜더링이 일어나게 된다.

만약 Component라는 컴포넌트가 랜더링이 되게 되면 value라는 변수가 초기화 되기 때문에 calculate함수가 반복적으로 호출될 것이다.

만약 calculate함수가 매우 무거운 작업을 이루고 있는 함수라면?

효율적인 컴포넌트를 기대하긴 어려울 것이다.

위 코드를 useMemo로 개선하려면?

function Component (){
 const value = useMemo(()=>calculate(),[])
 return <div>{value}</div>
}

위와 같이 개선 할 수 있을 것이다.

랜더링과정: 랜더링 - Component 함수 호출,memoization - 랜더링 - Component함수 호출,memoization 된 값을 재사용

useMemo 써보기

useMemo는 두 개의 인자를 받는다 .

하나는 콜백함수 , 하나는 의존성 배열이다.

콜백함수는 우리가 memoization 해줄 값을 리턴하는 함수이다.

이 콜백함수가 리턴하는 값이 바로 useMemo가 리턴하는 값이다.

const value = useMemo(()=>calculate(),[])
                    //저는 콜백함수 ,저는의존성배열

의존성배열은 왜 들어가는 걸까?

const value = useMemo(()=>calculate(),[item])
                     //저는 콜백함수 ,저는의존성배열

위의 의존성 배열이 가리키는 것은 item의 값이 변경될때만 callback함수를 호출해서
memoization된 값을 다시 업데이트 하고 memoization을 해주기 때문
에 의존성배열에 빈 배열을 넣게되면 처음 memoization이 이루어지고 그 다음부터 memoization된 값을 업데이트를 하지 않기 때문이다 .

useMemo의 주의해야하는 이유

useMemo를 사용한다는 것은 값을 재활용하기 위해 따로 메모리를 소비해서 저장한다는것이다.

그렇기 때문에 무분별한 memoization은 오히려 성능을 악화시킨다.

useMemo의 분석

import React, { useState } from "react";

const hardCalculate = (number) => {
  console.log("어려운 계산");
  for (let i = 0; i < 99999999; i++) {} //작업을 무겁게 만드는 코드
  if (isNaN(number + 10000)) {
    return 10000;
  }
  return number + 10000;
};

const App = () => {
  const [hardNumber, setHardNumber] = useState(1);

  const hardSum = hardCalculate(hardNumber);
  return (
    <div>
      <h3>어려운 계산기</h3>
      <input
        type="number"
        onChange={(e) => setHardNumber(parseInt(e.target.value))}
      ></input>
      <span> + 10000 ={hardSum}</span>
    </div>
  );
};

export default App;

위와같은 작업을 무겁게 만드는 코드로 입력한 숫자에 10000을 더하는 코드를 만들어봤다.

우리는 스테이트의 값을 변경 할 때 마다 저 무거운 함수가 반복적으로 호출되기 때문에 약간의 딜레이가 (1초가량) 생기게 된다.

그럼 저 무거운 코드를 사용 하지 않고 스테이트를 하나 더 만들어보자.

import React, { useState } from "react";

const hardCalculate = (number) => {
  console.log("어려운 계산");
  for (let i = 0; i < 999999999; i++) {}
  if (isNaN(number + 10000)) {
    return 10000;
  }
  return number + 10000;
};

const easyCalculate = (number) => {
  console.log("쉬운 계산");
  if (isNaN(number + 1)) {
    return 1;
  }
  return number + 1;
};

const App = () => {
  const [hardNumber, setHardNumber] = useState(1);
  const [easyNumber, setEasyNumber] = useState(1);

  const hardSum = hardCalculate(hardNumber);
  const easySum = easyCalculate(easyNumber);
  return (
    <div>
      <h3>어려운 계산기</h3>
      <input
        type="number"
        onChange={(e) => setHardNumber(parseInt(e.target.value))}
      ></input>
      <span> + 10000 ={hardSum}</span>

      <h3>쉬운 계산기</h3>
      <input
        type="number"
        onChange={(e) => setEasyNumber(parseInt(e.target.value))}
      ></input>
      <span> + 1 ={easySum}</span>
    </div>
  );
};

export default App;

위와같이 for loop를 도는 코드를 지웠고 가벼운 코드가 남게 만들었다.

저 코드를 복사해서 돌려보면 쉬운 계산기만 조작하는데 왜 또 딜레이가 생기지..?

무거운 작업은 없애버렸는데 말이다.

생각해보자 우리의 컴포넌트는 함수이다.

easyNumber를 바꾸기 때문에 컴포넌트가 다시 랜더링되고 초기화 될 것이다.

초기화가 되면 app 컴포넌트에 변수들이 초기화되고 컴포넌트 내부의 함수도 호출될것이다.

const hardSum = hardCalculate(hardNumber);

그것 때문에 어떤 스테이트를 변경해도 모두 위 코드를 부르기 때문에 문제가 되는것이다.

어떻게 개선 할 수 있을까?

import React, { useState, useMemo } from "react";

const hardCalculate = (number) => {
  console.log("어려운 계산");
  for (let i = 0; i < 999999999; i++) {}
  if (isNaN(number + 10000)) {
    return 10000;
  }
  return number + 10000;
};

const easyCalculate = (number) => {
  console.log("쉬운 계산");
  if (isNaN(number + 1)) {
    return 1;
  }
  return number + 1;
};

const App = () => {
  const [hardNumber, setHardNumber] = useState(1);
  const [easyNumber, setEasyNumber] = useState(1);

  const hardSum = useMemo(() => {
    return hardCalculate(hardNumber);
  }, [hardNumber]);
  const easySum = easyCalculate(easyNumber);
  return (
    <div>
      <h3>어려운 계산기</h3>
      <input
        type="number"
        onChange={(e) => setHardNumber(parseInt(e.target.value))}
      ></input>
      <span> + 10000 ={hardSum}</span>

      <h3>쉬운 계산기</h3>
      <input
        type="number"
        onChange={(e) => setEasyNumber(parseInt(e.target.value))}
      ></input>
      <span> + 1 ={easySum}</span>
    </div>
  );
};

export default App;

바로 app 컴포넌트 안에서
const hardSum = useMemo(() => {
return hardCalculate(hardNumber);
}, [hardNumber]); 를 해주어 메모리에 저 계산을 굳이 계속하지 않고 저장해두는 것이다.hardNumber가 바뀌지 않는이상 저 계산은 더이상 이루어지지 않을 것이며 바로 메모리에서 값을 추출해서 사용할것이다.

위와같이 사용하면 hardNumber가 바뀌는 순간은 똑같이 딜레이가 생길것이다.

하지만 easyNumber은 다르다 . easyNumber는 아주 빠른속도로 증가하고 감소하게 된다.

오늘은 이렇게 useMemo를 사용해야하는 상황 , 개선법 , 개념들을 공부했다 .

변화과정을 꼭 눈으로 보고 익히길 바란다.

0개의 댓글