리액트 최적화의 세가지 방법 - React.memo, useCallback, useMemo

posinity·2023년 9월 3일
0

React

목록 보기
56/58
post-custom-banner

이 글은 팀스파르타 리액트 숙련 학습 자료 강의를 정리한 글입니다

1. 리렌더링의 발생 조건

  1. 컴포넌트의 state가 바뀌었을 때
  2. 컴포넌트가 내려받은 props가 변경되었을 때
  3. 부모 컴포넌트가 리렌더링 된 경우 자식 컴포넌트는 모두 리렌더링

렌더링이 빈번하게 일어나는 것은 좋지 않다 > cost, 비용이 많이 든다.

2. memo(React.memo)

컴포넌트를 메모리에 저장해두고(캐싱) 필요할 때 갖다 쓰는 것을 말한다.
이렇게 되면 부모 컴포넌트의 state 변경에 의해 props 변경이 일어나지 않는 이상,
컴포넌트는 리렌더링 되지 않는다.
이것을 컴포넌트의 memoization 이라고 한다.

즉, React.memo는 컴포넌트를 기억(memoization)하는 것이다!

사용방법

export default React.memo(Box1);

자식 컴포넌트 export에 React.memo를 붙혀주기만 하면 된다.

3. useCallback

useCallback은 함수를 기억하는 것이다!

만약 카운트를 초기화해주는 함수를 만들고, 그것을 props로 보냈다고 하자.

// count를 초기화해주는 함수
  const initCount = () => {
    setCount(0);
  };

  return (
    <>
      <h3>카운트 예제입니다!</h3>
      <p>현재 카운트 : {count}</p>
      <button onClick={onPlusButtonClickHandler}>+</button>
      <button onClick={onMinusButtonClickHandler}>-</button>
      <div style={boxesStyle}>
        <Box1 initCount={initCount} />
      </div>
    </>
  );
}
function Box1({ initCount }) {
  console.log("Box1이 렌더링되었습니다.");

  const onInitButtonClickHandler = () => {
    initCount();
  };

  return (
    <div style={boxStyle}>
      <button onClick={onInitButtonClickHandler}>초기화</button>
    </div>
  );
}

export default React.memo(Box1);

  • 버튼이나, - 버튼을 누를 때 그리고 초기화 버튼을 누를 때 모두 box1 컴포넌트가 리렌더링된다.

리액트 메모를 통해 메모이제이션을 했는데도 리렌더링이 되는 이유는,
App.jsx가 리렌더링 되면서
초기화하는 initCount 코드가 다시 만들어지면서,
onInitButtonClickHandler코드도 다시 만들어지기 때문이다.

자바스크립트에서는 함수도 객체의 한 종류기 때문에, 모양은 같더라도 다시 만들어지면 주소값이 달라지게 된다.

사용방법

// 변경 전
const initCount = () => {
  setCount(0);
};

// 변경 후
const initCount = useCallback(() => {
  setCount(0);
}, []);

이렇게 만들면 리렌더링이 되지 않는다.

특정 상황에서는 리렌더링이 되게 하기

initCount 함수를 조금 바꿔보자

const initCount = useCallback(() => {
  console.log(`${count}에서 0으로 변경되었습니다.`);
  setCount(0);
}, []);

count의 값을 출력해주는 기능을 추가한다고 하자.
이 기능을 실행해보면, 실제로는 count는 올라가든 내려가든 0으로밖에 보이지 않는다.
이렇게 나오는 이유는 useCallback이 count가 0일 때의 시점을 기준으로 메모리에 함수를 저장했기 때문이다.

이 경우에, [] 의존성 배열 안에 count를 넣으면, count가 바뀔 때마다 새롭게 함수를 할당하게 된다.

const initCount = useCallback(() => {
  console.log(`${count}에서 0으로 변경되었습니다.`);
  setCount(0);
}, [count]);

4. useMemo

useMemo는 함수가 리턴하는 '값' 그 자체를 기억하는 것이다

동일한 값을 반환하는 함수를 계속 호출해야 한다면, 필요없는 렌더링을 하는 것이다.

사용방법

const value = useMemo(()=> {
	return 반환할_함수()
}, [dependencyArray]);

dependencyArray 값이 변경될 때만 반환할 함수가 호출된다.
그렇지 않을 경우 값을 가져오기만 한다.

아래와 같은 코드가 있다.

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

function ObjectComponent() {
  const [isAlive, setIsAlive] = useState(true);
  const [uselessCount, setUselessCount] = useState(0);

  const me = {
    name: "Ted Chang",
    age: 21,
    isAlive: isAlive ? "생존" : "사망",
  };

  useEffect(() => {
    console.log("생존여부가 바뀔 때만 호출해주세요!");
  }, [me]);

  return (
    <>
      <div>
        내 이름은 {me.name}이구, 나이는 {me.age}!
      </div>
      <br />
      <div>
        <button
          onClick={() => {
            setIsAlive(!isAlive);
          }}
        >
          누르면 살았다가 죽었다가 해요
        </button>
        <br />
        생존여부 : {me.isAlive}
      </div>
      <hr />
      필요없는 숫자 영역이에요!
      <br />
      {uselessCount}
      <br />
      <button
        onClick={() => {
          setUselessCount(uselessCount + 1);
        }}
      >
        누르면 숫자가 올라가요
      </button>
    </>
  );
}

export default ObjectComponent;

useEffect hook을 이용해서 me의 정보가 바뀌었을 때만 발동되게끔 dependency array를 넣어놨는데요. 엉뚱하게도 count를 증가하는 button을 눌러보면 계속 log가 찍힌다.

그 이유는 state가 바뀌게 되면 리렌더링이 되고, 컴포넌트 함수가 새로 호출되며,
me 객체도 다시 할당되게 되는데 이 때 다른 메모리 주소값을 할당받게 되어
me가 달라졌다고 생각하기 때문이다.

// 이건 일치해요
const a = 1;
const b = 1;

console.log(a === b); // true

// 하지만 이건 달라요
const me = {
  name: "ted chang",
  age: 21,
};

const you = {
  name: "ted chang",
  age: 21,
};

console.log(me === you); // false

이런 현상을 해결하기 위해 useMemo를 사용할 수 있다.

const me = useMemo(() => {
  return {
    name: "Ted Chang",
    age: 21,
    isAlive: isAlive ? "생존" : "사망",
  };
}, [isAlive]);

5. 최적화 주의사항

최적화의 원리는 바뀌지 않는 것을 메모리에 저장하는 것이므로
최적화를 너무 남발하게 되면 별도의 메모리 확보를 너무 많이 하기 되기 때문에
오히려 성능이 악화될 수 있다. 필요할 때만 쓸것!!!

profile
문제를 해결하고 가치를 제공합니다
post-custom-banner

0개의 댓글