React

[React] React.memo의 개념과 사용법

킹우현 2023. 2. 9. 06:00
const MyComponent = React.memo(function MyComponent(props) {
  /* props를 사용하여 렌더링 */
});
컴포넌트가 동일한 props로 동일한 결과를 렌더링해낸다면, React.memo를 호출하고 결과를 메모이징(Memoizing)하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있습니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다.

React.memo는 props 변화에만 영향을 줍니다. React.memo로 감싸진 함수 컴포넌트 구현에 useState, useReducer 또는useContext 훅을 사용한다면, 여전히 state나 context가 변할 때 다시 렌더링됩니다.

이 메서드는 오직 성능 최적화를 위하여 사용됩니다. 렌더링을 “방지”하기 위하여 사용하지 마세요. 버그를 만들 수 있습니다.
(출처 : React 공식문서)

 

앞서 Component에 대한 글을 작성한 적이 있었는데, Component라는 것은 결국 어떠한 인자(props)를 받고, React Element를 반환하는 '함수' 입니다.

 

이때, React Element를 반환하기 위해서 React.createElement를 사용할 수 있지만 더 간편하게 React Element를 생성하기 위해 JSX 문법을 사용하였다는 것도 다시 한번 리마인드해볼 수 있습니다.

(JSX는 컴파일되는 과정에서 다시 React.createElement라는 자바스크립트 코드로 변환됩니다)

 

그럼 이번에 다뤄볼 React.memo라는 컴포넌트는 무엇인지 한번 알아봅시다.

 

먼저 Button 이라는 이름을 가지는 컴포넌트를 생성해보겠습니다.

import React from "react";

const Button = ({ text, onClick }) => {
  console.log("Rerendered !");

  return (
    <button
      onClick={onClick}
      style={{
        backgroundColor: "tomato",
        border: "0",
        margin: "5px",
        padding: "5px 10px",
        borderRadius: "5px",
        color: "white",
      }}
    >
      {text}
    </button>
  );
};

export default Button;

 

위 Button 이라는 컴포넌트는 단순히 text와 onClick이라는 프로퍼티를 인자(props)로 받아서, 해당 text를 내용으로 가지고 onClick을 이벤트핸들러로 사용하는 버튼을 반환하는 컴포넌트입니다.

 

다음은 App 컴포넌트 입니다. 

import { memo, useState } from "react";
import Button from "./Button";

function App() {
  const [value, setValue] = useState("Original Value");
  const changeValue = () => {
    setValue("Changed Value");
  };

  return (
    <div>
      <Button text={value} onClick={changeValue} />
      <Button text="Second" />
    </div>
  );
}

export default App;

이 컴포넌트는 value라는 상태(state)와 상태를 변경해주는 setValue 함수를 가지는 컴포넌트입니다.(state에 관해서는 추후에 포스팅하도록 하겠읍니다 .. 😎)

 

또한 컴포넌트 내부에 Button이라는 컴포넌트를 2개 가지고 있는데, 각각 서로 다른 props를 전달해주고 있는 것을 확인할 수 있습니다.

 

React에서는 state가 변경될 때 마다 리렌더링(Re-render)이 발생하는데, 위 예제에서 만약에 첫번째 버튼을 클릭하면 어떤 일이 발생할까요?

맞습니다. 첫번째 Button은 onClick이라는 인자(props)를 받고 인자로 받은 onClick 함수를 클릭 이벤트의 이벤트 핸들러로 등록(클릭했을 경우 실행되도록 함)했기 때문에,

App 컴포넌트의 value라는 상태(state)를 변경시키고, 이는 리렌더링을 발생시키게 됩니다.

 

하지만 여기서 첫번째 Button은 props에 의하여 값이 변경되기 때문에 리렌더링이 필요하지만, 두번째 Button 또한 그럴까요?

 

두번째 Button은 props에 전달되는 값에 변화가 없기 때문에 리렌더링이 일어날 필요가 없습니다.

따라서 컴포넌트가 props로 동일한 결과를 렌더링하면 React.memo를 통해 결과를 기억, 즉 메모이징(Memoizaing)하여 추후에 다시 리렌더링이 일어나는 것을 방지할 수 있습니다 !

 

즉, React는 컴포넌트를 재렌더링하지 않고 마지막으로 렌더링된 결과를 재사용할 수 있습니다 !


한번 눈으로 확인해보면 좋을 것 같아 위와 같이 Button 컴포넌트에 console.log를 작성하고, 리렌더링이 얼마나 일어나는지 알아보도록 하겠습니다.

React.memo를 사용하지 않은 경우

 

먼저 처음으로 웹페이지가 렌더링 되었을 때, 두가지 Button 컴포넌트가 렌더링 되기 때문에 2번의 console.log가 찍힌 것을 알 수 있습니다.

 

이때, 첫번째 Button을 클릭하면 어떻게 될까요?

첫번째 Button에서 인자(props)로 전달받은 부모 컴포넌트(App)의 상태가 변경되었기 때문에 App 컴포넌트는 리렌더링될 것이고, 자식 컴포넌트인 두가지 Button 또한 리렌더링 되기 때문에 2번의 console.log가 찍힌 것을 확인할 수 있습니다.

 

React.memo를 사용한 경우

import { memo, useState } from "react";
import Button from "./Button";

const MemoBtn = memo(Button);

function App() {
  const [value, setValue] = useState("Original Value");
  const changeValue = () => {
    setValue("Changed Value");
  };

  return (
    <div>
      <MemoBtn text={value} onClick={changeValue} />
      <MemoBtn text="Second" />
    </div>
  );
}

export default App;

이번에는 MemoBtn 이라는 컴포넌트를 React.memo로 생성하여 작동시켜 보도록 하겠습니다. (const MemoBtn = memo(Button);)

 

처음으로 렌더링 되었을 경우에는 이전과 같은 상황으로 2번의 console.log가 찍히는 것을 확인할 수 있습니다.

 

하지만 첫번째 Button을 클릭했을 경우 ..

 

단 1번만 console.log가 찍히는 것을 확인할 수 있습니다. 이는 인자(props)의 변화에 의해 상태가 변경된 첫번째 Button만 리렌더링이 일어났고, 인자의 변화가 없는 두번째 Button은 리렌더링이 발생하지 않았기 때문입니다.

 

이상으로 React.memo에 관한 내용을 마치도록 하겠습니다 :)