[TIL #22] 231110_React Hooks 최적화 방법

Bora.K | 권보라·2023년 11월 10일
0

TIL

목록 보기
22/51
post-thumbnail

오늘 한 일


  • [알고리즘] Level.1 2문제
  • [내배캠] React 숙련 강의 9~12강

학습 내용


React Hooks - 최적화

부모 컴포넌트가 바뀌면 자식 컴포넌트도 리렌더링이 일어난다. 리액트에서 리렌더링이 자주 일어난다는 것은 cost가 많이 발생한다는 의미이다. 불필요한 렌더링을 최대한 줄이기 위한 방법이 바로 최적화(Optimization)이다.

1. React.memo

  • 컴포넌트 memoization
    React.memo를 통해 컴포넌트 자체를 메모리에 저장(캐싱)해두고 필요할 때 가져다 쓰는 것이다. 부모 컴포넌트의 state 변경으로 인해 props의 변경이 일어날 때에만 리렌더링이 된다.

  • 자식 컴포넌트를 export default할 때 React.memo를 사용한다.

export default React.memo(자식컴포넌트);

2. useCallback

  • 함수형 컴포넌트에서 인자로 들어오는 함수 자체를 memoization
    예를 들어 Box1.jsx에 있는 버튼을 누를 때 App.jsx에 있는 카운트가 초기화 되는 코드가 있다고 하면, App.jsx가 리렌더링되면 React.memo로 메모이제이션을 했더라도 Box1.jsx에 리렌더링이 발생하게 된다.

memoization이 되어있는데도 불구하고 리렌더링이 되는 이유는?

자바스크립트에서 함수도 객체의 한 종류이기 때문에 불변성이 없고, 같은 값이어도 별도의 공간에 저장하여 새로운 주소값을 바라보게 된다.

App 컴포넌트가 리렌더링될 때 initCount도 다시 만들어지고, initCount의 모양이 같더라도 주소값이 달라지므로, Box1.jsx는 props가 바뀌었다고 인지하여 Box1 컴포넌트도 리렌더링이 일어나게 된다.

  • useCallback(콜백함수, [dependency Array]) 사용

useCallback으로 함수를 memorization하여 특정 조건에서만 리렌더링이 되도록 할 수 있다. 이 때 의존성 배열(dependency array)에 count를 넣어주면, count가 변경될 때마다 새롭게 함수를 할당한다.

  • App.jsx
const [count, setCount] = useState(0);

// count를 초기화해주는 함수
const initCount = useCallback(() => {
  console.log(`${count}에서 0으로 변경되었습니다.`);
  setCount(0);
}, [count]);

// initCount 함수를 Box1에 props로 내려주는 부분

  return (
    <>
      <h3>카운트 예제입니다!</h3>
      <p>현재 카운트 : {count}</p>
      <button onClick={onPlusButtonClickHandler}>+</button>
      <button onClick={onMinusButtonClickHandler}>-</button>
      <div style={boxesStyle}>
        <Box1 initCount={initCount} />
        <Box2 />
        <Box3 />
      </div>
    </>
  );
}

...
  • Box1.jsx
...
// initCount 함수를 props로 받아옴
function Box1({ initCount }) {
  const onInitButtonClickHandler = () => {
    initCount();
  };

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

...

3. useMemo

  • 특정 value값을 memoization
    동일한 값을 반환하는 함수를 계속해서 호출해야하는 불필요한 렌더링을 방지하기 위해 그 값을 메모리에 저장한 후, 다시 함수를 호출하지 않고 값을 단순히 꺼내와서 사용할 수 있다. 이러한 기법을 ‘캐싱을 한다’고 표현한다.

  • useMemo(() ⇒ {return 반환할 함수()}, [dependency Array])
    dependency Array의 값이 변경될 때에만 반환할 함수가 호출된다.

  • const value = useMemo(() => heavyWork(), []);
    예를 들어 엄청 무거운 작업이 수행되는 heavyWork 함수가 있다면, value값을 세팅해 주고, useMemo()로 감싸주면 값이 바뀔 때에만 호출된다.

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

function HeavyButton() {
  const [count, setCount] = useState(0);

  const heavyWork = () => {
    for (let i = 0; i < 1000000000; i++) {}
    return 100;
  };

  const value = useMemo(() => heavyWork(), []);

  return (
    <>
      <p>나는 {value}을 가져오는 엄청 무거운 작업 컴포넌트야</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        누르면 아래 count가 올라가요!
      </button>
      <br />
      {count}
    </>
  );
}

export default HeavyButton;

useMemo를 남발하면 메모리를 너무 많이 확보하기 때문에 오히려 성능이 악화될 수 있으니 꼭 필요한 경우에만 사용하는 것이 좋다.


오늘의 회고


리액트 훅 최적화... 너무 어려운 개념이라서 강의를 두 번씩 들었다. 두 번째 들으니 첫 번째보다 좀 이해가 갔다. 몇 번 더 들으면 완전히 이해할 수 있을까?

오늘 과제 발제가 있었는데, 과제 내용만 봐도 너무 어려워 보였다. 시작조차 할 엄두가 안나는데, 과연 제출할 수 있을까? 우선 주말동안 리액트 숙련 강의를 모두 들어야겠다.

내일 할 일


  • [개인프로젝트] 그룹 아티스트 팬레터함 만들기
  • [내배캠] React 숙련 강의 13~23강
profile
Frontend Engineers

0개의 댓글