React.Memo, useMemo, useCallBack

psi·2024년 12월 19일

프론트엔드 개발 시, 성능최적화 부분에서 가장 많이 고려해야할 부분은 랜더링 최적화이다.
DOM 형성 및 reflow, repaint 과정을 최소화 하면서 리소스(비용) 절감하는건 프론트엔드 개발자로서 필수적인 소양이라 생각한다.

이번 글에서는 리액트에서 제공하는 React.memo, useMemo, useCallback을 활용해서 랜더링 최적화하는 방법에 대해 알아보자.

React.memo

react.memo는 고차컴포넌트(HOC, Higher Order Component)로
일반적인 컴포넌트를 최적화된 컴포넌트로 변신시켜준다 .

import { useState } from "react";
import ChildComponent from "./ChildComponent";

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

  console.log("부모 컴포넌트 랜더링");

  const addCount = () => {
    setCount(count + 1);
  };
  return (
    <>
      <p>클릭 확인용 : {count}</p>
      <div onClick={addCount}>버튼</div>
      <ChildComponent name={"홍길동"} age={25} />
    </>
  );
}

export default App;
import { useState, memo } from "react";

function ChildComponent({ name, age }) {
  console.log("자식 컴포넌트 랜더링");
  return (
    <>
      <div>
        <p>이름: {name}</p>
        <p>나이: {age}</p>
      </div>
    </>
  );
}

export default ChildComponent;

위와 같은 코드에서 App()의 버튼(div Tag)를 클릭하면


이처럼 자식 컴포넌트도 랜더링되는걸 볼 수 있다.
개발자 입장에서 아무런 변화도 없는 컴포넌트가 리랜더링되는건 두고볼 수 없다.
이때 활용할 수 있는 방법이 React.memo 다

import { useState, memo } from "react";

function ChildComponent({ name, age }) {
  console.log("자식 컴포넌트 랜더링");
  return (
    <>
      <div>
        <p>이름: {name}</p>
        <p>나이: {age}</p>
      </div>
    </>
  );
}

export default memo(ChildComponent);

자식 컴포넌트를 memo(ChildComponent) 즉, memo로 감싸준 후에 버튼을 눌러보면


부모컴포넌트가 랜더링되는것을 확인할 수 있다.

React.memo 로직
React.memo를 통해 감싼 component는 Props변화를 감지하여 랜더링된다.
React.memo는 부모 컴포넌트의 props가 변경되지 않는 한 자식 컴포넌트를 재렌더링하지 않는다.

React.memo 주의점

React.memo는 메모리에 할당되어 리소스를 차지하기 때문에 무분별한 사용은 오히려 리소스를 낭비할 수 있다.
1) 컴포넌트가 같은 props로 자주 랜더링 될때,
2) 컴포넌트가 랜더링될때마다 복잡한 로직을 처리해야된다면
이 두가지 경우를 고려하여 알맞게 활용해야한다.

useMemo, useCallback

이번에는 props를 content라는 변수명에 값을 담아 컴포넌트에 전달했다.

import { useState } from "react";
import ChildComponent from "./ChildComponent";

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

  console.log("부모 컴포넌트 랜더링");

  const content = {
    name: "홍길동",
    age: 25,
  };
  const addCount = () => {
    setCount(count + 1);
  };
  return (
    <>
      <p>클릭 확인용 : {count}</p>
      <div onClick={addCount}>버튼</div>
      <ChildComponent content={content} />
    </>
  );
}

export default App;


분명 자식컴포넌트를 React.memo를 통해 최적화된 컴포넌트를 만들었는데 다시 랜더링된다

그 이유는, 부모 컴포넌트가 랜더링되면서 content라는 변수명에
데이터는 일정하지만, 그 데이터를 담고 있는 메모리주소값은 달라지기 때문이다. 그렇기 때문에 React.memo입장에서는 props의 변화가 있다고 판단하여 자식 컴포넌트 또한 랜더링 하는것이다.

이를 해결하기 위해 useMemo를 사용할 수 있따.

  const content = useMemo(() => {
    return {
      name: "홍길동",
      age: 25,
    };
  }, []);

전달하는 Props를 useMemo 설정을 하면 자식컴포넌트는 랜더링되지않는다 !!

import { useState, useMemo, useCallback } from "react";
import ChildComponent from "./ChildComponent";

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

  console.log("부모 컴포넌트 랜더링");

  const content = useMemo(() => {
    return {
      name: "홍길동",
      age: 25,
    };
  }, []);

  const tellanything = () => {
    console.log("안녕!");
  };
  const addCount = () => {
    setCount(count + 1);
  };
  return (
    <>
      <p>클릭 확인용 : {count}</p>
      <div onClick={addCount}>버튼</div>
      <ChildComponent content={content} tellanything={tellanything} />
    </>
  );
}

export default App;
import { useState, memo } from "react";

function ChildComponent({ content, tellanything }) {
  console.log("자식 컴포넌트 랜더링");
  return (
    <>
      <div>
        <p>이름: {content.name}</p>
        <p>나이: {content.age}</p>
        <button onClick={tellanything}></button>
      </div>
    </>
  );
}

export default memo(ChildComponent);

이제는 변수가 아닌 함수를 넘겨주자
tellanything을 props로 넘겨주고 자식 컴포넌트에서 버튼을 클릭 시 이 함수를 실행시킨다

이번에도 분명 tellanything 함수는 변화가 없는데 자식까지 랜더링이 된다.
useMemo와 마찬가지로 함수의 경우 useCallback을 통해 최적화 할 수 있다 .

  const tellanything = useCallback(() => {
    console.log("안녕!");
  }, []);

이렇게 해당 함수를 useCallback을 통해 감싸주면
버튼을 누를 때, 자식 컴포넌트는 랜더링되지 않는다.

결론

React.memo, useMemo, useCallback의 간단한 동작 과정을 코드를 통해 알아봤다.

프론트엔드 개발자 입장에서 랜더링 최적화는 언제나 고민해야하는 과제이기 때문에 해당 기능을 적절히 활용하여 리소스 절감, 최적화된 프로젝트에 활용해보자

출처
React.memo

profile
사용자 경험을 최우선하며 논리적 문제 해결을 즐기는 개발자

0개의 댓글