[React Hooks 총정리] useMemo

혜빈·2024년 7월 8일
0

REACT 보충개념

목록 보기
32/48

컴포넌트 최적화 방법

  • useMemo, useCallback이 있음

useMemo 개념

  • useMemo에서 Memo는 Memoization을 의미함
  • 동일한 값을 리턴하는 함수를 반복적으로 호출해야 한다면 맨 처음 값을 계산할 때 해당 값에 메모리에 저장해서 필요할 때마다 또다시 계산하지 않고 메모리에서 꺼내서 재사용을 하는 기법
  • 자주 필요한 값을 맨 처음에 캐싱을 해두어 그 값이 필요할 때마다 다시 계산하지 않고 캐시에서 꺼내서 사용
function calculate() {
	return 10
}

value = calculate()

value = calculate()

value = calculate()


함수형 컴포넌트

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

function calculate () {
	return 10
};
  • 함수형 컴포넌트는 말 그대로 함수임
  • 함수형 컴포넌트 렌더링 -> component 함수 호출 -> 모든 내부 변수 초기화
  • 만약 calculate 함수가 무거운 일을 하는 함수라면 무의미한 반복을 하기 때문에 비효율적임
  • useMemo를 사용하면 효율적으로 작업 가능함

useMemo 사용시

  • 렌더링 -> component 함수 호출, Memoization -> 렌더링 -> component 함수 호출, Memoize 된 값을 재사용

useMemo 기본 형태

const value = useMemo(() => {
	return calculate();
}, [item]);
  • 두개의 인자를 받음
  • 첫 번째 인자로는 콜백함수, 두 번째 인자로는 배열을 받음
  • 첫 번째 인자 콜백함수 : Memoization 해줄 값을 계산해서 return 해주는 함수(useMemo가 return 하는 값)
  • 두 번째 인자 배열(의존성 배열) : 배열 안의 요소의 값이 업데이트 될 때만 콜백 함수를 다시 호출해서 Memoization 된 값을 업데이트 해서 다시 Memoization을 해줌
const value = useMemo(() => {
	return calculate();
}, []);
  • 만약 빈 배열을 넘겨주면 맨 처음 컴포넌트가 마운트 되었을 때만 값을 계산하고 이후에는 항상 Memoization 된 값을 꺼내와서 사용함

useMemo 주의사항

  • 값을 재활용 하기 위해서 따로 메모리를 소비해서 저장하는거라 불필요한 값을까지 모두 useMemo를 사용하면 오히려 성능이 악화될 수 있음
  • 필요할때만 적절하게 사용하기

실제 코드 구현

UseMemoHook.js

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

const hardCalculate = (number) => {
  console.log("어려운 계산");
  for (let i = 0; i < 99999999; i++) {} //생각하는 시간
  return number + 10000;
};

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

const UseMemoHook = () => {
  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"
        value={hardNumber}
        onChange={(e) => setHardNumber(parseInt(e.target.value))}
      ></input>
      <span> + 1000 = {hardSum}</span>

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

export default UseMemoHook;
  • UseMemoHook은 함수형 컴포넌트기 때문에 쉬운 계산기 또는 어려운 계산기의 숫자를 증가시켜주면 easyNumber State 또는 hardNumber State가 바뀌고 UseMemoHook 컴포넌트가 다시 렌더링 됨

  • 그럼 UseMemoHook 안에 정의되어있는 hardSum과 easySum 두개의 변수 모두 다 다시 초기화 됨

  • hardCalculate 함수와 easyCalculate 함수가 불리게 됨 -> 의미없이 돌아가게 됨 -> 비효율적임

  • Q. easyNumber State를 변경할 때 hardCalculate 함수가 불리지 않게 하는 방법은 없을까?

  • A. useMemo를 사용하자!

  • 어떤 조건이 만족 되었을 때만 변수들이 초기화 되도록 할 수 있음

  • 만약 조건이 만족 되지 않았다면 UseMemoHook 컴포넌트가 다시 렌더링 되더라도 다시 초기화를 시켜주는게 아니라 이전에 가지고 있던 값을 그대로 사용하게 해줌(= Memoization)

useMemo 적용한 코드

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

const hardCalculate = (number) => {
  console.log("어려운 계산");
  for (let i = 0; i < 99999999; i++) {} //생각하는 시간
  return number + 10000;
};

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

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

  // const hardSum = hardCalculate(hardNumber);

  const hardSum = useMemo(() => {
    return hardCalculate(hardNumber);
  }, [hardNumber]);
  // hardNumber가 변경되어야지 hardCalculate가 다시 불려서 hardSum을 초기화 해줌
  // 만약 hardNumber가 변경되지 않았다면 이전 값을 재사용함
  const easySum = easyCalculate(easyNumber);

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

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

export default UseMemoHook;
  • 이제 쉬운 계산기 카운트를 증가시키면 쉬운 계산만 작동하고, 어려운 계산기 카운트를 증가시키면 어려운 계산과 쉬운 계산이 같이 작동함
  const hardSum = useMemo(() => {
    return hardCalculate(hardNumber);
  }, [hardNumber]);
  • useMemo를 사용해서 hardNumber가 변경될때만 hardCalculate가 다시 불려서 hardSum을 초기화해주도록 작성했기 때문임

실제 코드 구현 2

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

function UseMemoHook2() {
  const [number, setNumber] = useState(0);
  const [isKorea, setIsKorea] = useState(true);

  const location = isKorea ? "한국" : "외국";

  useEffect(() => {
    console.log("useEffect 호출");
  }, [location]);
  // 처음 렌더링 될 때, location이 바뀌었을 때만 실행됨

  return (
    <div>
      <h2>하루에 몇끼 먹나요?</h2>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      ></input>
      <br />
      <h2>어느 나라에 있나요?</h2>
      <p>나라 : {location}</p>
      <button onClick={() => setIsKorea(!isKorea)}>비행기 타자~!</button>
      {/*  isKorea가 false면 true, true면 false로 바뀌줌 */}
    </div>
  );
}

export default UseMemoHook2;
  • 처음 렌더링 될 때, 의존성 배열 안의 location이 바뀌었을 때만 실행됨

  • 그런데 의존성으로 전달해준 location이 string과 같은 원시타입이 아니라 object라면 다르게 작동함

  • location을 object로 변경하면 location이 바뀌었을 때 뿐만 아니라 '하루에 몇끼 먹나요?' 카운트를 증가시킬때도 useEffect가 호출이 됨

원시(Primitive) 타입 VS 객체(Object) 타입

변수

  • 어떠한 값을 넣어 둘 수 있는 상자

원시타입

  • String, Number, Boolean, Null, Undefined, Biglnt, Symbol

  • 어떠한 변수에 원시 타입의 값을 할당하면 그 값은 변수라는 상자에 바로 들어감

  • 같은 원시값을 가지고 있는 두 가지 변수를 비교 연산자로 비교하면 true가 나옴
  • 변수라는 상자 안의 값이 동일하기 때문임

객체타입

  • 원시타입을 제외한 모든 것 (Object, Array ....)

  • 어떠한 변수에 객체 타입의 값을 할당하면 객체는 너무 크기 때문에 바로 변수라는 상자에 들어가는 것이 아니라,
    메모리상에 공간이 할당 되어 그 공간에 메모리 안에 보관이 됨
    그리고 변수 안에는 그 객체가 담긴 메모리의 주소가 할당이 됨

  • 똑같아 보이는 객체를 넣어준 두 가지의 변수를 비교하면 false가 나옴
  • 변수라는 상자 안에는 메모리상의 주소가 들어가 있고, 두 객체는 다른 주소에 저장되어있기 때문임
  • 다른 주소를 비교하는거니까 false가 나옴


  • '하루에 몇끼 먹나요?'를 증가시키면 number State가 바뀌기 때문에 UseMemoHook2 함수가 다시 호출이 됨
  • 그럼 location이라는 변수도 object를 다시 할당함

  • 하지만 object가 모양이 같더라도 메모리상의 주소가 다른 object이기 때문에 location이라는 변수는 또 생성된 object의 주소를 참조하게 됨

  • 해결방안 : UseMemoHook2 컴포넌트가 렌더링이 됐을 때 location 변수가 다시 초기화 되는 것을 막아주면 됨
    -> isKorea State가 바뀌었을 때만 초기화가 되게 해주기

  • useMemo를 사용하면 '비행기를 타자'를 누르면 useEffect가 실행되고(isKorea State가 바뀌었을 때 실행), '하루에 몇끼 먹나요?'를 증가시키면 useEffect가 실행되지 않음 -> 효과적으로 컴포넌트를 최적화 시킬 수 있음
profile
최강 개발자를 꿈꾸는 병아리

0개의 댓글