본문 바로가기
부트캠프 개발일지 2023-2024/React 리액트

[6주차] 리액트숙련: React Hooks - 최적화(React.memo, useCallback, useMemo)

by whereanna00 2023. 11. 8.

리랜더링의 발생 조건

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

 

최적화

리액트에서 리렌더링이 빈번하게, 자주 일어난다는 것은 비용이 많이 발생한다는 것이다. 이를 줄이는 작업을 최적화(Optimization)라고 한다. 리액트에서 불필요한 렌더링이 발생하지 않도록 최적화하는 대표적인 방법은 아래와 같다.

  • memo(React.memo) : 컴포넌트를 캐싱 (임시저장소에 컴포넌트를 저장)
  • useCallback : 함수를 캐싱
  • useMemo : 값을 캐싱

 

React.memo 사용하기

컴포넌트 파일 상단에 적기

React.memo

 

 

React.memo 예제

리랜더링이 되지 않으면 하는 자식 컴포넌트 jsx 파일 하단에 React.memo()로 감싸주면 된다.

export default React.memo(Box1);

 

 

위와 같이 React.memo 를 사용하면,

컴포넌트를 메모리에 저장해두고 필요할 때 갖다 쓰게 된다.

이렇게 하면 부모 컴포넌트의 state의 변경으로 인해 props가 변경이 일어나지 않는 한 컴포넌트는 리렌더링 되지 않는다.

???무슨뜻??

=> 

메모사용 중 언제??? 그 값이 바뀌는가?

 

 

가정) 부모컴포넌트로부터 스테이트 값이 한 자식에게 프롭스로 전달되는 상황이면, 부모 스테이트가 바뀌면 자식은(메모제이션 되어있는) 다시 리랜더링이 시작된다. == 부모의 바뀐 스테이트를 자식과 공유하는 상황에는 메모제이션되어있던 자식도 리랜더링 된다

 

 

이것을 컴포넌트 memoization 이라고 한다.


useCallback

인자로 들어오는 함수 자체를 기억(메모이제이션)하는 것

 

자바스크립트에서 함수는 객체이기 떄문에, 저장될때 메모리에 직접적으로 저장되는 것이 아니라 별도의 공간에 저장에 저장되는데 그 별도의 공간을 바라보고 있는 주소값을 저장한다. 함수형 컴포넌트가 리랜더링되면서 함수들이 위쪽에서부터 다시 만들어질 때, 새로운 주소값을 저장하게 된다. 따라서 자식 컴포넌트 입장에서는 부모의 props가 바뀌었다고 인식한다.

 

그럼 프롭스로 함수를 넘겨주면 리액트입장에선 서로 다른 함수 2개로 인식하기때문에 자식안에 있는 넘겨진 함수가 리랜더링이 된다.

서로 주소값이 다르다는 뜻. 그럼 주소값을 새로 생성하지 않고 같은 걸로 공유하고 싶으면, 처음 부모컴포넌트에서의 함수 데이터 주소값을 기억하면 된다. 그 방법이 useCallBack

 

 

해결방법:

iniCount라는 함수를 별도 메모리 공간에 저장을 해놓고 특정 조건이 아닌 이상 아예 변경되지 않도록 막는다.

useCallback Hook 을 사용한다!

 

 

기존

  const initCount = () => {
    setCount(0);
  }

 

변경 useCallback() 로 감싸준다. 거기에 의존성 배열도 추가하기

  const initCount = useCallback(() => {
    setCount(0);
  }, [])

 

 

 


useMemo

 

React.memo : 컴포넌트 메모이제이션

useCallback: 함수 메모이제이션

useMemo: 함수의 리턴값 또는 value 그 자체 값의 메모이제이션

 

 

 

useMemo 사용방법

// as-is
const value = 반환할_함수();

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

 

 

useMemo 예시

app.jsx

import './App.css';
import HeavyComponent from './components/HeavyComponent';



// heavy work -> 엄청 무거운 작업이라고 가정

function App() {


  return (
    <div>
      <nav style={{
        backgroundColor: "yellow",
      }}>네비게이션 바</nav>
      <HeavyComponent />
      <footer style={{
        backgroundColor:"green",
      }}>푸터영역</footer>
    </div>
  );
}

export default App;

 

 

compoment 폴더 안, HeavyComponent.jsx

import React from 'react';
import {useState} from 'react';

function HeavyComponent() {

  const [count, setCount] = useState(0);
  const heavyWork = () => {
    for(let i = 0; i<1000000000; i++){}
    return 100;
  }

  const value = heavyWork();

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

export default HeavyComponent

 

=> 카운트 플러스 버튼을 누를때마다 반복문이 계속 새로 실행되면서 딜레이가 일어난다.

useMemo 적용하기

한번 반환된 값을 저장해놓고 변하지 않을때까지 쓰게 하기

import React from 'react';
import {useState} from 'react';
import {useMemo} from 'react';

function HeavyComponent() {

  const [count, setCount] = useState(0);
  const heavyWork = () => {
    for(let i = 0; i<1000000000; i++){}
    return 100;
  }

  const value = useMemo(()=> heavyWork(), [])
  console.log(`value는 ${value}입니다.`);

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

export default HeavyComponent

 

 

 

 

useMemo 주의할점

useMemo를 남발하게 되면 별도의 메모리 확보를 너무나 많이 하게 되기 때문에 오히려 성능이 악화될 수 있다. 필요할 때만 쓰도록!

728x90
반응형